Organizar niveles / salas en un mundo basado en texto estilo MUD

12

Estoy pensando en escribir un pequeño juego de aventuras basado en texto, pero no estoy particularmente seguro de cómo debería diseñar el mundo desde un punto de vista técnico.

Mi primer pensamiento es hacerlo en XML, diseñado algo como lo siguiente. Disculpas por la gran cantidad de XML, pero me pareció importante explicar completamente lo que estoy haciendo.

<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, tendría que haber más que esto. La interacción con personas y enemigos, así como la muerte y la finalización son adiciones necesarias. Como el XML es bastante difícil de trabajar, probablemente crearía algún tipo de editor mundial.

Me gustaría saber si este método tiene alguna desventaja, y si hay una forma "mejor" o más estándar de hacerlo.

Polinomio
fuente
3
Personalmente, no trataría el XML como algo más que un formato de serialización. Si resume la pregunta "de alguna manera voy a leer y escribir esto en el disco" (usando algo como XML, JSON, buffers de protocolo, formato binario personalizado, lo que sea), entonces la pregunta se convierte en "qué datos necesito almacenar ", que es algo que solo tú puedes responder dependiendo de cuáles sean tus requisitos de juego.
Tetrad
Buen punto. Sin embargo, he visto juegos que usan estilos como este antes y han resultado ser muy restrictivos. Sin embargo, en este caso, el flujo y la lógica del juego son bastante simples, por lo que podría funcionar bien y evitar que implemente un motor de secuencias de comandos. Me interesa principalmente si una estructura fija (habitaciones separadas, puertas, objetos, variables en un archivo de definición en algún lugar) es viable o no.
Polinómico el
Intentando no hacer eco de Tetrad, pero si planeas hacer un editor mundial (lo que sugeriría a menos que el juego sea muy corto), entonces tu formato de archivo no hace ninguna diferencia ya que trabajarás con él en el editor, frente a la codificación de las habitaciones.
Mike Cluck

Respuestas:

13

Si no estás completamente casado con C #, entonces la forma "más estándar" de hacerlo es utilizar una de las muchas herramientas de creación de aventuras de texto que ya existen para ayudar a las personas a hacer exactamente este tipo de juego. Estas herramientas le brindan un analizador que ya funciona, manejo para la muerte, guardar / restaurar / deshacer, interacción de personajes y otras funcionalidades de aventura de texto estándar similares. En este momento, los sistemas de autor más populares son Inform y TADS (aunque también hay media docena de otros disponibles)

Inform puede compilarse en la mayoría de los conjuntos de instrucciones de máquina virtual de Z Machine utilizados por los juegos de Infocom, o en los conjuntos de instrucciones de máquina virtual glulx más recientes. TADS, por otro lado, compila en su propio código de máquina virtual.

Los intérpretes de ficción interactivos más modernos pueden ejecutar cualquier tipo de binario (en los viejos tiempos, a menudo se necesitaban intérpretes separados para los juegos de TADS de los juegos de ZMachine de los juegos de glulx. Pero afortunadamente, esos días ya han terminado). Los intérpretes están disponibles por solo sobre cualquier plataforma que quieras; Mac / PC / Linux / BSD / iOS / Android / Kindle / browser / etc. Así que ya tienes una plataforma multiplataforma bien cuidada.

Para la mayoría de las plataformas, el intérprete recomendado actualmente es Gargoyle , pero hay muchas otras, así que no dude en experimentar.

La codificación en Inform (especialmente la última versión) lleva un poco de tiempo acostumbrarse, ya que se está promocionando más a los autores que a los ingenieros, por lo que su sintaxis parece extraña y casi conversacional. En la sintaxis de Inform 7, su ejemplo se vería así:

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

Mientras que TADS se parece más a un lenguaje de programación tradicional, y el mismo juego en TADS se ve así:

#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 sistemas están disponibles gratuitamente, se usan con mucha frecuencia y tienen una gran cantidad de documentación tutorial (disponible en los enlaces que proporcioné anteriormente), por lo que vale la pena echarles un vistazo y elegir el que prefiera.

Tenga en cuenta que los dos sistemas tienen comportamientos estándar sutilmente diferentes (aunque ambos pueden modificarse). Aquí hay una captura de pantalla del juego que se está jugando, tal como se compiló de la fuente Inform:

Informar captura de pantalla

Y aquí hay uno del juego que se está jugando (dentro de un terminal; la tipografía puede ser mucho mejor que esto), tal como se compiló de la fuente de Tads:

Captura de pantalla TADS3

Puntos interesantes a tener en cuenta: TADS le ofrece una pantalla de 'puntaje' en la parte superior derecha de forma predeterminada (pero puede desactivarla), mientras que Inform no (pero puede activarla). Informar le informará por defecto los estados abiertos / cerrados de los elementos en la descripción de la habitación, Tads no. Tads tiende a tomar acciones automáticamente por ti para ejecutar los comandos del jugador (a menos que le digas que no lo haga), donde Inform tiende a no hacerlo (a menos que se lo digas).

Cualquiera de los dos se puede usar para hacer cualquier tipo de juego (ya que ambos son altamente configurables), pero Inform está más estructurado para producir ficción interactiva de estilo moderno (a menudo con rompecabezas mínimos y más estilo narrativo), donde TADS está más estructurado hacia la producción de aventuras de texto a la antigua (a menudo fuertemente centradas en acertijos y definiendo rigurosamente la mecánica del modelo mundial del juego).

Trevor Powell
fuente
Esto es muy bueno e informativo, pero IMO no responde la pregunta. Iba a hacer básicamente esta misma pregunta. Me gustaría saber más acerca de si este XML es o no un enfoque válido o si existen dificultades o debilidades.
DLeh
1
@DLeh La pregunta era "Me gustaría saber si este método tiene algún inconveniente, y si hay una forma" mejor "o más estándar de hacerlo". Esta respuesta proporciona la mejor y más-forma-estándar-de- haciéndolo .
Trevor Powell
Pero desde que preguntó sobre "trampas y debilidades": la implementación de Inform tiene 19 líneas de largo. El ejemplo de TADS tiene 40 líneas de largo. La implementación XML requiere 126 líneas (y sería aún más larga si se ajustara a las palabras en 80 columnas y contuviera espacios en blanco para la legibilidad, de la forma en que lo hacen las implementaciones Inform y TADS).
Trevor Powell
Además de ser mucho más cortos, los ejemplos de Inform y TADS también admiten más funciones. Por ejemplo, en ambos puedes poner el cuchillo en los armarios, lo que no es compatible en absoluto en la versión XML.
Trevor Powell
1
También vale la pena señalar que la versión XML está horneando el contenido de los armarios en la descripción de los armarios. Es decir, hay un mensaje codificado sobre qué imprimir al abrir o mirar los armarios (abiertos), que le indica que hay baterías en su interior. ¿Pero qué pasa si el jugador ya ha tomado las baterías? La versión XML le dirá que hay baterías adentro (porque esa es la única cadena que tiene disponible para mostrar), mientras que las versiones Inform y TADS le dirán que los armarios están vacíos.
Trevor Powell