Pages

Tuesday, June 26, 2012

Unity3D + C#: Zero Waypoints AI Movement for 2D Games

The context is this: I wanna make a simple enemy AI which would move in two directions (left and right), along a floor tiles on air... like those mushroom men in Super Mario.



For most beginners, such as myself when I first started using Unity to make 2D games, my method would be placing two waypoints on each ends of the floor tiles, and then make my AI move towards one of them. When the AI is closed to a waypoint on one side, it would switch and move towards the waypoint on the other side... over and over again. 


As simple as this method is, it can be a pain in the ass to place waypoints at every single AI-movement-area you have in your game, assuming if you like to do last minute level design



Thus, I've decided to make an AI which doesn't need to depends on the waypoints to move around, and the solution is actually pretty simple. All I have to do, is to shoot 4 rays out of two different positions (left and right) around the AI, and check whether it detects any obstacle (wall, floor tiles). If they did, the AI would turn and change its direction.


Now I'm gonna explain how to actually do it, code-wise. 

*Download the full scripts:
https://dl.dropbox.com/u/40305586/ZeroWaypointAi/ZeroWaypointAi.unitypackage

1. To start, I'll first create a few white coloured cubes as the "floor tiles", while a red coloured cube as the AI, and then change the camera to orthographic mode


2. Next, I'll create a new C# Behaviour Script, name it as "ZeroWaypointAi", and start making a few variables:

// length of raycast
public float rayLength = 1;

// number of units the ray's position will be away from player
public int rayPosUnit = 1;

// movement speed of the AI
public int speed = 2;

// something to move the AI later on
private Vector3 moveDirection;

3. Then, I'll create an enum called "WalkDirection", which contains two states: "WalkLeft" and "WalkRight", and then a private variable called "walkDir" for the enum.

private WalkDirection walkDir;

4. In the Start function, I'll randomly select a direction for the AI to walk to when the game start.

protected void Start()
{
    int rand = Random.Range(0, 2);
    if (rand == 0)
    {
        walkDir = WalkDirection.WalkLeft;
    }
    else
    {
        walkDir = WalkDirection.WalkRight;
    }
}

5. In the Update function, I'll create a few new variables for the ray's shooting position.

protected void Update()
{
    // need this to start shooting ray
    RaycastHit hit;

    // left pos is one "rayPosUnit" away from the AI's pos
    Vector3 leftPos = transform.position;
    leftPos.x -= rayPosUnit;

    // right pos is one "rayPosUnit" away from the AI's pos
    Vector3 rightPos = transform.position;
    rightPos.x += rayPosUnit;
}

6. Under these variables, I'll start doing some if and else statement to check a few things:
- shoot a ray from the side which the AI is headed (left or right), to see whether there's any obstacle on that side. If the ray detected any obstacle, switch direction~!
- shoot a downwards ray from the side which the AI is headed (left or tight), to see whether there's still a floor for this AI to move towards. If there's no floor (means if it continue moving in that direction, it'll fall if you add gravity to it), switch direction~!

if (walkDir == WalkDirection.WalkLeft)
{
    if (!Physics.Raycast(leftPos, -transform.up, out hit, rayLength))
    {
        // if it didn't hit anything, change direction
        walkDir = WalkDirection.WalkRight;
    }Debug.DrawRay(leftPos, -transform.up * rayLength, Color.green);
    if (Physics.Raycast(transform.position, -transform.right, out hit, rayLength))
    {
        walkDir = WalkDirection.WalkRight;
    }Debug.DrawRay(transform.position, -transform.right * rayLength, Color.green);

    moveDirection.x = -speed * Time.deltaTime;
}
else if (walkDir == WalkDirection.WalkRight)
{
    if (!Physics.Raycast(rightPos, -transform.up, out hit, rayLength))
    {
        // if it didn't hit anything, change direction
        walkDir = WalkDirection.WalkLeft;
    }Debug.DrawRay(rightPos, -transform.up * rayLength, Color.green);
    if (Physics.Raycast(transform.position, transform.right, out hit, rayLength))
    {
        walkDir = WalkDirection.WalkLeft;
    }Debug.DrawRay(transform.position, transform.right * rayLength, Color.green);

    moveDirection.x = speed * Time.deltaTime;
}

*Note that I also added a line after the if and else statement:

moveDirection.x = speed * Time.deltaTime;

It's purpose it to move the AI along the X-axis by that amount of "speed".

7. Lastly, the AI won't be able to move itself without a proper function to move it. Thus, add one last line below the if and else statement:

// move the AI
transform.Translate(moveDirection);

The completed AI should look something like this:



If you tested out the ZeroWaypointAi package on Unity and clicked Play, you should be able to see in the "Scene", that two green rays (forward and downwards) are coming from the direction the AI is headed.  

No comments: