Completing the Motion Trilogy: The Reusable TranslationFSM

Completing the Motion Trilogy: The Reusable TranslationFSM

Leader posted 4 min read

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.

A Hyper-Detailed, Architecturally Impossible Synthesis of Consciousness and Digital Matter. The image is a frenetic, deeply complex digital vista where a central Luminous Eye dominates a vast, glowing circuit landscape. This eye, suspended mid-air, is a sphere of intense, fractal energy—a pulsating vortex of pink, violet, and electric blue light—that powerfully suggests an emergence of digital consciousness or a Technological Singularity itself. The core is a bottomless black aperture, ringed by a white-hot plasma disc. Below this ocular energy source, the light dissipates into an intricate, copper-gold and neon-blue Circuit Board Megastructure that stretches to the horizon, impossibly dense with exaggerated microchips, glowing resistor arrays, and power conduits that form deep, glowing canyons. The background is a dark, holographic projection field displaying complex schematics, mathematical models, and flowing data streams, reinforcing the theme of Absolute Digital Engineering. The entire scene is bathed in a dramatic, opposing light source—a warm, orange-gold glow from the left and an ice-cold, electric-blue glow from the right—creating a maximalist, high-contrast visual experience that definitively rejects the minimalist simplicity of conventional design and announces: Technology is intricate, overwhelming, and infinitely complex.

If you read this far, tweet to the author to show them you care. Tweet a Thanks

1 Comment

1 vote

More Posts

The Grand Finale: Orchestrating Complex Motion with Multi-Context FSMs

The Singularity Workshop - Oct 15

Building a Reusable State Machine for Oscillating Rotation in Unity

The Singularity Workshop - Oct 14

Applying the FSM Context Pattern to Scaling: The Reusable ScalarFSM

The Singularity Workshop - Oct 14

When Unity Glitches, You Build Your Own Loop: The Four-Line App Core

The Singularity Workshop - Oct 17

The FSM Agent Architecture: Scaling NavMesh Agents with Decoupled Logic

The Singularity Workshop - Oct 15
chevron_left