Introduction
First system we have is the physics system. This system is split into four main groups:
-
Movement mechanics:
- Mechanics Controller.
- Realtime Body.
-
Simulator:
- Motion Assets.
- Simulator.
-
Mapper:
- Reach networks.
- Step Points.
- Sensors.
- Path Finder.
-
Actions:
- Actions.
- Action Runner.
Second, we have is our GOAP system.
And we also have some utils. Like: PolyGraph, collision detections and especial ray casting.
Goap Agent
For NPC GOAP setup, We need GOAPAget and GOAPExecuter.
Movement Mechanics Setup
Full setup, from mechanics to implementations on a Rigidbody2D.
Mechanics Controller
First thing we need to setup the NPC's mechanics.
In order to integrate it with Eddie2D, we will need to setup our mechanics logic inside a class extending MovementController.
using UnityEngine;
public class NPCController : MovementController
{
// =============================
// Setup custom input parameters
// =============================
...
public override void FixedUpdate()
{
// =============================
// Custom mechanics logic
// =============================
...
}
public override void ResetInputs()
{
// =============================
// Reset/Default inputs
// =============================
...
}
}
Note:
MovementControllerhasFixedUpdatefunction inherited fromScriptedAction. This FixedUpdate will not be called on Unity's lifecycle by itself.
Realtime Body
To integrate our controller to the NPC's realtime Rigidbody2D we can create a new MonoBehaviour class that encapsulates our Rigidbody2D inside a RealtimeRigidbody class, creates an instance of our controller and make calls to its FixedUpdate in realtime.
using System;
using UnityEngine;
public class PlayerMechanicsBody : MonoBehaviour
{
MovementManager movementManager;
private void Awake()
{
Rigidbody2D rigidbody = GetComponent<Rigidbody2D>();
RealtimeRigidbody body = new RealtimeRigidbody(rigidbody);
movementManager = new MovementManager(body, movementParameters);
}
private void FixedUpdate()
{
movementManager.FixedUpdate();
}
}
Just like that. Your integration to Eddie2D movement is ready.
Simulator Setup
To simulate motions we use MotionSimulator, which is a class that handels physics simulation updates and reconrds positions snapshots.
A simulator can take a set of MotionAssets that are custom input controllers we can create (like: full jump, minor jump ect.) and returns the motion snapshot of each one.
Motion Assets
You can create a custom MotionAsset that can handle your custom controller inputs.
using UnityEngine;
[CreateAssetMenu(fileName = "NewInputControlMotion", menuName = "Motion/InputControl")]
public class InputControlMotion : MotionAsset
{
[SerializeField]
// =============================
// Custom values
// =============================
...
public override MotionInstance Instance()
{
// Note: for custom runtime values create a custom MotionInstance
return new MotionInstance(this);
}
public override void Setup(MovementController movementController, MotionInstance instance)
{
NPCController npcController = (NPCController)movementController;
// =============================
// Custom input setup
// =============================
...
}
}
Note:
MotionAssetis aScriptableObject.
Now you can create custom motions in your Unity Editor that you can later use in your simulator.
Note: For custom runtime values you will need a custom
MotionInstanceclass. You can see how in the examples.
Simulator
Some parameters you would want to have control over in the simulator (like: for instance if the npc is on ground is different in realtime than simulations). In order to gain control over them in the simulator, we recommend creating a new class (NPCSimulatorController) extending NPCController and override any special methods for simulations.
How can we run the simulator?
using UnityEngine;
using static MotionSimulator;
public class MotionGraphsCalculator : MonoBehaviour, IMotionSimulatorEvents
{
[SerializeField]
private PlayerMechanicsBody playerBody;
[SerializeField]
private MotionAsset[] motionAssets;
private NPCSimulatorController _controller;
private MotionSimulator _simulatorInstance;
public void OnMotionSetup()
{
// =============================
// This is called before each motion calculations starts
// =============================
...
}
public void OnMotionLateUpdate()
{
// =============================
// This is called after each calculation frame update
// =============================
...
}
private void onSimulationFinished(MotionSimulator.MotionGraphs graphs)
{
// =============================
// This is called after all motions are simulated
// =============================
...
}
void Awake()
{
Rigidbody2D playerRB = playerBody.GetComponent<Rigidbody2D>();
float gravity = Mathf.Abs(Physics2D.gravity.y);
gravity *= playerRB.gravityScale;
SimulationBody body = new SimulationBody(Vector2.zero, Vector2.zero, playerRB.linearDamping, gravity);
_controller = new NPCSimulatorController(body, playerBody.getMovementParameters());
simulatorInstance = new MotionSimulator(this, _controller, motionAssets);
}
void Start()
{
StartCoroutine(simulatorInstance.CalculateSimulations(onSimulationFinished));
}
}
First, we create a SimulationBody with the real players physics values. We create an instance of our controller, instance of our MotionSimulator, pass the motion assets we want to calculate and then we call the coroutine CalculateSimulations.
Mapper Setup
Reach Network
Reach network is simply a set of Vector2[] each one representing a reachable area of a motion with the origin position.
Step Points
A step point is like an object in your world that the npc can be resting at (a platform in a 2d platformer game).
StepPoint is an abstract class. It has a propertie named network that is of type ReachNetwork, representing reach levels of the platform.
It also has an abstract method MobilitySurface InitMobilitySurface(). It expects a mobility surface that is simply the mobility area on that specific step (for a platform it is the surface on the top of the platform).
Note: For a platforms mobility surface you can use the integrated
PlatformStand. You just need to pass the size and the origin.
Sensors
After simulations you can use the motion graphs to create sensors (For example: a full jump reach sensor).
You can build sensors using separate reach networks, one for the right and one for the left.
Path Finder
Simply use PathFinder.findPath(StepPoint start, StepPoint end). It uses A* algo and works with StepPoint.