Game AI Systems
v1.0.0Game AI development guide covering behavior trees, state machines, pathfinding, and decision-making systems. Use when implementing enemy AI, NPC behaviors, f...
Security Scan
OpenClaw
Benign
high confidencePurpose & Capability
The name and description match the SKILL.md content: game AI design and Godot/C# examples. There are no unexpected credentials, binaries, or unrelated dependencies requested.
Instruction Scope
SKILL.md contains code examples and design guidance only. It does not instruct the agent to read system files, access environment variables, send data to external endpoints, or perform actions outside the stated domain.
Install Mechanism
No install spec or code files are present—this is instruction-only and nothing will be written to disk or downloaded during install.
Credentials
No environment variables, credentials, or config paths are requested; the skill does not ask for secrets or unrelated access.
Persistence & Privilege
always is false and model invocation is allowed (default). This is normal for a user-invocable instructional skill and does not grant elevated, persistent privileges.
Assessment
This skill is an offline guide with code samples and appears safe and coherent for the stated purpose. Before enabling it for autonomous agent use, you may: (1) review the SKILL.md yourself to confirm it contains only the expected guidance, (2) be cautious if future versions add install scripts or request environment variables, and (3) prefer skills from a known source if you require provenance guarantees.Like a lobster shell, security has layers — review code before you run it.
latest
游戏 AI 开发指南
AI 架构选择
架构对比
有限状态机 (FSM):
优点: 简单、直观、易于调试
缺点: 状态多时难以维护
适用: 简单敌人、Boss 阶段
行为树 (BT):
优点: 模块化、可复用、易扩展
缺点: 需要框架支持
适用: 复杂 NPC、战术 AI
效用系统 (Utility AI):
优点: 灵活、智能决策
缺点: 参数调节困难
适用: 模拟人生类、策略游戏
GOAP:
优点: 目标导向、动态规划
缺点: 计算成本高
适用: 智能敌人、复杂行为
有限状态机 (FSM)
基础实现
public enum EnemyState
{
Idle,
Patrol,
Chase,
Attack,
Flee
}
public class EnemyFSM : Node
{
private EnemyState _currentState;
private Dictionary<EnemyState, IState> _states;
public override void _Ready()
{
_states = new Dictionary<EnemyState, IState>
{
[EnemyState.Idle] = new IdleState(this),
[EnemyState.Patrol] = new PatrolState(this),
[EnemyState.Chase] = new ChaseState(this),
[EnemyState.Attack] = new AttackState(this),
[EnemyState.Flee] = new FleeState(this)
};
ChangeState(EnemyState.Idle);
}
public void ChangeState(EnemyState newState)
{
_states[_currentState]?.Exit();
_currentState = newState;
_states[_currentState]?.Enter();
}
public override void _Process(double delta)
{
_states[_currentState]?.Update(delta);
}
}
public interface IState
{
void Enter();
void Update(double delta);
void Exit();
}
public class ChaseState : IState
{
private readonly EnemyFSM _fsm;
private readonly CharacterBody2D _owner;
private NavigationAgent2D _navAgent;
public ChaseState(EnemyFSM fsm)
{
_fsm = fsm;
_owner = fsm.GetParent<CharacterBody2D>();
_navAgent = _owner.GetNode<NavigationAgent2D>("NavigationAgent2D");
}
public void Enter()
{
// 开始追逐动画
_owner.GetNode<AnimationPlayer>("AnimationPlayer").Play("run");
}
public void Update(double delta)
{
var player = GetTree().GetFirstNodeInGroup("player") as Node2D;
if (player == null) return;
// 更新目标位置
_navAgent.TargetPosition = player.GlobalPosition;
// 移动向玩家
var direction = _owner.ToLocal(_navAgent.GetNextPathPosition()).Normalized();
_owner.Velocity = direction * _owner.Speed;
_owner.MoveAndSlide();
// 检查距离
float distance = _owner.GlobalPosition.DistanceTo(player.GlobalPosition);
if (distance < 50)
{
_fsm.ChangeState(EnemyState.Attack);
}
else if (distance > 500)
{
_fsm.ChangeState(EnemyState.Patrol);
}
}
public void Exit()
{
_owner.Velocity = Vector2.Zero;
}
}
行为树 (Behavior Tree)
节点类型
复合节点:
Sequence: 顺序执行(一个失败则失败)
Selector: 选择执行(一个成功则成功)
Parallel: 并行执行
装饰节点:
Inverter: 反转结果
Repeater: 重复执行
UntilFail: 直到失败
叶子节点:
Condition: 条件检查
Action: 执行动作
行为树实现
public enum NodeStatus
{
Running,
Success,
Failure
}
public abstract class BTNode
{
public abstract NodeStatus Execute();
}
public class Sequence : BTNode
{
private readonly List<BTNode> _children = new();
private int _currentChild = 0;
public Sequence(params BTNode[] children)
{
_children.AddRange(children);
}
public override NodeStatus Execute()
{
while (_currentChild < _children.Count)
{
var status = _children[_currentChild].Execute();
if (status == NodeStatus.Running)
return NodeStatus.Running;
if (status == NodeStatus.Failure)
{
_currentChild = 0;
return NodeStatus.Failure;
}
_currentChild++;
}
_currentChild = 0;
return NodeStatus.Success;
}
}
public class Selector : BTNode
{
private readonly List<BTNode> _children = new();
private int _currentChild = 0;
public Selector(params BTNode[] children)
{
_children.AddRange(children);
}
public override NodeStatus Execute()
{
while (_currentChild < _children.Count)
{
var status = _children[_currentChild].Execute();
if (status == NodeStatus.Running)
return NodeStatus.Running;
if (status == NodeStatus.Success)
{
_currentChild = 0;
return NodeStatus.Success;
}
_currentChild++;
}
_currentChild = 0;
return NodeStatus.Failure;
}
}
// 使用示例
public class EnemyAI : Node
{
private BTNode _behaviorTree;
public override void _Ready()
{
_behaviorTree = new Selector(
// 优先攻击
new Sequence(
new Condition(IsPlayerInRange),
new Condition(HasAmmo),
new Action(Attack)
),
// 追逐玩家
new Sequence(
new Condition(CanSeePlayer),
new Action(ChasePlayer)
),
// 巡逻
new Action(Patrol)
);
}
public override void _Process(double delta)
{
_behaviorTree.Execute();
}
}
寻路算法
A* 算法
public class AStar
{
private readonly Grid _grid;
public List<Point> FindPath(Point start, Point end)
{
var openSet = new PriorityQueue<Point>();
var cameFrom = new Dictionary<Point, Point>();
var gScore = new Dictionary<Point, float>();
var fScore = new Dictionary<Point, float>();
openSet.Enqueue(start, 0);
gScore[start] = 0;
fScore[start] = Heuristic(start, end);
while (openSet.Count > 0)
{
var current = openSet.Dequeue();
if (current == end)
return ReconstructPath(cameFrom, current);
foreach (var neighbor in _grid.GetNeighbors(current))
{
float tentativeGScore = gScore[current] + _grid.GetCost(current, neighbor);
if (!gScore.ContainsKey(neighbor) || tentativeGScore < gScore[neighbor])
{
cameFrom[neighbor] = current;
gScore[neighbor] = tentativeGScore;
fScore[neighbor] = gScore[neighbor] + Heuristic(neighbor, end);
if (!openSet.Contains(neighbor))
openSet.Enqueue(neighbor, fScore[neighbor]);
}
}
}
return null; // 无路径
}
private float Heuristic(Point a, Point b)
{
// 曼哈顿距离
return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y);
}
private List<Point> ReconstructPath(Dictionary<Point, Point> cameFrom, Point current)
{
var path = new List<Point> { current };
while (cameFrom.ContainsKey(current))
{
current = cameFrom[current];
path.Insert(0, current);
}
return path;
}
}
Godot NavigationAgent2D
public class EnemyNavigation : CharacterBody2D
{
private NavigationAgent2D _navAgent;
private Node2D _target;
public override void _Ready()
{
_navAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
// 配置
_navAgent.PathDesiredDistance = 10.0f;
_navAgent.TargetDesiredDistance = 10.0f;
_navAgent.Radius = 20.0f;
// 连接信号
_navAgent.VelocityComputed += OnVelocityComputed;
}
public void SetTarget(Node2D target)
{
_target = target;
UpdatePath();
}
private async void UpdatePath()
{
if (_target == null) return;
// 异步计算路径
_navAgent.TargetPosition = _target.GlobalPosition;
await ToSignal(_navAgent, NavigationAgent2D.SignalName.PathChanged);
}
public override void _PhysicsProcess(double delta)
{
if (_navAgent.IsNavigationFinished())
return;
var nextPos = _navAgent.GetNextPathPosition();
var direction = GlobalPosition.DirectionTo(nextPos);
var velocity = direction * Speed;
// 避障
_navAgent.Velocity = velocity;
}
private void OnVelocityComputed(Vector2 safeVelocity)
{
Velocity = safeVelocity;
MoveAndSlide();
}
}
群体行为
Boids 算法
public class Boid : Node2D
{
private Vector2 _velocity;
private List<Boid> _neighbors = new();
public override void _Process(double delta)
{
// 更新邻居列表
UpdateNeighbors();
// 计算三个力
var separation = CalculateSeparation();
var alignment = CalculateAlignment();
var cohesion = CalculateCohesion();
// 合并力
var acceleration = separation * 1.5f + alignment * 1.0f + cohesion * 1.0f;
// 更新速度和位置
_velocity += acceleration * (float)delta;
_velocity = _velocity.LimitLength(MaxSpeed);
Position += _velocity * (float)delta;
}
private Vector2 CalculateSeparation()
{
var steer = Vector2.Zero;
int count = 0;
foreach (var neighbor in _neighbors)
{
float distance = Position.DistanceTo(neighbor.Position);
if (distance > 0 && distance < SeparationRadius)
{
var diff = Position - neighbor.Position;
diff = diff.Normalized() / distance;
steer += diff;
count++;
}
}
if (count > 0)
steer /= count;
return steer;
}
private Vector2 CalculateAlignment()
{
var avgVelocity = Vector2.Zero;
int count = 0;
foreach (var neighbor in _neighbors)
{
if (Position.DistanceTo(neighbor.Position) < AlignmentRadius)
{
avgVelocity += neighbor._velocity;
count++;
}
}
if (count > 0)
{
avgVelocity /= count;
avgVelocity = avgVelocity.Normalized() * MaxSpeed;
return avgVelocity - _velocity;
}
return Vector2.Zero;
}
private Vector2 CalculateCohesion()
{
var center = Vector2.Zero;
int count = 0;
foreach (var neighbor in _neighbors)
{
if (Position.DistanceTo(neighbor.Position) < CohesionRadius)
{
center += neighbor.Position;
count++;
}
}
if (count > 0)
{
center /= count;
return (center - Position).Normalized() * MaxSpeed - _velocity;
}
return Vector2.Zero;
}
}
决策系统
效用 AI
public class UtilityAI
{
private readonly List<UtilityAction> _actions = new();
public void AddAction(UtilityAction action)
{
_actions.Add(action);
}
public UtilityAction ChooseBestAction()
{
float bestScore = float.MinValue;
UtilityAction bestAction = null;
foreach (var action in _actions)
{
float score = action.CalculateScore();
// 添加随机性
score += GD.Randf() * 0.1f;
if (score > bestScore)
{
bestScore = score;
bestAction = action;
}
}
return bestAction;
}
}
public abstract class UtilityAction
{
public abstract float CalculateScore();
public abstract void Execute();
}
public class AttackAction : UtilityAction
{
private readonly Enemy _enemy;
public AttackAction(Enemy enemy)
{
_enemy = enemy;
}
public override float CalculateScore()
{
float score = 0f;
// 玩家距离近,分数高
float distance = _enemy.DistanceToPlayer();
score += Mathf.Clamp(1.0f - distance / 200.0f, 0, 1) * 0.5f;
// 有弹药,分数高
score += _enemy.HasAmmo ? 0.3f : 0.0f;
// 生命值高,更倾向于攻击
score += _enemy.HealthPercent * 0.2f;
return score;
}
public override void Execute()
{
_enemy.Attack();
}
}
感知系统
视觉感知
public class VisionSensor : Node2D
{
[Export] public float ViewDistance = 300.0f;
[Export] public float ViewAngle = 90.0f;
[Export] public LayerMask VisionLayer;
public Node2D Target { get; private set; }
public override void _Process(double delta)
{
Target = null;
var players = GetTree().GetNodesInGroup("player");
foreach (Node2D player in players)
{
if (CanSee(player))
{
Target = player;
break;
}
}
}
private bool CanSee(Node2D target)
{
var direction = target.GlobalPosition - GlobalPosition;
float distance = direction.Length();
// 检查距离
if (distance > ViewDistance)
return false;
// 检查角度
float angle = Mathf.RadToDeg(direction.Angle());
float angleDiff = Mathf.Abs(Mathf.AngleDifference(angle, GlobalRotationDegrees));
if (angleDiff > ViewAngle / 2)
return false;
// 射线检测
var space = GetWorld2D().DirectSpaceState;
var query = new PhysicsRayQueryParameters2D
{
From = GlobalPosition,
To = target.GlobalPosition,
CollisionMask = VisionLayer
};
var result = space.IntersectRay(query);
if (result.Count > 0)
{
var collider = result["collider"].As<Node>();
return collider == target;
}
return true;
}
// 可视化调试
public override void _Draw()
{
// 绘制视野扇形
DrawArc(Vector2.Zero, ViewDistance, -ViewAngle / 2, ViewAngle / 2, 32, Colors.Yellow, 1.0f);
}
}
参考资源
- 行为树模式: references/behavior-trees.md
- 寻路算法: references/pathfinding.md
- 高级技术: references/advanced-ai.md
Comments
Loading comments...
