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: | ![]() |
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.
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;
}
}
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;
}
}
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);
}
}
}
![]() |
contact@rubenbimmel.nl | ![]() |
![]() |
Artstation | ![]() |
GitHub |