Seamos honestos:a todos nos encanta jugar, especialmente en nuestras computadoras. Hasta que Internet se generalizó, la mayoría de nosotros jugábamos juegos de computadora solos, generalmente contra oponentes de IA. Fue divertido, pero tan pronto como te diste cuenta de cómo funcionaba la mecánica del juego, el juego perdió la mayor parte de su magia.
El desarrollo de Internet movió los juegos en línea. Ahora, podemos jugar contra oponentes humanos y probar nuestras habilidades contra las de ellos. ¡No más juegos de memoria!
Entonces surgieron los juegos masivos multijugador en línea (MMO) y lo cambiaron todo. Miles de jugadores se encontraron en los mismos universos de juego, compitiendo por los recursos, negociando, comerciando y peleando. Para hacer posibles estos juegos, se necesitaba una estructura de base de datos que pudiera almacenar toda la información relevante.
En este artículo, diseñaremos un modelo que incorpore los elementos más comunes que se encuentran en los juegos MMO. Discutiremos cómo usarlo, sus limitaciones y sus posibles mejoras.
Introducción a los modelos de datos para juegos MMO
Hay muchos juegos MMO muy populares hoy en día, e involucran todo tipo de escenarios. Me centraré aquí en juegos de estrategia como Ogame , Travian , Esparta :Guerra de imperios y Imperia en línea . Estos juegos tienen más que ver con la planificación, la construcción y la elaboración de estrategias, y menos con la acción directa.
Los juegos MMO están ambientados en diferentes universos, son visualmente diferentes y usan opciones de juego más o menos diferentes. Aún así, algunas ideas son las mismas. Los jugadores compiten por ubicaciones, luchan por ellas y forman alianzas con (y contra) otros jugadores. Construyen estructuras, recolectan recursos e investigan tecnologías. Construyen unidades (como guerreros, tanques, comerciantes, etc.) y las usan para comerciar con aliados o luchar con oponentes. Todo eso debe estar respaldado en nuestra base de datos.
Podemos pensar en estos juegos como juegos de mesa en línea con muchas casillas indexadas. Cada cuadrado puede tener muchas acciones diferentes asociadas con él; algunas acciones incluirán múltiples cuadrados, p. cuando movemos unidades o recursos de un lugar a otro.
La base de datos se divide en cinco áreas principales:
Players / Users
Alliances
Locations and Structures
Research and Resources
Units
Las siete tablas no agrupadas restantes están relacionadas con las unidades y describen la posición de las unidades y los movimientos en el juego. Veremos cada una de estas áreas con mucho más detalle, comenzando con Jugadores y Alianzas .
Jugadores y Alianzas
Sin duda, los jugadores son la parte más importante de cualquier juego.
El player
La tabla contiene una lista de todos los jugadores registrados que participan en una instancia de juego. Almacenaremos los nombres de usuario, las contraseñas y los nombres de pantalla de los jugadores. Estos se almacenarán en el user_name
, password
y nickname
atributos respectivamente.
Los nuevos usuarios deberán proporcionar una dirección de correo electrónico durante el registro. Se generará un código de confirmación y se les enviará, al que responderán. Actualizaremos la confirmation_date
atributo cuando el usuario verifica su dirección de correo electrónico. Entonces, esta tabla tiene tres claves únicas:user_name
, nickname
y email
.
Cada vez que un usuario inicia sesión, se agrega un nuevo registro en el login_history
mesa. Todos los atributos de esta tabla se explican por sí mismos. El logout_time
es específico. Puede ser NULL cuando la sesión actual del usuario está activa o cuando los usuarios abandonan el juego (sin cerrar sesión) debido a problemas técnicos. En el login_data
atributo, almacenaremos detalles de inicio de sesión como la ubicación geográfica de un jugador, la dirección IP y el dispositivo y el navegador que utilizan.
La mayoría de los juegos MMO nos permiten cooperar con otros jugadores. Una de las formas estándar de cooperación entre jugadores es la alianza. Los jugadores comparten sus "datos privados" en el juego (estado en línea, planes, ubicación de sus ciudades y colonias, etc.) con otros para beneficiarse de las acciones aliadas y por pura diversión.
La alliance
tabla almacena información básica sobre alianzas de juego. Cada uno tiene un alliance_name
único que almacenaremos. También tendremos un campo, date_founded
, que almacena cuando se fundó la alianza. Si se disuelve una alianza, almacenaremos esa información en el date_disbanded
atributo.
El alliance_member
tabla relaciona jugadores con alianzas. Los jugadores pueden unirse y abandonar la misma alianza más de una vez. Debido a esto, el player_id
– alliance_id
par no es una clave única. Mantendremos información sobre cuándo un jugador se une a la alianza y cuándo (si) se va en el date_from
y date_to
los campos. El membership_type_id
atributo es una referencia al membership_type
diccionario; almacena el nivel actual de derechos de los jugadores en la alianza.
Los derechos de los jugadores en una alianza pueden cambiar con el tiempo. Las membership_actions
, membership_type
y actions_allowed
juntas definen todos los derechos posibles para los miembros de la alianza. Este modelo no permite a los jugadores definir sus propios niveles de derechos en una alianza, pero eso podría lograrse fácilmente agregando nuevos registros en el membership_type
diccionario y almacenar información sobre las alianzas con las que están relacionados.
En resumen:los valores almacenados en estas tablas los definimos nosotros durante la configuración inicial; cambiarán solo si introducimos nuevas opciones.
El membership_history
table almacena todos los datos relacionados con los roles o derechos de los jugadores dentro de una alianza, incluido el rango cuando estos derechos eran válidos. (Por ejemplo, podría tener permisos de "principiante" durante un mes y luego "membresía completa" a partir de ese momento). El date_to
el atributo es NULLable porque los derechos actualmente activos aún no han terminado.
Las membership_actions
El diccionario contiene una lista de todas las acciones que los jugadores pueden realizar en una alianza. Cada acción tiene su propio action_name
y la lógica del juego se basa en estos nombres. Podemos esperar valores como “ver lista de miembros” , “ver los estados de los miembros” y “enviar mensaje” aquí.
El membership_type
El diccionario contiene los nombres únicos de los grupos de acción utilizados en el juego. Las actions_allowed
table asigna acciones a los tipos de membresía. Cada acción se puede asignar a un tipo solo una vez. Por lo tanto, la membership_action
- membership_type
par forma la clave única para esta tabla.
Ubicaciones y Estructuras
Las ubicaciones de juego son áreas donde los jugadores recolectan recursos y construyen estructuras y unidades. Algunos juegos tienen un rango predefinido de ubicaciones posibles, mientras que otros pueden permitir que los usuarios definan sus propias ubicaciones.
En un espacio 3D, las ubicaciones se pueden definir con coordenadas [x:y:z]. Si un juego tiene un rango predefinido, es posible que no permita a los jugadores usar ninguna ubicación fuera del rango [0:1000] para los tres ejes, por lo que estamos limitados a un espacio de 1000 * 1000 * 1000.
Por otro lado, tal vez queramos permitir que los jugadores ingresen las coordenadas exactas de su nueva ubicación, p. [1001:2073:4] – y queremos que el juego lo procese por ellos.
Mantendremos una lista de todas las ubicaciones utilizadas en una instancia de nuestro juego en la location
mesa. Cada ubicación tiene su propio nombre, pero los nombres no son únicos. Por otro lado, las coordinates
El atributo debe contener solo valores únicos. Las coordenadas de ubicación se almacenan como valores de texto, por lo que podemos almacenar coordenadas para juegos 3D como [112:72:235]. Las coordenadas para juegos 2D se pueden almacenar como <1102:98>.
En algunos juegos, las ubicaciones tendrán varios cuadrados que se utilizan para albergar estructuras o unidades. Mantendremos esa información en la dimension
atributo, que es un campo de texto. Una dimensión puede ser simplemente el número de cuadrados en una cuadrícula 2D o 3D. El player_id
El atributo almacena información sobre el propietario actual de esa ubicación. Puede ser NULL cuando las ubicaciones están predefinidas y los jugadores compiten para ocuparlas.
La structure
La tabla contiene una lista de todas las estructuras que podemos construir en varios lugares del juego. Las estructuras representan mejoras que nos permiten producir mejores unidades, realizar nuevos tipos de investigación, producir más recursos, etc. Cada estructura utilizada en el juego tiene su propio structure_name
único. . Algunas posibles structure_name
los valores son "granja", "mina de mineral", "planta solar" y "centro de investigación".
Podemos esperar que cada estructura se actualice varias veces, por lo que también almacenaremos información sobre su nivel actual. Cada actualización mejora el rendimiento de las estructuras, por lo que produce más recursos o nos permite usar nuevas funciones en el juego. No podemos saber el nivel máximo de actualización por adelantado, por lo que definiremos todo lo relacionado con el nivel (costos, tiempo de actualización y producción) con fórmulas. Todas las fórmulas almacenadas en la base de datos son el núcleo de la mecánica del juego, y su ajuste es crucial para el equilibrio del juego y la jugabilidad en general.
Ese también es el caso con upgrade_time_formula
atributo. Un valor de ejemplo para este campo es “
En la mayoría de los casos, hay requisitos que deben cumplirse antes de que los jugadores realicen determinadas acciones. Tal vez necesitemos completar una cantidad definida de investigación antes de que podamos construir nuevas estructuras o viceversa. Guardaremos el nivel de investigación necesario para construir estructuras en prerequisite_research
mesa. Las relaciones y el nivel de estructura necesarios para iniciar varias investigaciones se mantienen en la prerequisite_structure
mesa. En ambas tablas, las claves foráneas research_id
y structure_id
se emparejan para formar una clave única. El level_required
El atributo es el único valor.
Estas dos tablas, prerequisite_research
y prerequisite_structure
, también forman el núcleo del juego.
Para cada estructura, definiremos una lista de requisitos previos:otras estructuras y sus niveles mínimos que los jugadores deben tener para comenzar a construir. Guardaremos estos datos en la structure_required
mesa. Aquí, structure_id
representa la estructura que queremos construir; structure_required_id
es una referencia a la(s) estructura(s) de requisitos previos y level
es el nivel requerido.
La structure_built
La tabla almacena información sobre los niveles de estructura actuales en una ubicación determinada. La upgrade_ongoing
se establecerá solo si hay una actualización en curso, mientras que upgrade_end_time
El atributo contendrá una marca de tiempo una vez que se complete la actualización.
La structure_formula
tabla relaciona estructuras y recursos. El par de claves foráneas de esta tabla forma su clave única. Esta tabla también tiene dos atributos de texto que contienen fórmulas con upgrade_time_formula
. Los necesitamos porque debemos definir los recursos gastados en la construcción de cada estructura. También necesitamos definir la producción de recursos después de la actualización, si la estructura genera algún recurso (es decir, la mina de mineral producirá
Investigación y recursos
La investigación (o tecnologías) en los juegos suele ser un requisito para la creación de otras funciones. Sin ciertos niveles de investigación, no se pueden construir nuevas estructuras o tipos de unidades. La investigación también puede tener sus propios requisitos. Uno de los más comunes es el nivel de una estructura determinada, generalmente llamada "laboratorio de investigación". O tal vez los jugadores necesitan completar un cierto nivel de investigación antes de poder comenzar una nueva investigación. Todos estos requisitos serán tratados en esta sección. A continuación, podemos encontrar el modelo de datos para Investigación y Recursos:
La research
La tabla contiene una lista de todas las posibles acciones de investigación en nuestro juego. Utiliza la misma lógica que la structure
mesa. El research_name
atributo es la clave única de la tabla, mientras que la upgrade_time_formula
El campo contiene una representación de texto de la fórmula de requisitos de tiempo de investigación, con upgrade_formula
almacenado en el research_formula
mesa.
Al igual que con las estructuras, definiremos la lista de todas las demás investigaciones y sus niveles que deben completarse antes de que podamos comenzar otro tipo de investigación. Guardaremos estos datos en el research_required
tabla, donde research_id
representa la investigación deseada; research_required_id
es una referencia a la investigación de requisitos previos y level
es el nivel requerido.
La investigación está relacionada con jugadores individuales, y para cada jugador – investigar ch pair debemos almacenar el nivel de investigación actual de un jugador y cualquier estado de actualización en curso. Almacenaremos esta información usando el research_level
tabla de la misma manera que usamos la structure_built
mesa.
Los recursos como la madera, el mineral, las gemas y la energía se extraen o recolectan y se usan más tarde para construir estructuras y otras mejoras. Guardaremos una lista de todos los recursos del juego en el resource
diccionario. El único atributo aquí es resource_name
campo, y también es la clave única de la tabla.
Para realizar un seguimiento de la cantidad actual de recursos en cada ubicación, usaremos el resources_on_location
mesa. De nuevo, un par de claves foráneas (resource_id
y location_id
) forma la clave única de la tabla, mientras que el number
El atributo almacena los valores de recursos actuales.
Unidades y Movimientos
Los recursos se utilizan para producir unidades. Las unidades se pueden usar para transportar recursos, atacar a otros jugadores o, en general, saquear e incendiar.
La lista de tipos de unidades utilizadas en nuestro juego se almacena en la unit
diccionario con un solo valor, unit_name
; ese atributo es la clave única de esta tabla. Algunas unidades de juego comunes son "espadachín", "crucero de batalla", "grifo", "caza a reacción", "tanque", etc.
Necesitamos describir cada unidad con características específicas. Una lista de todas las características posibles se almacena en la characteristic
diccionario. El characteristic_name
El campo contiene un valor único. Los valores en este campo podrían incluir:"ataque", "defensa" y "puntos de golpe". Asignaremos características a las unidades usando unit_characteristic
relación. El par de claves foráneas de unit_id
y characteristic_id
forman la clave única de la tabla. Usaremos solo un atributo, value
, para almacenar el valor deseado.
La research_unit
La tabla contiene una lista de todas las actividades de investigación que deben finalizar antes de que podamos comenzar la producción de un tipo de unidad determinado. El unit_cost
La tabla define los recursos necesarios para producir una sola unidad. Ambas tablas tienen claves únicas compuestas por el par de claves foráneas (research_id
o resources_id
combinado con unit_id
) y un campo de valor (cost
y level_required
).
Y ahora, la parte divertida. La producción es divertida, pero mover unidades y actuar es aún mejor. Ya presentamos la unit
tabla, pero la mantendremos aquí debido a cómo se relaciona con otras tablas.
Las unidades están estacionadas en una ubicación o se están moviendo entre ubicaciones. Agregando el player_id
determina quién es el propietario de la ubicación o del grupo que se mueve entre las ubicaciones.
Si las unidades solo están estacionadas en la ubicación dada, almacenaremos esa ubicación y la cantidad de unidades estacionadas allí. Para hacerlo, usaremos las units_on_location
mesa.
Cuando las unidades no están estacionadas, se están moviendo. Tendremos que almacenar su punto de partida y su destino. Además, necesitamos definir posibles acciones durante los movimientos. Todas estas acciones se almacenan en el movement_type
diccionario. El type_name
el atributo es único mientras que allows_wait
El atributo determina si una acción permite esperar en el punto de destino.
Podemos mover un solo tipo de unidad, pero en casi todos los casos moveremos muchas unidades de varios tipos de unidades diferentes. Ese grupo compartirá datos comunes y los almacenaremos en el group_movement
mesa. En esta tabla, definiremos los siguientes elementos:
- el jugador que inició esa acción
- el tipo de acción
- el punto de partida
- el punto de destino
- la
arrival_time
en el destino - la
return_time
al punto de partida - el
wait_time
en el destino
La return_time
El atributo puede ser NULL si se trata de un viaje de ida y wait_time
lo define el jugador. Las unidades que pertenecen a un grupo están definidas por valores almacenados en el units_in_group
mesa. El par de claves foráneas de units_id
y group_moving_id
forma la clave única de la tabla. El número de unidades del mismo tipo dentro de un grupo se define en el number
atributo.
Cada movimiento puede transportar recursos de un lugar a otro. Por lo tanto, definiremos una relación de muchos a muchos entre el group_movement
y los resources
mesas. Además de las claves principal y externa, los resources_in_group
la tabla contiene solo el number
atributo. Este campo almacena la cantidad de recursos que los jugadores mueven desde el punto de partida hasta su destino.
En la mayoría de los casos, los jugadores pueden llamar a otros para que se unan a su aventura. Para respaldar eso, usaremos dos tablas:allied_movement
y allied_groups
. Un jugador iniciará una acción conjunta y eso creará un nuevo récord en el allied_movement
mesa. Todos los grupos de unidades que toman parte en una acción aliada están definidos por valores almacenados en allied_groups
mesa. Cada grupo se puede asignar a una acción aliada solo una vez, por lo que las claves foráneas forman la clave única de esta tabla.
Este modelo nos da la estructura básica necesaria para construir un juego de estrategia MMO. Contiene las características más importantes del juego:ubicaciones, estructuras, recursos, investigación y unidades. También los relaciona, nos permite definir requisitos previos en la base de datos y también almacena la mayor parte de la lógica del juego en la base de datos.
Una vez que se completan estas tablas, se define la mayor parte de la lógica del juego y no esperamos que se agreguen nuevos valores. Casi todas las tablas tienen un valor de clave único, ya sea un nombre de función o un par de claves externas. Cambiar las características de las unidades y las fórmulas de producción/costo nos permitirá cambiar el equilibrio del juego en la capa de la base de datos.
¿Cómo cambiarías este modelo? ¿Qué te gusta y qué harías diferente? ¡Cuéntanos en la sección de comentarios!