Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(+1)

AI Bot Opponent Improvements

Previous AI Bot Build

The original state of the bot opponent was configured to be locked on the Y and Z axis and follow the ball on the X axis. This meant when you played against the bot it was not very lively and the bot seemed to be extremely stationary. Additionally, with this build the bot was way too good as it always new the perfect calculation to remain on the Y and Z axis which meant it rarely missed the ball. To make the bot more lively and act more like an actually opponent, I decided to take a page from my previous game (Free Range) and leverage the Unity AI NavMesh.

Current AI Bot Build

As mentioned, I decided to leverage the Unity AI NavMesh for the bot to act more natural. I started by selecting my tennis court and configuring the court to be a static object, I then baked the NavMesh as a walkable area. However, I don't want the opponent to walk on the player side. So I created a new plane that only exists on the opponent side and created a new navigation area "BotSide" and baked the newly static plane. I then deactivated the mesh renderer of this plane. Now I have two objects (seen as colors in the gif) with the NavMesh applied: Green where the player can walk, Red where the bot can walk.

Now that I have the NavMesh set up, I simply added a NavMeshAgent to the opponent and set the area mask to "BotSide". I also set the Angular Speed to 0 so the bot didn't rotate all over the place.  with the NavMeshAgent set up I was able to rewrite the opponent movement script to move towards the ball. However, when I did this the opponent just B-lined it right to the net when it was on the player side. Therefore, I created a cube on the player side, set it as a trigger, deactivated the mesh renderer, and applied a script to the ball to change a public boolean if the ball enters and exists this cube on the player side:

using UnityEngine;
public class BallManager : MonoBehaviour
{
    public bool isPlayerSide;
    void Start()
    {
        isPlayerSide = true;
    }
    public void OnTriggerEnter(Collider col) {
        if (col.transform.tag == "PlayerSide") isPlayerSide = true;
    }
    public void OnTriggerExit(Collider col) {
        if (col.transform.tag == "PlayerSide") isPlayerSide = false;
    }

With this "isPlayerSide" variable configured I am able to call it within the opponent bot script and if the ball is on the player side then the bot will go to the back line and simply follow the ball on the X axis. However, once the ball crosses out of the isPlayerSide then the opponent will run after the ball to hit it across the net. 

using UnityEngine;
using UnityEngine.AI;
public class BotManager : MonoBehaviour
{
    public Transform ball, botResetPosition;
    void Start()
    {
        navMeshAgent = GetComponent<NavMeshAgent>();
    }
    void Update()
    { 
        if (ballCheck.GetComponent<BallManager>().isPlayerSide) {
            navMeshAgent.destination = new Vector3 (ball.position.x, botResetPosition.position.y, botResetPosition.position.z);
        }
        else {
            navMeshAgent.destination = ball.position;
        }
    }


After testing this out the bot plays a lot more like a real opponent and makes mistakes and is a lot easier and more fun than before. The gif above (sorry if there is an orange light filter applied) details how the opponent interacts with the ball on both sides now. I still need to update the animations, but I feel this is a great improvement from the previous build!

Random Tidbit

I thought it was more accurate (and more cute) to get rid of the tennis racket animation and instead have the dogs jump and hit the ball with their snout. You can see a rough start in the gif. It is much more on brand here and I love it!