在现代游戏开发中,AI 行为的可扩展性与维护性是衡量项目质量的重要指标。策略模式(Strategy Pattern)通过把一组算法封装为独立的类,让它们可以互相替换,从而实现了行为的可插拔和组合。下面将以 C++ 为例,演示如何在游戏 AI 中实现一个基于策略模式的寻路与决策系统。
1. 需求概述
- 寻路算法:支持多种寻路策略(A*、Dijkstra、BFS 等)。
- 攻击决策:根据敌人距离、血量、技能冷却等因素动态选择技能。
- 行为树的简化:把行为树中的“叶子节点”抽象为策略对象,提升复用性。
2. 设计思路
- 抽象策略接口:统一声明策略方法(如
Execute())。 - 上下文对象:维护当前状态(如位置、目标、技能列表)。
- 具体策略实现:不同算法实现各自的逻辑。
- 组合使用:AIController 通过策略组合来决定整体行为。
3. 代码实现
3.1 共同接口
// IStrategy.h
#pragma once
#include <memory>
class AIContext; // 前向声明
class IStrategy
{
public:
virtual ~IStrategy() = default;
virtual void Execute(AIContext& ctx) = 0;
};
3.2 AI 上下文
// AIContext.h
#pragma once
#include <vector>
#include <memory>
#include <unordered_map>
#include <string>
#include "IStrategy.h"
struct Vector2 { float x, y; };
struct Skill
{
std::string name;
float cooldown;
float remainingCD = 0.0f;
};
class AIContext
{
public:
Vector2 position;
Vector2 target;
std::vector <Skill> skills;
// 方便调试:当前激活的策略类型
std::string currentStrategy;
// 统一调度接口
void Update(float deltaTime);
};
// AIContext.cpp
#include "AIContext.h"
void AIContext::Update(float deltaTime)
{
// 这里简化为只执行寻路策略
// 在实际项目中可同时执行多种策略
// e.g., 路径规划 + 决策
}
3.3 具体策略:A* 寻路
// AStarStrategy.h
#pragma once
#include "IStrategy.h"
class AStarStrategy : public IStrategy
{
public:
void Execute(AIContext& ctx) override;
};
// AStarStrategy.cpp
#include "AStarStrategy.h"
#include "AIContext.h"
#include <queue>
#include <unordered_set>
#include <cmath>
struct Node
{
Vector2 pos;
float g; // 从起点到该点的代价
float h; // 启发式估价
float f() const { return g + h; }
};
struct NodeComparator
{
bool operator()(const Node& a, const Node& b) const { return a.f() > b.f(); }
};
float heuristic(const Vector2& a, const Vector2& b)
{
return std::sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
void AStarStrategy::Execute(AIContext& ctx)
{
// 简单的网格化 A*,假设每个单元格为 1.0f
std::priority_queue<Node, std::vector<Node>, NodeComparator> openSet;
std::unordered_set<std::string> closedSet;
Node start{ ctx.position, 0.0f, heuristic(ctx.position, ctx.target) };
openSet.push(start);
while (!openSet.empty())
{
Node current = openSet.top();
openSet.pop();
std::string key = std::to_string(current.pos.x) + "," + std::to_string(current.pos.y);
if (closedSet.count(key)) continue;
closedSet.insert(key);
if (std::abs(current.pos.x - ctx.target.x) < 0.1f &&
std::abs(current.pos.y - ctx.target.y) < 0.1f)
{
// 到达目标,更新 AI 状态
ctx.currentStrategy = "A* reached target";
break;
}
// 生成四个邻居
std::vector <Vector2> neighbors = {
{ current.pos.x + 1.0f, current.pos.y },
{ current.pos.x - 1.0f, current.pos.y },
{ current.pos.x, current.pos.y + 1.0f },
{ current.pos.x, current.pos.y - 1.0f }
};
for (auto& nb : neighbors)
{
Node next{ nb,
current.g + 1.0f, // 假设每步代价为 1
heuristic(nb, ctx.target) };
openSet.push(next);
}
}
}
3.4 具体策略:BFS 纯行走
// BFSSteeringStrategy.h
#pragma once
#include "IStrategy.h"
class BFSSteeringStrategy : public IStrategy
{
public:
void Execute(AIContext& ctx) override;
};
// BFSSteeringStrategy.cpp
#include "BFSSteeringStrategy.h"
#include "AIContext.h"
#include <queue>
void BFSSteeringStrategy::Execute(AIContext& ctx)
{
// 这里仅演示 BFS 走向目标的最短步数
std::queue <Vector2> q;
q.push(ctx.position);
std::unordered_set<std::string> visited;
visited.insert(std::to_string(ctx.position.x) + "," + std::to_string(ctx.position.y));
while (!q.empty())
{
Vector2 cur = q.front(); q.pop();
if (std::abs(cur.x - ctx.target.x) < 0.1f &&
std::abs(cur.y - ctx.target.y) < 0.1f)
{
ctx.currentStrategy = "BFS reached target";
break;
}
std::vector <Vector2> dirs = {
{ cur.x + 1, cur.y }, { cur.x - 1, cur.y },
{ cur.x, cur.y + 1 }, { cur.x, cur.y - 1 }
};
for (auto& d : dirs)
{
std::string key = std::to_string(d.x) + "," + std::to_string(d.y);
if (visited.count(key)) continue;
visited.insert(key);
q.push(d);
}
}
}
3.5 AI 控制器
// AIController.h
#pragma once
#include "AIContext.h"
#include <vector>
#include <memory>
class AIController
{
public:
AIController(AIContext& ctx) : context(ctx) {}
void RegisterStrategy(std::unique_ptr <IStrategy> strat) { strategies.push_back(std::move(strat)); }
void Update(float deltaTime);
private:
AIContext& context;
std::vector<std::unique_ptr<IStrategy>> strategies;
};
// AIController.cpp
#include "AIController.h"
void AIController::Update(float deltaTime)
{
// 简单轮询:先执行寻路,再执行攻击决策
for (auto& strat : strategies)
{
strat->Execute(context);
}
// 更新技能冷却
for (auto& skill : context.skills)
{
if (skill.remainingCD > 0.0f)
skill.remainingCD -= deltaTime;
}
}
4. 运行示例
int main()
{
AIContext ctx;
ctx.position = {0, 0};
ctx.target = {5, 3};
AIController controller(ctx);
controller.RegisterStrategy(std::make_unique <AStarStrategy>());
controller.RegisterStrategy(std::make_unique <BFSSteeringStrategy>());
for (int i = 0; i < 10; ++i)
{
controller.Update(0.016f); // 16 ms 每帧
std::cout << "Step " << i << ": Strategy used -> " << ctx.currentStrategy << '\n';
}
return 0;
}
运行后,你会看到 AI 先尝试 A* 寻路,然后若未成功则退回到 BFS,展示了策略模式对不同算法的灵活切换。
5. 进一步扩展
- 状态模式:把 AI 的整体状态(巡逻、追踪、攻击)封装成状态类,进一步解耦。
- 行为树:将策略作为行为树的叶子节点,实现更细粒度的组合。
- 热更新:通过脚本或插件方式动态加载策略,实现快速迭代。
策略模式的核心优势在于“开放-闭合原则”:对扩展开放,对修改封闭。只要添加新的策略实现即可,无需改动现有代码,极大提升游戏 AI 的可维护性和可扩展性。