This pack gives you the tools to create your 2D entities much faster, AI or Player controlled.

Create your game faster.

Drag and drop to set up. Code optional.

Get Your characters moving how you want.

About This Pack

This pack contains code I have developed to make setting up characters in a 2D game much easier.

I use this pack in my games to allow me to get straight to programming the other main gameplay elements and skip the long period of time it can take to create a good controller.

Not only does this pack come with pre-setup prefabs allowing you to skip coding but it also contains useful components and architecture that allow you to expand the controller, or even make your own using the components and states, to better fit your game.

This pack comes with:

  • Input readers
    (classes that read input from the player using Unity's old or new input system)
  • Controllers
    (components that translate inputs to movement info but also allow for easy switching from an AI controller to a User controller during run time)
  • Movement script
    (the main script that utilizes all the components and allow inputs to control the state of the entity)
  • States
    (movement states that give your entity a wide array of movement options such as sliding or wall climbing. You can even create your own and add them to the movement script using the state base class and interface.)
  • Collision detection
    (advanced collision detection class that easily let's you get important information about an entity such as the ground angle under them or if they are near a wall. Useful for any entity controller.)
  • Animation script
    (a script that allows you full control over the animation played based on the movement information of an entity through the use of a scriptable object. No animator needed. Unity animator may still be used.)
  • Pre-made prefabs
    (Already set up. Just drag them into your scene and customize their animations and movement stats to fit your game.)
  • Debug visuals and readouts
    (Run into some unwanted behavior or a bug? Toggle on the various debug options made available through the scripts to see the raycasts and collision checks being made, or read out the entity's current movement state flags.)

Technical Details

/Setup

Set Up

Right out of the box you should have all that you need to get an entity moving around in just under a minute. All you need to do is create a game object, give it a sprite and animator, attach the EntityMovementScript, attach a controller, set up a few variables and you are done.

Step 0:

Project Settings > Physics 2D > Queries Start In Colliders = false

This step is important to do before using the Entity Movement Script. Select Edit at the top of the screen, go to Project Settings, then Physics 2D. Here you will need to make sure the setting “Queries Start In Colliders” is disabled, the check box is not checked. This is due to the detection method of the scripts.

Step 1:

Create object with a sprite, rigidbody and collider.

Create the base. This is the object you will be using the controller on to move, you can skip this step if you have one already set up. If you don't already have a game object ready to do this you can right click in the “Hierarchy” window, usually found on the left side of the screen, then select “create empty”. This will give you your empty object. Next in the “inspector” window, found on the right, it will have your new object selected and showing all you have is a transform. Click “Add Component” under the transform and using the search bar search for a “Sprite Renderer” and add it. Do the same for “Animator”, “Capsule Collider 2D”, and “Rigidbody 2D”, as well. If you have not done so add a sprite to the sprite renderer so you can see your object. The Rigidbody2D BodyType should be set to Dynamic, Collision Detection set to Continuous, and Interpolate set to Interpolate. Under Constraints also freeze rotation. A Gravity Scale between 1 and 5 is recommended.

Step 2:

Add EntityMovementScript. Set "Ground Layers" and add states.

Next add the “EntityMovementScript” from this package. It can be found in the “MonoBehaviors” file under “Movement”. Most variables will be preset with values I have used but they can all be changed or even set to be ignored. Some will need to be set before this can be used for the first time. Variables also have helpful Tooltips to explain what they do and what settings may do what actions. Just hover your mouse over the variable’s name to see the Tooltip. To ensure it works properly look for the “Ground Layers” variable in the inspector, select the drop down tab next to it, then select the layers you will be using for this entity to walk on. This will tell the script which colliders it can walk on. You will also need to select the states you will be using from the drop down tab found near the top of the component called “Add State”. This allows control over the states your entity can be in. Each state has its own variables and events for customization and control over movements. (Ai controllers will still work if the old input is disabled. This will only affect player inputs.)

Step 3:

Add a Controller or PlayerInput component.

Attach a controller. This is a script that lets the EntityMovementScript know what inputs are being given and what actions to take. Two come ready with this package, one for player input and one as an example of a simple “ai” controller. Coding will be necessary for more controllers. To set up the player controller add the “PlayerControllerScript” found in the “MonoBehaviours” folder under “Controllers”. Then add a “ScriptableInputMap” to the player controller which outlines the actions of each input. One comes pre-made under the “Scriptables” folder found in the “Scripts” folder. One may also be made by right clicking in the “Project” window at the bottom, then selecting Create -> ScriptableObjects -> ScriptableInputMap. To use Unity's New input system instead make sure you have it added to your project, then instead of adding the controller script add the PlayerInput component added in from the input system. Then in the inspector of the PlayerInput component add the provided action map.

Step 4:

Handle animations.

Lastly you will need to set up how your animations are handled. If this is done you may skip this step. To do this you must choose which method of animation you will be using, two ways are provided and ready to use. Both use an Animator, Animations, and an Animation Controller. Examples found in the “Animation” folder. To use the Animator method with animation transitions being controlled by the animator simply add the “AnimUpdater” script, found in the “MonoBehaviours” folder under “Animation”, to the entity. Make sure your entity has a Controller added to the Animator component with defined transitions and you are done. To use the script based way of playing animations add the “PlayerAnimationScript”, found in the “MonoBehaviours” folder under “Animation”, to the entity. This method uses a scriptable object to define the animations of the entity in each state, a pre-made one can be found in the “Scripts” folder under “Scriptables”. One can also be made by right clicking in the Project window at the bottom of the screen then navigating to Create -> ScriptableObjects -> ScriptableAnimationList. Fill the list with your animations and their respective states then add the scriptable list to the PlayerAnimationScript and you are done. This method was created to make any animation transition into any other animation with easier set up.

Notes:

Gravity is often adjusted but while moving on the ground the gravity is determined from the Rigidbody2D’s gravity scale before play. Increasing this will make it easier for the entity to stick to the ground when moving quickly over sharp edges but increasing this too high may also affect movement across angled ground. When adjusting collider size, be sure to re adjust the size of the ground checks and their offsets so that horizontally they are within the collider and that vertically they are peaking out of the collider. They only need to be slightly out of the collider to work properly, however in some cases it may help to have the foot offset lowered out of the collider more to keep the entity grounded better but this may also interfere with other movements such as jumping if lowered too much. When adjusting these they may be seen in play mode by enabling gizmos during play. Some debug visuals are also provided without code changes under the “Debug Visuals” heading in the inspector for the Entity Movement Script. Log Flags will show in the console the different movement info that is being detected. Draw Movement Component Debug will show a line from the entity that represents the velocity of the entity which may be useful if the entity is getting stuck. Draw Collision Component Debug will display the three rays used for slope detection and ground info with blue being the ray in the front of the entity and red at the back. It is recommended that the ground check size of the feet stay between these rays. The movement script does support BoxCollider2D and CircleCollider2D but CapsuleCollider2D is recommended as that was used during most testing. When setting up animations, animations need to be in animation controller with the same name as the animation but do not need to be connected to anything if using PlayerAnimationScript. Connections are made in code. For easy use your animations should have the sprites centered to prevent X axis offsets of collider. CollisionCheckComponent can be used to get the position of entity since it calculates the position using collider offset and direction of entity. Also accounts for rotation.
/Documentation

Flow Chart

EntityMovementScript

StateManager


--Interfaces--

ISimple Move

IMove

IEntity Movement

IMove StateManager Access

IMoveState FullAccess


--InfoStructs--

MoveFlags

MovementInfo

MoveConstraintFlags

MovementConstraints


--UtilityClasses--

Controller

InputHelperClass

MoveComponent

CollisionCheckComponent


--States--

MoveState Abstract

Grounded

In Air

On Wall

EntityMovementScript

This script acts as the main container and driver of the movement systems. This class initializes the states specified for use from the AddStates variable, then adds them to a state manager.

  • Determines the active state of entity using a ground check and wall check
  • Implements interfaces to allow external interactions with entity such as adding force or getting the entity's movement info.
  • Implements interface to allow states to make changes to entity while having data persist across multiple states such as jump count and collider size changes. This is done through IEntityMovement and should Not be used by anything other than a state for data stability reasons as changes can be critical.
  • Draws many debug visuals such as ground check area, wall check ray, and others.

State Manager

This class is a container of states and is to be used by another class. This class contains methods to manage the active state out of a collection of multiple states.

States are updated within this class using the IMoveStateFullAccess interface.

ISimple Move

This is used as a base interface for anything that will move and contains functions for getting and setting the velocity of an object as well as adding force.

Think of this as being the interface to interact with a moveable ball.
The ball is just a ball and not controlled by a player. This interface can be added and implemented as a way for the player to hit the ball and add force to it by searching for this interface on the object.

IMove

Inherits ISimpleMove interface.

Acts as a way to access the movement info of an object. This interface is meant to be used on objects that will have MovementInfo and MovementConstraints. Within EntityMovementScript this interface provides a way to access such classes.

IEntity Movement

Inherits IMove interface.

Intended to only be explicitly implemented making it so that functions can not be accessed through a reference to the object but rather accessed through the interface itself. This is done to maintain security of the functions as they are only meant to be used by states held within EntityMovementScript's StateManager.

GetTransformRef() is a function within the IEntityMovement interface. Accessing this function is done like so:

EntityMovementScript player; IEntityMovement entityInterface = player; Transform transform = entityInterface.GetTransformRef();


EntityMovementScript explicitly implements IEntityMovement. Access to the functions is done through the interface. Attempting to use GetTransformRef() or any other functions of IEntityMovement through player will not work.

The base MoveState already stores a reference to this interface of its parent in the variable m_parentInterface. Use this when adding a new state or adjusting a preexisting one.

IMove StateManager Access

Allows limited access to an object's StateManager. Specifically this interface allows other objects to get access to the MoveState of an object that implements this interface.

This interface is used as a way to access and change the stats of EntityMovementScript's states. Below is an example of how to make a trigger that will set the player's run speed to 20.
(A similar script example can be found in the Entity Controller pack called ChangePlayerStatsTrigger within the Scripts folder.)

void OnTriggerEnter2D(Collider2D col) { if (col.gameObject.GetComponent<IMoveStateManagerAccess>() != null) { IMoveStateManagerAccess moveStateManager = col.gameObject.GetComponent<IMoveStateManagerAccess>(); GroundedState groundedState = moveStateManager.GetState("GroundedState") as GroundedState; if (groundedState != null) { groundedState.m_stats.runSpeed = 20f; } } }

IMoveState FullAccess

Like IEntityMovement this is an explicitly implemented interface. This interface is explicitly implemented on all concrete MoveStates (OnWall, GroundedState, …).

It is required that this interface be explicitly implemented on any new states added for the state to be used properly in StateManager.

The interface is designed to only be used by StateManager. The interface allows StateManager to access important functions within states in order to update them and transition states while making it so that an outside class can not accidentally call these functions on a state when gaining access to them through IMoveStateManagerAccess.

MoveFlags

An enum in which each value is a flag allowing multiple values to be true or false at a time.
Microsoft flags documentation.

These flags are used to describe the state of a moving entity such as if an entity is grounded and crouching. They are contained in the MovementInfo class These flags can be seen in the inspector during play if LogFlags is checked in the EntityMovementScript or if the Inspector is in Debug mode and the MoveInfo dropdown is expanded.

This class is largely used within each state, specifically the HandleInput functions. An example of use outside of EntityMovementScript can be seen in the PlayerAnimationScript as the script obtains the flags through MovementInfo and checks the flags to determine which animation to play.

MoveConstraintFlags

An enum in which each value is a flag allowing multiple values to be true or false at a time.
Microsoft flags documentation.

This set of flags is used within MoveStates to constrain or prevent a type of movement such as running or jumping and are contained in the MovementConstraints struct.
It is up to the state to implement and check these flags

MovementConstraints

This struct contains MoveConstraintFlags and provides various bitwise functions to set and check the flags.

MovementInfo

This class contains MoveStateFlags and provides various bitwise functions to set and check the flags. It also contains a few extra variables to further describe the state of an entity such as direction it is facing and directional input axis to describe movement direction.

This class is largely used within each state, specifically the HandleInput functions.
An example of use outside of EntityMovementScript can be seen in the PlayerAnimationScript as the script obtains the flags through MovementInfo and checks the flags to determine which animation to play.

Controller

Abstract MonoBehaviour class used within states to retrieve input data.

On Awake EntityMovementScript finds the controller on the entity and passes it to each state as it initializes them. The states use the controller within the HandleInput function to read inputs and take action based on inputs and constraints.

Controllers can be found in the MonoBehaviors folder. A player controller and a simple AI controller can be found there.

PlayerControllerScript is a script that utilizes the InputContainer class to read inputs and store the data (type of input, if it is held down, if it is currently down, if it is new this frame, and if it is locked) within the container. The inputs are set through a scriptable object, ScriptableInputMap, that maps inputs to actions which are enum values that dictate how the input will be used elsewhere.

Note that the PlayerControllerScript is the implementation of Unity's old input system and will not be used if the new system is being used. Instead InputHelperClass will be used if an entity is to be controlled by PlayerInput.

InputHelperClass

This class is used by states when a controller is not set and EntityMovementScript has detected a PlayerInput component (a component defined by Unity's new input system) on the entity.
Unity documentation for input system.

InputHelperClass utilizes the action map to read inputs and allows states to read the data of actions in their HandleInputWithInputHelper functions letting them use the new input system as a way to receive input.
This class also includes a function helpful for debugging that reads out the input and its state in the Inspector of EntityMovementScript in the Input Action Display text area during play.

This class is important for the use of the new input system paired with a state machine. By having the InputActionMap stored externally from the states and directly reading the inputs within the state this allows inputs to persist across state changes.

MoveComponent

This class contains simple functions for simple movement of a rigid body such as setting velocity, adding force, smoothly setting velocity with Vector3.SmoothDamp, as well as drawing a debug line that shows velocity.

CollisionCheckComponent

(“Ground” in this context is any Collider within specified layers to check. EX. a collider in a collision layer labeled “ground”)

Class in charge of various collision checks and data storing. Utilizes a mix of raycasts and overlap shapes for area checks.

Allows an object to obtain slope info about the ground below it, if it is on the ground, if there is ground above it, if the object is on a ledge, the RaycastHit2D information of the SlopeCheck rays, as well as other information useful for 2d movement.

This class also allows for the ability to ignore collision with specific objects and undoing the ignoring of said objects to allow for an object to pass through others under desired circumstances.

The class uses three rays for slope checking and three points for area checks (head, middle, feet).
There are also debug options that will draw these rays and the ground normal for error checking.

MoveState Abstract

The abstract class of states that are stored and managed by StateManager within EntityMovementScript. EntityMovementScript uses StateManager to set the active state but EntityMovementScript holds the logic to change states based on movement info.

A state dictates the actions available to an entity in that moment based on input and movement info. States manipulate the position and movement info of an entity based on input, received from either a Controller or InputHelperClass, the entity's movement restrictions, and the current movement info (an entity can not run if it is not grounded).

States utilize an entity's MoveComponent as well as its CollisionCheckComponent to move the entity. A reference to the entity's EntityMovementScript through its IEntityMovement interface for extra functions that aid in moving the entity while having data persist across states.

Concrete States Must explicitly implement the IMoveStateFullAccess in order for StateManager to update them correctly.

Concrete states also implement their own stats classes since this data will be unique per type of state. The same goes for events contained within the states.

States include a name variable at base level that should be set to the actual name of the state class. This makes it clear which state is retrieved when checking an entity's state externally and wanting to change the state's stats since casting will be needed from the base class.
See IMoveStateManagerAccess for an example of using a states name or ChangePlayerStatsTrigger within the project. Name is used to retrieve the base of a specified class then cast to its actual concrete form.

Grounded

This state is in charge of all actions an entity can take while on the “ground”, a collider in a specified layer.

This state used a number of variables to allow an entity to walk, run, dash, crouch, slide, and even jump (This jump however is only responsible for the first jump to get the entity off the ground. Following jumps will be handled by InAir state). All this while on a ground collider of any angle as long as the angle of the ground is less than the specified walkable slope. A slope too steep will cause the entity to slide off.

Primary movement is done through manipulation of an entity's friction, gravity, and velocity.

In Air

This state is in charge of all actions an entity can take while not grounded such as aerial movement, jumping, ascending and descending speeds, and dashing in the air.

On Wall

This state is in charge of all actions an entity can take while not grounded and near a wall such as sliding down a wall, wall jumps of various kinds, wall climbing, and wall grabbing.

/ExtraInfo

Controllers And The Entity Movement Script:

The relationship between these two scripts is simply that the controllers control the movement script. On its own the movement script will do nothing, it is made to take input from a controller and carry out actions based on the state it is in and the inputs given. Controllers are anything that inherits from the “ControllerBase” class found in the “DataContainers” folder under “Scripts”. Controllers are classes that put out inputs, or directions, for other classes to listen to. They may determine their directions from any source; AI decisions, simple if statements based on variables, or physical input from keys or buttons being pressed by a human.

Animation And The Entity Movement Script

Animation is independent from the Entity Movement Script, no changing of the movement script is needed for animation. The movement script uses an interface that other classes may reference to grab movement information from to determine outcomes. This can be seen in both animation scripts that come with this package found in the “MonoBehaviours” folder. This makes it easy to use the Movement script along with any animation method or even other scripts that may need to know about the entity’s movement.

More About The Entity Movement Script

The Entity Movement Script functions as a state machine. This means that based on the entity’s situation it will use a different state for actions it can take such as jumping and moving. States that come set up in this package are Grounded, In Air, and On Wall. More may be added in code for more situations or current states may be modified for greater movement capabilities. These states have their own stats and events that determine how the entity will behave. These stats and events can be customized in the inspector under drop down tabs with their respective names. The states an entity has access to is determined by the states added in the inspector when setting up. Not having a state included will limit movement capabilities which may be desired for some entities that have more basic movements. The events use Unity events for ease of set up without code but may be changed in code. Particle systems come with the package to demonstrate use of events. More events may also be added in code for greater customization.