Completing the Motion Trilogy: The Reusable TranslationFSM
We've conquered Rotation and streamlined Scaling using our FSM Context Pattern. In this article, we tackle the final primitive motion—Translation (linear movement)—to complete our core motion trilogy.
The biggest takeaway here is the demonstration of true architectural reuse. We define a new Context class and a new FSM, but the logic flow remains 100% identical to the Rotation and Scaling demos.
1. The Context: Swapping Scale for Position Parameters
We isolate the specific data required for linear position change into the TranslationContext class.
The primary conceptual difference from the Scale/Rotation demos lies in how boundaries are established: Translation must calculate its absolute boundaries relative to the object's starting local position (OriginalPosition). This ensures the oscillation is centered around the object's initial placement.
| Previous Context Parameters | Translation Context Parameters | Purpose |
| ScaleSpeed (float) | MoveSpeed (float) | Linear speed of position change. |
| ScaleAxis (Vector3) | MoveAxis (Vector3) | The axis (or axes) to apply the movement along. |
| MaxScale / MinScale | MaxPosition / MinPosition | The absolute position bounds, calculated from OriginalPosition. |
TranslationContext.cs: Absolute Boundary Calculation
The TranslationContext calculates the precise MaxPosition and MinPosition by using the dot product to find the object's starting coordinate along the movement axis, and then adding/subtracting the desired displacement.
public class TranslationContext : IStateContext
{
// The new parameters that define linear movement
public float MoveSpeed = 1.0f;
public float MaxPosition = 5.0f;
public float MinPosition = -5.0f;
public Vector3 MoveAxis;
public Vector3 OriginalPosition = Vector3.zero;
// ... Constructor logic
// 2. Calculate the absolute Max/Min boundaries:
this.MaxPosition = initialCoord + maxPos;
this.MinPosition = initialCoord + minPos;
// ...
}
2. The FSM Definition: The Same Engine, New States
The Finite State Machine flow is identical to the Rotation and Scalar demos (Move $\rightarrow$ Max $\rightarrow$ Move Back $\rightarrow$ Min). The blueprint name is now "TranslationFSM".
| Scalar FSM | Translation FSM (Change) |
| States: ScalingUp | States: MovingForward |
| States: ScalingDown | States: MovingBackward |
| Predicate: Apex | Predicate: MaxReached |
| Predicate: Root | Predicate: MinReached |
The Core Logic (Simple Vector Math)
The state updates are handled by direct vector math applied to localPosition using Time.deltaTime for smooth, framerate-independent movement.
// The OnUpdateMovingForward function within TranslationContext.cs
private void OnUpdateMovingForward(IStateContext context)
{
if (context is TranslationContext tc)
{
// Add the movement vector based on speed and time
tc.TransformHandle.localPosition += tc.MoveAxis * (tc.MoveSpeed * Time.deltaTime);
}
}
The transition predicates, MaxReached and MinReached, use the Dot Product to extract the object's current coordinate along the axis, comparing it to the absolute MaxPosition/MinPosition boundary values calculated earlier.
3. The Entry Point: Final Proof of Reusability
The SimpleTranslationDemo.cs handles the instantiation. We simply provide three different MoveAxis vectors to three unique TranslationContext instances, all utilizing the same "TranslationFSM" definition.
private void OnEnterExecuting(IStateContext context)
{
if (context is SimpleTranslationDemo std)
{
float moveSpeed = 1.0f;
float maxDisplacement = 2.0f;
float minDisplacement = -2.0f;
// 1. X-axis: MoveAxis (1, 0, 0)
TranslationContext xAxisContext = new TranslationContext(std.Xaxis, new Vector3(1, 0, 0), moveSpeed, maxDisplacement, minDisplacement);
std.XaxisHandle = xAxisContext.Status;
// 2. Y-axis: MoveAxis (0, 1, 0)
TranslationContext yAxisContext = new TranslationContext(std.Yaxis, new Vector3(0, 1, 0), moveSpeed, maxDisplacement, minDisplacement);
std.YaxisHandle = yAxisContext.Status;
// 3. Z-axis: MoveAxis (0, 0, 1)
TranslationContext zAxisContext = new TranslationContext(std.Zaxis, new Vector3(0, 0, 1), moveSpeed, maxDisplacement, minDisplacement);
std.ZaxisHandle = zAxisContext.Status;
}
}
Conclusion: Architectural Reusability Confirmed
With the implementation of the TranslationFSM, we have successfully completed our core motion trilogy, elegantly managing Rotation, Scaling, and Translation using a single, unified architectural approach.
The key takeaway is the profound level of architectural reuse we achieved. By isolating the unique data (the what) into the Context class and retaining the generic logic flow (the how) within the FSM blueprint, we were able to build three distinct motion systems with virtually identical FSM definitions. This pattern has proven its ability to handle different primitive operations—be it altering localRotation, localScale, or localPosition—simply by swapping out the specialized context. This kind of separation of concerns leads to robust, maintainable, and highly reusable code.
The final proof of concept in SimpleTranslationDemo.cs, simultaneously orchestrating three separate translation contexts with one FSM blueprint, underscores the pattern's efficiency and flexibility.
Up Next: The Grand Finale
We've mastered the primitives. In the final article of this series, we'll connect the three systems—RotationFSM, ScalingFSM, and TranslationFSM—to a single object. Get ready for the Combined Motion Demo, where we'll show an object spinning, pulsing, and oscillating all at once, driven by multiple, independently managed FSM contexts. This is where the true power of this decoupled architecture shines! Keep an eye out for the final installment.
See the demo in action:
YouTube Channel Video
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.
