We've successfully established a reusable architectural pattern by isolating the data (Context) from the logic flow (Finite State Machine) for the three primitive motions: Rotation, Scaling, and Translation. We created three distinct, independent blueprints—"RotationFSM," "ScalarFSM," and "TranslationFSM"—each driving separate objects on various axes.
Now, for the grand finale: we will demonstrate the true power of this decoupled architecture by applying all three FSM blueprints simultaneously to the same three objects, achieving a perfectly synchronized, multi-dimensional motion effect.
This isn't about creating a new FSM; it's about proving the flexibility and reusability of the existing ones. We will show that one object can be driven by multiple, independently managed FSM contexts, eliminating the need for a monolithic, complex state machine.
The Power of Decoupled Contexts
The core principle remains the same: the FSM definition is the reusable logic (the how), and the Context is the unique data (the what).
In our previous demos, we created one Context per object. In the Combined Motion Demo, we create three Contexts per object.
| Object | Rotation Demo | Scalar Demo | Translation Demo | Combined Demo |
| X-axis Cube | RotationContext | ScaleContext | TranslationContext | RotationFSM, ScalarFSM, TranslationFSM |
| Y-axis Cube | RotationContext | ScaleContext | TranslationContext | RotationFSM, ScalarFSM, TranslationFSM |
| Z-axis Cube | RotationContext | ScaleContext | TranslationContext | RotationFSM, ScalarFSM, TranslationFSM |
The FSM framework handles each Context instance completely independently. When the Unity Update() loop runs, it executes the update for all nine individual FSM instances (three contexts * three objects), resulting in the compound motion.
The Entry Point: SimpleRotationScalarTranslationDemo.cs
The SimpleRotationScalarTranslationDemo.cs file is the orchestrator, instantiating the nine required Contexts in its main FSM's OnEnterExecuting method.
Instantiation Logic: Three-in-One
The code combines the instantiation logic from all three previous demos. For the XaxisCube, we instantiate a ScaleContext, a TranslationContext, and a RotationContext, all referencing the same demo.XaxisCube Transform.
private void OnEnterExecuting(IStateContext context)
{
if (context is SimpleRotationScalarTranslationDemo demo)
{
// === X-AXIS CUBE ===
// 1. Scale FSM (Uses ScaleContext/ScalarFSM)
ScaleContext scX = new ScaleContext(demo.XaxisCube, new Vector3(1, 0, 0), 0.5f, 3f, 0.5f);
// 2. Translation FSM (Uses TranslationContext/TranslationFSM)
TranslationContext tcX = new TranslationContext(demo.XaxisCube, new Vector3(1, 0, 0), 1f, 2f, -2f);
// 3. Rotation FSM (Uses RotationContext/RotationFSM)
RotationContext rcX = new RotationContext(demo.XaxisCube, new Vector3(1, 0, 0), 90f, 45f, -45f);
// === Y-AXIS CUBE ===
// ... (Similar instantiation for Y-axis cube, using (0, 1, 0) for the axis)
ScaleContext scY = new ScaleContext(demo.YaxisCube, new Vector3(0, 1, 0), 0.5f, 3f, 0.5f);
TranslationContext tcY = new TranslationContext(demo.YaxisCube, new Vector3(0, 1, 0), 1f, 2f, -2f);
RotationContext rcY = new RotationContext(demo.YaxisCube, new Vector3(0, 1, 0), 90f, 45f, -45f);
// === Z-AXIS CUBE (Note: Scale is multi-axis for visual flair) ===
// ... (Similar instantiation for Z-axis cube, using various axes)
ScaleContext scZ = new ScaleContext(demo.ZaxisCube, new Vector3(1, 1, 0), 0.5f, 3f, 0.5f);
TranslationContext tcZ = new TranslationContext(demo.ZaxisCube, new Vector3(0, 0, 1), 1f, 2f, -2f);
RotationContext rcZ = new RotationContext(demo.ZaxisCube, new Vector3(0, 0, 1), 90f, 45f, -45f);
}
}
The Decoupled FSMs in Action
Because each Context is an independent data container, the FSM framework executes them separately, resulting in simultaneous, non-interfering motion:
- The
RotationContext drives the TransformHandle.localRotation.
- The
ScaleContext drives the TransformHandle.localScale.
- The
TranslationContext drives the TransformHandle.localPosition.
Each FSM instance has its own independent state and transitions, yet they all operate on the same object's Transform. The result is a fascinating display: the Z-axis Cube, for instance, oscillates along the Z-axis (Translation), pulses in size along the X and Y axes (Scaling), and swings around the Z-axis (Rotation).
Conclusion: True Architectural Victory
This Combined Motion Demo is the ultimate proof of the FSM Context Pattern's power:
- Separation of Concerns: We fully separated the primitive operations. Modifying the boundary check for rotation, for instance, has zero impact on the translation or scaling logic.
- High Reusability: We did not write a single new state machine definition for this complex demo. We simply reused the "RotationFSM," "ScalarFSM," and "TranslationFSM" blueprints.
- Scalable Complexity: An object's behavior is now a composition of independent FSM-driven movements. If we need to add a "Color Modulation FSM," we simply instantiate a fourth context for each object.
This decoupled architecture provides the foundation for robust, maintainable, and highly extensible game systems. You've escaped Update() spaghetti and unlocked the ability to compose complex behaviors from simple, reusable, and independent parts.
See it in action on YouTube (Don't forget to like and subscribe!):
Support the Vision and See the Code!
If this pattern has helped clean up your Update() loop, please consider supporting our development! Your support ensures we can continue to provide advanced, architectural solutions for cleaner game development.
Resources & Code:
The FSM Package (Unity Asset Store):
https://assetstore.unity.com/packages/slug/332450
NuGet Package (Non-Unity Core):
https://www.nuget.org/packages/TheSingularityWorkshop.FSM_API
GitHub Repository:
https://github.com/TrentBest/FSM_API
Support Our Work:
Patreon Page:
https://www.patreon.com/c/TheSingularityWorkshop
Support Us (PayPal Donation):
https://www.paypal.com/donate/?hosted_button_id=3Z7263LCQMV9J
We'd love to hear your thoughts! Please Like this post, Love the code, and Share your feedback in the comments.
