【发布时间】:2015-01-14 20:19:44
【问题描述】:
我正在开发一个多人游戏服务器,该游戏必须控制数千个在世界各地奔跑的生物。每个生物都有一个带有心跳方法的 AI,如果玩家在附近,每隔几毫秒/秒就会调用一次,这样他们就可以做出反应。
目前 AI 使用枚举器作为“例程”,例如
IEnumerable WanderAround(int radius)
{
// Do something
}
从“状态方法”中调用,这些方法在 foreachs 中调用,在心跳中产生,因此您在每次滴答时都回到同一个位置。
void OnHeartbeat()
{
// Do checks, maybe select a new state method...
// Then continue the current sequence
currentState.MoveNext();
}
当然,例程也必须在循环中调用,因为否则它们不会执行。但由于我不是编写这些 AI 的人,而是不一定是程序员的新手,因此我在服务器启动时编译它们之前预编译 AI(简单的 .cs 文件)。这给了我这样的 AI 脚本:
override IEnumerable Idle()
{
Do(WanderAround(400));
Do(Wait(3000));
}
override IEnumerable Aggro()
{
Do(Attack());
Do(Wait(3000));
}
Do 被一个迭代例程调用的 foreach 替换。
我真的很喜欢这种设计,因为 AI 易于理解,但功能强大。这不是简单的状态,但也不难理解/编写行为树。
现在我的实际“问题”,我不喜欢Do 包装器,我不喜欢预编译我的脚本。但是我想不出没有循环的任何其他方法来实现它,由于冗长和编写这些脚本的人的技能水平,我想隐藏它。
foreach(var r in Attack()) yield return r;
我希望有一种方法可以在没有显式循环的情况下调用例程,但这是不可能的,因为我必须从 state 方法中让步。
而且我不能使用 async/await,因为它不符合我所依赖的 tick 设计(AI 可能非常复杂,老实说我不知道如何使用 async 来实现它)。另外,我只是将Do() 与await 进行交易,并没有太大的改进。
所以我的问题是:任何人都可以想办法摆脱那个循环包装器吗?我愿意使用其他可以用作脚本的 .NET 语言(在服务器启动时编译它们),如果有一种以某种方式支持的话。
【问题讨论】:
-
你能改用
events,让每个AI实现一个事件处理程序吗?然后你所做的就是引发事件(空闲、仇恨等),每个 AI 都会按照脚本进行响应。 .NET 框架将处理所有订阅 AI 事件处理程序的循环。 -
不起作用,因为脚本编写者定义的动作不会立即执行 =/ 例如,一个 AI 可能应该走 100 个单位,然后说点什么,稍等片刻,然后攻击, 都是为了响应一个事件。而且由于 AI 太多,我不想给每个 AI 分配自己的线程/任务。
标签: c# multiplayer enumerator game-ai behavior-tree