MetaAgent,一个转向行为模板库






4.89/5 (18投票s)
2003年5月13日
5分钟阅读

115454
用于创建具有(有趣)类生命行为的自主代理的库。
引言
本文介绍 MetaAgent,一个用于创建转向行为的 C++ 库。

关于行为的一些历史
1986 年,Craig Reynolds 正在编写 boids,一个用于动画动物运动(如鸟群和鱼群)的计算机模型。他于 [Craig Reynolds, 87] 发表了一篇关于它的技术论文。他的方法因其简单性而令人惊叹,因为该模型基于 3 个简单规则:
分离:一个 boid 应该避开它的邻居。为此,您只需将 boid 远离其最近邻居的中心即可。
对齐:一个 boid 倾向于将其速度与邻居对齐。
内聚:一个 boid 倾向于靠近它的邻居
这 3 个规则产生的力通过求和(带权重)合并在一起并应用于 boid。Craig Reynolds 的 boids 已经并且仍然在他的个人网页[^]上飞翔。
此后,Craig Reynolds 又发布了一篇重要论文 [Craig Reynolds, 99],描述了许多行为,以“赋予”自主角色“生命”:目标追踪、避障、漫游等...
MetaAgent 和 OpenSteer
MetaAgent 并非唯一围绕转向行为的项目。事实上,它是另一个项目 OpenSteer(由 Craig Reynolds 发起)的小妹妹。

为什么是另一个库?
首先,玩自主角色很有趣,如果您计划学习 C++,这是一个很棒的项目。这基本上就是 MetaAgent 的开始方式:一个用于测试泛型编程和元编程的“游乐场”。
构建另一个库的真正原因是 OpenSteer 主要是一个 C 函数包装到一些 C++ 类中的集合(好吧,我夸张了……)。MetaAgent 计划(并有望成功)利用 C++ 和泛型编程的全部力量来创建行为。
MetaAgent 指南
以下是该项目尝试遵循的一些指南
- 将所有类分解为正交的策略(我稍后会谈到它)
- 使用信号和槽进行渲染,
- 尽可能多地使用 STL 和 Boost
策略类设计
我在 Andrei Alexandrescu 的著名书籍《Modern C++ design》中遇到了策略类设计,参见 [Alexandrescu, 2001]。基本思想是通过组合小类(称为
Andrei Alexandrescu 用整整一章的篇幅介绍了策略设计,我将尝试在下面的代理行为创建中进行说明。
自主代理如何工作?
代理基本上是一个身体(动力学),它根据其大脑(行为)移动。它可以分解为几个部分
- 身体,实现动力学
- 大脑,由一个行为组成
使用策略构建代理
策略分解
“就好像 [some_host_class] 充当了一个小型代码生成引擎,你配置了它生成代码的方式。”Andrei Alexandrescu。
让我们从构建代理的动态模型开始。这个主体必须能够移动并对转向力(将由行为给定)做出反应。
如前所述,我们希望使用策略。因此,我们希望将该模型分解为正交策略。让我们看看以下事实
- 无论动态模型类型如何,您总是可以检索质心状态。
- 动态模型不需要知道代理“大脑”中发生了什么,它只需要最终的转向

动态模型和行为可以看作是策略
template<
typename ModelPolicy,
typename BehaviorPolicy
>
class agent : public ModelPolicy, public BehaviorPolicy
如您所见,`agent` 继承自 `ModelPolicy` 和 `BehaviorPolicy`,因此它继承了它们的所有方法!`agent` 被称为宿主类,因为它是由策略构建的。
确定接口
与经典接口(纯虚方法的集合)不同,策略接口是松散定义的。只需在宿主类中使用策略方法,无需任何事先声明,如果它们未在策略类中定义,编译器将报错。因此,我们只需编写一个方法,让代理思考和行动
template<
typename ModelPolicy,
typename BehaviorPolicy
>
class agent : public ModelPolicy, public BehaviorPolicy
{
public:
void think_and_act()
{
第一步,思考并计算转向力。这将是 BehaviorPolicy
的工作。
// vec is some 2D vector
vec steering_force = think( get_acceleration(), get_velocity(), get_position() );
第二步,将计算出的转向力施加到模型上并积分方程
act( steering_force ); // move according to the steering force -> ModelPolicy
};
};
太棒了,我们刚刚定义了 ModelPolicy
和 BehaviorPolicy
的接口。
实现 ModelPolicy
实现策略的类称为策略类。最简单的动态模型是点质量模型,通过显式欧拉方案积分
class point_mass_model { public: void act( vec steering ) { m_acceleration = m_steering / m_mass; m_velocity += m_acceleration; m_position += m_velocity; } protected: vec m_acceleration; vec m_velocity; vec m_position; };
现在,`point_mass_model` 类几乎可以使用了。我们只需要为状态添加一些 getter(`get_acceleration` 等),因为 `BehaviorPolicy` 需要它们
class point_mass_model
{
...
vec const & get_accelartion() const { return m_acceleration;};
...
};
实现 BehaviorPolicy
行为策略类只需要实现 think
方法。以下行为使代理
- 以圆形移动(通过取垂直于速度的方向)
// this class makes the agent go round struct circle_move_behavior { // this is the interface to implement vec think( vec const& acceleration, vec const& veloctiy, vec const& position ) const { return -perpendicular( velocity ); }; };
- 追踪目标(通过将速度指向目标)
struct seek_behavior { // the target vec m_target; // this is the interface to implement vec think( vec const& acceleration, vec const& veloctiy, vec const& position ) const { return m_target - position; }; };
在宿主类中合并策略
策略的魔力就在这里。通过合并不同的策略,我们创建了完全不同的代理
// agent will go round
agent< point_mass_model, circle_move_behavior > circle_mover;
// this agent will track a target
agent< point_mass_model, seek_behavior > seeker;
更好的是,您可以通过以下方式简单地更改目标
seeker.m_target = new_target;
因为 seeker 继承自 seek_behavior
并且 m_target
是一个公共属性。
小结
使用策略,创建具有不同动力学和行为的代理就像更改一些模板参数一样简单。这是 MetaAgent 背后的主要思想。

想参与吗?
以上是对使用策略构建行为可能性的非常粗略的描述。例如,寻求目标可以分解为
- 预测目标碰撞点:
PredictoryPolicy
- 跟踪预测的碰撞点:
TrackerPolicy
。
然后,您可以灵活地组合各种预测器和跟踪器。
如果您感兴趣,可以访问 MetaAgent WikiWikiWeb[^] 学习/贡献项目。
以下是 MetaAgent 演示应用程序的一些快照

漫游行为。

寻求行为变体。
历史
05-20-2003 | 已修复图像链接到新站点 |
05-12-2003 | 初次发表 |
参考
[MetaAgent] | http://metaagent.sourceforge.net[^] |
[Craig Reynolds, 87] | http://www.red3d.com/cwr/papers/1987/boids.html[^] |
[Craig Reynolds, 99] | http://www.red3d.com/cwr/papers/1999/gdc99steer.html[^] |
[OpenSteer] | http://opensteer.sourceforge.net[^] |