In this article, we’ll be creating an enemy for our 2D platformer in Unity, and implementing a simple behaviour: patrolling from wall to wall. While simple, this is widely used in platformers and other kind of games and the logic can be used in many situations.
Going from Right to Left
As this is part of our serie on making a platformer, grounded enemies can only go left or right.
Let’s create a new file for our script: “PatrollingWallToWall.cs”, and set some public properties for the movement speed, and whether the monster is going left or right. As they are public, we can set their values from the inspector, but we’ll also set some default values.
What’s more, since our enemies are sprites being displayed, we’ll need to have access to the SpriteRenderer component to flip the sprite when it turns around.
Finally, we’ll need our enemy to move. So, we’ll use the bIsGoingRight boolean to get the direction our ennemy is going, multiply it by the time elapsed and the monster’s movement speed. The result will be used as an input of the transform.Translate method to move our gameobject according to its movement speed and current direction.
We’ll put the logic of detecting walls and turning around in the CheckForWalls function, which we’ll explain in a bit, but for now our class looks like this:
publicclassPatrollingWallToWall : MonoBehaviour{publicfloat mMovementSpeed =3.0f;publicbool bIsGoingRight =true;privateSpriteRenderer _mSpriteRenderer;// Start is called before the first frame updatevoidStart() { _mSpriteRenderer = gameObject.GetComponent<SpriteRenderer>(); _mSpriteRenderer.flipX = bIsGoingRight; }voidUpdate() {// if the ennemy is going right, get the vector pointing to its rightVector3 directionTranslation = (bIsGoingRight) ? transform.right :-transform.right; directionTranslation *= Time.deltaTime * mMovementSpeed; transform.Translate(directionTranslation);//CheckForWalls(); }}
If you put this script on your Monster Game Object, it should move in one direction, great stuff! Now we can have our monster detect walls and turn around.
Turn Around, Vile Creature!
So how do we detect walls? Well, we want the enemy to turn around when it touches a wall, so we’ll be using the simple solution of raycasting.
Raycasting if you don’t know already, consists in creating a ray from an initial position, in a certain direction, for a finite or infinite distance and check for collisions.
The usability for our usecase is obvious: if the monster is going right, check for a collision at a short range. And if there is a collision, check if we are colliding with the ground. If we are, it’s to go back the way we came from! The code goes as follows and should be self explanatory:
privatevoidCheckForWalls() {Vector3 raycastDirection = (bIsGoingRight) ? Vector3.right : Vector3.left;// Raycasting takes as parameters a Vector3 which is the point of origin, another Vector3 which gives the direction, and finally a float for the maximum distance of the raycastRaycastHit2D hit = Physics2D.Raycast(transform.position + raycastDirection * mRaycastingDistance -newVector3(0f, 0.25f, 0f), raycastDirection, 0.075f);// if we hit something, check its tag and act accordinglyif (hit.collider !=null) {if (hit.transform.tag =="Terrain") { bIsGoingRight =!bIsGoingRight; _mSpriteRenderer.flipX = bIsGoingRight; } } }
Also, we’ll need to add another propriety to our class, to define the raycasting casting distance to avoid our raycast colliding with the instantiator of the raycast (that is, we don’t want the ennemy to detect itself).
publicfloat mRaycastingDistance =1f;
To revert the direction in which the monster is going, we are basically doing the same thing as in the Start method: change the value of bIsGoingRight, flip the Sprite, and let our Update loop take care of the rest. We’re done!
To Note: if you are paying attention, you will realize we also substracted Vector3(0f, 0.25f, 0f) in the raycast initial position. We do this to enforce raycasting at an appropriate level: our snail monster takes up almost 2 spaces in the Y axis, so if we raycasting from the Y position of the center of our sprite, we’ll miss the floor and our monster will be stuck going right and trying to find a collision.
Extending this Class
This is a very simple behaviour but what it really is, is a building block for more complex behaviours.
For example, you can modify the raycasting part to check whether the monster is about to fall with simple checks: if there is no ground at the bottom right of the monster, that’s a hole. Or it can be used for a more complex AI; imagine a magician, that would run away when the player comes too close! The appropriate behaviour can be started thanks to raycasting like we did above.