Сопрограммы хорошо подходят для этого, так как вы можете хранить все свои локальные переменные состояния без проблем. То есть без необходимости вручную сохранять его где-либо в контексте.
Но я не вижу систему событий как альтернативу. Больше как дополнение, которое вы, скорее всего, захотите иметь в дополнение к системе сценариев на основе сопрограмм.
Пример (в несколько связном C ++):
Вы реализовали поведение, используя сопрограммы по следующим направлениям:
class EnterHouse : public NPCBehavior
{
EnterHouse(House theHouse) { _theHouse = theHouse; }
void Begin() { _theHouse.AddNPC(NPC()); }
void Update()
{
NPC().WalkTo(_theHouse.GetDoor().Position());
NPC().FaceObject(_theHouse.GetDoor());
NPC().PlayAnimation(NPC().Animations().Get(eAnimKnockOnDoor));
Sleep(1.0f);
NPC().OpenDoor(_theHouse.GetDoor());
}
void End() { /* Nothing */ }
private House _theHouse;
}
Представьте себе, что методы в NPC сами будут создавать объекты NPCBehavior, помещать их в какой-либо стек поведения и возвращаться из вызова после завершения этого поведения.
Вызов Sleep(1.0f)
приведет к планировщику сценариев и позволит запускать другие сценарии. WalkTo
, FaceObject
, PlayAnimation
и OpenDoor
также вызовут Sleep
для получения дохода. Либо на основе известной продолжительности анимации, чтобы периодически просыпаться, чтобы увидеть, что поисковик и система передвижения сделаны пешком или что-то еще.
Что произойдет, если NPC столкнется с ситуацией, с которой ему придется столкнуться на пути к двери? Вам не нужно проверять все эти события везде в вашем коде на основе сопрограмм. Наличие системы событий, дополняющей сопрограммы, сделает это легко:
Мусорная корзина опрокидывается на : Мусорная корзина может транслировать событие всем ближайшим NPC. Объект NPC решает поместить новый объект поведения в свой стек, чтобы пойти и исправить его. Поведение WalkTo
где-то уступает в вызове Sleep
, но теперь поведение FixTrashcan
выполняется из-за события. Когда FixTrashcan
завершится, поведение WalkTo
проснется от Sleep
и никогда не узнает об инциденте с мусорной корзиной. Но он все еще будет на пути к двери, а под ним мы все еще бежим EnterHouse
.
Произошел взрыв : Взрыв передает событие точно так же, как мусорная корзина, но на этот раз объект NPC решает сбросить его рабочие режимы и нажать FleeInPanic
. Он не вернется к EnterHouse
.
Я надеюсь, вы понимаете, что я имею в виду, когда события и сопрограммы живут вместе в системе ИИ. Вы можете использовать сопрограммы для сохранения локального состояния, в то же время уступая вашему планировщику сценариев, и вы можете использовать события для обработки прерываний и централизации логики для их обработки, не загрязняя ваше поведение.
Если вы еще не видели этой статьи Томаса Тонга о том, как реализовать однопоточные сопрограммы в C / C ++, я очень рекомендую это.
Он использует только малейший бит встроенной сборки (одну инструкцию) для сохранения указателя стека, и код легко переносится на целую кучу платформ. Я работал на Wintel, Xbox 360, PS3 и Wii.
Еще одна приятная вещь в настройке планировщика / сценария состоит в том, что становится ненужным голодать за пределами экрана или удаленные AI-персонажи / объекты сценариев, если вам нужны ресурсы для чего-то еще. Просто соедините его с системой приоритетов в вашем планировщике, и все готово.