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>();
|
StopCurrentAnimMontage(InStateContext);
|
||||||
if(SMC && MontageToPlay)
|
|
||||||
{
|
|
||||||
FAlphaBlendArgs BlendOutArgs = MontageToPlay->GetBlendOutArgs();
|
|
||||||
SMC->GetAnimInstance()->Montage_Stop(BlendOutArgs.BlendTime, MontageToPlay);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OnExit(InStateContext);
|
OnExit(InStateContext);
|
||||||
@ -115,7 +114,7 @@ void UFFState::Update(float OneFrame, const FFFStateContext& InStateContext)
|
|||||||
|
|
||||||
if(bStateHasDuration && InStateContext.Parent->GetTicksInState() >= StateDuration)
|
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) ||
|
if((ReqMovementMode != EMovementMode::MOVE_None && NewMovementMode != ReqMovementMode) ||
|
||||||
((ReqMovementMode == MOVE_Custom) && NewCustomMode != RequiredCustomMode))
|
((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)
|
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);
|
OnPlayMontage(InStateContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +195,7 @@ bool UFFState::OnCanTransition_Implementation(const FFFStateContext& InStateCont
|
|||||||
return true;
|
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)
|
void UFFState::OnPlayMontage_Implementation(const FFFStateContext& InStateContext)
|
||||||
{
|
{
|
||||||
if(InStateContext.Avatar)
|
if(InStateContext.Avatar)
|
||||||
@ -213,3 +219,22 @@ UWorld* UFFState::GetWorld() const
|
|||||||
|
|
||||||
return nullptr;
|
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;
|
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
|
* 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.
|
||||||
@ -120,6 +131,12 @@ public:
|
|||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="UFF State Properties")
|
||||||
UAnimMontage* MontageToPlay;
|
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
|
* 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.
|
* Called whenever this state is transitioned out of into a new state.
|
||||||
*
|
*
|
||||||
* Calls appropriate Blueprint hooks.
|
* 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.
|
* Called whenever this state is active and the game logic ticks.
|
||||||
@ -191,7 +210,7 @@ public:
|
|||||||
* @param InStateContext
|
* @param InStateContext
|
||||||
*/
|
*/
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
virtual void Finish(const FFFStateContext& InStateContext);
|
virtual void Finish(const FFFStateContext& InStateContext, EFFStateFinishReason StateFinishReason);
|
||||||
|
|
||||||
// TODO: document
|
// TODO: document
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
@ -260,4 +279,7 @@ public:
|
|||||||
// UObject interface
|
// UObject interface
|
||||||
virtual UWorld* GetWorld() const override;
|
virtual UWorld* GetWorld() const override;
|
||||||
// End of UObject interface
|
// 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"));
|
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);
|
UFFState* NewState = FindStateWithName(NewStateName);
|
||||||
if(NewState)
|
if(NewState)
|
||||||
{
|
{
|
||||||
GoToState(NewState);
|
GoToState(NewState, StateFinishReason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UFFStateMachineComponent::GoToState(UFFState* NewState)
|
void UFFStateMachineComponent::GoToState(UFFState* NewState, EFFStateFinishReason StateFinishReason)
|
||||||
{
|
{
|
||||||
check(CurrentState);
|
check(CurrentState);
|
||||||
check(NewState);
|
check(NewState);
|
||||||
@ -115,8 +115,7 @@ void UFFStateMachineComponent::GoToState(UFFState* NewState)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CurrentState->Exit(GetCurrentStateContext(), StateFinishReason);
|
||||||
CurrentState->Exit(GetCurrentStateContext());
|
|
||||||
TicksInState = 0;
|
TicksInState = 0;
|
||||||
TickStateWasEntered = GetWorld()->GetGameState<AFFGameState>()->GetCurrentTick();
|
TickStateWasEntered = GetWorld()->GetGameState<AFFGameState>()->GetCurrentTick();
|
||||||
CurrentSubStateLabel = NAME_None;
|
CurrentSubStateLabel = NAME_None;
|
||||||
@ -125,12 +124,12 @@ void UFFStateMachineComponent::GoToState(UFFState* NewState)
|
|||||||
CurrentState = NewState;
|
CurrentState = NewState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UFFStateMachineComponent::GoToEntryState()
|
void UFFStateMachineComponent::GoToEntryState(EFFStateFinishReason StateFinishReason)
|
||||||
{
|
{
|
||||||
// can't have an entry state if there are no states
|
// can't have an entry state if there are no states
|
||||||
check(States.Num() > 0);
|
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
|
// It is OK to transition if state's "CanTransitionToSelf" is true
|
||||||
if(StateToTransitionTo)
|
if(StateToTransitionTo)
|
||||||
{
|
{
|
||||||
GoToState(StateToTransitionTo);
|
GoToState(StateToTransitionTo, EFFStateFinishReason::SFT_Interrupted);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -72,20 +72,26 @@ public:
|
|||||||
* Transitions from CurrentState to the new state with the name passed to this function
|
* 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
|
* 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
|
* 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
|
* 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
|
* Transitions from CurrentState to the default entry state
|
||||||
|
*
|
||||||
|
* TODO: document StateFinishReason
|
||||||
*/
|
*/
|
||||||
void GoToEntryState();
|
void GoToEntryState(EFFStateFinishReason StateFinishReason);
|
||||||
|
|
||||||
UFUNCTION(BlueprintPure)
|
UFUNCTION(BlueprintPure)
|
||||||
FORCEINLINE int64 GetTicksInState() const { return TicksInState; }
|
FORCEINLINE int64 GetTicksInState() const { return TicksInState; }
|
||||||
|
Loading…
Reference in New Issue
Block a user