Reach Network

ReachNetwork is basicly a set of polygon colliders, representing reach levels.

For this example, we will create a reach network for platforms and for the NPC.

Platform Reach Network

A reach network for a platform is basicly the max reach area for each jump type from that platform. It can be simply thought of as the reach area of a jump to the right from the right edge of the platform added to the reach area of a jump to the left from the left edge of the platform.

Currently, we only have one jump type which is full jump, but we will create a reach network for our platform that accepts multiple jump motion graphs.

Lets create a PlatformNetwork class extending ReachNetwork that will go over each calculated motion graph and create a reach level for it.

using UnityEngine;
using System.Linq;


public class PlatformNetwork : ReachNetwork
{
    public PlatformNetwork(Transform origin, Vector2[][] reachLevels) : base(origin, reachLevels)
    {
    }


    public static PlatformNetwork Instance(Transform transform, DistanceCalculator.MotionGraphs graphs)
    {
        Vector2[][] levelColliders = new Vector2[graphs.orderedGraphs.Length][];

        // Get the platform's BoxCollider2D half size
        Vector2 halfSize = transform.GetComponent<BoxCollider2D>().bounds.size / 2;


        // Go through each motion graph
        for (int i = 0; i < graphs.orderedGraphs.Count(); i++)
        {
            // Gets the graph's downwards motion and converts it to a PolyGraph
            PolyGraph motionGraph = new PolyGraph(graphs.orderedGraphs[i].down);

            // Sets the graph's offset to the right top corner of the platform (max reach to the right)
            motionGraph.AddOffset(new Vector2(halfSize.x, halfSize.y));
            // Scales the graph accordingly to the platforms actual scale
            motionGraph.Scale(new Vector2(1 / transform.localScale.x, 1 / transform.localScale.y));

            // Creats a clone of motionGraph
            PolyGraph flippedGraph = new PolyGraph(motionGraph.bakeGraph());

            // Flips the graph on the X axis (max reach to the left)
            flippedGraph.flipX();

            // Reverses the flippedGraph verticies order so they can match the order direction of motionGraph
            levelColliders[i] = PolyGraph.merge(motionGraph.bakeGraph(), PolyGraph.reverse(flippedGraph.bakeGraph()));
        }

        return new PlatformNetwork(transform, levelColliders);
    }
}

A StepPoint is an object where the NPC can rest at or move to another one from it (like: Platform, Wall, etc.)

In our example, a platform is a StepPoint.

Lets create a Platform class that we can instansiate its reach network and its mobility surface so it can be detected by other reach networks:

using UnityEngine;


public class Platform : StepPoint
{
    private PlatformStand platformStand;


    private void buildNetwork(DistanceCalculator.MotionGraphs graphs)
    {
        // Builds and sets the StepPoint network
        network = PlatformNetwork.Instance(transform, graphs);
    }

    private void OnEnable()
    {
        // Call buildNetwork when the graphs are simulated
        DistanceCalculator.Simulated += buildNetwork;
    }

    private void OnDisable()
    {
        DistanceCalculator.Simulated -= buildNetwork;
    }


    // MobilitySurface is the actual mobility space on a StepPoint
    protected override MobilitySurface InitMobilitySurface()
    {
        BoxCollider2D mainCollider = GetComponent<BoxCollider2D>();

        // For a platform, a mobility surface is the surface above it
        // (All the space where the NPC can stand on the platform).
        // You can set its height to be the same as the NPC but 0.01f also works
        // as we are only interested in the landing part
        Vector2 size = new Vector2(mainCollider.size.x, 0.01f);
        Vector2 offset = new Vector2(0, mainCollider.size.y / 2);

        // We can use the already built PlatformStand MobilitySurface.
        // For a custom MobilitySurface you can create a class extending MobilitySurface.
        platformStand = new PlatformStand(this, offset, size);

        return platformStand;
    }


    public PlatformStand getPlatformStand()
    {
        return platformStand;
    }
}

Note: For this example we are building the network on DistanceCalculator.Simulated event. But for a more proffesional approach you should build the motion graphs in a loading screen. That way you can directly access the graphs as they are already calculated in the loading scene.

Greate, now we have our platform network ready.

NPC Reach Network

Adding a reach network to our NPC can act as sensors and help the NPC time its jump.

Lets create JumpSensors class and give it two reach networks for each side (right/left).

using UnityEngine;
using System.Linq;


public class JumpSensors : MonoBehaviour
{
    private ReachNetwork rightReach;
    private ReachNetwork leftReach;


    private void OnEnable()
    {
        DistanceCalculator.Simulated += buildSensors;
    }

    private void OnDisable()
    {
        DistanceCalculator.Simulated -= buildSensors;
    }


    private void buildSensors(DistanceCalculator.MotionGraphs graphs)
    {
        int graphsCount = graphs.orderedGraphs.Count();

        Vector2[][] rightPolys = new Vector2[graphsCount][];
        Vector2[][] leftPolys = new Vector2[graphsCount][];

        for (int i = 0; i < graphs.orderedGraphs.Count(); i++)
        {
            // This is the downwards movement graph above and including the 0 y axis
            Vector2[] jumpStopped = graphs.orderedGraphs[i].DownStopped();

            // Adds a vertical line on the 0 x axis
            // Equal to the height of the graph
            Vector2[] reachPoly = PolyGraph.merge(jumpStopped, new Vector2[] {
                new Vector2(0, jumpStopped[jumpStopped.Length - 1].y),
                new Vector2(0, jumpStopped[0].y)
            });

            PolyGraph graph = new PolyGraph(reachPoly);

            rightPolys[i]   = reachPoly;
            leftPolys[i]    = graph.flipX().bakeGraph();
        }

        rightReach  = new ReachNetwork(transform, rightPolys);
        leftReach   = new ReachNetwork(transform, leftPolys);
    }


    // This function returs `StepPoint` in reach of a specific jump type
    public Collider2D[] reach(bool isRight, int jumpType)
    {
        ReachNetwork reachNetwork = isRight ? rightReach : leftReach;

        return reachNetwork.ReachByType(jumpType);

    }
}

Done!