Implement input sequence and landed events
This commit is contained in:
parent
d89face881
commit
08d52a913f
@ -51,11 +51,11 @@ struct FFFInputCondition
|
||||
GENERATED_BODY()
|
||||
|
||||
// Buttons required for this specific condition to be valid
|
||||
UPROPERTY(EditAnywhere)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 RequiredButtons;
|
||||
|
||||
// The button state required for condition to be valid i.e. pressed or released
|
||||
UPROPERTY(EditAnywhere)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
EFFButtonState RequiredButtonState;
|
||||
|
||||
FFFInputCondition()
|
||||
@ -70,10 +70,10 @@ struct FFFInputSequence
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<FFFInputCondition> Sequence;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 MaxDuration;
|
||||
|
||||
FFFInputSequence()
|
||||
|
@ -32,6 +32,11 @@ void AFFPlayerController::FixedTick(float OneFrame)
|
||||
//SendInputsToRemote();
|
||||
}
|
||||
|
||||
bool AFFPlayerController::CheckInputSequence(const FFFInputSequence& InInputSequence)
|
||||
{
|
||||
return InputBuffer->CheckInputSequence(InInputSequence);
|
||||
}
|
||||
|
||||
|
||||
bool AFFPlayerController::CheckInputSequences(const TArray<FFFInputSequence>& InputSequences)
|
||||
{
|
||||
|
@ -52,6 +52,8 @@ public:
|
||||
// End of IFFSystemInterface
|
||||
|
||||
// IFFStateOwnerInterface
|
||||
virtual bool CheckInputSequence(const FFFInputSequence& InInputSequence) override;
|
||||
|
||||
virtual bool CheckInputSequences(const TArray<FFFInputSequence>& InputSequences) override;
|
||||
// End of IFFStateOwnerInterface
|
||||
|
||||
|
@ -10,6 +10,11 @@
|
||||
// UE includes
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
|
||||
void UFFState::Init()
|
||||
{
|
||||
OnInit();
|
||||
}
|
||||
|
||||
bool UFFState::CanTransition(const FFFStateContext& InStateContext)
|
||||
{
|
||||
/**
|
||||
@ -17,12 +22,11 @@ bool UFFState::CanTransition(const FFFStateContext& InStateContext)
|
||||
* if so then
|
||||
* Check if the avatar is in the correct stance to perform this action
|
||||
* 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 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
|
||||
*/
|
||||
IFFStateAvatarInterface* SAI = Cast<IFFStateAvatarInterface>(InStateContext.Avatar);
|
||||
@ -80,6 +84,30 @@ void UFFState::Exit(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);
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void UFFState::PostInitProperties()
|
||||
{
|
||||
UObject::PostInitProperties();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
UWorld* UFFState::GetWorld() const
|
||||
{
|
||||
UFFStateMachineComponent* SMC = Cast<UFFStateMachineComponent>(GetOuter());
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include "FFState.generated.h"
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FFFStateContext
|
||||
{
|
||||
@ -34,6 +33,22 @@ struct FFFStateContext
|
||||
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
|
||||
* and logic to run when the state is entered, exited, and active.
|
||||
@ -99,6 +114,17 @@ public:
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
||||
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
|
||||
* 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);
|
||||
|
||||
// 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,
|
||||
* usually due to a lack of player input.
|
||||
@ -146,6 +183,14 @@ public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
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
|
||||
*/
|
||||
@ -172,19 +217,23 @@ public:
|
||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
||||
void OnUpdate(float OneFrame, const FFFStateContext& InStateContext);
|
||||
|
||||
// TODO: document
|
||||
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")
|
||||
void OnHit(const FFFStateContext& InStateContext);
|
||||
|
||||
// TODO: document
|
||||
// TODO: pass in hitdata struct as well
|
||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
||||
void OnBlock(const FFFStateContext& InStateContext);
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category="UFF|State|Events")
|
||||
void OnInputEvent(const FFFStateContext& InStateContext);
|
||||
|
||||
// UObject interface
|
||||
virtual void PostInitProperties() override;
|
||||
|
||||
virtual UWorld* GetWorld() const override;
|
||||
// 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)
|
||||
{
|
||||
// CurrentState should never be null
|
||||
|
@ -101,6 +101,9 @@ public:
|
||||
*/
|
||||
FFFStateContext GetCurrentStateContext();
|
||||
|
||||
// Events
|
||||
virtual void Landed(const FHitResult& Hit);
|
||||
|
||||
// IFFSystemInterface interface
|
||||
virtual void FixedTick(float OneFrame) override;
|
||||
// End of IFFSystemInterface interface
|
||||
|
@ -21,5 +21,7 @@ class UNREALFIGHTINGFRAMEWORK_API IFFStateOwnerInterface
|
||||
GENERATED_BODY()
|
||||
|
||||
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