Download

Component communication with controller

Hi Guys

The context here is, an UActorComponent that controls spell cast and need to ask the player controller to select a target and the PC calls back later with what the player has selected.
At the moment we’re using delegates and some sorta of function callbacks, I’m wondering if someone knows a more elegant/better way to implement this in C++.
Any help is welcome, thank you!

This is the delegate code used by the component, it creates a delegate where the player controller can bind to it, and another function where the player controller can callback.


    
/** When the cast gives time to the user select a target, the cast is put on hold while wait for an answer from the player controller  */
    DECLARE_DELEGATE_OneParam(FRequestDataASync_TargetActor, int32);

UCLASS(ClassGroup = Common, BlueprintType, HideCategories = (Sockets, Collision, Variable, Tags))
class USpellCastComponent : public UActorComponent
{

    GENERATED_UCLASS_BODY()

/** Create the delegates used by the component */    
public:
    FRequestDataASync_TargetActor& GetDelegateTargetActorASync() { return TargetActorASyncDelegate; };
private:
    FRequestDataASync_TargetActor TargetActorASyncDelegate;

public:

    /** Controller calls back here when it has a target to pass */
        void TargetActorAcquired(int32 CastID, TWeakObjectPtr<AActor> Actor);
}


Atm we’re binding the request target delegate inside the begin play of the player character:



void AMaster_PlayerCharacter::BeginPlay()
{
    Super::BeginPlay();

    // Get the player controller and bind target delegates
    AController* Controller = GetController();
    AMaster_PlayerController* Master_PlayerController = Cast<AMaster_PlayerController>(Controller);

    // Bind it
    SpellCastComponent->GetDelegateTargetActorASync().BindUObject(Master_PlayerController, &AMaster_PlayerController::SendCursorToSelectTargetActor);
}


And here the controller calls back to the cast component when the player has selected a target:



// Attempt to get an actor under the cursor
FHitResult HitResult(ForceInit);
GetHitResultUnderCursorByChannel(UEngineTypes::ConvertToTraceType(ECC_Camera), true, HitResult);
TWeakObjectPtr<AActor> SelectedActor = HitResult.Actor;

// Send back to cast component the Actor
ACharacter* Character = GetCharacter();
AMaster_PlayerCharacter* MyCharacter = Cast<AMaster_PlayerCharacter>(Character);
MyCharacter->SpellCastComponent->TargetActorAcquired(CurrentTargetCastID, SelectedActor);



I would avoid binding a delegate to the controller in BeginPlay as it might not necessarily be set yet. A safer place would be for example in PossessedBy, don’t forget to unbind the delegate either when the character is unpossessed though (might lead to unexpected behavior)! However, depending on your game design I would try to make the interface between the character and the controller more abstracted and not mixing up the character, its components and the player controller to that level. I would rather have the component tell the controller (maybe over the character) that a target is required, when the controller selected a target it calls a generic “OnTargetActorSelected” function on the character which passes it on to its components. If the target selection is very specific to one character or a spell then maybe it makes more sense to implement the target selection on the character or component.

saw the problem with begin play right after testing in a dedicated server environment (eheheh), moving it to possessed solved, thanks for the tip.
I’m avoiding the character to make the component as generic as possible, I wish to use it in future projects without too much copy paste code.