Start of state system rework

This commit is contained in:
Kevin Poretti 2023-07-15 21:36:46 -04:00
parent 85f0cbf804
commit 4cf3834b2d
6 changed files with 172 additions and 89 deletions

View File

@ -5,51 +5,60 @@
// FF includes // FF includes
#include "FFStateMachineComponent.h" #include "FFStateMachineComponent.h"
void UFFState::InitActorInfo(AActor* InOwner, AActor* InAvatar)
void UFFStateBehavior::Enter(const FFFStateContext& InStateContext)
{ {
Owner = InOwner; OnEnter(InStateContext);
Avatar = InAvatar;
} }
void UFFState::Enter() void UFFStateBehavior::Exit(const FFFStateContext& InStateContext)
{ {
TicksInState = 0; OnExit(InStateContext);
OnEnter();
} }
void UFFState::Exit() void UFFStateBehavior::Update(float OneFrame, const FFFStateContext& InStateContext)
{ {
OnExit(); OnUpdate(OneFrame, InStateContext);
} }
void UFFStateBehavior::OnLanded_Implementation(const FFFStateContext& InStateContext)
void UFFState::Update(float OneFrame)
{
TicksInState++;
OnUpdate(OneFrame);
}
void UFFState::OnEnter_Implementation()
{ {
} }
void UFFState::OnExit_Implementation() void UFFStateBehavior::OnEnter_Implementation(const FFFStateContext& InStateContext)
{ {
} }
void UFFState::OnUpdate_Implementation(float OneFrame) void UFFStateBehavior::OnExit_Implementation(const FFFStateContext& InStateContext)
{ {
} }
UWorld* UFFState::GetWorld() const void UFFStateBehavior::OnUpdate_Implementation(float OneFrame, const FFFStateContext& InStateContext)
{
}
void UFFStateBehavior::OnHit_Implementation(const FFFStateContext& InStateContext)
{
}
void UFFStateBehavior::OnBlock_Implementation(const FFFStateContext& InStateContext)
{
}
void UFFStateBehavior::OnInputEvent_Implementation(const FFFStateContext& InStateContext)
{
}
UWorld* UFFStateBehavior::GetWorld() const
{ {
UFFStateMachineComponent* SMC = Cast<UFFStateMachineComponent>(GetOuter()); UFFStateMachineComponent* SMC = Cast<UFFStateMachineComponent>(GetOuter());
if(SMC) if(SMC)

View File

@ -7,25 +7,11 @@
#include "FFState.generated.h" #include "FFState.generated.h"
/** USTRUCT(BlueprintType)
* A state is an object that provides rules and conditions for when a state can be transitioned into struct FFFStateData
* and logic to run when the state is entered, exited, and active.
*/
UCLASS()
class UNREALFIGHTINGFRAMEWORK_API UFFState : public UObject
{ {
GENERATED_BODY() GENERATED_BODY()
public:
/**
* Initializes pointers to what owns this state and what avatar this state represents.
*
* @param InOwner Actor that owns this state machine.
* @param InAvatar Actor that this state machine represents.
*/
virtual void InitActorInfo(AActor* InOwner, AActor* InAvatar);
/** Name of this state */ /** Name of this state */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="UFF|State") UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="UFF|State")
FName Name; FName Name;
@ -38,21 +24,56 @@ public:
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
uint8 StateType; uint8 StateType;
/** How many ticks have elapsed since this state was entered */ };
UPROPERTY(BlueprintReadOnly, Category="UFF|State")
int32 TicksInState;
USTRUCT(BlueprintType)
struct FFFStateContext
{
GENERATED_BODY()
/** Actor that owns the avatar. Typically a player controller. */
AActor* Owner;
/**
* Actor that represents the player's avatar or an object associated with the player's avatar.
* This is typically a character or a weapon.
*/
AActor* Avatar;
/**
* Data associated with this state.
* For example this can be new movement values or data about the hitboxes if this state represents an attack.
*/
FFFStateData StateData;
};
/**
* A state is an object that provides rules and conditions for when a state can be transitioned into
* and logic to run when the state is entered, exited, and active.
*/
UCLASS()
class UNREALFIGHTINGFRAMEWORK_API UFFStateBehavior : public UObject
{
GENERATED_BODY()
public:
/** Name of this state behavior */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="UFF|State")
FName Name;
// TODO: since state's are now purely behavioral can we remove these function calls?
// They are basically redundant with OnEnter, OnUpdate, OnExit, etc.
/** /**
* Called whenever this state is transitioned into. * Called whenever this state is transitioned into.
* *
* Resets TicksInState and calls appropriate Blueprint hooks * Resets TicksInState and calls appropriate Blueprint hooks
*/ */
void Enter(); void Enter(const FFFStateContext& InStateContext);
/** /**
* Called whenever this state is transitioned out of into a new state. * Called whenever this state is transitioned out of into a new state.
*/ */
void Exit(); void Exit(const FFFStateContext& InStateContext);
/** /**
* Called whenever this state is active and the game logic ticks. * Called whenever this state is active and the game logic ticks.
@ -61,19 +82,19 @@ public:
* *
* @param OneFrame the time that elapses during one fixed tick * @param OneFrame the time that elapses during one fixed tick
*/ */
void Update(float OneFrame); void Update(float OneFrame, const FFFStateContext& InStateContext);
/** /**
* Blueprint hook that is called whenever this state is transitioned into * Blueprint hook that is called whenever this state is transitioned into
*/ */
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events") UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
void OnEnter(); void OnEnter(const FFFStateContext& InStateContext);
/** /**
* Blueprint hook that is called whenever this state is transitioned out of into a new state * Blueprint hook that is called whenever this state is transitioned out of into a new state
*/ */
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events") UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
void OnExit(); void OnExit(const FFFStateContext& InStateContext);
/** /**
* Blueprint hook that is called whenever this state is active and the game logic ticks * Blueprint hook that is called whenever this state is active and the game logic ticks
@ -81,25 +102,21 @@ public:
* @param OneFrame the time that elapses during one fixed tick * @param OneFrame the time that elapses during one fixed tick
*/ */
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events") UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
void OnUpdate(float OneFrame); void OnUpdate(float OneFrame, const FFFStateContext& InStateContext);
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
void OnLanded(const FFFStateContext& InStateContext);
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
void OnHit(const FFFStateContext& InStateContext);
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
void OnBlock(const FFFStateContext& InStateContext);
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
void OnInputEvent(const FFFStateContext& InStateContext);
// UObject interface // UObject interface
virtual UWorld* GetWorld() const override; virtual UWorld* GetWorld() const override;
// End of UObject interface // End of UObject interface
protected:
/**
* Actor that owns this state.
* This will typically be a player controller that possesses the avatar.
*/
UPROPERTY(BlueprintReadOnly)
AActor* Owner;
/**
* The avatar is an actor that this state represents.
* This will typically be a pawn or character.
*/
UPROPERTY(BlueprintReadOnly)
AActor* Avatar;
}; };

View File

@ -0,0 +1,6 @@
// Unreal Fighting Framework by Kevin Poretti
#include "State/FFStateContextInterface.h"
// Add default functionality here for any IFFStateContextInterface functions that are not pure virtual.

View File

@ -0,0 +1,31 @@
// Unreal Fighting Framework by Kevin Poretti
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "FFStateContextInterface.generated.h"
UINTERFACE(MinimalAPI)
class UFFStateContextInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class UNREALFIGHTINGFRAMEWORK_API IFFStateContextInterface
{
GENERATED_BODY()
public:
virtual bool CheckStateEnabled(uint8 StateType) = 0;
virtual bool CheckStance(uint8 Stance) = 0;
virtual bool CheckStateEntryConditions(const TArray<uint8>& EntryConditions) = 0;
virtual bool CheckInputSequences(const TArray<FFFInputSequence>& InputSequences) = 0;
};

View File

@ -14,13 +14,13 @@ UFFStateMachineComponent::UFFStateMachineComponent()
void UFFStateMachineComponent::Initialize() void UFFStateMachineComponent::Initialize()
{ {
for(const TSubclassOf<UFFState>& CurrState : DefaultStates) for(const TSubclassOf<UFFStateBehavior>& CurrState : DefaultStates)
{ {
UFFState* TempState = AddState(CurrState); UFFStateBehavior* TempState = AddState(CurrState);
if(!CurrentState) // first state to be created is the entry into this state machine if(!CurrentState) // first state to be created is the entry into this state machine
{ {
CurrentState = TempState; CurrentState = TempState;
CurrentState->Enter(); CurrentState->Enter(GetCurrentStateContext());
} }
} }
} }
@ -33,13 +33,12 @@ void UFFStateMachineComponent::InitActorInfo(AActor* InOwner, AActor* InAvatar)
} }
UFFState* UFFStateMachineComponent::AddState(TSubclassOf<UFFState> StateClassToAdd) UFFStateBehavior* UFFStateMachineComponent::AddState(TSubclassOf<UFFStateBehavior> StateClassToAdd)
{ {
UFFState* TempState = NewObject<UFFState>(this, StateClassToAdd); UFFStateBehavior* TempState = NewObject<UFFStateBehavior>(this, StateClassToAdd);
if(TempState) if(TempState)
{ {
States.Add(TempState); States.Add(TempState);
TempState->InitActorInfo(Owner, Avatar);
return TempState; return TempState;
} }
@ -47,9 +46,9 @@ UFFState* UFFStateMachineComponent::AddState(TSubclassOf<UFFState> StateClassToA
} }
void UFFStateMachineComponent::AddStates(const TArray<TSubclassOf<UFFState>>& StateClassesToAdd) void UFFStateMachineComponent::AddStates(const TArray<TSubclassOf<UFFStateBehavior>>& StateClassesToAdd)
{ {
for(const TSubclassOf<UFFState>& CurrState : StateClassesToAdd) for(const TSubclassOf<UFFStateBehavior>& CurrState : StateClassesToAdd)
{ {
AddState(CurrState); AddState(CurrState);
} }
@ -61,13 +60,15 @@ void UFFStateMachineComponent::RemoveState(FName StateToRemove)
UE_LOG(LogTemp, Error, TEXT("UFFStateMachineComponent::RemoveState is not yet implemented")); UE_LOG(LogTemp, Error, TEXT("UFFStateMachineComponent::RemoveState is not yet implemented"));
} }
void UFFStateMachineComponent::SwitchStates(UFFState* NewState) void UFFStateMachineComponent::SwitchStates(UFFStateBehavior* NewState)
{ {
check(NewState); check(NewState);
CurrentState->Exit();
CurrentState->Exit(GetCurrentStateContext());
CurrentState = NewState; CurrentState = NewState;
CurrentState->Enter(); CurrentState->Enter(GetCurrentStateContext());
TicksInState = 0;
} }
@ -77,9 +78,18 @@ FName UFFStateMachineComponent::GetCurrentStateName() const
} }
UFFState* UFFStateMachineComponent::FindStateWithName(FName StateName) FFFStateContext UFFStateMachineComponent::GetCurrentStateContext() const
{ {
for (UFFState* CurrState : States) FFFStateContext CurrStateContext;
CurrStateContext.Owner = Owner;
CurrStateContext.Avatar = Avatar;
return CurrStateContext;
}
UFFStateBehavior* UFFStateMachineComponent::FindStateWithName(FName StateName)
{
for (UFFStateBehavior* CurrState : States)
{ {
if(CurrState->Name == StateName) if(CurrState->Name == StateName)
{ {
@ -106,7 +116,7 @@ void UFFStateMachineComponent::FixedTick(float OneFrame)
{ {
// Should we switch states? // Should we switch states?
for(UFFState* CurrState : States) for(UFFStateBehavior* CurrState : States)
{ {
// Check if the state is enabled // Check if the state is enabled
@ -128,7 +138,8 @@ void UFFStateMachineComponent::FixedTick(float OneFrame)
check(CurrentState); check(CurrentState);
// Tick current state // Tick current state
CurrentState->Update(OneFrame); TicksInState++;
CurrentState->Update(OneFrame, GetCurrentStateContext());
// Debug // Debug
} }

View File

@ -47,14 +47,14 @@ public:
* *
* @return A pointer to the state that was added or nullptr if there was an issue adding or creating the state * @return A pointer to the state that was added or nullptr if there was an issue adding or creating the state
*/ */
UFFState* AddState(TSubclassOf<UFFState> StateClassToAdd); UFFStateBehavior* AddState(TSubclassOf<UFFStateBehavior> StateClassToAdd);
/** /**
* Creates an instance of the state classes and adds newly created states to this state machine. * Creates an instance of the state classes and adds newly created states to this state machine.
* *
* @param StateClassesToAdd Array of state class types to be added to this state machine * @param StateClassesToAdd Array of state class types to be added to this state machine
*/ */
void AddStates(const TArray<TSubclassOf<UFFState>>& StateClassesToAdd); void AddStates(const TArray<TSubclassOf<UFFStateBehavior>>& StateClassesToAdd);
/** /**
* Destroys the state with corresponding name and removes it from this state machine. * Destroys the state with corresponding name and removes it from this state machine.
@ -66,7 +66,7 @@ public:
* *
* Triggers the Exit callback on the CurrentState and the Enter callback on the new state * Triggers the Exit callback on the CurrentState and the Enter callback on the new state
*/ */
void SwitchStates(UFFState* NewState); void SwitchStates(UFFStateBehavior* NewState);
/** /**
* Returns the name of the current state * Returns the name of the current state
@ -74,6 +74,11 @@ public:
UFUNCTION(BlueprintPure) UFUNCTION(BlueprintPure)
FName GetCurrentStateName() const; FName GetCurrentStateName() const;
/**
*
*/
FFFStateContext GetCurrentStateContext() const;
// IFFSystemInterface interface // IFFSystemInterface interface
virtual void FixedTick(float OneFrame) override; virtual void FixedTick(float OneFrame) override;
// End of IFFSystemInterface interface // End of IFFSystemInterface interface
@ -93,24 +98,28 @@ protected:
UPROPERTY(BlueprintReadOnly) UPROPERTY(BlueprintReadOnly)
AActor* Avatar; AActor* Avatar;
/** How many ticks have elapsed since the currently active state was entered */
UPROPERTY(BlueprintReadOnly, Category="UFF|State")
int32 TicksInState;
/** /**
* States classes to create and add to this state machine when the game starts * States classes to create and add to this state machine when the game starts
*/ */
UPROPERTY(EditDefaultsOnly, Category="UFF|State Machine") UPROPERTY(EditDefaultsOnly, Category="UFF|State Machine")
TArray<TSubclassOf<UFFState>> DefaultStates; TArray<TSubclassOf<UFFStateBehavior>> DefaultStates;
/** Current active state for this state machine */ /** Current active state for this state machine */
UPROPERTY(BlueprintReadOnly) UPROPERTY(BlueprintReadOnly)
UFFState* CurrentState; UFFStateBehavior* CurrentState;
// States that have been added // States that have been added
UPROPERTY(BlueprintReadOnly) UPROPERTY(BlueprintReadOnly)
TArray<UFFState*> States; TArray<UFFStateBehavior*> States;
/** /**
* Returns the state with corresponding name * Returns the state with corresponding name
*/ */
UFFState* FindStateWithName(FName StateName); UFFStateBehavior* FindStateWithName(FName StateName);
// UActorComponent interface // UActorComponent interface
virtual void BeginPlay() override; virtual void BeginPlay() override;