In part 2 of this Unity tutorial series you’ll learn how to implement Unity ECS character movement.
Welcome to part 2 of Making a Survival Shooter Game, a Unity ECS tutorial series where you’ll learn how to create a game using Unity ECS.
In part 1 we configured the editor and imported the Entities package, which contains all of the classes needed to write code using Unity ECS.
And in this post you’ll learn how to implement Unity ECS character movement using a couple of simple classes.
Implementing Unity ECS Character Movement
The goal is to move the player GameObject in response to input. This is a fairly simple task when writing traditional Unity code.
Normally you’d just create a MonoBehaviour, add some move logic to it’s Update method, and attach it to the GameObject that you wanted to move. The MonoBehaviour would be responsible for holding all of the data and executing all of the logic.
But how do we approach this problem using Unity ECS? Well first we need to understand how to use the Entity Component System (ECS) pattern.
The most important thing to know about writing ECS code is that you should always focus on two things: data and behavior.
The Importance of Data and Behavior
Generally speaking, every entity in your game performs some sort of behavior. Whether it’s as simple as the player moving around the scene (which we’ll be implementing shortly) or as complicated as an NPC choosing the most optimal path to reach its target.
And every behavior requires some sort of data. Whether it’s to information to keep track of state or settings that produce varying outcomes.
When using ECS, these concepts are separated into two concrete types that you have complete control over. Behavior comes in the form of systems and data comes in the form components.
We’ll use these two concrete types to implement Unity ECS character movement.
Starting With The Data
In Unity ECS, data, or components, can be implemented in two ways: the pure way and the hybrid way. We’ll take a look at the pure way later on in this series. But we’ll be sticking to hybrid ECS for the rest of this post.
Hybrid ECS components are simply MonoBehaviours that contain nothing more than data that relates to a single aspect of an entity. That means no logic and no Update method!
In our case, we’ll need input data in order to know where to move the player GameObject. More specifically, we’ll need data from the vertical and horizontal axes of the controller.
Well thanks to Unity’s documentation, we know that the Input class has a method that returns this information in the form of a float value ranging from -1 to 1 based the name of the axis that’s passed in.
So, without worrying about how we’ll retrieve or use that data, all we need to do is simply implement a MonoBehaviour with two floats. One float for horizontal data and another for vertical data.
This component, which I’ve named “InputComponent”, will serve as the container for input data moving forward.
Create The Logic To Define Our Behavior
Now that we have our data in place we can move on to defining our Unity ECS character movement behavior. In Unity ECS, behavior, or systems, can also be implemented using their own pure and hybrid approaches.
Hybrid systems come in the form of classes that derive from the ComponentSystem class and override it’s OnUpdate method. ComponentSystem is a special class that’s included in the Entities package that we imported in part 1.
So we’ll implement a new system called “PlayerMovementSystem” and use it’s OnUpdate method to execute some logic that’ll produce the player movement behavior.
I’ve decided to implement RigidBody based movement for this example. So the system needs to query the scene for all entities that have a RigidBody and InputComponent in their collection of components.
The ComponentSystem base class exposes a method called GetEntities. GetEntities has a generic type parameter that expects a struct type. It uses this struct to figure out which entities to pull from the scene.
The struct must contain a subset of valid components in the form of fields. GetEntities analyzes all of the entities in the scene and returns a list of the ones that have the components listed as fields in the struct.
The PlayerMovementSystem class’s struct, which I’ve names “Filter”, contains RigidBody and InputCopoment fields. It then uses GetEntities and the Filter struct to iterate through all of the appropriate entities, applying movement logic to each one.
So now we’ve got our player movement behavior defined, which is based on input data.
In part 3 of this series, we’ll begin consuming player input and transforming it into the data that our player movement system needs to operate correctly.
We’ll also take a look at an alternative method for filtering entities at the system level.