Ruben Bimmel

Game Development

Platformer AI

Assignment: Create an AI that can battle with both a player and another AI
Duration: 2 months
Team size: Solo project
Role: Development
Skills:
Links: GitHub

This AI uses a modular utility system. This way behaviour can be created without programming. The AI uses an A* algorithm for path finding. This was really challenging to implement. I never used this algorithm before. The fact that the algorithm needed to know all of the possible moves for a platformer made it even more complex.

The base classes for this system were inspired by the way Unreal uses the pawn and controller classes. Pawns contain all of the information about the moves that can be executed. This way players and AI use the same rule set. The navigation system of the AI defines the input to get the pawn to move to a target position.

De AI also needed to be able to avoid other players. To do this all tactical positions on the map are marked. When a player gets close the AI will start to make a list of the nearby positions. It then picks the one that is the furthest away from the other player. In most cases this works really great and the AI starts circling around the player. However this system often fails to avoid the player when the AI gets into a corner of the map.

It turned out to be very complicated to make the AI follow the calculated path. Because the path is continuously changing it often occurs that the AI fails to make a jump. The AI is definitely not perfect but it is a very fun AI to play against. The small errors and the continuously changing of the AIs target makes it feel alive. The only major problem is that there are a few things the AI simply cannot do. When you know these it becomes really easy to defeat them.

Abstract Controller class

Open »
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Controller : MonoBehaviour {

    protected Pawn pawn;

    // Possess a pawn
    public void Possess(Pawn newPawn) {
        pawn = newPawn;
        newPawn.SetController(this);

        // parent this controller to the pawn
        if (newPawn.transform != transform) {
            transform.parent = newPawn.transform;
            transform.localPosition = Vector3.zero;
        }
    }

    // Return the current possessed pawn
    public Pawn GetPawn () {
        return pawn;
    }
}

Abstract AI Controller class

Open »
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class AIController : Controller {

    protected NavigationSystem navigation;

    // Update is called every frame
    protected virtual void Update () {
        // Apply the navigation
        if (navigation != null) {
            pawn.SetVelocity(navigation.GetDirection());
        }
    }

    // Get connected navigation system instace
    public NavigationSystem GetNavigationSystem () {
        return navigation;
    }
}

Platformer AI class

Open »
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlatformerAI : AIController {
    public ProceduralTiles.Level level;
    public Agent agent;
    public Context context;

    // Use this for initialization
    protected void Start() {
        SideScrollerPawn SSPawn = (SideScrollerPawn)pawn;

        // Create new navigation system
        if (SSPawn) {
            navigation = new PlatformNavigator(SSPawn, level);
        }

        // Initialise agent
        if (agent) {
            agent.Initialise();
        }
    }

    // Update is called once per frame
    protected override void Update() {
        // Update agent
        if (agent && context) {
            agent.UpdateStates(context);
        }
    }
}
« Back to portfolio

Contact