u/Flashy_Head6065

The location of the character’s bindings in the code

Hello everyone

I’ve decided to tidy up the player’s code and I’m not sure where all the bindings should go—in the Character class or in the PlayerController class (for single-player)

I've got a very strange implementation at the moment:

//MainCharacter.h  
    ////////////
    ///Input
private:
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
    UInputAction* JumpAction;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
    UInputAction* MoveAction;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
    UInputAction* LookAction;

protected:
    TObjectPtr<AMainPlayerController> PC;

//MainCharacter.cpp
void AMainCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    
    if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
    {
       // Move
       EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMainCharacter::Move);
       EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Completed, this, &AMainCharacter::MoveCompleted);

       // Look
       EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMainCharacter::Look);
       EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Completed, this, &AMainCharacter::LookCompleted);

       // Jump
       EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &AMainCharacter::Jump);
       EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &AMainCharacter::StopJumping);
    }    
}

void AMainCharacter::PossessedBy(AController* NewController)
{
    Super::PossessedBy(NewController);

    PC = Cast<AMainPlayerController>(NewController);
}

void AMainCharacter::UnPossessed()
{
    PC = nullptr;
    
    Super::UnPossessed();
}

//MainPlayerController.cpp
void AMainPlayerController::BeginPlay()
{
    Super::BeginPlay();

    if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
    {
       for (TObjectPtr<UInputMappingContext>& MappingContext: DefaultMappingContexts)
       {
          Subsystem->AddMappingContext(MappingContext, 0);
       }
    }
}

I'm completely confused😐

So I’d like to know how to organise everything properly and where, for example, I can create binds for my character’s abilities?

Thx.

reddit.com
u/Flashy_Head6065 — 4 days ago

The question of achieving complete independence of systems

I'm interested in what approach would be best for achieving system independence.

For example, let's take my recent need to retrieve a player's level for the stats system. I store the player's level in my level system.

The solution that came to mind was to use an interface.
Just create a function to retrieve the level and a delegate to subscribe to level updates.

//LevelProvider.h

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLevelChanged, int32, NewLevel);
class PROJECT_API ILevelProvider
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
    int32 GetLevel() const;

    virtual FOnLevelChanged& OnLevelChanged() = 0;
};

//LevelComponent.h

class ODWIRKSRPG_API ULevelComponent: public UActorComponent, public ILevelProvider
{
    GENERATED_BODY()

public: 
    ULevelComponent();

    virtual int32 GetLevel_Implementation() const override;
    virtual FOnLevelChanged& OnLevelChanged() override { return LevelChangedEvent; }

private:
    FOnLevelChanged LevelChangedEvent;
    
    int32 Level;
}

//LevelComponent.cpp

int32 ULevelComponent::GetLevel_Implementation() { return Level; }

void ULevelComponent::LevelUp()
{
  LevelChangedEvent.Broadcast(Level);
}

//StatsComponent.h

class ODWIRKSRPG_API UStatsComponent : public UActorComponent
{
    GENERATED_BODY()

public:
    UStatsComponent();
    
    //
    //...code..
    //

private:
    TScriptInterface<class ILevelProvider> LevelProvider;

    UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category="StatsSystem",meta=(AllowPrivateAccess))
    int32 AvailablePoints = 0;
}

//StatsComponent.cpp

void UStatsComponent::BeginPlay()
{
    //
    //...code..
    //

    AActor* Owner = GetOwner();
    if (Owner && Owner->Implements<ULevelProvider>())
    {
        LevelProvider = TScriptInterface<ILevelProvider>(Owner);
    }

    LevelProvider->OnLevelChanged().AddDynamic(this, &UStatsComponent::OnOwnerLevelChanged);
    AvailablePoints += LevelProvider->GetLevel();
}

It all seems to be working fine, but if I want to retrieve additional data from other systems, do I need to create new interfaces?

Are there any better ways to do this?

Thanks.

reddit.com
u/Flashy_Head6065 — 15 days ago