在MUD样式的基于文本的世界中组织关卡/房间


12

我正在考虑编写一个小型的基于文本的冒险游戏,但是我不确定如何从技术角度设计世界。

我的第一个想法是使用XML进行设计,其设计如下所示。对大量XML表示歉意,但我认为完全解释我在做什么很重要。

<level>
    <start>
        <!-- start in kitchen with empty inventory -->
        <room>Kitchen</room>
        <inventory></inventory>
    </start>
    <rooms>
        <room>
            <name>Kitchen</name>
            <description>A small kitchen that looks like it hasn't been used in a while. It has a table in the middle, and there are some cupboards. There is a door to the north, which leads to the garden.</description>
            <!-- IDs of the objects the room contains -->
            <objects>
                <object>Cupboards</object>
                <object>Knife</object>
                <object>Batteries</object>
            </objects>
            </room>
        <room>
            <name>Garden</name>
            <description>The garden is wild and full of prickly bushes. To the north there is a path, which leads into the trees. To the south there is a house.</description>
            <objects>
            </objects>
        </room>
        <room>
            <name>Woods</name>
            <description>The woods are quite dark, with little light bleeding in from the garden. It is eerily quiet.</description>
            <objects>
                <object>Trees01</object>
            </objects>
        </room>
    </rooms>
    <doors>
        <!--
            a door isn't necessarily a door.
            each door has a type, i.e. "There is a <type> leading to..."
            from and to are references the rooms that this door joins.
            direction specifies the direction (N,S,E,W,Up,Down) from <from> to <to>
        -->
        <door>
            <type>door</type>
            <direction>N</direction>
            <from>Kitchen</from>
            <to>Garden</to>
        </door>
        <door>
            <type>path</type>
            <direction>N</direction>
            <from>Garden</type>
            <to>Woods</type>
        </door>
    </doors>
    <variables>
        <!-- variables set by actions -->
        <variable name="cupboard_open">0</variable>
    </variables>
    <objects>
        <!-- definitions for objects -->
        <object>
            <name>Trees01</name>
            <displayName>Trees</displayName>
            <actions>
                <!-- any actions not defined will show the default failure message -->
                <action>
                    <command>EXAMINE</command>
                    <message>The trees are tall and thick. There aren't any low branches, so it'd be difficult to climb them.</message>
                </action>
            </actions>
        </object>
        <object>
            <name>Cupboards</name>
            <displayName>Cupboards</displayName>
            <actions>
                <action>
                    <!-- requirements make the command only work when they are met -->
                    <requirements>
                        <!-- equivilent of "if(cupboard_open == 1)" -->
                        <require operation="equal" value="1">cupboard_open</require>
                    </requirements>
                    <command>EXAMINE</command>
                    <!-- fail message is the message displayed when the requirements aren't met -->
                    <failMessage>The cupboard is closed.</failMessage>
                    <message>The cupboard contains some batteires.</message>
                </action>
                <action>
                    <requirements>
                        <require operation="equal" value="0">cupboard_open</require>
                    </requirements>
                    <command>OPEN</command>
                    <failMessage>The cupboard is already open.</failMessage>
                    <message>You open the cupboard. It contains some batteries.</message>
                    <!-- assigns is a list of operations performed on variables when the action succeeds -->
                    <assigns>
                        <assign operation="set" value="1">cupboard_open</assign>
                    </assigns>
                </action>
                <action>
                    <requirements>
                        <require operation="equal" value="1">cupboard_open</require>
                    </requirements>
                    <command>CLOSE</command>
                    <failMessage>The cupboard is already closed.</failMessage>
                    <message>You closed the cupboard./message>
                    <assigns>
                        <assign operation="set" value="0">cupboard_open</assign>
                    </assigns>
                </action>
            </actions>
        </object>
        <object>
            <name>Batteries</name>
            <displayName>Batteries</displayName>
            <!-- by setting inventory to non-zero, we can put it in our bag -->
            <inventory>1</inventory>
            <actions>
                <action>
                    <requirements>
                        <require operation="equal" value="1">cupboard_open</require>
                    </requirements>
                    <command>GET</command>
                    <!-- failMessage isn't required here, it'll just show the usual "You can't see any <blank>." message -->
                    <message>You picked up the batteries.</message>
                </action>
            </actions>
        </object>
    </objects>
</level>

显然,除了此以外,还需要做更多的事情。与人和敌人的互动以及死亡和完成都是必不可少的。由于XML很难使用,因此我可能会创建某种世界编辑器。

我想知道此方法是否有任何不足之处,以及是否有“更好”的方法或更标准的方法。


3
就个人而言,我不会将XML视为序列化格式。如果您抽象出“我将以某种方式将其读写到磁盘上”的问题(使用XML,JSON,协议缓冲区,自定义二进制格式之类的东西),那么问题将变成“我需要存储哪些数据”,只有您才能真正根据自己的游戏要求来回答。
Tetrad

好点子。但是,我之前已经看到游戏使用过这样的样式,并且事实证明它们确实具有限制性。但是,在这种情况下,游戏流程和逻辑非常简单,因此它可能会很好地工作,并使我免于实现脚本引擎。我主要对这样的固定结构(分隔的房间,门,对象,定义文件中的变量在某处)是否可行感兴趣。
多项式

尽量不呼应Tetrad,但是如果您打算制作一个世界编辑器(除非游戏会非常短,否则我建议您这样做),那么文件格式没有任何区别,因为您将在其中使用它。编辑,而不是硬编码房间。
Mike Cluck

Answers:


13

如果您不完全喜欢C#,那么“更标准”的方法是使用许多现有的文本冒险创建工具来帮助人们制作这种游戏。这些工具为您提供了一个已经正常运行的解析器,可以处理死亡,保存/恢复/撤消,角色交互以及其他类似的文本冒险功能标准位。目前,最受欢迎的创作系统是InformTADS(尽管还有其他六种可用)

Inform可以编译为Infocom游戏使用的大多数Z Machine虚拟机指令集,也可以编译为最新的glulx虚拟机指令集。另一方面,TADS会编译成自己的虚拟机代码。

两种类型的二进制文件都可以由大多数现代的交互式小说解释器运行(在过去,您经常需要为TADS游戏和ZMachine游戏或glulx游戏提供单独的解释器。但值得庆幸的是,这些日子基本上已经过去了。)关于您想要的任何平台;Mac / PC / Linux / BSD / iOS / Android / Kindle /浏览器/等。因此,您已经很好地跨平台并且得到了真正的照顾。

对于大多数平台,当前推荐的解释器是Gargoyle,但还有很多其他解释器,因此请随时尝试。

Inform编码(尤其是最新版本)需要一点时间习惯,因为它主要是针对作者而不是工程师进行营销,因此其语法看起来很奇怪,而且几乎是对话式的。用Inform 7的语法,您的示例如下所示:

"My Game" by Polynomial

Kitchen is a room. "A small kitchen that looks like it hasn't been used in a 
while. It has a table in the middle, and there are some cupboards. There is a 
door to the north, which leads to the garden."

In the Kitchen is a knife and some cupboards.  The cupboards are fixed in 
place and closed and openable.  In the cupboards are some batteries.

Garden is north of Kitchen. "The garden is wild and full of prickly bushes. 
To the north there is a path, which leads into the trees. To the south there 
is a house."

Woods is north of Garden.  "The woods are quite dark, with little light bleeding 
in from the garden. It is eerily quiet."  

Trees are scenery in the Woods.  "The trees are tall and thick. There aren't any 
low branches, so it'd be difficult to climb them."

TADS看起来更像是一种传统的编程语言,而TADS中的同一游戏看起来像这样:

#charset "us-ascii"
#include <adv3.h>
gameMain: GameMainDef
    initialPlayerChar = me
;
versionInfo: GameID
    name = 'My Game'
    byline = 'by Polynomial'
;
startroom: Room                  /* we could call this anything we liked */ 
    roomName = 'Kitchen'         /* the displayed "name" of the room */ 
    desc = "A small kitchen that looks like it hasn't been used 
            in a while. It has a table in the middle, and there 
            are some cupboards. There is a door to the north, 
            which leads to the garden." 
    north = garden         /* where 'north' will take us */ 
; 

+me: Actor
; 

cupboards: OpenableContainer
    vocabWords = 'cupboard/cupboards' 
    name = 'cupboards' 
    isPlural = true
    location = startroom 
; 
battery: Thing
    name = 'battery'
    location = cupboards
;
knife: Thing
    name = 'knife'
    location = startroom
;
garden: Room
    roomName = 'Garden'
    desc = "The garden is wild and full of prickly bushes. To the 
            north there is a path, which leads into the trees. To 
            the south there is a house." 
    north = woods
    south = startroom
; 
woods: Room
    roomName = 'Woods'
    desc = "The woods are quite dark, with little light bleeding 
            in from the garden. It is eerily quiet."
    south = garden
;
trees: Decoration
    desc = "The trees are tall and thick. There aren't any low 
            branches, so it'd be difficult to climb them."
    location = woods
;

两种系统都是免费提供的,使用频率很高,并且有大量的教程文档(可从我在上面给出的链接中获得),因此值得将它们都检查一下并挑选您喜欢的一个。

请注意,这两个系统的标准行为确实有所不同(尽管都可以修改)。这是正在播放的游戏的屏幕截图,摘自Inform来源:

通知屏幕截图

这是从Tads来源编译而来的一个游戏(在终端内–排版可能比这好得多):

TADS3屏幕截图

需要注意的有趣点:默认情况下,TADS在右上角为您显示“得分”显示(但您可以将其关闭),而Inform则没有(但您可以将其打开)。默认情况下,Inform会告诉您房间描述中项目的打开/关闭状态,Tad则不会。Tads倾向于自动为您执行操作,以执行播放器命令(除非您告诉它不这样做),而Inform则倾向于不执行(除非您告诉它这样做)。

任一种都可以用来制作任何类型的游戏(因为它们都是高度可配置的),但是Inform的结构更趋向于制作现代风格的互动小说(通常带有最小的谜题和风格上的叙述),而TADS则更加结构化尝试制作老式的文字冒险游戏(通常着重于谜题并严格定义游戏世界模型的机制)。


这是非常酷和有用的信息,但是imo并未回答问题。我基本上会问这个完全相同的问题。我想了解更多有关此XML是否有效的方法,或者它会有任何陷阱或弱点。
DLeh 2014年

1
@DLeh问题是“我想知道此方法是否有任何不足之处,以及是否有“更好”或更标准的方法来进行此操作”,此答案提供了更好,更标准的方法。做它
Trevor Powell

但是,由于您询问了“陷阱和弱点”:Inform实现的长度为19行。TADS示例为40行长。XML实现需要126行(如果将其换行为80列,并且为了便于辨认而包含空白,则该长度甚至会更长,这是Inform和TADS实现的方式)。
Trevor Powell 2014年

除了简短得多,Inform和TADS示例还支持更多功能。例如,在这两种工具中,您都可以将刀放入柜子中,而XML版本根本不支持。
Trevor Powell

1
还值得注意的是,XML版本正在将橱柜的内容烘焙到橱柜的描述中。也就是说,在打开或查看(打开的)橱柜时,会有一条硬编码的消息指示要打印的内容,它告诉您内部有电池。但是,如果播放器已经拿走电池该怎么办?XML版本将告诉您内部有电池(因为这是唯一可以显示的字符串),而Inform和TADS版本将告诉您橱柜是空的。
Trevor Powell
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.