在冒险游戏中编码不同的状态


12

我正在计划一款冒险游戏,无法根据故事的进展状态弄清楚实现关卡行为的正确方法。

我的单人游戏拥有一个巨大的世界,玩家必须在游戏中的各个点与城镇中的人们互动。但是,根据故事的进展,会向玩家呈现不同的内容,例如,行会领导将地点从城镇广场更改为城市的各个地点;门仅在完成特定例程后的一天中的特定时间才能解锁;仅在达到特定里程碑后,才会发生不同的切换屏幕/触发事件。

我天真地想到了最初使用switch {}语句来决定NPC应该说什么或可以找到NPC的想法,并且只有在检查了全局game_state变量的条件之后,才能使任务目标可交互。但是我意识到,为了改变对象的行为,我会很快遇到很多不同的游戏状态和切换情况。该switch语句也将非常难以调试,而且我猜它可能也很难在关卡编辑器中使用。

所以我想,不是让单个对象具有多个状态,而是应该让同一个对象的多个实例具有一个状态。这样,如果我使用诸如关卡编辑器之类的工具,则可以将NPC的实例放置在他可能出现的所有不同位置,也可以将其放置在每个对话状态的实例。但这意味着在关卡周围会漂浮着许多不活跃,不可见的游戏对象,这可能会给内存带来麻烦,或者在关卡编辑器中很难看到,我不知道。

或者简单地为每个游戏状态创建一个相同但独立的级别。这感觉是最干净,没有错误的处理方式,但是感觉就像是大量的手工工作,以确保该关卡的每个版本确实彼此相同。

我所有的方法都觉得效率低下,所以回顾一下我的问题,是否有更好的或标准化的方法来实现根据故事进展状态来实现某个级别的行为?

PS:我还没有关卡编辑器-考虑使用JME SDK之类的东西还是自己制作。

Answers:


9

我认为在这种情况下,您需要的是State Design Pattern。创建每个实例而不是每个游戏对象具有多个实例,而是将其行为封装在单独的类中。创建多个类,每种可能的行为一个,并为所有类提供相同的接口。将一个与您的游戏对象(初始状态)相关联,并在条件发生变化(达到里程碑,一天中的时间过去等)时切换该对象的状态(即,根据您的游戏逻辑将其与其他对象相关联)并更新其属性(如果适用)。

一个状态界面外观的示例(完全组成-只是为了说明此方案提供的控制级别):

interface NPCState {
    Scene whereAmI(NPC o);
    String saySomething(NPC o);
}

还有两个实现类:

class Busy implements NPCState {
    Scene whereAmI(NPC o) {
        return o.getWorkScene();
    }
    String saySomething(NPC o) {
        return "Can't talk now, I'm busy!";
    }
}

class Available implements NPCState {
    Scene whereAmI(NPC o) {
        return TAVERN;
    }
    String saySomething(NPC o) {
        String[] choices = o.getRandomChat();
        return choices[RANDOM.getInt(choices.length)];
    }
}

以及切换状态:

// The time of day passed from "afternoon" to "evening"
NPCState available = new Available();
for ( NPC o : list ) {
    Scene oldScene = o.state.whereAmI(o);
    o.state = available;
    Scene newScene = o.state.whereAmI(o);
    moveGameObject(o, oldScene, newScene);
    ...

重要的NPC可能具有其自定义状态,状态选择逻辑可能更具可自定义性,并且对于游戏的不同方面,您可以具有不同的状态(在此示例中,我使用单个类来告知位置和聊天,但是您可以将它们分开它们并进行许多组合)。

这也适用于关卡编辑器:您可以使用一个简单的组合框来切换关卡的“全局”状态,然后根据需要让游戏对象出现在该状态中来添加和重新放置游戏对象。当游戏引擎具有正确的状态时,它仅负责将对象实际“添加”到场景中-但其参数仍然可以以用户友好的方式进行编辑。

(免责声明:我几乎没有游戏编辑器的实际经验,因此我可以对专业编辑器的工作方式充满信心;但是我对状态模式的观点仍然成立,以这种方式组织代码应该是干净,可维护的,而不是浪费系统资源。)


您知道,您可以将此State设计模式与我描述的关联数组结合使用。您可以按照此处所述对状态对象进行编码,然后按照我的建议使用关联数组在不同的状态对象之间进行选择。
2012年

我同意,将游戏与引擎分开也很好,并且硬编码游戏逻辑加强了两者之间的耦合(减少了重用的可能性)。不过,这是一个折衷方案,因为根据预期行为的复杂程度,尝试“软编码”所有内容都可能导致不必要的混乱。在这种情况下,可能需要使用混合方法(即具有“通用”状态转换逻辑,但也允许合并自定义代码)
mgibsonbr 2012年

因此,据我所知,存在一对一的btwn NPCState和GameState映射。然后,我将NPC放入一个数组中并对其进行迭代,在观察到游戏状态变化时分配新的NPCState。NPCState必须能够知道如何处理发送给它的每个差异NPC,因此从本质上讲,NPCState包含给定状态下所有NPC的行为吗?我喜欢所有行为都干净地存储在一个NPCState中,该行为可以清晰地映射到游戏编辑器的实现中,但这有点使NPCState变得非常庞大。
Cardin 2012年

哦,我想我误会了。我对其进行了一些更改,以包括观察员。因此,它是每个差异NPC的一个差异NPCState,除了超级通用的Crowd NPC可以共享状态。对于每个游戏状态,NPC将向观察者注册其自身及其NPCState。因此,观察者将确切知道哪个NPC已注册,以更改在哪个游戏状态下的行为,并简单地遍历它们。在游戏编辑器方面,游戏编辑器只需要将信号传递给观察者即可更改整个关卡的状态。
Cardin 2012年

1
是的,就是这个主意!重要的NPC将拥有许多州,并且状态转换将主要取决于完成的里程碑。通用NPC有时也会对里程碑做出反应,甚至使其选择的状态取决于内部属性(假设所有NPC都有默认的初始状态,当您第一次与其中一个人交谈时,他会自我介绍,然后输入正常状态切换周期)。
mgibsonbr

2

我会考虑的选择是使单个对象响应不同的游戏状态,或者在不同的游戏状态中发挥不同的作用。这两个之间的选择取决于我在游戏中到底想做什么(不同的状态是什么?游戏如何在状态之间转换?等等)。

无论哪种方式,我都不会通过将状态硬编码到游戏代码中来实现。与其在NPC对象中使用大量的switch语句,不如将NPC行为从数据文件加载到关联数组中,然后使用该关联数组为其关联状态运行不同的行为,如下所示:

if (state in behaviors) {
  behaviors[state]();
}

该数据文件将是一种脚本语言吗?我认为纯文本数据文件可能不足以描述行为。无论如何,您应该对它进行动态加载是正确的。我不太想使用游戏编辑器来生成有效的Java代码,它肯定必须进行一些解析。
Cardin 2012年

1
好吧,这是我最初的想法,但是看到mgibsonbr的回答后,我意识到您可以将各种行为编码为单独的类,然后在数据文件中仅说明哪些行为类处于哪个状态。在运行时将数据加载到关联数组中。
2012年

哦,是的,这绝对简单!:相比于嵌入类似的Lua哈哈的场景d ..
卡丹

2

使用观察者模式来查找里程碑更改该怎么办?如果发生更改,则某个类将识别出该更改并处理例如必须对npc进行的更改。

代替提到的状态设计模式,我将使用策略模式。

如果一个npc有n种与角色交互的方式以及可能存在的m个位置,则最多需要设计(m * n)+1个类。使用策略模式,您最终将得到n + m + 1个类,但是其他npc也可以使用这些策略。

因此,可能会有一个处理里程碑的类,而观察该类并处理npc或敌人或应更改的类的类。如果观察者得到更新,他们将决定是否必须对他们统治的实例进行更改。例如,NPC类将在构造函数中通知NPC-Manager何时必须更新以及必须更新什么。


观察者模式似乎很有趣。我认为这可以明确地将一切责任留给全国人大,向国家观察员注册。策略模式感觉很像Unity Engine的Trigger和AI行为,用于共享行为btwn diff游戏对象(我认为)。听起来可行。我NT肯定有什么优点/缺点的权利,但这种团结采用同样的方法也有所安心哈哈..
卡丹

我只使用了这两种模式几次,所以我无法告诉您有关缺点的信息:-/但是,我认为在单一职责和可以测试每种策略的情况下很好:)在许多不同的类别中使用一种策略,您想找到使用该策略的每个类别。
TOAOGG 2012年

0

给出的所有方法都是有效的。这取决于您在任何给定时刻所处的环境。许多冒险或MMO都将这些结合使用。

例如,如果一个关键事件改变了大部分水平(例如收债员清理了您的公寓,其中的每个人都被捕),通常更容易用看起来相似的第二个房间替换整个房间。

太太,如果角色在地图上走来走去,并在不同的地方做不同的事情,您通常会有一个演员在不同的行为对象中旋转(例如,向前走/没有对话,站在这里/关于米奇的死的对话),这可能包括如果目标已实现,则“隐藏”。

就是说,通常具有手动创建的对象的重复项不会引起任何问题。您可以创建多少个对象?如果创建的对象超出游戏的循环范围,请查看它们的“隐藏”属性并跳过,则引擎速度太慢。因此,我不必担心太多。实际上,许多在线游戏都这样做。某些角色或物品始终存在,但不会显示给没有相应任务的角色。

您甚至可以结合使用以下方法:在您的公寓大楼中有两扇门。一个人通往“收债员之前”的公寓,一个人通往之后的公寓。当您进入走廊时,实际上只显示与您的故事进展相关的走廊。这样,您就可以拥有一种通用机制,即“在故事的当前点可见项目”和只有一个目的地的门。或者,您可以制作更复杂的门,这些门具有可以互换的行为,其中之一是“进入完整公寓”,另一个“进入空公寓”。如果实际上只是门的目的地发生了变化,但是如果它的外观也发生了变化(例如,您首先要开门的门前的大锁),这似乎是没有意义的,

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.