Start implementing state machine

This commit is contained in:
Kevin Poretti 2023-06-04 15:56:36 -04:00
parent d869d0105f
commit a42e4c6667
3 changed files with 219 additions and 16 deletions

View File

@ -2,8 +2,9 @@
#pragma once #pragma once
// UE includes
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "FEState.generated.h" #include "FEState.generated.h"
/** /**
@ -19,6 +20,9 @@ public:
/** /**
* Initializes pointers to what owns this state and what avatar this state represents. * 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); virtual void InitActorInfo(AActor* InOwner, AActor* InAvatar);

View File

@ -1,28 +1,135 @@
// Unreal Fighting Engine by Kevin Poretti // Unreal Fighting Engine by Kevin Poretti
// FE includes
#include "FEState.h"
#include "FEStateMachineComponent.h" #include "FEStateMachineComponent.h"
// Sets default values for this component's properties
UFEStateMachineComponent::UFEStateMachineComponent() UFEStateMachineComponent::UFEStateMachineComponent()
{ {
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features // Don't use Unreal's tick instead use a fixed tick
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bCanEverTick = false;
// ...
} }
// Called when the game starts void UFEStateMachineComponent::Initialize()
{
for(const TSubclassOf<UFEState>& CurrState : DefaultStates)
{
UFEState* TempState = AddState(CurrState);
if(!CurrentState) // first state to be created is the entry into this state machine
{
CurrentState = TempState;
CurrentState->Enter();
}
}
}
void UFEStateMachineComponent::InitActorInfo(AActor* InOwner, AActor* InAvatar)
{
Owner = InOwner;
Avatar = InAvatar;
}
UFEState* UFEStateMachineComponent::AddState(TSubclassOf<UFEState> StateClassToAdd)
{
UFEState* TempState = NewObject<UFEState>(this, StateClassToAdd);
if(TempState)
{
States.Add(TempState);
TempState->InitActorInfo(Owner, Avatar);
return TempState;
}
return nullptr;
}
void UFEStateMachineComponent::AddStates(const TArray<TSubclassOf<UFEState>>& StateClassesToAdd)
{
for(const TSubclassOf<UFEState>& CurrState : StateClassesToAdd)
{
AddState(CurrState);
}
}
void UFEStateMachineComponent::RemoveState(FName StateToRemove)
{
UE_LOG(LogTemp, Error, TEXT("UFEStateMachineComponent::RemoveState is not yet implemented"));
}
void UFEStateMachineComponent::SwitchStates(UFEState* NewState)
{
check(NewState);
CurrentState->Exit();
CurrentState = NewState;
CurrentState->Enter();
}
FName UFEStateMachineComponent::GetCurrentStateName() const
{
return CurrentState ? CurrentState->Name : NAME_None;
}
UFEState* UFEStateMachineComponent::FindStateWithName(FName StateName)
{
for (UFEState* CurrState : States)
{
if(CurrState ->Name == StateName)
{
return CurrState;
}
}
UE_LOG(LogTemp, Warning,
TEXT("Could not find state in state machine with name %s on %s"), *StateName.ToString(), *Owner->GetName());
return nullptr;
}
void UFEStateMachineComponent::BeginPlay() void UFEStateMachineComponent::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
// ... Initialize();
} }
void UFEStateMachineComponent::FixedTick(float OneFrame) void UFEStateMachineComponent::FixedTick(float OneFrame)
{ {
// Should we switch states?
for(UFEState* CurrState : States)
{
// Check if the state is enabled
// Check state entry conditions if there are any
// Check input conditions if there are any
// If all state entry conditions are good and at least one input condition is good then we can transition
// Lastly just check if the state we're about to transition into isn't the current state.
// It is OK to transition if state's "CanTransitionToSelf" is true
// SwitchStates(NewState);
// return;
}
// CurrentState should never be null
// TODO: Should probably assert or whatever UE's equivalent is
check(CurrentState);
// Tick current state
CurrentState->FixedTick(OneFrame);
// Debug
} }

View File

@ -2,25 +2,117 @@
#pragma once #pragma once
// FE includes
#include "UnrealFightingEngine/IFESystemInterface.h"
// UE includes
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Components/ActorComponent.h" #include "Components/ActorComponent.h"
#include "UnrealFightingEngine/IFESystemInterface.h"
#include "FEStateMachineComponent.generated.h" #include "FEStateMachineComponent.generated.h"
/**
* A state machine is a component that evaluates and controls the transitions for state objects that
* are a part of this state machine.
*
* This component also calls the appropriate state logic when a state is changed or the component ticks.
*/
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UNREALFIGHTINGENGINE_API UFEStateMachineComponent : public UActorComponent, public IIFESystemInterface class UNREALFIGHTINGENGINE_API UFEStateMachineComponent : public UActorComponent, public IIFESystemInterface
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
// Sets default values for this component's properties
UFEStateMachineComponent(); UFEStateMachineComponent();
protected: /**
// Called when the game starts * Creates and adds default states and enters the first state
virtual void BeginPlay() override; */
void Initialize();
public: /**
* Initializes pointers to what owns this state machine and what avatar this state represents.
*
* These pointers will also be passed to the states owned by this state machine.
*
* @param InOwner Actor that owns this state machine.
* @param InAvatar Actor that this state machine represents.
*/
virtual void InitActorInfo(AActor* InOwner, AActor* InAvatar);
/**
* Creates an instance of the state class and adds newly created state to this state machine.
*
* @param StateClassToAdd State class type to be added to this state machine
*
* @return A pointer to the state that was added or nullptr if there was an issue adding or creating the state
*/
UFEState* AddState(TSubclassOf<UFEState> StateClassToAdd);
/**
* 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
*/
void AddStates(const TArray<TSubclassOf<UFEState>>& StateClassesToAdd);
/**
* Destroys the state with corresponding name and removes it from this state machine.
*/
void RemoveState(FName StateToRemove);
/**
* Transitions from CurrentState to the new state passed to this function
*
* Triggers the Exit callback on the CurrentState and the Enter callback on the new state
*/
void SwitchStates(UFEState* NewState);
/**
* Returns the name of the current state
*/
UFUNCTION(BlueprintPure)
FName GetCurrentStateName() const;
// IIFESystemInterface interface
virtual void FixedTick(float OneFrame) override; virtual void FixedTick(float OneFrame) override;
// End of IIFESystemInterface interface
protected:
/**
* Actor that owns this state machine.
* This will typically be a player controller that possesses the avatar.
*/
UPROPERTY(BlueprintReadOnly)
AActor* Owner;
/**
* The avatar is an actor that this state machine represents.
* This will typically be a pawn or character the state machine is attached to.
*/
UPROPERTY(BlueprintReadOnly)
AActor* Avatar;
/**
* States classes to create and add to this state machine when the game starts
*/
UPROPERTY(EditDefaultsOnly, Category="UFE|State Machine")
TArray<TSubclassOf<UFEState>> DefaultStates;
/** Current active state for this state machine */
UPROPERTY(BlueprintReadOnly)
UFEState* CurrentState;
// States that have been added
UPROPERTY(BlueprintReadOnly)
TArray<UFEState*> States;
/**
* Returns the state with corresponding name
*/
UFEState* FindStateWithName(FName StateName);
// UActorComponent interface
virtual void BeginPlay() override;
// End of UActorComponent interface
}; };