Implement input sequence and landed events
This commit is contained in:
parent
d89face881
commit
08d52a913f
@ -51,11 +51,11 @@ struct FFFInputCondition
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
// Buttons required for this specific condition to be valid
|
// Buttons required for this specific condition to be valid
|
||||||
UPROPERTY(EditAnywhere)
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
int32 RequiredButtons;
|
int32 RequiredButtons;
|
||||||
|
|
||||||
// The button state required for condition to be valid i.e. pressed or released
|
// The button state required for condition to be valid i.e. pressed or released
|
||||||
UPROPERTY(EditAnywhere)
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
EFFButtonState RequiredButtonState;
|
EFFButtonState RequiredButtonState;
|
||||||
|
|
||||||
FFFInputCondition()
|
FFFInputCondition()
|
||||||
@ -70,10 +70,10 @@ struct FFFInputSequence
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere)
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
TArray<FFFInputCondition> Sequence;
|
TArray<FFFInputCondition> Sequence;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere)
|
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||||
int32 MaxDuration;
|
int32 MaxDuration;
|
||||||
|
|
||||||
FFFInputSequence()
|
FFFInputSequence()
|
||||||
|
@ -32,6 +32,11 @@ void AFFPlayerController::FixedTick(float OneFrame)
|
|||||||
//SendInputsToRemote();
|
//SendInputsToRemote();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AFFPlayerController::CheckInputSequence(const FFFInputSequence& InInputSequence)
|
||||||
|
{
|
||||||
|
return InputBuffer->CheckInputSequence(InInputSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool AFFPlayerController::CheckInputSequences(const TArray<FFFInputSequence>& InputSequences)
|
bool AFFPlayerController::CheckInputSequences(const TArray<FFFInputSequence>& InputSequences)
|
||||||
{
|
{
|
||||||
|
@ -52,6 +52,8 @@ public:
|
|||||||
// End of IFFSystemInterface
|
// End of IFFSystemInterface
|
||||||
|
|
||||||
// IFFStateOwnerInterface
|
// IFFStateOwnerInterface
|
||||||
|
virtual bool CheckInputSequence(const FFFInputSequence& InInputSequence) override;
|
||||||
|
|
||||||
virtual bool CheckInputSequences(const TArray<FFFInputSequence>& InputSequences) override;
|
virtual bool CheckInputSequences(const TArray<FFFInputSequence>& InputSequences) override;
|
||||||
// End of IFFStateOwnerInterface
|
// End of IFFStateOwnerInterface
|
||||||
|
|
||||||
|
@ -10,6 +10,11 @@
|
|||||||
// UE includes
|
// UE includes
|
||||||
#include "Components/SkeletalMeshComponent.h"
|
#include "Components/SkeletalMeshComponent.h"
|
||||||
|
|
||||||
|
void UFFState::Init()
|
||||||
|
{
|
||||||
|
OnInit();
|
||||||
|
}
|
||||||
|
|
||||||
bool UFFState::CanTransition(const FFFStateContext& InStateContext)
|
bool UFFState::CanTransition(const FFFStateContext& InStateContext)
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -17,12 +22,11 @@ bool UFFState::CanTransition(const FFFStateContext& InStateContext)
|
|||||||
* if so then
|
* if so then
|
||||||
* Check if the avatar is in the correct stance to perform this action
|
* Check if the avatar is in the correct stance to perform this action
|
||||||
* Check if the state is enabled
|
* Check if the state is enabled
|
||||||
* Check that all state entry conditions are met if there are any
|
|
||||||
*
|
*
|
||||||
* Check to see if the owner implements the StateOwnerInterface
|
* Check to see if the owner implements the StateOwnerInterface
|
||||||
* Check input 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
|
* If at least one input condition is good then we can transition
|
||||||
* so return true otherwise return false
|
* so return true otherwise return false
|
||||||
*/
|
*/
|
||||||
IFFStateAvatarInterface* SAI = Cast<IFFStateAvatarInterface>(InStateContext.Avatar);
|
IFFStateAvatarInterface* SAI = Cast<IFFStateAvatarInterface>(InStateContext.Avatar);
|
||||||
@ -80,6 +84,30 @@ void UFFState::Exit(const FFFStateContext& InStateContext)
|
|||||||
|
|
||||||
void UFFState::Update(float OneFrame, const FFFStateContext& InStateContext)
|
void UFFState::Update(float OneFrame, const FFFStateContext& InStateContext)
|
||||||
{
|
{
|
||||||
|
IFFStateOwnerInterface* SOI = Cast<IFFStateOwnerInterface>(InStateContext.Owner);
|
||||||
|
// TODO: maybe a check/asset is better because I can't think of a reason that not having the Owner implement
|
||||||
|
// IFFStateOwnerInterface is valid. Although there could be objects that have state that don't necessarily directly
|
||||||
|
// respond to input and don't have an Owner/controller responsible for them. Or they do have an owner but are associated
|
||||||
|
// with another Avatar like a character who is owned by a player/player controller.
|
||||||
|
if(!SOI)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error, TEXT("Owner of FFFStateContext does not implement IFFStateOwnerInterface"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(FFFInputEventHandler InputHandler : InputHandlers)
|
||||||
|
{
|
||||||
|
if(SOI->CheckInputSequence(InputHandler.RequiredSequence))
|
||||||
|
{
|
||||||
|
if(InputHandler.Delegate.ExecuteIfBound(InStateContext))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error,
|
||||||
|
TEXT("Trying to execute an input handler delegate on %s but it is not bound"),
|
||||||
|
*Name.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OnUpdate(OneFrame, InStateContext);
|
OnUpdate(OneFrame, InStateContext);
|
||||||
|
|
||||||
if(bStateHasDuration && InStateContext.Parent->GetTicksInState() >= StateDuration)
|
if(bStateHasDuration && InStateContext.Parent->GetTicksInState() >= StateDuration)
|
||||||
@ -89,18 +117,60 @@ void UFFState::Update(float OneFrame, const FFFStateContext& InStateContext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UFFState::Landed(const FHitResult& Hit, const FFFStateContext& InStateContext)
|
||||||
|
{
|
||||||
|
OnLanded(Hit, InStateContext);
|
||||||
|
|
||||||
|
// TODO: might want to also finish the state here by default and have the subclasses overwrite
|
||||||
|
// that behavior in the few cases we don't want to finish the state when we land
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UFFState::Hit(const FFFStateContext& InStateContext)
|
||||||
|
{
|
||||||
|
OnHit(InStateContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UFFState::Block(const FFFStateContext& InStateContext)
|
||||||
|
{
|
||||||
|
OnBlock(InStateContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void UFFState::Finish(const FFFStateContext& InStateContext)
|
void UFFState::Finish(const FFFStateContext& InStateContext)
|
||||||
{
|
{
|
||||||
InStateContext.Parent->GoToEntryState();
|
InStateContext.Parent->GoToEntryState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UFFState::RegisterInputHandler(const FFFInputSequence& InRequiredSequence, FFFInputEventDelegate InDelegate)
|
||||||
|
{
|
||||||
|
FFFInputEventHandler TempHandler;
|
||||||
|
TempHandler.RequiredSequence = InRequiredSequence;
|
||||||
|
TempHandler.Delegate = InDelegate;
|
||||||
|
InputHandlers.Add(TempHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UFFState::OnInit_Implementation()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool UFFState::OnCanTransition_Implementation(const FFFStateContext& InStateContext)
|
bool UFFState::OnCanTransition_Implementation(const FFFStateContext& InStateContext)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UFFState::PostInitProperties()
|
||||||
|
{
|
||||||
|
UObject::PostInitProperties();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
UWorld* UFFState::GetWorld() const
|
UWorld* UFFState::GetWorld() const
|
||||||
{
|
{
|
||||||
UFFStateMachineComponent* SMC = Cast<UFFStateMachineComponent>(GetOuter());
|
UFFStateMachineComponent* SMC = Cast<UFFStateMachineComponent>(GetOuter());
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
#include "FFState.generated.h"
|
#include "FFState.generated.h"
|
||||||
|
|
||||||
|
|
||||||
USTRUCT(BlueprintType)
|
USTRUCT(BlueprintType)
|
||||||
struct FFFStateContext
|
struct FFFStateContext
|
||||||
{
|
{
|
||||||
@ -34,6 +33,22 @@ struct FFFStateContext
|
|||||||
class UFFStateMachineComponent* Parent;
|
class UFFStateMachineComponent* Parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_DELEGATE_OneParam(FFFInputEventDelegate, const FFFStateContext&, InStateContext);
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FFFInputEventHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||||
|
FFFInputSequence RequiredSequence;
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||||
|
FFFInputEventDelegate Delegate;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A state is an object that provides rules and conditions for when a state can be transitioned into
|
* 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.
|
* and logic to run when the state is entered, exited, and active.
|
||||||
@ -99,6 +114,17 @@ public:
|
|||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
||||||
UAnimMontage* MontageToPlay;
|
UAnimMontage* MontageToPlay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event delegates to call when a certain input condition is detected in the Owner's input buffer
|
||||||
|
*/
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="UFF State Events")
|
||||||
|
TArray<FFFInputEventHandler> InputHandlers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when state is first created.
|
||||||
|
*/
|
||||||
|
virtual void Init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if Avatar's is in the correct stance AND
|
* Returns true if Avatar's is in the correct stance AND
|
||||||
* the state type is enabled (or the state can be hit or whiff cancelled from the current state) AND
|
* the state type is enabled (or the state can be hit or whiff cancelled from the current state) AND
|
||||||
@ -130,6 +156,17 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void Update(float OneFrame, const FFFStateContext& InStateContext);
|
virtual void Update(float OneFrame, const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
virtual void Landed(const FHitResult& Hit, const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
// TODO: pass in hitdata struct as well
|
||||||
|
virtual void Hit(const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
// TODO: pass in hitdata struct as well
|
||||||
|
virtual void Block(const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when you want to exit from this state but no eligible transitions exist to other states,
|
* Called when you want to exit from this state but no eligible transitions exist to other states,
|
||||||
* usually due to a lack of player input.
|
* usually due to a lack of player input.
|
||||||
@ -146,6 +183,14 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
virtual void Finish(const FFFStateContext& InStateContext);
|
virtual void Finish(const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
UFUNCTION(BlueprintCallable)
|
||||||
|
virtual void RegisterInputHandler(
|
||||||
|
const FFFInputSequence& InRequiredSequence, FFFInputEventDelegate InDelegate);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintNativeEvent, Category="UFF|State|Events")
|
||||||
|
void OnInit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blueprint hook for overriding the CanTransition logic
|
* Blueprint hook for overriding the CanTransition logic
|
||||||
*/
|
*/
|
||||||
@ -172,19 +217,23 @@ public:
|
|||||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
||||||
void OnUpdate(float OneFrame, const FFFStateContext& InStateContext);
|
void OnUpdate(float OneFrame, const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
||||||
void OnLanded(const FFFStateContext& InStateContext);
|
void OnLanded(const FHitResult& Hit, const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
// TODO: pass in hitdata struct as well
|
||||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
||||||
void OnHit(const FFFStateContext& InStateContext);
|
void OnHit(const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
|
// TODO: document
|
||||||
|
// TODO: pass in hitdata struct as well
|
||||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
||||||
void OnBlock(const FFFStateContext& InStateContext);
|
void OnBlock(const FFFStateContext& InStateContext);
|
||||||
|
|
||||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
|
||||||
void OnInputEvent(const FFFStateContext& InStateContext);
|
|
||||||
|
|
||||||
// UObject interface
|
// UObject interface
|
||||||
|
virtual void PostInitProperties() override;
|
||||||
|
|
||||||
virtual UWorld* GetWorld() const override;
|
virtual UWorld* GetWorld() const override;
|
||||||
// End of UObject interface
|
// End of UObject interface
|
||||||
};
|
};
|
||||||
|
@ -151,6 +151,14 @@ FFFStateContext UFFStateMachineComponent::GetCurrentStateContext()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UFFStateMachineComponent::Landed(const FHitResult& Hit)
|
||||||
|
{
|
||||||
|
check(CurrentState)
|
||||||
|
|
||||||
|
CurrentState->Landed(Hit, GetCurrentStateContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void UFFStateMachineComponent::FixedTick(float OneFrame)
|
void UFFStateMachineComponent::FixedTick(float OneFrame)
|
||||||
{
|
{
|
||||||
// CurrentState should never be null
|
// CurrentState should never be null
|
||||||
|
@ -101,6 +101,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
FFFStateContext GetCurrentStateContext();
|
FFFStateContext GetCurrentStateContext();
|
||||||
|
|
||||||
|
// Events
|
||||||
|
virtual void Landed(const FHitResult& Hit);
|
||||||
|
|
||||||
// IFFSystemInterface interface
|
// IFFSystemInterface interface
|
||||||
virtual void FixedTick(float OneFrame) override;
|
virtual void FixedTick(float OneFrame) override;
|
||||||
// End of IFFSystemInterface interface
|
// End of IFFSystemInterface interface
|
||||||
|
@ -21,5 +21,7 @@ class UNREALFIGHTINGFRAMEWORK_API IFFStateOwnerInterface
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual bool CheckInputSequences(const TArray<FFFInputSequence>& InputSequences) = 0;
|
virtual bool CheckInputSequence(const FFFInputSequence& InInputSequence) = 0;
|
||||||
|
|
||||||
|
virtual bool CheckInputSequences(const TArray<FFFInputSequence>& InInputSequences) = 0;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user