Voy a estar ligeramente en desacuerdo con todos y decir que el enfoque relacional es razonable aquí. Lo interesante aquí es que los artículos pueden tener múltiples roles. El problema principal será que el mapeo entre este diseño relacional y un diseño OO en el código no se sentirá "natural", pero creo que en el lado de la base de datos, múltiples roles pueden expresarse limpiamente (sin codificaciones extrañas o redundancia, solo se une) .
Lo primero que debe decidir es qué cantidad de datos es específica del elemento y cuánto es compartida por todos los elementos de un tipo determinado.
Esto es lo que haría si todos los datos son específicos del artículo:
// ITEMS table: attributes common to all items
item_id | name | owner | location | sprite_id | ...
1 | Light Saber | 14 (Tchalvek) | 381 (Tchalvek house) | 5663 | ...
// WEAPONS table: attributes for items that are weapons
item_id | damage | damage_type | durability | ...
1 | 5 | sharp | 13 | ...
// LIGHTING table: attributes for items that serve as lights
item_id | radius | brightness | duration | ...
1 | 3 meters | 50 | 8 hours | ...
En este diseño, cada elemento está en la tabla Elementos, junto con los atributos que tienen todos (o la mayoría) de los elementos. Cada rol adicional que un elemento puede jugar es una mesa separada.
Si quieres usarlo como arma, lo buscarías en la tabla de Armas. Si está allí, entonces se puede usar como arma. Si no está allí, no se puede usar como arma. La existencia del registro te dice si es un arma. Y si está allí, todos sus atributos específicos de armas se almacenan allí. Dado que esos atributos se almacenan directamente en lugar de forma codificada, podrá realizar consultas / filtros con ellos. (Por ejemplo, para la página de métricas de su juego, es posible que desee agregar jugadores por tipo de daño de arma, y podría hacerlo con algunas combinaciones y un tipo de daño de grupo).
Un elemento puede tener múltiples roles y existir en más de una tabla específica de roles (en este ejemplo, tanto arma como iluminación).
Si es solo un booleano como "es así de retenible", lo pondría en la tabla Elementos. Puede valer la pena almacenar en caché "es un arma", etc., para que no tenga que realizar una búsqueda en las Armas y otras tablas de roles. Sin embargo, agrega redundancia, por lo que debe tener cuidado de mantenerlo sincronizado.
La recomendación de Ari de tener una tabla adicional por tipo también se puede utilizar con este enfoque si algunos datos no varían según el elemento. Por ejemplo, si el daño del arma no varía por elemento, pero los roles aún varían por elemento, puede factorizar los atributos de armas compartidas en una tabla:
// WEAPONS table: attributes for items that are weapons
item_id | durability | weapon_type
1 | 13 | light_saber
// WEAPONTYPES table: attributes for classes of weapons
weapon_type_id | damage | damage_type
light_saber | 5 | energy
Otro enfoque sería si los roles desempeñados por los elementos no varían por elemento, sino solo por tipo de elemento. En ese caso, pondrías el item_type en la tabla Items, y puedes almacenar las propiedades como "¿es un arma" y "es holdable" y "es una luz" en una tabla ItemTypes. En este ejemplo, también hago que los nombres de los artículos no varíen por artículo:
// ITEMS table: attributes per item
item_id | item_type | owner | location
1 | light_saber | 14 (Tchalvek) | 381 (Tchalvek house)
// ITEMTYPES table: attributes shared by all items of a type
item_type | name | sprite_id | is_holdable | is_weapon | is_light
light_saber | Light Saber | 5663 | true | true | true
// WEAPONTYPES table: attributes for item types that are also weapons
item_type | damage | damage_type
light_saber | 5 | energy
Es probable que los tipos de elementos y los tipos de armas no cambien durante el juego, por lo que puede cargar esas tablas en la memoria una vez y buscar esos atributos en una tabla hash en lugar de con una unión a la base de datos.
Desafortunadamente, situaciones como esta son donde las bases de datos relacionales (como SQL) se quedan cortas, y las bases de datos no relacionales (como MongoDB ) se destacan. Dicho esto, no es imposible modelar los datos en una base de datos relacional, y dado que parece que su base de código ya depende de SQL, aquí está el modelo con el que iría:
En lugar de crear un elemento y usar una tabla, cree una tabla y una tabla de referencia "[item] _type" para cada tipo de elemento.
Aquí están algunos ejemplos:
Esta solución le brinda mucha flexibilidad y escalabilidad a largo plazo, reduce (si no elimina) el espacio desperdiciado y es autodocumentado (a diferencia de "item_use_data"). Implica un poco más de configuración por parte del desarrollador, pero, en mi opinión, es la solución más elegante disponible para situaciones en las que necesita usar una base de datos relacional para almacenar sus datos. En términos generales, las bases de datos no relacionales son mucho mejores para el desarrollo del juego, ya que son más flexibles en la forma en que modelan los datos, a la vez que son más efectivas que las bases de datos basadas en SQL, lo que las convierte en una opción de "ganar-ganar".
Edición 1: se corrigió un error con el campo potion_type_id
Edición 2: se agregaron más detalles en bases de datos no relacionales versus relacionales para proporcionar una perspectiva adicional
fuente
En primer lugar, volcar el enfoque de herencia orientado a objetos e ir con un sistema basado en componentes .
Una vez que haya hecho eso, el diseño SQL de repente se vuelve mucho más fácil. Tiene una tabla para cada tipo de componente con un número de ID compartido. Si desea el ítem # 17, busque "ID de ítem 17" en cada tabla. Cualquier tabla que tenga una clave obtiene su componente agregado.
Su tabla de artículos contiene todos los datos requeridos para los artículos en general (nombre, precio de venta, peso, tamaño, cualquier otra cosa que se comparta entre todos los artículos). Su tabla de armas contiene todos los datos apropiados para las armas, su tabla de pociones contiene todos los datos apropiados. para pociones, tu tabla de armadura contiene todos los datos apropiados para la armadura. Quiere una coraza, esa es una entrada en Objeto y una entrada en Armadura. Si quieres un yelmo de espada que puedas beber, solo pones una entrada en cada mesa, y bam, ya terminaste.
Tenga en cuenta que este mismo patrón no es particularmente específico de un elemento: puede usarlo para criaturas, zonas, cualquier otra cosa que desee. Es sorprendentemente versátil.
fuente
Usar SQL fue tu grave error. Absolutamente NO es adecuado para almacenar datos estáticos de diseño de juegos.
Si no puede alejarse de SQL, consideraría almacenar elementos en forma serializada. Es decir
Por supuesto, eso es feo y arroja todas las "sutilezas" de SQL por la ventana, pero ¿realmente las necesita ? Lo más probable es que leas todos los datos de tus artículos al comienzo del juego de todos modos, y nunca
SELECT
por otra cosa que no seaitem_id
.fuente
Dependiendo de cuántos rasgos es probable que necesite, podría usar una máscara de bits simple para objetos con los diferentes bits correspondientes a diferentes rasgos:
Luego puede hacer pruebas de bits simples para ver si un objeto se puede usar para una determinada función.
Por lo tanto, la "botella de vidrio" puede tener un valor de 101111, lo que significa que se puede guardar, se puede usar como arma, se rompe fácilmente, se puede tirar y puede contener líquidos.
Cualquier editor que cree para elementos puede tener un conjunto simple de casillas de verificación para habilitar / deshabilitar rasgos en un objeto.
fuente
En nuestro proyecto tenemos atributos_elemento para los diferentes "datos adicionales" que puede tener un elemento. Se presenta algo como esto:
Luego tenemos una tabla de atributos que se ve así:
Sin embargo, Ari Patrick tiene razón, en última instancia, los db relacionales no están hechos para esto. La desventaja es que tenemos algunos procedimientos bastante complejos para generar nuevos elementos (hecho a través de una herramienta externa, que recomiendo encarecidamente, no intentes agregarlos manualmente, solo te confundirás)
La otra opción que tiene es usar un lenguaje de secuencias de comandos para crear las plantillas de elementos, luego puede analizarlas fácilmente y usarlas para crear nuevos elementos. Por supuesto, todavía tiene que guardar los datos del elemento en la base de datos, pero en ese momento no tiene que preocuparse por los detalles de la creación de nuevos elementos, puede simplemente copiar un archivo de script antiguo, cambiar algo de información y estará bien. ir.
Honestamente, si tuviéramos que rehacer cómo creamos nuevos elementos estáticos, probablemente optaríamos por un enfoque mucho más simple utilizando plantillas de elementos de secuencias de comandos.
fuente
Esta es su típica relación de muchos a muchos, nada demasiado esotérico para cualquier base de datos relacional capaz. Tiene muchos rasgos para cualquier objeto, y uno o más tipos de objeto diferentes pueden usar cualquier rasgo. Modele tres relaciones (tablas), siendo una relación de asociación, y ya está. La indexación adecuada le asegurará lecturas rápidas de datos.
Coloque un ORM en su código y debería tener muy pocos problemas entre DB y middleware. Muchos ORM también pueden generar automáticamente las clases y, por lo tanto, se vuelven aún más "invisibles".
En cuanto a las bases de datos NoSQL, no hay razón para que no pueda hacerlo con ellas. Actualmente está de moda animar esa tecnología en trapos comerciales y blogs, pero vienen con una serie de sus propios problemas: plataformas relativamente inmaduras, ideales para lecturas simples que cambian con poca frecuencia (como un par de uno a muchos en un perfil) pero pobre para lecturas o actualizaciones dinámicas complejas, cadena de herramientas de soporte deficiente, datos redundantes y los problemas de integridad que lo acompañan, etc. Sin embargo, ofrecen el atractivo de una mejor escalabilidad porque evitan capacidades transaccionales de diversos grados y su sobrecarga, y modelos más fáciles de distribución / replicación .
El duende habitual de las bases de datos relacionales frente a las bases de datos NoSQL es el rendimiento. Diferentes RDMBS tienen diferentes grados de sobrecarga involucrados que los hacen menos preferidos para escalar a los niveles de Facebook o Twitter. Sin embargo, es muy poco probable que enfrentes esos problemas. Incluso entonces, los sistemas de servidor simples basados en SSD pueden hacer que el debate sobre el rendimiento sea inútil.
Seamos claros: la mayoría de las bases de datos NoSQL son tablas hash fundamentalmente distribuidas, y lo limitarán de la misma manera que lo haría una tabla hash en su código, es decir. No todos los datos encajan bien en ese modelo. El modelo relacional es mucho más poderoso para modelar relaciones entre datos. (El factor de confusión es que la mayoría de los RDBMS son sistemas heredados que están mal ajustados para las demandas del popular 0.0001% de la web, es decir, Facebook et al.)
fuente
Aquí es donde encuentro que los mapeadores OR más modernos son realmente útiles, como Entity Framework 4 y su función de código primero (CTP) . Simplemente escriba las clases y sus relaciones en código regular y sin siquiera tener que decorarlas o ejecutar manualmente ninguna herramienta, EF generará el almacén de respaldo en formato SQL para usted, con las tablas de enlaces requeridas y todo ... realmente desata la creatividad imo ^^
fuente
Esto es lo que estoy considerando ahora:
Dado que cada "rasgo" esencialmente requiere cambios en el código de todos modos, así que decidí mantener los rasgos (y cualquier información predeterminada que requieran) en el código mismo (al menos por ahora).
P.ej
$traits = array('holdable'=>1, 'weapon'=>1, 'sword'=>array('min_dam'=>1, 'max_dam'=>500));
Luego, los elementos obtienen un campo "trait_data" en la base de datos que utilizará la
json_encode()
función para almacenarse en el formato JSON.El plan es que los elementos heredarán todas las estadísticas predeterminadas de sus rasgos principales y solo tendrán rasgos de anulación que especifiquen diferencias con respecto a lo que los rasgos establecen como predeterminados.
La desventaja del campo de rasgos es que si bien podría editar la parte json a mano ... ... no será realmente fácil o seguro hacerlo con los datos que están en la base de datos.
fuente
Esto es un poco hacky, y no garantizo que sea un buen diseño de base de datos; mis clases de DB fueron hace un tiempo, y no me vienen a la mente rápidamente. Pero si se garantiza que los elementos tienen, digamos, menos de 10 elementos, puede asignar a cada elemento un campo de atributo1 y un campo de atributo2, y así sucesivamente hasta el atributo10. Eliminaría su necesidad de la tabla de atributos de elementos de muchos a muchos, a costa de limitar el número de atributos.
En retrospectiva, estoy bastante seguro de que es un diseño terrible, pero significa que puede quedarse con una base de datos relacional cómoda y no tener que entrar en un territorio desconocido. Depende de usted decidir si la compensación vale la pena.
fuente
Nevermind dio una buena respuesta, pero me pregunto, ¿necesitas una base de datos para esto? Almacenar todos los datos en un archivo plano y cargarlos en el programa en el lanzamiento parece ser la forma más inteligente de hacerlo.
Editar:
Correcto, en un entorno impulsado por solicitudes sin estado, es mejor que guarde los datos en una base de datos. Escriba sus datos en un archivo y escriba un fragmento de código para convertirlo en una base de datos del tipo que Nevermind sugirió.
Alternativamente, si el número de objetos no es demasiado grande, declarar el lote literalmente en un archivo de código puede ser el método más rápido.
fuente