Keep your place in this quest

Log in or sign up for free to subscribe, follow lesson progress, and access more learning content.

随着游戏玩法的增加,您最终需要具有不同的行为的对象,具体取决于它们当前正在做什么。一个敌人可以是待命、巡逻、追逐、攻击、晕厥或死亡。一个门可以是关闭、打开、打开或锁定。一个Boss战斗可以经历几个阶段。

这就是状态机派上用场的地方。

image.png

在上面的图像中,您可以看到Crave为新项目创建的默认敌人,状态机在右侧打开。我们处于“漫游”状态,有内部状态,例如“随机选择器”(初始状态,以绿色表示)、“前往随机位置”和“待命”。您可以看到状态之间的过渡由箭头表示,每个状态可以拥有自己的逻辑,无论是用Python编写,如图所示,还是用逻辑砖。

状态机帮助您将行为组织到清晰的状态中,而不是编写一个包含无关条件的大脚本。

在本课程中,您将学习:

  • 状态是什么。
  • 过渡是什么。
  • 为什么层次状态机有用。
  • Cave的状态机组件如何适配一个实体。
  • On EnterOn UpdateOn Exit的工作原理。
  • 如何在状态和过渡中使用Python和逻辑砖。

目标是首先理解思维模型。一旦这有意义,编辑器的使用就会变得容易得多。

什么是状态?

状态描述了一个对象当前正在做什么。

image.png

例如,一个敌人可能有以下状态:

  • Idle
  • Patrol
  • Chase
  • Attack
  • Dead

通常情况下,这些状态中只有一个应该控制敌人。如果敌人在Patrol,它就不应该同时执行攻击行为。如果它处于Dead,它就不应该继续追逐玩家。

这使得游戏玩法更加容易理解,因为每个状态都有明确的职责。

状态 可能的行为
Idle 静止不动并等待。
Patrol 在点之间移动。
Chase 朝向玩家移动。
Attack 播放攻击动画并造成伤害。
Dead 禁用移动并播放死亡动画。

与其问“敌人在这一帧应该做什么?”来自一个巨大的脚本,不如问“敌人当前处于哪个状态?”

什么是过渡?

过渡是将状态机从一个状态移动到另一个状态的规则。

image.png

例如:

  • 当敌人应该开始移动时,从IdlePatrol,经过一个计时器。
  • 当玩家被检测到时,从PatrolChase
  • 当敌人足够接近时,从ChaseAttack
  • 当攻击完成时,从AttackChase
  • 当健康值达到零时,从任何状态到Dead

过渡不是行为本身。它是做出决定的规则,表示“现在我们应该更改状态。”

这种分离很重要。Attack状态可以专注于攻击,而过渡可以决定何时进入或离开该状态。

为什么层次状态机有用

Cave使用一种层次状态机的方法。这意味着状态可以组织在其他状态内部。这是有用的,因为许多行为共享一个共同的父想法。

例如,一个敌人可以组织成:

Alive
    Idle
    Patrol
    Chase
    Attack
Dead

IdlePatrolChaseAttack状态都属于更广泛的Alive状态。这使结构更容易理解,并且可以帮助您避免在多个地方重复相同的逻辑。

对于初学者项目,您可以从简单的平面状态机开始。但是随着游戏玩法的复杂化,层次结构有助于保持逻辑清晰。

状态机组件

要在场景中使用状态机,实体需要一个状态机组件

该组件将实体连接到状态机资产。资产定义状态、过渡和逻辑,而组件在游戏玩法中运行这些逻辑。

这与其他Cave系统相似:

资产 组件
Python脚本 Python组件运行它。
动画 动画组件播放它。
状态机 状态机组件运行它。

在实践中,这意味着您的敌人实体可以具有状态机组件,并且该组件可以运行敌人AI状态机。

状态机资产可以具有属性,您可以通过访问属性选项卡来查看这些属性,并且能够在状态机组件中本地修改这些属性,从而使您能够创建具有不同设置的模块化状态机,以适应不同类型的实体。

状态事件

每个状态可以对三个重要时刻做出反应:

事件 运行时间 常见用途
On Enter 当状态开始时。 重置计时器、初始化变量、播放动画、选择目标。
On Update 当状态处于活动状态时。 移动、检查距离、更新计时器。
On Exit 离开状态时。 停止效果、清除临时值。

例如,一个Attack状态可能像这样工作:

  • On Enter:播放攻击动画。
  • On Update:等待击打时刻或动画回调。
  • On Exit:清除攻击标志。

这使每个状态感觉像一个小的自包含行为。

状态中的Python

状态机可以使用Python进行逻辑处理。

在每个状态的Python代码中,Cave提供了有用的变量,如entityscene,因此状态可以控制拥有状态机组件的实体。

例如,一个状态可以在进入时播放声音:

cave.playSound("Enemy Alert", volume=0.8)

或者它可以读取实体的属性:

health = entity.properties.get("health", 100)

这使状态逻辑与其所属的行为紧密相连。

过渡中的Python

过渡也可以使用Python。对于过渡Python逻辑,Cave使用一个名为result的变量。如果result变为True,则可以进行过渡。例如,过渡到Dead状态可以像这样检查健康:

health = entity.properties.get("health", 100)
result = health <= 0

ChaseAttack的过渡可以检查距离,如果您的敌人脚本或属性提供该值:

player = scene.get("Player")
playerPos = player.getTransform().getWorldPosition()
entityPos = entity.getTransform().getWorldPosition()

distanceToPlayer = (playerPos - entityPos).length()
result = distanceToPlayer < 2.0

确切的数据取决于您的游戏,但想法始终是相同的:过渡决定何时是时候从一个状态转移到另一个状态。

提示:在过渡期间,来自于状态的所有局部变量都是可用的。因此,在前面的示例中,我们计算实体与玩家之间的距离,我们可以在进入状态的状态中将玩家变换和实体变换存储到两个变量中,以节省一些资源,因为它的缓存,之后我们不再需要每帧获取这些数据。

状态机中的逻辑砖

状态机还可以使用逻辑砖。

当您想要直观地构建行为时,这非常有用,而不是编写Python代码。对于许多游戏玩法任务,逻辑砖是一种非常易于使用的方法,可以连接事件、条件和动作。

它们的工作方式与上面解释的状态内部的Python代码或过渡相同。状态的逻辑砖和过渡的逻辑砖已经初始化,并且您可以开始构建所需的砖块。

您可以根据所构建的行为的清晰程度混合Python和逻辑砖。如果状态或过渡都有,那么两者都会执行,并且如果其中之一或另一个发生,过渡将发生。

何时使用状态机

当对象具有明确的行为模式时,使用状态机。

好的例子包括:

  • 敌AI。
  • Boss阶段。
  • 门和可交互对象。
  • 任务或任务流程。
  • 剧情进展。
  • 角色能力状态。

对于微小的单动作对象,您可能不需要状态机。如果一个拾取物体仅播放一个声音并消失,则一个小的Python脚本或逻辑砖设置可能就足够了。

您应该记住的内容

状态机将游戏玩法组织为状态和过渡。

状态描述当前发生的事情。过渡决定何时切换到其他内容。On EnterOn UpdateOn Exit帮助保持每个状态的专注和可读性。

对于简单的行为,脚本可能就足够了。对于具有多种模式的行为,状态机通常使逻辑更容易理解,并且更易于扩展。