pygame 构建游戏实体、构建世界

1,016 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情

pygame 实现状态机

imp大脑的两种状态形成了一个非常简单的状态机。一个州通常定义两件事:

·npc当时在做什么

·在什么情况下它应该切换到另一种状态

从探索状态到寻找状态的条件是

self.can_see(玩家)-换句话说,“我(小鬼)能看到玩家吗?相反的条件(不是self.can_see(玩家))用于从寻找中回来到探索。图7-1是imp的状态机的示意图,它实际上是它的大脑。箭头定义状态与切换状态必须满足的条件之间的链接。状态机中的链接始终是单向的,但可能有另一个链接返回到原始状态。在恢复到原始状态之前,也可能有几个中间状态,这取决于NPC行为的复杂性。

image.png

除了当前行为和条件之外,状态还可能包含进入操作和退出操作。进入操作是在进入新状态之前执行的操作,通常用于执行要运行的状态所需的一次性操作。对于 imp 的状态机中的寻道状态,进入操作可能会计算朝向玩家的航向,并播放噪音以指示它已看到玩家 , 或者准备战斗所需的任何其他内容。退出操作与进入操作相反,是

在离开状态时执行。

让我们创建一个更有趣的状态机,以便我们可以将其付诸实践。我们将创建一个蚂蚁巢穴的模拟。昆虫经常在以下情况下使用:

尝试AI,因为它们具有非常简单的行为,易于建模。在我们的模拟世界中,我们将有三个实体:树叶,蜘蛛和蚂蚁本身。叶子将在屏幕上的随机位置生长,并被蚂蚁收获并返回巢穴。蜘蛛在屏幕上徘徊,只要它们不靠近巢穴,蚂蚁就会容忍它们。如果蜘蛛进入巢穴,它将被追逐和咬伤,直到它死亡或设法到达足够远的地方。

注意 尽管我们在此模拟中使用了昆虫主题,但我们将编写的 AI 代码适用于许多场景。如果我们用巨大的“机甲”机器人、坦克和油滴来代替蚂蚁、蜘蛛和树叶,那么模拟仍然有意义。

游戏实体

虽然我们有三种不同类型的实体,但最好为包含公共属性和操作的游戏实体提供一个基类。这样,我们就不需要为每个实体复制代码,并且可以轻松添加其他实体,而无需太多额外的工作。

实体需要存储其名称(“蚂蚁”、“树叶”或“蜘蛛”),以及其当前位置、目的地、速度和用于在屏幕上表示它的图像。您可能会发现“叶子”实体将具有目的地和速度,这很奇怪。我们不会有神奇的行走叶子;我们将简单地将它们的速度设置为零,以便它们不会移动。这样,我们仍然可以用与其他实体相同的方式处理叶子。除了这些信息之外,我们还需要为游戏实体定义一些常用函数。我们需要一个函数来将实体呈现到屏幕上,另一个函数来处理实体(即,更新其在屏幕上的位置)。清单 7-3 显示了用于创建 GameEntity 类的代码,该类将用作每个实体的基础。

image.png

image.png

GameEntity 类还保留了对世界的引用,我们将使用世界来存储所有实体的位置。这个 World 对象很重要,因为它是实体了解模拟中其他实体的方式。实体还需要一个ID来识别它在世界上,以及一个用于其大脑的状态机器对象(我们将在后面定义)。

GameEntity 的渲染功能只是将实体的图像投影到屏幕上,但首先调整坐标,使当前位置位于图像的中心下方,而不是左上角。我们这样做是因为实体将被视为具有点和半径的圆,这将在需要检测与其他实体的交互时简化数学运算。

GameEntity对象的过程函数首先调用自我.大脑.think,它将运行状态机来控制实体(通常是通过改变其

目的地)。在这个模拟中,只有蚂蚁会使用状态机,但我们可以将AI添加到任何实体中。如果我们尚未为实体构建状态机,则此调用将简单地返回而不执行任何操作。流程函数的其余部分将实体移动到其目标(如果尚未存在)。

构建世界

现在我们已经创建了一个 GameEntity 类,我们需要为实体创建一个可以居住的世界。这个模拟对世界来说并不多- 只是一个巢穴,由屏幕中心的一个圆圈表示,以及许多不同类型的游戏实体。World 级(参见清单 7-4)绘制嵌套并管理其实体。

image.png

image.png

因为我们有许多GameEnt对象,所以使用Python列表对象来存储它们是很自然的。虽然这可以工作,但我们会遇到问题;当一个实体需要从世界中移除(即它死了)时,我们会必须搜索列表以查找其索引,然后调用 del 将其删除。搜索列表可能会很慢,并且只会随着列表的增长而变慢。存储实体的更好方法是使用Python字典,即使有很多实体,它也可以有效地找到实体。

要在字典中存储实体,我们需要一个值用作键,它可以是字符串、数字或其他值。想每个蚂蚁的名字是很困难的,所以我们只需按顺序对蚂蚁进行编号:第一只蚂蚁是#0,第二只蚂蚁是#1,依此类推。此数字是实体的 ID,存储在每个 GameEntity 对象中,以便我们始终可以在字典中找到该对象(参见图 7-2)。

image.png

注释 将增量数字映射到其值的字典类似于列表,但如果删除值,键不会向下洗牌。因此,5号蚂蚁仍然是蚂蚁5号,即使蚂蚁#4被移除。

世界级中的大多数功能都负责以某种方式管理实体。有一个添加实体函数可以将实体添加到世界中,一个

remove_entity函数将其从世界中移除,以及一个给定其id查找实体的get函数。如果 get 在实体字典中找不到 id,它将返回 None。这很有用,因为它会告诉我们一个实体已被删除(id值永远不会被重用)。考虑一下一群蚂蚁正在紧追入侵巢穴的蜘蛛的情况。每个蚂蚁对象都存储它正在追逐的蜘蛛的ID,并将查找它(使用get)以检索蜘蛛的位置。然而,在某些时候,不幸的蜘蛛将被派遣并从世界上移除。发生这种情况时,任何对带有蜘蛛ID的get函数的调用都将返回None,因此蚂蚁将知道它们可以停止追逐并返回其他职责。

同样在世界级中,我们有一个进程和一个渲染函数。World 对象的流程函数调用每个实体的流程函数,以使其有机会更新其位置。渲染函数类似;除了绘制背景之外,它还调用每个实体的相应渲染函数,以在其位置绘制相应的图形。

最后,在Wor1d类中有一个名为get_close_entity的函数,它找到一个与世界上某个位置有一定距离的实体。这在模拟中的多个位置使用。

说明 实施NPC时,一般应限制信息

因为它可以得到,因为像真实的人一样,NPC可能不一定知道世界上正在发生的一切。我们用蚂蚁模拟这一点,只让它们看到有限距离内的物体。