Je vais être légèrement en désaccord avec tout le monde et dire que l'approche relationnelle est raisonnable ici. Ce qui est intéressant ici, c'est que les éléments peuvent avoir plusieurs rôles. Le problème principal sera que le mappage entre cette disposition relationnelle et une disposition OO dans le code ne semblera pas «naturel», mais je pense que du côté de la base de données, plusieurs rôles peuvent être exprimés proprement (sans codages étranges ou redondance, juste des jointures) .
La première chose à décider est la quantité de données spécifique à un élément et la quantité partagée par tous les éléments d'un type donné.
Voici ce que je ferais si toutes les données sont spécifiques à un élément:
// 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 | ...
Dans cette conception, chaque élément se trouve dans le tableau Éléments, ainsi que les attributs de tous (ou la plupart) des éléments. Chaque rôle supplémentaire qu'un élément peut jouer est une table distincte.
Si vous souhaitez l'utiliser comme une arme, vous devriez le rechercher dans le tableau des armes. S'il est là, alors il est utilisable comme arme. S'il n'est pas là, il ne peut pas être utilisé comme arme. L'existence du dossier vous indique s'il s'agit d'une arme. Et s'il est là, tous ses attributs spécifiques aux armes y sont stockés. Étant donné que ces attributs sont stockés directement au lieu de sous une forme codée, vous pourrez effectuer des requêtes / filtres avec eux. (Par exemple, pour la page de métriques de votre jeu, vous voudrez peut-être agréger les joueurs par type de dégâts d'arme, et vous pourrez le faire avec quelques jointures et un groupe par damage_type.)
Un élément peut avoir plusieurs rôles et exister dans plusieurs tables spécifiques à un rôle (dans cet exemple, à la fois arme et éclairage).
Si c'est juste un booléen comme "est-ce que cela peut être tenu", je le mettrais dans la table Items. Il peut être utile de mettre en cache "est-ce une arme", etc., afin que vous n'ayez pas à effectuer de recherche sur les armes et autres tables de rôles. Cependant, il ajoute de la redondance, vous devez donc faire attention à le synchroniser.
La recommandation d'Ari d'avoir une table supplémentaire par type peut également être utilisée avec cette approche si certaines données ne varient pas par article. Par exemple, si les dégâts de l'arme ne varient pas par élément, mais que les rôles varient toujours par élément, vous pouvez factoriser les attributs d'armes partagés dans un tableau:
// 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
Une autre approche consisterait à ce que les rôles joués par les éléments ne varient pas par élément, mais uniquement par type d'élément. Dans ce cas, vous mettriez le item_type dans la table Items et pouvez stocker les propriétés comme "est-ce une arme" et "est-il détenu" et "est-ce une lumière" dans une table ItemTypes. Dans cet exemple, je fais également en sorte que les noms des articles ne varient pas par article:
// 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
Il est probable que les types d'élément et les types de fichiers ne changent pas pendant le jeu, vous pouvez donc simplement charger ces tables en mémoire une fois et rechercher ces attributs dans une table de hachage au lieu d'une jointure de base de données.
Malheureusement, dans de telles situations, les bases de données relationnelles (telles que SQL) sont insuffisantes et les bases de données non relationnelles (telles que MongoDB ) excellent. Cela étant dit, il n'est pas impossible de modéliser les données dans une base de données relationnelle, et comme il semble que votre base de code soit déjà dépendante de SQL, voici le modèle avec lequel j'irais:
Au lieu de créer un élément et utilise une table, créez une table et une table de référence "[item] _type" pour chaque type d'élément.
Voici quelques exemples:
Cette solution vous donne beaucoup de flexibilité et d'évolutivité à long terme, elle réduit (sinon élimine) l'espace perdu et est auto-documentée (contrairement à "item_use_data"). Cela implique un peu plus de configuration du côté du développeur, mais, à mon avis, c'est la solution la plus élégante disponible pour les situations où vous devez utiliser une base de données relationnelle pour stocker vos données. De manière générale, les bases de données non relationnelles sont bien meilleures pour le développement de jeux, car elles sont plus flexibles dans la façon dont elles modélisent les données, tout en étant plus performantes que les bases de données SQL - ce qui en fait un choix "gagnant-gagnant".
Edit 1: Correction d'une erreur avec le champ potion_type_id
Edit 2: Ajout de plus de détails sur les bases de données non relationnelles vs relationnelles pour fournir une perspective supplémentaire
la source
Tout d'abord, videz l'approche d'héritage orientée objet et optez pour un système basé sur les composants .
Une fois que vous avez fait cela, la mise en page SQL devient soudainement beaucoup plus facile. Vous avez une table pour chaque type de composant avec un numéro d'identification partagé. Si vous voulez l'article # 17, vous allez chercher "ID d'article 17" dans chaque table. Toute table qui a une clé obtient son composant ajouté.
Votre table d'objets contient toutes les données requises pour les articles en général (nom, prix de vente, poids, taille, tout ce qui est partagé entre tous les articles.) Votre table d'armes contient toutes les données appropriées pour les armes, votre table de potions contient toutes les données appropriées pour les potions, votre table d'armure contient toutes les données appropriées pour l'armure. Vous voulez un plastron, c'est une entrée dans l'objet et une entrée dans l'armure. Vous voulez un heaume d'épée que vous pouvez boire, vous mettez simplement une entrée dans chaque table, et bam, vous avez terminé.
Gardez à l'esprit que ce même modèle n'est pas particulièrement spécifique à l'objet - vous pouvez l'utiliser pour les créatures, les zones, tout ce que vous souhaitez. C'est étonnamment polyvalent.
la source
L'utilisation de SQL a été votre grave erreur. Il n'est absolument PAS adapté au stockage de données statiques de conception de jeux.
Si vous ne pouvez pas vous éloigner de SQL, j'envisagerais de stocker des éléments dans un fichier sérialisé à partir de. C'est à dire
Bien sûr, c'est moche et jette toutes les subtilités SQL par la fenêtre, mais en avez-vous réellement besoin ? Très probablement, vous lisez toutes vos données d'objets au début du jeu de toute façon, et jamais
SELECT
par autre chose queitem_id
.la source
Selon le nombre de traits dont vous aurez probablement besoin, vous pouvez utiliser un masque de bits simple pour les objets avec les différents bits correspondant à différents traits:
Ensuite, vous pouvez faire des tests binaires simples pour voir si un objet peut être utilisé pour une certaine fonction.
Ainsi, une "bouteille en verre" pourrait avoir une valeur de 101111, ce qui signifie qu'elle peut être tenue, peut être utilisée comme une arme, elle se casse facilement, vous pouvez la jeter et elle peut contenir des liquides.
Tout éditeur que vous créez pour les éléments peut alors avoir un simple ensemble de cases à cocher pour activer / désactiver les traits sur un objet.
la source
Sur notre projet, nous avons item_attributes pour les différentes "données supplémentaires" qu'un élément peut avoir. Il est présenté quelque chose comme ceci:
Ensuite, nous avons une table d'attributs qui ressemble à ceci:
Ari Patrick a raison cependant, les db relationnels ne sont finalement pas faits pour ça. L'inconvénient est que nous avons des procédures assez complexes pour générer de nouveaux éléments (effectués via un outil externe - que je recommande fortement, n'essayez pas de les ajouter manuellement, vous ne ferez que vous embrouiller)
L'autre option que vous avez consiste à utiliser un langage de script pour créer les modèles d'élément, puis vous pouvez facilement les analyser et les utiliser pour créer de nouveaux éléments. Bien sûr, vous devez toujours enregistrer les données des éléments dans la base de données, mais à ce stade, vous n'avez pas à vous soucier des détails de la création de nouveaux éléments, vous pouvez à peu près simplement copier et l'ancien fichier de script, modifier certaines informations et vous êtes bon aller.
Honnêtement, si nous devions refaire la façon dont nous créons de nouveaux éléments statiques, nous opterions probablement pour une approche beaucoup plus simple en utilisant des modèles d'élément de script.
la source
Il s'agit de votre relation plusieurs-à-plusieurs typique, rien de trop ésotérique pour une base de données relationnelle capable. Vous avez de nombreux traits pour n'importe quel objet, et n'importe quel trait peut être utilisé par un ou plusieurs types d'objets différents. Modélisez trois relations (tables), l'une étant une relation d'association, et vous avez terminé. Une indexation appropriée vous assurera des lectures de données rapides.
Ajoutez un ORM dans votre code et vous devriez avoir très peu de problèmes de va-et-vient entre la base de données et le middleware. De nombreux ORM sont capables de générer eux-mêmes les classes eux-mêmes et deviennent ainsi encore plus "invisibles".
Quant aux bases de données NoSQL, il n'y a aucune raison que vous ne puissiez pas le faire avec celles-ci. Il est actuellement à la mode de cheerlead pour cette technologie dans les chiffons commerciaux et les blogs, mais ils viennent avec une multitude de ses propres problèmes: plates-formes relativement immatures, idéales pour les lectures simples rarement modifiées (comme les twits un à plusieurs dans un profil) mais médiocre pour les lectures ou mises à jour dynamiques complexes, la chaîne d'outils de support médiocre, les données redondantes et les problèmes d'intégrité qui les accompagnent, etc. Cependant, ils offrent l'attrait d'une meilleure évolutivité car ils évitent à des degrés divers les capacités transactionnelles et ses frais généraux, et des modèles plus faciles de distribution / réplication .
Le hobgoblin habituel des bases de données relationnelles par rapport aux bases de données NoSQL est la performance. Différents RDMBS ont différents degrés de frais généraux impliqués, ce qui les rend moins préférés pour la mise à l'échelle des niveaux de Facebook ou Twitter. Cependant, il est très peu probable que vous rencontriez ces problèmes. Même dans ce cas, de simples systèmes de serveurs SSD peuvent rendre le débat sur les performances inutile.
Soyons clairs: la plupart des bases de données NoSQL sont des tables de hachage fondamentalement distribuées et vous limiteront de la même manière qu'une table de hachage dans votre code, c'est-à-dire. toutes les données ne correspondent pas bien à ce modèle. Le modèle relationnel est beaucoup plus puissant pour modéliser les relations entre les données. (Le facteur de confusion est que la plupart des SGBDR sont des systèmes hérités qui sont mal réglés pour les demandes des 0,0001% populaires du Web, à savoir Facebook et al.)
la source
C'est là que je trouve que les mappeurs OR plus modernes sont vraiment utiles, comme Entity Framework 4 et sa fonctionnalité de code premier (CTP) . Vous écrivez simplement les classes et leurs relations en code normal et sans même avoir à les décorer ou à exécuter manuellement des outils, EF générera le magasin de sauvegarde au format SQL pour vous, avec les tables de liens requises et tout ... cela libère vraiment la créativité imo ^^
la source
Voici ce que j'envisage maintenant:
Étant donné que chaque "trait" nécessite essentiellement des changements dans le code, j'ai donc décidé de simplement conserver les traits (et toutes les données par défaut dont ils ont besoin) dans le code lui-même (au moins pour l'instant).
Par exemple
$traits = array('holdable'=>1, 'weapon'=>1, 'sword'=>array('min_dam'=>1, 'max_dam'=>500));
Ensuite, les éléments obtiennent un champ "trait_data" dans la base de données qui utilisera la
json_encode()
fonction pour être stocké au format JSON.Il est prévu que les objets héritent de toutes les statistiques par défaut de leurs traits parents et n'auront que des traits de substitution qui spécifient des différences par rapport à ce que les traits définissent comme valeurs par défaut.
L'inconvénient du champ des traits est que même si je pouvais éditer la partie json à la main ... ... ce ne sera pas vraiment facile ou sûr de le faire avec les données qui sont dans la base de données.
la source
C'est un peu hacky, et je ne garantis pas que c'est un bon design DB; mes cours de DB étaient il y a quelque temps, et ils ne me viennent pas rapidement à l'esprit. Mais s'il est garanti que les éléments contiennent, par exemple, moins de 10 éléments, vous pouvez attribuer à chaque élément un champ attribut1 et un champ attribut2, et ainsi de suite jusqu'à attribut10. Cela éliminerait votre besoin de la table plusieurs-à-plusieurs d'attribut d'article, au prix de limiter le nombre d'attributs.
En y repensant, je suis presque sûr que c'est une conception terrible, mais cela signifie que vous pouvez vous en tenir à une base de données relationnelle confortable et ne pas avoir à aller en territoire inconnu. C'est à vous de décider si le compromis en vaut la peine.
la source
Nevermind a donné une bonne réponse, mais je me demande, avez-vous même besoin d'une base de données pour cela? Stocker toutes les données dans un fichier ordinaire et les charger dans le programme au lancement semble être la façon la plus intelligente de le faire.
Éditer:
droite, dans un environnement piloté par demande sans état, vous feriez mieux de conserver les données dans une base de données. Écrivez vos données dans un fichier et écrivez un morceau de code pour le transformer en une base de données du type Nevermind suggéré.
Alternativement, si le nombre d'objets n'est pas trop grand, déclarer le lot littéralement dans un fichier de code peut être la méthode la plus rapide.
la source