Organização de níveis / salas em um mundo baseado em texto no estilo MUD

12

Estou pensando em escrever um pequeno jogo de aventura baseado em texto, mas não tenho muita certeza de como devo projetar o mundo do ponto de vista técnico.

Meu primeiro pensamento é fazê-lo em XML, projetado algo como o seguinte. Desculpas pela enorme pilha de XML, mas achei importante explicar completamente o que estou fazendo.

<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>

Obviamente, haveria mais do que isso. A interação com pessoas e inimigos, bem como a morte e a conclusão são adições necessárias. Como o XML é bastante difícil de trabalhar, eu provavelmente criaria algum tipo de editor mundial.

Gostaria de saber se esse método tem alguma queda e se existe uma maneira "melhor" ou mais padrão de fazê-lo.

Polinomial
fonte
3
Pessoalmente, eu não trataria o XML como algo além de um formato de serialização. Se você abstrair a pergunta "de alguma forma eu vou ler e gravar isso no disco" (usando algo como XML, JSON, buffers de protocolo, formato binário personalizado, o que seja), a pergunta se tornará "quais dados eu preciso armazenar ", algo que somente você pode realmente responder, dependendo de quais são os requisitos do seu jogo.
Tetrad
Bom ponto. No entanto, eu já vi jogos usarem estilos como esse antes e eles se mostraram realmente restritivos. Nesse caso, porém, o fluxo e a lógica do jogo são bastante simples, portanto podem funcionar bem e me impedir de implementar um mecanismo de script. Estou interessado principalmente em saber se uma estrutura fixa (salas, portas, objetos, variáveis ​​em um arquivo de definição em algum lugar) é viável ou não.
Polynomial
Tentando não ecoar o Tetrad, mas se você planeja criar um editor mundial (o que eu sugeriria, a menos que o jogo seja muito curto), seu formato de arquivo não faz nenhuma diferença, pois você estará trabalhando com ele o editor, em vez de codificar os quartos.
Mike Cluck

Respostas:

13

Se você não está completamente apegado ao C #, a maneira "mais padrão" de fazer isso é usar uma das muitas ferramentas de criação de aventura de texto que já existem para ajudar as pessoas a fazer exatamente esse tipo de jogo. Essas ferramentas oferecem a você um analisador que já funciona, manipulação para morte, salvar / restaurar / desfazer, interação de caracteres e outros bits padrão semelhantes de funcionalidade de aventura de texto. No momento, os sistemas de criação mais populares são o Inform e o TADS (embora existam meia dúzia de outros disponíveis também)

O Inform pode compilar na maioria dos conjuntos de instruções da máquina virtual Z Machine usados ​​pelos jogos da Infocom ou nos conjuntos de instruções da máquina virtual glulx mais recentes. O TADS, por outro lado, compila em seu próprio código de máquina virtual.

Qualquer um dos tipos de binários pode ser executado pela maioria dos intérpretes de ficção interativa modernos (antigamente, você costumava precisar de intérpretes separados para jogos TADS dos jogos ZMachine e glulx. Mas, felizmente, esses dias estão basicamente terminados agora.) Os intérpretes estão disponíveis por apenas sobre qualquer plataforma que você deseja; Mac / PC / Linux / BSD / iOS / Android / Kindle / navegador / etc. Portanto, você já tem uma boa plataforma cruzada e realmente se importa.

Para a maioria das plataformas, o intérprete atualmente recomendado é Gargoyle , mas há muitos outros. Portanto, à vontade para experimentar.

A codificação no Inform (especialmente a versão mais recente) leva um pouco de tempo para se acostumar, já que é mais voltada para os autores do que para os engenheiros e, portanto, sua sintaxe parece estranha e quase conversacional. Na sintaxe do Inform 7, seu exemplo seria assim:

"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."

Enquanto o TADS se parece mais com uma linguagem de programação tradicional, e o mesmo jogo no TADS se parece com o seguinte:

#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
;

Ambos os sistemas estão disponíveis gratuitamente, são usados ​​com muita frequência e possuem uma grande quantidade de documentação tutorial (disponível nos links que forneci acima); portanto, vale a pena conferir os dois e escolher o que você preferir.

Observe que os dois sistemas têm comportamentos padrão sutilmente diferentes (embora ambos possam ser modificados). Aqui está uma captura de tela do jogo em execução, compilada na fonte Inform:

Informar captura de tela

E aqui está um do jogo que está sendo jogado (dentro de um terminal - a tipografia pode ser muito melhor do que isso), conforme compilado da fonte do Tads:

Captura de tela do TADS3

Pontos interessantes a serem observados: o TADS fornece uma exibição de 'pontuação' no canto superior direito por padrão (mas você pode desativá-lo), enquanto o Inform não (mas você pode ativá-lo). O Inform informará por padrão os estados de itens abertos / fechados na descrição do quarto, o Tads não. Tads tende a executar ações automaticamente para você, a fim de executar comandos do jogador (a menos que você diga para não), onde o Inform tende a não fazer (a menos que você diga).

Qualquer um pode ser usado para criar qualquer tipo de jogo (pois ambos são altamente configuráveis), mas o Inform é mais estruturado para produzir ficção interativa no estilo moderno (geralmente com quebra-cabeças mínimos e mais narrativa no estilo), onde o TADS é mais estruturado para produzir aventuras de texto à moda antiga (muitas vezes fortemente focadas em quebra-cabeças e definir rigorosamente a mecânica do modelo mundial do jogo).

Trevor Powell
fonte
isso é muito interessante e informativo, mas a imo não responde à pergunta. Eu ia fazer basicamente exatamente essa mesma pergunta. Gostaria de saber mais sobre se esse XML é ou não uma abordagem válida ou se existem armadilhas ou fraquezas.
DLeh
1
@DLeh A pergunta era: "Gostaria de saber se esse método tem alguma queda e se existe uma maneira" melhor "ou mais padrão de fazê-lo". Essa resposta fornece a maneira melhor e mais padrão de fazê-lo. fazendo-o .
Trevor Powell
Mas desde que você perguntou sobre "armadilhas e fraquezas": a implementação do Inform tem 19 linhas. O exemplo do TADS tem 40 linhas. A implementação do XML requer 126 linhas (e seria ainda mais longa se estivesse dividida em 80 colunas e contivesse espaço em branco para maior legibilidade, da mesma forma que as implementações do Inform e do TADS).
Trevor Powell
Além de muito mais curtos, os exemplos do Inform e do TADS também oferecem suporte a mais recursos. Por exemplo, em ambos, você pode colocar a faca nos armários, o que não é suportado na versão XML.
Trevor Powell
1
Também é importante notar que a versão XML está inserindo o conteúdo dos armários na descrição dos armários. Ou seja, há uma mensagem codificada sobre o que imprimir ao abrir ou olhar para os armários (abertos), informando que há baterias no interior. Mas e se o jogador já pegou as pilhas? A versão XML informará que há baterias dentro (porque essa é a única string disponível para exibição), enquanto as versões Inform e TADS informarão que os armários estão vazios.
Trevor Powell