Where To Start Learning Ai/CPU and Boss scripting?

Hello people of Unity forums, I’m sorta stuck/lost here. I’ve been looking into implementing Ai/CPU and bosses into my game, but I haven’t found any tutorials that actually helped me, also most are in Java script which get’s annoying to translate to C#. Can anyone point me somewhere or recommend something to me, maybe even give me some pointers from yourself? I know you guys will probably need to know what type of bosses and Ai I’m trying to make, but just think the Ai/CPU doesn’t attack me instead they avoid certain obstacles that could kill them or me, and the bosses just pace back and forward sometimes trying to attack you directly, and other times doing some special move, also some could fly. Well that’s it I hope you guys can help, thanks for reading.

The way I’ve always handled this is through states.

Basically I create an enumeration of different states. Based on your description a rough guess would be something like this:

public enum BossActionType
{
    Idle,
    Moving,
    AvoidingObstacle,
    Patrolling,
    Attacking
}

The boss will then have a variable to determine its current state:

private BossActionType eCurState = BossActionType.Idle;

And in my Update method I use a switch to see which state is currently active.

switch (eCurState)
{
    case BossActionType.Idle:
        HandleIdleState();
        break;

    case BossActionType.Moving:
        HandleMovingState();
        break;

    case BossActionType.AvoidingObstacle:
        HandleAvoidingObstacleState();
        break;

    case BossActionType.Patrolling:
        HandlePatrollingState();
        break;

    case BossActionType.Attacking:
        HandleAttackingState();
        break;
}

What brings the boss (or any other enemy) to life is the chaining together of many small states.

By breaking down each state and handling them individually you can focus only on what the Boss needs to do while in that state.

For instance, HandleMovingState() could have the enemy just moving around randomly until it encounters an obstacle. Then you switch to the AvoidingObstacle state. Now, the coding would be different because the problem is different. In this state the Boss needs to focus on getting around the obstacle. After the boss succeeds in avoiding the obstacle you switch back to the Moving state. Occasionally, you may put the boss into Idle state. Perhaps he would stop and look around. If the player moves within a certain proximity of the Boss you can put the Boss into Attacking state. And so forth.

Basically each state you can think of as a little program of its own. This allows you to really nail it down as detailed as you like. You only need to code for the things that can and should happen while it is in that state. For example, if this was for your player, your HandleJumping() would not need to check for another jump because the player is already in the air. Unless, of course you want to support double jumps. In that case you’d end up in the HandleDoubleJump() state and you would not need to check for another jump because it can not be done while in this state.
This helps to clean your code clean and manageable.

1 Like

Wow very informative thank you very much. By the way, are these states for animation or things like transform.position, rigidbody2D.AddForce (newVector2 (0, jump));, and other coding in order to make the boss/Ai move and do all kinds of things?

It’s just a state machine. It’s a common construct for logic. What you do in the cases is up to you , so you could write code that triggers animations, moves the object, figures out where the player is (if applicable in that state) and so on.

1 Like

Hopefully the mods don’t boot me soon for posting the link to my WIP game.

But I am going to do it because this will illustrate it. It uses the web player so you can play it in your browser:
http://supergar.com/Unity3D/platformcollect/pgdemo.html

The states I am talking about are the current state of an object. The action the object is currently performing.

The states are just a reference. Basically just labels you come up with to describe the different types of behavior you want to have.

You will have a block of code, ideally a method to handle each state.
You can also create a new script for each state.

Inside each of your state methods you will control movement, animation, performing any special things that should be done while in that state (such as checking for the player being within a certain range, checking for an obstacle, etc).

Anyway, getting to the example game.

Do you see how the snakes patrol the platforms?

There is a sequence of actions (states) happening that control their behavior.

Initially, the snakes all start out in the Idle state.
In this state, they just wait (kind of looking out) for a period of time.

Their state then changes to Patrolling. In this state they move, animate and check if they can see the player.
If they can see the player and the player is not too far away their state changes to Chasing. In this state their movement and animation speed is greatly accelerated.

If they never change to Chasing then they will continue patrolling. In either case, when they reach the end of the platform their state changes to TurnAround. Once TurnAround completes they reenter the Idle state. This causes them to look like they move across the platform. Stop and look out. Then turn and head back the other way.

Basically, this is the only way I would recommend to make any game. Heck I even use this approach in my day job doing business programming. And it is definitely better than the code I have seen which is a monolithic Update trying to check umpteen variables and flags just to get something to move around and jump and so forth.

It allows you to break down complex behaviors and make it all simple. You just string a bunch of different states (behaviors) together and it can end up looking pretty damn impressive. And it is not just a matter of changing their animation to look like they are doing something. With this approach, they really are doing it. The animation is just the visual aspect.

I hope that all helps to make it clearer.

Ah so that’s the reason thank you so much for your help. I appreciate all of this. Thank you too orb. :slight_smile:

Sorry for coming back, but this is related. So I’ve been messing around with moving the Ai/CPU, so far I got it to move back and forward, but I have a question. How can I change states? I haven’t figured out how to do it yet so I’ve been using coroutines as a temporary replacement. Here’s how my script looks like:

public class CPUScript : MonoBehaviour {

    private BossActionType eCurState = BossActionType.Idle;
    private bool dirRight = true;
    public float speed = 2.0f;
   

    public enum BossActionType
    {
        Idle,
        Moving,
        AvoidingObstacle,
        Patrolling,
        Attacking
    }

    IEnumerator MoveLeft()
    {
        yield return new WaitForSeconds (1.0f);
        dirRight = false;
        if (dirRight == false)
                        transform.Translate (-Vector2.right * speed * Time.deltaTime);
                        StartCoroutine (MoveRight ());
               
    }

    IEnumerator MoveRight()
    {
        yield return new WaitForSeconds (1.0f);
        dirRight = true;
        if (dirRight)
            transform.Translate (Vector2.right * speed * Time.deltaTime);
        StartCoroutine(MoveLeft());
    }

    void Update(){


    switch (eCurState)
    {
    case BossActionType.Idle:
            if (dirRight)
                transform.Translate (Vector2.right * speed * Time.deltaTime);
            StartCoroutine(MoveLeft());
            
        //HandleIdleState();
        break;
       
    case BossActionType.Moving:
            if(dirRight == false && transform.position.x >= 4.0f)
            transform.Translate (-Vector2.right * speed * Time.deltaTime);
            Debug.Log("Sweet Sweet");
        //HandleMovingState();
        break;
       
    case BossActionType.AvoidingObstacle:
        //HandleAvoidingObstacleState();
        break;
       
    case BossActionType.Patrolling:
        //HandlePatrollingState();
        break;
       
    case BossActionType.Attacking:
        //HandleAttackingState();
        break;
    }
}
}

Ok you may not like this suggestion very much but first you should learn the underlying principles.

MITs 6.034 is a pretty good start

I stumbled across it when I was playing with GAs for evolving pursuit target preferences

After that you should ofc watch the AI related Unite 2014 talks and other Tutorials… @AngryAnt gave a good one and also published the src on his repo. (The state machines in there are quite nice to get one going and exactly what you’re looking for)

1 Like

You may want to check out the video and the Unite stuff. Not seen any of it so I can’t comment on it.

It’s simple stuff though. Some people build fancy behavior systems complete with GUIs. But it all seems like such overkill to me. But then a lot of the things do. In my day job I have seen systems using threads to implement messaging systems that in reality only needed delegates to throw some events. People have a tendency to make their code overly complex, over engineering. So whatever you do keep it as simple as you can.

Anyway, if you want I can knock out a little example project in Unity.

That would be great if you can make an example project. :slight_smile: I just finished the video that Graph posted and are currently viewing tutorials on the AngryAnt site. So I wouldn’t mind it would help a lot.

Okay, I will knock out something simple. Probably do that tomorrow.

Okay thank you very much.

Update: Well I’ve been messing around with the Ai now and I got something that’s half smart. You see sometimes the Ai is smart and dodges the obstacles no problem, but other times it’s a machine that only knows how to kill itself. Here’s the script(it’s a little messy):

public class CPUScript : MonoBehaviour {

    private BossActionType eCurState = BossActionType.Idle;
    private bool dirRight = true;
    bool turning = false;
    bool jump = false;
    public float speed = 2.0f;
    public float jumpForce = 700f;
    int clickCount;
    bool facingRight = true;
      

    public enum BossActionType
    {
        Idle,
        Moving,
        AvoidingObstacle,
        Patrolling,
        Attacking
    }
  

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.tag == "Player") {
            eCurState = BossActionType.Moving;
          
                }
        if (other.tag == "Bomb") {
            jump = true;
            eCurState = BossActionType.AvoidingObstacle;
                }
        if (other.tag == "Dangerous") {
                        Debug.Log ("HSK");

                Destroy(gameObject);
            Debug.Log("Dead");
            }
        }

    IEnumerator MoveLeft()
    {
        yield return new WaitForSeconds (3.0f);
        dirRight = false;
        if (dirRight == false)
                        transform.Translate (-Vector2.right * speed * Time.deltaTime);
        turning = false;
                        StartCoroutine (MoveRight ());
              
    }

    IEnumerator MoveRight()
    {
        yield return new WaitForSeconds (3.0f);
        dirRight = true;
        if (dirRight)
            transform.Translate (Vector2.right * speed * Time.deltaTime);
        turning = true;
        StartCoroutine(MoveLeft());
    }

    void Update(){


    switch (eCurState)
    {
    case BossActionType.Idle:
            if (dirRight)
                transform.Translate (Vector2.right * speed * Time.deltaTime);
            turning = true;
            StartCoroutine(MoveLeft());

        //HandleIdleState();
        break;
      
    case BossActionType.Moving:
            if(dirRight == false && transform.position.x >= 4.0f)
            transform.Translate (Vector2.right * speed * Time.deltaTime);
            Debug.Log("Sweet Sweet");
        //HandleMovingState();
        break;
      
    case BossActionType.AvoidingObstacle:
            if(jump == true){
            transform.Translate (-Vector2.right * speed * Time.deltaTime);
            rigidbody2D.AddForce (new Vector2 (0, jumpForce));
            jump = false;
            }
        //HandleAvoidingObstacleState();
        break;
      
    case BossActionType.Patrolling:
        //HandlePatrollingState();
        break;
      
    case BossActionType.Attacking:
        //HandleAttackingState();
        break;
    }
}

    void FixedUpdate()
    {

        float move = Input.GetAxis ("Horizontal");
        rigidbody2D.velocity = new Vector2 (move * speed, rigidbody2D.velocity.y);

        if (turning == true && !facingRight)
            Flip ();
        else if (turning == false && facingRight)
            Flip ();
    }

    void Flip()
    {
        facingRight = !facingRight;
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }
}

Excellent! Glad to see you have made progress.

I knocked out a simple example. Coming up with an idea to illustrate the states was the greatest challenge!

What I decided to go with is a basic worker from an RTS game. Basically, something like the old Warcraft where lumberjacks head over to the woods, cut down trees then return the wood to store house.

The lumberjack starts in the Idle state.
Then he chooses a tree and enters the MovingToForest state.
Once he arrives, he enters the GatheringWood state.
Once finished, he enters the ReturningToStorehouse state.
Once he arrives at the storehouse he enters the UnloadingWood state.
If there are more trees left, he chooses another tree and enters the MovingToForest state.
Otherwise, he enters the MovingToRestPoint state.
Finally, when he reaches the rest point he enters WorkIsDone state.

So basically, you will see the Lumberjack continually heading to the forest, chopping down trees, returning the wood to the storehouse… until finally he has cleared the entire forest. Then he will go to the rest point and that is the end.

This is a very simple example. And the graphics are very basic. Just geometric shapes.

You can check out the web player example here:
http://supergar.com/Unity3D/StatesRTSexample/StatesRTSexample.html

And download the project by right clicking on this link:
http://supergar.com/Unity3D/StatesRTSexample/StatesExample.zip

Of course, you can also use coroutines, lerp and so forth if you prefer.

I am used to doing everything manually through code so I just did it the way I always do handling everything myself.

Anyway, I hope this helps.

Thank you very much. Wow a lot of stuff to look at. I better look at it carefully, thanks again.:slight_smile:

1 Like

You’re welcome. Glad to help. :slight_smile:

I made a small update this morning just to refactor a bit of code and use two new functions. Decreased the LumberjackControl script by a few lines. Project files (all 150 KB of them) are available at the link I posted prevously.

A lot can be done with the states. Their real power, in my opinion, is that in addition to being able to create some complex behavior quite easily… it also isolates the current behavior so you are only dealing with conditions and processing necessary for this current state. Which means each state method becomes quite lean and mean so it serves as an optimization method as well. Of course, keeping code clean is always nice too.

Good luck with your project!

Hi, I’m new in game development and want to learn how to make Boss AI. I found that the link you post before didn’t work anymore. Can you please upload it again? Because it will help me a lot.

Thank you

I don’t have time to do it right now because I am working hard on the launch of my first tiny paid game. I just decided to check the forum for a brain break for a few minutes.

But… it’s very easy stuff. Like everything a person can make state management as complex or easy as they want to. I simply use a switch / select case structure to sort it out.

All you need to do (and I think I covered this above probably… not sure since I think this is something from years ago but I would think I had) is focus on breaking down complex behaviors in to a series of much simpler behaviors. Each of these are categorized into states or if more complex into states and tasks.

State is your top level such as PATROL and task is MOVE (to next way point or move along with a pathfinding algorithm or move to a destination using a navmesh, etc) and anything else like perhaps the entity periodically stops to look around so task would become SCAN_ENVIRONMENT or SLL (Stop, Look around and Listen) and depending on the outcome of that task would return to MOVE to continue movement or control may be passed to a new state such as COMBAT with task being EngagePlayer or whatever.

In this way you can implement complex / interesting behavior while keeping it all very simple because each task only handles a very specific piece of the overall behavior.

That was very informative WOW…