哦,行! -在Unity中制作简单的游戏AI

去年,我到达了“保护”开发阶段,需要实现一些基本的AI。

作为软件工程师,我主要从事自动化领域的工作。 自动化带来了一定的智能需求,即使它是非常基础的。 如今,最热门的AI主题是机器和深度学习。 我的工作最接近我的是情感分析的奇怪地方。

因此,面对新生的,空置的游戏角色闲置地站在他们全新的空间站的甲板上,我认为最好对实现和管理其行为的最佳技术进行一些研究。

我需要一种简单的方法来给每个角色一个状态,并让他们根据他们所处的状态来执行特定任务。这引起了诸如

  • 如何在状态之间转换每个NPC?
  • 如何使每个单独的NPC根据其处于的状态执行特定的任务?
  • 每个状态如何包含每个行为?

我读到了很多有关管理游戏AI的难以理解,冗长且令人恐惧的文字,直到我遇到了一个似乎可以满足所有要求的解决方案:有限状态机。

什么是有限状态机?

简而言之,有限状态机可以随时存在于许多状态之一中。 每个状态都带有随状态而变化的行为。 该解释的重点在于,机器具有多种状态之一。 许多状态是有限的。 将机器视为具有x个潜在状态并且当前状态可以随时更改的对象很有用。 更重要的是,当状态改变时,状态包含的行为将被播放。

那么我们如何将其应用于游戏角色呢?

每个字符的有限状态机

描绘场景:

您正在俯视您的空间站。 它刚刚收到了几名无所事事的新兵。 你是他们的上帝。 他们在等待您的订单。 我单击我的角色之一以选择它们,然后单击我的新植物园。 这个角色将要去照料一个巨大的温室中的植物。

我需要触发行为,为此,我需要更改角色的状态。 他们目前的状态是,什么都没有。 他们闲着。 除了站立凝视太空外,没有其他行为与该状态相关。 但是,当我选择它们然后选择植物园时,我会更改角色的状态。

角色的新状态为enroutetosystem。 请注意,状态如何不会变得混乱或类似。 在这一点上,我们并不十分在意角色开始为植物园配乐的最终结果,因为这是一个新状态,我们将在稍后更改。 现在,我们只想去那儿。 到达那里后,我们可以将状态更改为Manningarboretum ,这将带来不同的行为。

我们可以随时更改状态。 因此,如果我想阻止角色在前往植物园的路上行走,可以将状态更改为“空闲”。 空闲状态有什么行为? 没有。 所以角色现在静止不动了。 我们改变了状态并导致行为改变。

每当我们更改状态时,我们都会从一组行为过渡到另一组行为。 我们说,“ 当您处于此状态时,请执行此操作” ,我可以随时更改一个状态,但是我必须始终停止当前状态。 对于以这种方式在行为之间进行转换,要记住的最重要的事情。 我拥有的状态类型是有限的 ,一次只能占据一个状态。

编程有限状态机

有限状态机可以像您想要的那样基本或复杂。 对于Protectorate,我使用的是Unity游戏引擎,因此,希望长时间运行的大多数逻辑和行为都封装在协程中。 每个协程体现了角色可以执行的一系列行为。 此外,当我进入状态时,会启动一个协程,而当退出状态时,会终止一个协程。 基本流程如下:

  1. 输入状态
  2. 启动关联的协程。
  3. 退出以上状态
  4. 停止上述协程
  5. 重复相关的必需状态。

上面的伪代码可能看起来像这样:

ENTER IDLE STATE

START COROUTINE(DO NOTHING)

...........time passes

EXIT IDLE STATE

STOP COROUTINE(DO NOTHING

ENTER ENROUTETODESTINATION STATE

START COROUTINE(WALK TO DESTINATION)

...........time passes.......character arrives which triggers:

EXIT ENROUTETODESTINATION STATE

STOP COROUTINE(WALK TO DESTINATION)

ENTER MAN SYSTEM STATE

START COROUTINE(MAN SYSTEM) etc

在Unity中,我们正在编写C#代码。 为此,我们可以轻松地将状态表示为枚举内的一组常量。

public enum State
{
Idle,
EnRouteToSystem,
ManningSystem,
EnRouteToQuarters,
InQuarters,
EnRouteToGenericLocation,
Sleeping
}

然后,我使用get创建State枚举的实例; 并设置 属性,这样我们就可以通过将状态设置为上述值之一来启动或停止相关的行为协程。

 private State _currentState; 
  public State state 
{
get
{
return _currentState;
}
set
{
ExitState(_currentState);
_currentState = value;
EnterState(_currentState);
}
}

在下面,您可以看到我们如何退出当前状态并进入新状态,从而导致新协程的启动和停止,从而使行为立即改变。

 void ExitState(State state) 
{
switch (state)
{
case State.Idle:
StopCoroutine("Idle");
break;
case State.EnRouteToSystem:
StopCoroutine("EnRouteToSystem");
break;
case State.ManningSystem:
StopCoroutine("ManningSystem");
break;
case State.EnRouteToQuarters:
StopCoroutine("EnRouteToQuarters");
break;
case State.InQuarters:
StopCoroutine("InQuarters");
break;
case State.EnRouteToGenericLocation:
StopCoroutine("EnRouteToGenericLocation");
break;
case State.Sleeping:
StopCoroutine("Sleeping");
break;
default:
break;
}
  } 
 void EnterState(State state) 
{
switch (state)
{
case State.Idle:
StartCoroutine("Idle");
break;
case State.EnRouteToSystem:
StartCoroutine("EnRouteToSystem");
break;
case State.ManningSystem:
StartCoroutine("ManningSystem");
break;
case State.EnRouteToQuarters:
StartCoroutine("EnRouteToQuarters");
break;
case State.InQuarters:
StartCoroutine("InQuarters");
break;
case State.EnRouteToGenericLocation:
StartCoroutine("EnRouteToGenericLocation");
break;
case State.Sleeping:
StartCoroutine("Sleeping");
break;
default:
break;
}
}

它不是SkyNet,但至少是一种自动更改和执行脚本操作的方式。 这种实现有限状态机的方式的好处在于它是可扩展的。 从理论上讲,您只需要向列表中添加新状态。 您的协程可以处于所需的任何级别。

至于这是否是真正的干净代码……..在这篇文章中,我们不要处理吗?