Advanced Artificial Intelligence in .NET Gaming
Artificial Intelligence (AI) drives the behavior of non‑player characters (NPCs), dynamic difficulty, procedural content, and more. This guide explores advanced AI techniques available in the .NET ecosystem, including behavior trees, utility AI, pathfinding, and machine learning integration.
Key Concepts
- Decision Making: How agents choose actions based on game state.
- Modularity: Keep AI logic reusable across multiple entities.
- Performance: Optimize update loops and avoid expensive calculations during gameplay.
All examples target .NET 8 with the Microsoft.Xna.Framework
and MonoGame
libraries, but concepts are applicable to any .NET game engine.
Behavior Trees
Behavior trees provide a hierarchical decision making structure that is easy to visualize and extend.
// Simple behavior tree example
using System;
using GameAI;
public abstract class Node {
public abstract bool Tick();
}
public class Selector : Node {
private readonly Node[] _children;
public Selector(params Node[] children) => _children = children;
public override bool Tick() {
foreach (var child in _children)
if (child.Tick()) return true;
return false;
}
}
public class Sequence : Node {
private readonly Node[] _children;
public Sequence(params Node[] children) => _children = children;
public override bool Tick() {
foreach (var child in _children)
if (!child.Tick()) return false;
return true;
}
}
public class ActionNode : Node {
private readonly Func<bool> _action;
public ActionNode(Func<bool> action) => _action = action;
public override bool Tick() => _action();
}
// Usage
var tree = new Selector(
new Sequence(
new ActionNode(() => EnemyInSight()),
new ActionNode(() => Attack())
),
new ActionNode(() => Patrol())
);
Utility AI
Utility AI scores possible actions and selects the highest‑scoring option each tick.
// Utility AI snippet
using System.Collections.Generic;
using System.Linq;
public class UtilityAgent {
private readonly Dictionary<string, Func<float>> _options;
public UtilityAgent() {
_options = new Dictionary<string, Func<float>> {
{ "Attack", () => Health * EnemyProximity },
{ "Flee", () => (1-Health) * ThreatLevel },
{ "Heal", () => (1-Health) * (HasPotion?1:0) }
};
}
public string ChooseAction() {
return _options
.OrderByDescending(kv => kv.Value())
.First().Key;
}
// Example state variables
public float Health = 0.8f; // 0-1
public float EnemyProximity = 0.6f; // 0-1
public float ThreatLevel = 0.9f; // 0-1
public bool HasPotion = true;
}
A* Pathfinding
The A* algorithm finds the shortest path on a grid while considering obstacles.
// Simplified A* implementation
public class Node {
public int X, Y;
public bool Walkable;
public float G, H;
public Node Parent;
public float F => G + H;
}
public class AStar {
private readonly Node[,] grid;
private readonly int width, height;
public AStar(Node[,] grid){ this.grid=grid; width=grid.GetLength(0); height=grid.GetLength(1); }
private IEnumerable<Node> GetNeighbors(Node n){
var dirs = new (int dx,int dy)[]{(0,1),(1,0),(0,-1),(-1,0)};
foreach(var (dx,dy) in dirs){
int nx=n.X+dx, ny=n.Y+dy;
if(nx>=0 && ny>=0 && nx0){
var current = open.OrderBy(node=>node.F).First();
if(current==goal) return Reconstruct(current);
open.Remove(current);
closed.Add(current);
foreach(var neighbor in GetNeighbors(current)){
if(closed.Contains(neighbor)) continue;
float tentativeG = current.G + 1; // assume cost=1
if(!open.Contains(neighbor) || tentativeG < neighbor.G){
neighbor.Parent = current;
neighbor.G = tentativeG;
neighbor.H = Math.Abs(neighbor.X-goal.X)+Math.Abs(neighbor.Y-goal.Y);
if(!open.Contains(neighbor)) open.Add(neighbor);
}
}
}
return null; // no path
}
private List<Node> Reconstruct(Node end){
var path = new List<Node>();
for(var n=end; n!=null; n=n.Parent) path.Add(n);
path.Reverse();
return path;
}
}
Integrating ML Models
Use Microsoft.ML
to add data‑driven behavior such as difficulty prediction or decision networks.
// Example: Predicting player skill level
using Microsoft.ML;
using Microsoft.ML.Data;
public class PlayerData {
public float Accuracy;
public float ReactionTime;
public float DamagePerMinute;
}
public class SkillPrediction {
[ColumnName("Score")]
public float PredictedSkill;
}
public class SkillModel {
private readonly PredictionEngine<PlayerData, SkillPrediction> _engine;
public SkillModel(string modelPath){
var mlContext = new MLContext();
ITransformer model = mlContext.Model.Load(modelPath, out var inputSchema);
_engine = mlContext.Model.CreatePredictionEngine<PlayerData, SkillPrediction>(model);
}
public float Predict(PlayerData data){
return _engine.Predict(data).PredictedSkill;
}
}
Compile your model with ML.NET
and load the .zip
file at runtime.
For more details, see the Advanced Gaming Docs Index.