Add **Interruption options** to State Machine transition
This commit is contained in:
@@ -1552,7 +1552,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
: base(id, context, nodeArch, groupArch)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int TransitionsDataIndex => 0;
|
||||
}
|
||||
@@ -1564,6 +1564,28 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <seealso cref="ISurfaceContext"/>
|
||||
internal class StateMachineTransition : ISurfaceContext
|
||||
{
|
||||
/// <summary>
|
||||
/// State transition interruption flags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum InterruptionFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Transition rule will be rechecked during active transition with option to interrupt transition.
|
||||
/// </summary>
|
||||
RuleRechecking = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Interrupted transition is immediately stopped without blending out.
|
||||
/// </summary>
|
||||
Instant = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The packed data container for the transition data storage. Helps with serialization and versioning the data.
|
||||
/// Must match AnimGraphBase::LoadStateTransition in C++
|
||||
@@ -1574,31 +1596,16 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 32)]
|
||||
internal struct Data
|
||||
{
|
||||
/// <summary>
|
||||
/// The transition flag types.
|
||||
/// </summary>
|
||||
// Must match AnimGraphStateTransition::FlagTypes
|
||||
[Flags]
|
||||
public enum FlagTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// The none.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The enabled flag.
|
||||
/// </summary>
|
||||
Enabled = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The solo flag.
|
||||
/// </summary>
|
||||
Solo = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The use default rule flag.
|
||||
/// </summary>
|
||||
UseDefaultRule = 4,
|
||||
InterruptionRuleRechecking = 8,
|
||||
InterruptionInstant = 16,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1641,21 +1648,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public int Unused2;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the data has a given flag set.
|
||||
/// </summary>
|
||||
/// <param name="flag">The flag.</param>
|
||||
/// <returns><c>true</c> if the specified flag is set; otherwise, <c>false</c>.</returns>
|
||||
public bool HasFlag(FlagTypes flag)
|
||||
{
|
||||
return (Flags & flag) == flag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the flag to the given value.
|
||||
/// </summary>
|
||||
/// <param name="flag">The flag.</param>
|
||||
/// <param name="value">If set to <c>true</c> the flag will be set, otherwise it will be cleared.</param>
|
||||
public void SetFlag(FlagTypes flag, bool value)
|
||||
{
|
||||
if (value)
|
||||
@@ -1683,7 +1680,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <summary>
|
||||
/// If checked, the transition can be triggered, otherwise it will be ignored.
|
||||
/// </summary>
|
||||
[EditorOrder(10), DefaultValue(true), Tooltip("If checked, the transition can be triggered, otherwise it will be ignored.")]
|
||||
[EditorOrder(10), DefaultValue(true)]
|
||||
public bool Enabled
|
||||
{
|
||||
get => _data.HasFlag(Data.FlagTypes.Enabled);
|
||||
@@ -1698,7 +1695,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <summary>
|
||||
/// If checked, animation graph will ignore other transitions from the source state and use only this transition.
|
||||
/// </summary>
|
||||
[EditorOrder(20), DefaultValue(false), Tooltip("If checked, animation graph will ignore other transitions from the source state and use only this transition.")]
|
||||
[EditorOrder(20), DefaultValue(false)]
|
||||
public bool Solo
|
||||
{
|
||||
get => _data.HasFlag(Data.FlagTypes.Solo);
|
||||
@@ -1713,7 +1710,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <summary>
|
||||
/// If checked, animation graph will perform automatic transition based on the state animation pose (single shot animation play).
|
||||
/// </summary>
|
||||
[EditorOrder(30), DefaultValue(false), Tooltip("If checked, animation graph will perform automatic transition based on the state animation pose (single shot animation play).")]
|
||||
[EditorOrder(30), DefaultValue(false)]
|
||||
public bool UseDefaultRule
|
||||
{
|
||||
get => _data.HasFlag(Data.FlagTypes.UseDefaultRule);
|
||||
@@ -1725,9 +1722,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The transition order (higher first).
|
||||
/// The transition order. Transitions with the higher order are handled before the ones with the lower order.
|
||||
/// </summary>
|
||||
[EditorOrder(40), DefaultValue(0), Tooltip("The transition order. Transitions with the higher order are handled before the ones with the lower order.")]
|
||||
[EditorOrder(40), DefaultValue(0)]
|
||||
public int Order
|
||||
{
|
||||
get => _data.Order;
|
||||
@@ -1743,7 +1740,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <summary>
|
||||
/// The blend duration (in seconds).
|
||||
/// </summary>
|
||||
[EditorOrder(50), DefaultValue(0.1f), Limit(0, 20.0f, 0.1f), Tooltip("Transition blend duration (in seconds).")]
|
||||
[EditorOrder(50), DefaultValue(0.1f), Limit(0, 20.0f, 0.1f)]
|
||||
public float BlendDuration
|
||||
{
|
||||
get => _data.BlendDuration;
|
||||
@@ -1755,9 +1752,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The blend mode.
|
||||
/// Transition blending mode for blend alpha.
|
||||
/// </summary>
|
||||
[EditorOrder(60), DefaultValue(AlphaBlendMode.HermiteCubic), Tooltip("Transition blending mode for blend alpha.")]
|
||||
[EditorOrder(60), DefaultValue(AlphaBlendMode.HermiteCubic)]
|
||||
public AlphaBlendMode BlendMode
|
||||
{
|
||||
get => _data.BlendMode;
|
||||
@@ -1768,6 +1765,29 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transition interruption options.
|
||||
/// </summary>
|
||||
[EditorOrder(70), DefaultValue(InterruptionFlags.None)]
|
||||
public InterruptionFlags Interruption
|
||||
{
|
||||
get
|
||||
{
|
||||
var flags = InterruptionFlags.None;
|
||||
if (_data.HasFlag(Data.FlagTypes.InterruptionRuleRechecking))
|
||||
flags |= InterruptionFlags.RuleRechecking;
|
||||
if (_data.HasFlag(Data.FlagTypes.InterruptionInstant))
|
||||
flags |= InterruptionFlags.Instant;
|
||||
return flags;
|
||||
}
|
||||
set
|
||||
{
|
||||
_data.SetFlag(Data.FlagTypes.InterruptionRuleRechecking, value.HasFlag(InterruptionFlags.RuleRechecking));
|
||||
_data.SetFlag(Data.FlagTypes.InterruptionInstant, value.HasFlag(InterruptionFlags.Instant));
|
||||
SourceState.SaveTransitions(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rule graph data.
|
||||
/// </summary>
|
||||
|
||||
@@ -172,25 +172,12 @@ public:
|
||||
/// </summary>
|
||||
enum class FlagTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// The none.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The enabled flag.
|
||||
/// </summary>
|
||||
Enabled = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The solo flag.
|
||||
/// </summary>
|
||||
Solo = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The use default rule flag.
|
||||
/// </summary>
|
||||
UseDefaultRule = 4,
|
||||
InterruptionRuleRechecking = 8,
|
||||
InterruptionInstant = 16,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@@ -1521,12 +1521,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// State Machine
|
||||
case 18:
|
||||
{
|
||||
ANIM_GRAPH_PROFILE_EVENT("State Machine");
|
||||
const int32 maxTransitionsPerUpdate = node->Values[2].AsInt;
|
||||
const bool reinitializeOnBecomingRelevant = node->Values[3].AsBool;
|
||||
const bool skipFirstUpdateTransition = node->Values[4].AsBool;
|
||||
|
||||
ANIM_GRAPH_PROFILE_EVENT("State Machine");
|
||||
|
||||
// Prepare
|
||||
auto& bucket = context.Data->State[node->BucketIndex].StateMachine;
|
||||
auto& data = node->Data.StateMachine;
|
||||
@@ -1558,6 +1557,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Reset all state buckets pof the graphs and nodes included inside the state machine
|
||||
ResetBuckets(context, data.Graph);
|
||||
}
|
||||
#define END_TRANSITION() \
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph); \
|
||||
bucket.CurrentState = bucket.ActiveTransition->Destination; \
|
||||
bucket.ActiveTransition = nullptr; \
|
||||
bucket.TransitionPosition = 0.0f
|
||||
|
||||
// Update the active transition
|
||||
if (bucket.ActiveTransition)
|
||||
@@ -1567,11 +1571,42 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Check for transition end
|
||||
if (bucket.TransitionPosition >= bucket.ActiveTransition->BlendDuration)
|
||||
{
|
||||
// End transition
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph);
|
||||
bucket.CurrentState = bucket.ActiveTransition->Destination;
|
||||
bucket.ActiveTransition = nullptr;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
END_TRANSITION();
|
||||
}
|
||||
// Check for transition interruption
|
||||
else if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionRuleRechecking))
|
||||
{
|
||||
const bool useDefaultRule = EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule);
|
||||
if (bucket.ActiveTransition->RuleGraph && !useDefaultRule)
|
||||
{
|
||||
// Execute transition rule
|
||||
auto rootNode = bucket.ActiveTransition->RuleGraph->GetRootNode();
|
||||
if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0]))
|
||||
{
|
||||
bool cancelTransition = false;
|
||||
if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant))
|
||||
{
|
||||
cancelTransition = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blend back to the source state (remove currently applied delta and rewind transition)
|
||||
bucket.TransitionPosition -= context.DeltaTime;
|
||||
bucket.TransitionPosition -= context.DeltaTime;
|
||||
if (bucket.TransitionPosition <= ZeroTolerance)
|
||||
{
|
||||
cancelTransition = true;
|
||||
}
|
||||
}
|
||||
if (cancelTransition)
|
||||
{
|
||||
// Go back to the source state
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph);
|
||||
bucket.ActiveTransition = nullptr;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1595,11 +1630,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Check for instant transitions
|
||||
if (bucket.ActiveTransition && bucket.ActiveTransition->BlendDuration <= ZeroTolerance)
|
||||
{
|
||||
// End transition
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph);
|
||||
bucket.CurrentState = bucket.ActiveTransition->Destination;
|
||||
bucket.ActiveTransition = nullptr;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
END_TRANSITION();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1620,6 +1651,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
|
||||
// Update bucket
|
||||
bucket.LastUpdateFrame = context.CurrentFrameIndex;
|
||||
#undef END_TRANSITION
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user