Options for when to stop an anim montage when the state ends
This commit is contained in:
parent
d9ec367b82
commit
d53fba996a
@ -69,16 +69,15 @@ void UFFState::Enter(const FFFStateContext& InStateContext)
|
||||
}
|
||||
|
||||
|
||||
void UFFState::Exit(const FFFStateContext& InStateContext)
|
||||
void UFFState::Exit(const FFFStateContext& InStateContext, EFFStateFinishReason StateFinishReason)
|
||||
{
|
||||
if(InStateContext.Avatar)
|
||||
|
||||
|
||||
if(InStateContext.Avatar &&
|
||||
(bStopMontageOnStateEnd && StateFinishReason == EFFStateFinishReason::SFT_DurationMetOrExceeded || StateFinishReason == EFFStateFinishReason::SFT_Interrupted) ||
|
||||
(bStopMontageOnMovementModeChange && StateFinishReason == EFFStateFinishReason::SFT_NotInReqMovementMode))
|
||||
{
|
||||
USkeletalMeshComponent* SMC = InStateContext.Avatar->GetComponentByClass<USkeletalMeshComponent>();
|
||||
if(SMC && MontageToPlay)
|
||||
{
|
||||
FAlphaBlendArgs BlendOutArgs = MontageToPlay->GetBlendOutArgs();
|
||||
SMC->GetAnimInstance()->Montage_Stop(BlendOutArgs.BlendTime, MontageToPlay);
|
||||
}
|
||||
StopCurrentAnimMontage(InStateContext);
|
||||
}
|
||||
|
||||
OnExit(InStateContext);
|
||||
@ -115,7 +114,7 @@ void UFFState::Update(float OneFrame, const FFFStateContext& InStateContext)
|
||||
|
||||
if(bStateHasDuration && InStateContext.Parent->GetTicksInState() >= StateDuration)
|
||||
{
|
||||
Finish(InStateContext);
|
||||
Finish(InStateContext, EFFStateFinishReason::SFT_DurationMetOrExceeded);
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +141,7 @@ void UFFState::MovementModeChanged(EMovementMode PrevMovementMode, uint8 Previou
|
||||
if((ReqMovementMode != EMovementMode::MOVE_None && NewMovementMode != ReqMovementMode) ||
|
||||
((ReqMovementMode == MOVE_Custom) && NewCustomMode != RequiredCustomMode))
|
||||
{
|
||||
Finish(InStateContext);
|
||||
Finish(InStateContext, EFFStateFinishReason::SFT_NotInReqMovementMode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,9 +158,15 @@ void UFFState::Block(const FFFStateContext& InStateContext)
|
||||
}
|
||||
|
||||
|
||||
void UFFState::Finish(const FFFStateContext& InStateContext)
|
||||
void UFFState::Finish(const FFFStateContext& InStateContext, EFFStateFinishReason StateFinishReason)
|
||||
{
|
||||
InStateContext.Parent->GoToEntryState();
|
||||
// TODO: I really don't like having to pass this state finish reason into this GoToEntryState
|
||||
// function, which then passes it to GoToState, which then passes it back to the state through
|
||||
// it's "Exit" function all so we can stop the current anim montage when we exit from a state if
|
||||
// the appropriate flags are set. I think having this state finish reason is good but I may want
|
||||
// to rethink the way we handle logic for ending a state and which class is in charge of handling
|
||||
// what
|
||||
InStateContext.Parent->GoToEntryState(StateFinishReason);
|
||||
}
|
||||
|
||||
|
||||
@ -175,6 +180,7 @@ void UFFState::RegisterInputHandler(const FFFInputSequence& InRequiredSequence,
|
||||
|
||||
void UFFState::PlayMontage(const FFFStateContext& InStateContext)
|
||||
{
|
||||
// TODO: think of a better way to handle optionally playing montages other than the one set as MontageToPlay
|
||||
OnPlayMontage(InStateContext);
|
||||
}
|
||||
|
||||
@ -189,7 +195,7 @@ bool UFFState::OnCanTransition_Implementation(const FFFStateContext& InStateCont
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// TODO: think of a better way to handle optionally playing montages other than the one set as MontageToPlay
|
||||
void UFFState::OnPlayMontage_Implementation(const FFFStateContext& InStateContext)
|
||||
{
|
||||
if(InStateContext.Avatar)
|
||||
@ -213,3 +219,22 @@ UWorld* UFFState::GetWorld() const
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void UFFState::StopCurrentAnimMontage(const FFFStateContext& InStateContext)
|
||||
{
|
||||
USkeletalMeshComponent* SMC = InStateContext.Avatar->GetComponentByClass<USkeletalMeshComponent>();
|
||||
if(SMC)
|
||||
{
|
||||
// TODO: Need a system for keeping track what anim montages were played during a state
|
||||
// so we can stop only those when the state ends (and if the bStopMontageOnStateEnd flag is set)
|
||||
// ALSO, this will be important for when we need to fast forward an animation when the game rollsback
|
||||
// and we have to resimulate to the current local tick
|
||||
// For now just stop all anim montages
|
||||
UAnimMontage* CurrAnim = SMC->GetAnimInstance()->GetCurrentActiveMontage();
|
||||
if(CurrAnim)
|
||||
{
|
||||
SMC->GetAnimInstance()->Montage_Stop(CurrAnim->BlendOut.GetBlendTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,17 @@ struct FFFInputEventHandler
|
||||
FFFInputEventDelegate Delegate;
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EFFStateFinishReason : uint8
|
||||
{
|
||||
// TODO: document
|
||||
SFT_Interrupted UMETA(DisplayName="Interrupted"),
|
||||
SFT_DurationMetOrExceeded UMETA(DisplayName="Duration Reached"),
|
||||
SFT_NotInReqMovementMode UMETA(DisplayName="Not In Required Movement Mode"),
|
||||
SFT_MAX UMETA(Hidden)
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -120,6 +131,12 @@ public:
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
||||
UAnimMontage* MontageToPlay;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
||||
bool bStopMontageOnStateEnd;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
||||
bool bStopMontageOnMovementModeChange;
|
||||
|
||||
/**
|
||||
* Event delegates to call when a certain input condition is detected in the Owner's input buffer
|
||||
*/
|
||||
@ -150,8 +167,10 @@ public:
|
||||
* Called whenever this state is transitioned out of into a new state.
|
||||
*
|
||||
* Calls appropriate Blueprint hooks.
|
||||
*
|
||||
* TODO: document StateFinishReason
|
||||
*/
|
||||
virtual void Exit(const FFFStateContext& InStateContext);
|
||||
virtual void Exit(const FFFStateContext& InStateContext, EFFStateFinishReason StateFinishReason);
|
||||
|
||||
/**
|
||||
* Called whenever this state is active and the game logic ticks.
|
||||
@ -191,7 +210,7 @@ public:
|
||||
* @param InStateContext
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable)
|
||||
virtual void Finish(const FFFStateContext& InStateContext);
|
||||
virtual void Finish(const FFFStateContext& InStateContext, EFFStateFinishReason StateFinishReason);
|
||||
|
||||
// TODO: document
|
||||
UFUNCTION(BlueprintCallable)
|
||||
@ -260,4 +279,7 @@ public:
|
||||
// UObject interface
|
||||
virtual UWorld* GetWorld() const override;
|
||||
// End of UObject interface
|
||||
|
||||
private:
|
||||
void StopCurrentAnimMontage(const FFFStateContext& InStateContext);
|
||||
};
|
||||
|
@ -89,16 +89,16 @@ void UFFStateMachineComponent::RemoveState(FName StateToRemove)
|
||||
UE_LOG(LogTemp, Error, TEXT("UFFStateMachineComponent::RemoveState is not yet implemented"));
|
||||
}
|
||||
|
||||
void UFFStateMachineComponent::GoToState(FName NewStateName)
|
||||
void UFFStateMachineComponent::GoToState(FName NewStateName, EFFStateFinishReason StateFinishReason)
|
||||
{
|
||||
UFFState* NewState = FindStateWithName(NewStateName);
|
||||
if(NewState)
|
||||
{
|
||||
GoToState(NewState);
|
||||
GoToState(NewState, StateFinishReason);
|
||||
}
|
||||
}
|
||||
|
||||
void UFFStateMachineComponent::GoToState(UFFState* NewState)
|
||||
void UFFStateMachineComponent::GoToState(UFFState* NewState, EFFStateFinishReason StateFinishReason)
|
||||
{
|
||||
check(CurrentState);
|
||||
check(NewState);
|
||||
@ -115,8 +115,7 @@ void UFFStateMachineComponent::GoToState(UFFState* NewState)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CurrentState->Exit(GetCurrentStateContext());
|
||||
CurrentState->Exit(GetCurrentStateContext(), StateFinishReason);
|
||||
TicksInState = 0;
|
||||
TickStateWasEntered = GetWorld()->GetGameState<AFFGameState>()->GetCurrentTick();
|
||||
CurrentSubStateLabel = NAME_None;
|
||||
@ -125,12 +124,12 @@ void UFFStateMachineComponent::GoToState(UFFState* NewState)
|
||||
CurrentState = NewState;
|
||||
}
|
||||
|
||||
void UFFStateMachineComponent::GoToEntryState()
|
||||
void UFFStateMachineComponent::GoToEntryState(EFFStateFinishReason StateFinishReason)
|
||||
{
|
||||
// can't have an entry state if there are no states
|
||||
check(States.Num() > 0);
|
||||
|
||||
GoToState(States[0]);
|
||||
GoToState(States[0], StateFinishReason);
|
||||
}
|
||||
|
||||
|
||||
@ -213,7 +212,7 @@ void UFFStateMachineComponent::FixedTick(float OneFrame)
|
||||
// It is OK to transition if state's "CanTransitionToSelf" is true
|
||||
if(StateToTransitionTo)
|
||||
{
|
||||
GoToState(StateToTransitionTo);
|
||||
GoToState(StateToTransitionTo, EFFStateFinishReason::SFT_Interrupted);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -72,20 +72,26 @@ public:
|
||||
* Transitions from CurrentState to the new state with the name passed to this function
|
||||
*
|
||||
* Triggers the Exit callback on the CurrentState and the Enter callback on the new state
|
||||
*
|
||||
* TODO: document StateFinishReason
|
||||
*/
|
||||
void GoToState(FName NewStateName);
|
||||
void GoToState(FName NewStateName, EFFStateFinishReason StateFinishReason);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* TODO: document StateFinishReason
|
||||
*/
|
||||
void GoToState(UFFState* NewState);
|
||||
void GoToState(UFFState* NewState, EFFStateFinishReason StateFinishReason);
|
||||
|
||||
/**
|
||||
* Transitions from CurrentState to the default entry state
|
||||
*
|
||||
* TODO: document StateFinishReason
|
||||
*/
|
||||
void GoToEntryState();
|
||||
void GoToEntryState(EFFStateFinishReason StateFinishReason);
|
||||
|
||||
UFUNCTION(BlueprintPure)
|
||||
FORCEINLINE int64 GetTicksInState() const { return TicksInState; }
|
||||
|
Loading…
Reference in New Issue
Block a user