Link Search Menu Expand Document

Enemy States

The states our enemy is going to have are Idle and Moving. The change between states will happen if the player is within sight of the enemy or not.

EnemyController Script

Add a script to the Enemy GameObject called EnemyController. Initially, we’ll do some testing to find the ideal SIGHT_DISTANCE of the enemy, that is, the distance between the Player and the Enemy where the Enemy will start moving.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyController : MonoBehaviour
{
    public GameObject player;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        float distance = Vector3.Distance(player.transform.position, gameObject.transform.position);
        // Log the distance so that we can find the ideal sight range of the enemy
        Debug.Log(distance);
    }
}

In the Unity Editor, drag your Player GameObject from the Hierarchy to the Player variable in the EnemyController.

Enemy Controller

The code we’ve written will log the distance between the Player and the Enemy. Run the program and navigate your Player towards the Enemy. In the Console, take note of the distance between them, and consider when you want the enemy to “see” the player. In my testing, I’ve found that a distance of 8 seems pretty good.

EnemyController Script With States

Enemy state machine

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyController : MonoBehaviour
{
    enum EnemyState {
        Idle,
        Moving
    }

    private EnemyState currentState = EnemyState.Idle;
    private SpriteRenderer sr;
    // The value we found in Debug.Log above.
    private const float SIGHT_DISTANCE = 8.0f;
    // The two points that the Enemy will move between in the Moving state.
    private const float RIGHT_MAX = 27.5f;
    private const float LEFT_MAX = 22.5f;

    private int direction = -1;
    private float xSpeed = 0.02f;

    public GameObject player;

    void IdleState(float distance) {
        sr.color = Color.white;

        // switch to moving if in sight range
        if (distance <= SIGHT_DISTANCE) {
            currentState = EnemyState.Moving;
        }
    }

    void MovingState(float distance) {
        sr.color = Color.yellow;

        // move back and forth
        if (transform.position.x >= RIGHT_MAX) {
            direction = -1;
        } else if (transform.position.x <= LEFT_MAX) {
            direction = 1;
        }
        transform.position = new Vector3(transform.position.x + direction * xSpeed, transform.position.y, transform.position.z);

        // switch to idle if out of sight range
        if (distance > SIGHT_DISTANCE) {
            currentState = EnemyState.Idle;
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        sr = GetComponent<SpriteRenderer>();
    }

    // Update is called once per frame
    void Update()
    {
        float distance = Vector3.Distance(player.transform.position, transform.position);
        if (currentState == EnemyState.Idle) {
            IdleState(distance);
        } else if (currentState == EnemyState.Moving) {
            MovingState(distance);
        }
    }
}

And that’s it! You now have a Player with a state machine and an Enemy with a state machine.