Soyons honnêtes :nous aimons tous jouer à des jeux, en particulier sur nos ordinateurs. Jusqu'à ce qu'Internet se généralise, la plupart d'entre nous jouaient seuls à des jeux informatiques, généralement contre des adversaires IA. C'était amusant, mais dès que vous avez compris comment fonctionnaient les mécanismes de jeu, le jeu a perdu la majeure partie de sa magie.
Le développement d'Internet a déplacé les jeux en ligne. Maintenant, nous pouvons jouer contre des adversaires humains et tester nos compétences contre les leurs. Fini le gameplay par cœur !
Puis les jeux massivement multijoueurs en ligne (MMO) sont apparus et ont tout changé. Des milliers de joueurs se sont retrouvés dans les mêmes univers de jeu, se disputant les ressources, négociant, échangeant et se battant. Pour rendre ces jeux possibles, une structure de base de données était nécessaire pour stocker toutes les informations pertinentes.
Dans cet article, nous allons concevoir un modèle qui intègre les éléments les plus courants trouvés dans les jeux MMO. Nous verrons comment l'utiliser, ses limites et ses améliorations possibles.
Une introduction aux modèles de données pour les jeux MMO
Il existe aujourd'hui de nombreux jeux MMO très populaires, et ils impliquent toutes sortes de scénarios. Je vais me concentrer ici sur les jeux de stratégie comme Ogame , Travian , Sparte :Guerre des Empires et Imperia Online . Ces jeux concernent davantage la planification, la construction et l'élaboration de stratégies, et moins l'action directe.
Les jeux MMO se déroulent dans des univers différents, sont visuellement différents et utilisent des options de jeu plus ou moins différentes. Pourtant, certaines idées sont les mêmes. Les joueurs se disputent des emplacements, se battent pour eux et forment une alliance avec (et contre) d'autres joueurs. Ils construisent des structures, collectent des ressources et recherchent des technologies. Ils construisent des unités (comme des guerriers, des chars, des commerçants, etc.) et les utilisent pour commercer avec des alliés ou pour combattre avec des adversaires. Tout cela doit être pris en charge dans notre base de données.
On peut considérer ces jeux comme des jeux de société en ligne avec de nombreuses cases indexées. Chaque carré peut avoir de nombreuses actions différentes qui lui sont associées; certaines actions incluront plusieurs carrés - par ex. lorsque nous déplaçons des unités ou des ressources d'un endroit à un autre.
La base de données est divisée en cinq zones principales :
Players / Users
Alliances
Locations and Structures
Research and Resources
Units
Les sept tableaux non groupés restants sont liés aux unités et décrivent la position de l'unité et les mouvements dans le jeu. Nous examinerons chacun de ces domaines de manière beaucoup plus détaillée, en commençant par les joueurs et Alliances .
Joueurs et alliances
Sans aucun doute, les joueurs sont la partie la plus importante de tout jeu.
Le player
table contient une liste de tous les joueurs inscrits prenant part à une instance de jeu. Nous conserverons les noms d'utilisateur, les mots de passe et les pseudonymes des joueurs. Ceux-ci seront stockés dans le user_name
, password
, et nickname
attributs respectivement.
Les nouveaux utilisateurs devront fournir une adresse e-mail lors de l'inscription. Un code de confirmation sera généré et leur sera envoyé, auquel ils répondront. Nous mettrons à jour la confirmation_date
attribut lorsque l'utilisateur vérifie son adresse e-mail. Ainsi, cette table a trois clés uniques :user_name
, nickname
et email
.
Chaque fois qu'un utilisateur se connecte, un nouvel enregistrement est ajouté dans le login_history
table. Tous les attributs de ce tableau sont explicites. Le logout_time
est spécifique. Il peut être NULL lorsque la session actuelle de l'utilisateur est active ou lorsque les utilisateurs quittent le jeu (sans se déconnecter) en raison de problèmes techniques. Dans les login_data
attribut, nous stockerons les informations de connexion telles que l'emplacement géographique d'un joueur, son adresse IP, ainsi que l'appareil et le navigateur qu'il utilise.
La plupart des jeux MMO nous permettent de coopérer avec d'autres joueurs. L'une des formes standard de coopération entre joueurs est l'alliance. Les joueurs partagent leurs "données privées" dans le jeu (statut en ligne, plans, emplacement de leurs villes et colonies, etc.) avec d'autres pour bénéficier d'actions alliées et pour le pur plaisir.
L'alliance
table stocke des informations de base sur les alliances de jeu. Chacun a un alliance_name
unique que nous stockerons. Nous aurons également un champ, date_founded
, qui stocke quand l'alliance a été fondée. Si une alliance est dissoute, nous stockerons ces informations dans le date_disbanded
attribut.
Le alliance_member
table relie les joueurs avec des alliances. Les joueurs peuvent rejoindre et quitter la même alliance plus d'une fois. Pour cette raison, le player_id
– alliance_id
paire n'est pas une clé unique. Nous conserverons les informations concernant le moment où un joueur rejoint l'alliance et quand (si) il part dans le date_from
et date_to
des champs. Le membership_type_id
l'attribut est une référence au membership_type
dictionnaire; il stocke le niveau actuel des droits des joueurs dans l'alliance.
Les droits des joueurs dans une alliance peuvent changer au fil du temps. Les membership_actions
, membership_type
et actions_allowed
ensemble, les tables définissent tous les droits possibles pour les membres de l'alliance. Ce modèle ne permet pas aux joueurs de définir leurs propres niveaux de droits dans une alliance, mais cela pourrait être accompli assez facilement en ajoutant de nouveaux enregistrements dans le membership_type
dictionnaire et stocker des informations sur les alliances auxquelles ils sont liés.
Pour résumer :les valeurs stockées dans ces tables sont définies par nous lors de la configuration initiale; ils ne changeront que si nous introduisons de nouvelles options.
Le membership_history
La table stocke toutes les données concernant les rôles ou les droits des joueurs au sein d'une alliance, y compris la plage de validité de ces droits. (Par exemple, il pourrait avoir des autorisations « novice » pendant un mois, puis « membre à part entière » à partir de ce moment-là.) Le date_to
L'attribut est NULLable car les droits actuellement actifs ne sont pas encore terminés.
Les membership_actions
dictionnaire contient une liste de toutes les actions que les joueurs peuvent faire dans une alliance. Chaque action a son propre action_name
et la logique du jeu est construite autour de ces noms. Nous pouvons nous attendre à des valeurs telles que "voir la liste des membres" , "afficher les statuts des membres" et "envoyer un message" ici.
Le membership_type
dictionnaire contient les noms uniques des groupes d'actions utilisés dans le jeu. Le actions_allowed
table attribue des actions aux types d'adhésion. Chaque action ne peut être affectée qu'une seule fois à un type. Par conséquent, l'action membership_action
- membership_type
paire constitue la clé unique de cette table.
Lieux et Structures
Les lieux de jeu sont des zones où les joueurs collectent des ressources et construisent des structures et des unités. Certains jeux ont une gamme prédéfinie d'emplacements possibles, tandis que d'autres peuvent permettre aux utilisateurs de définir leurs propres emplacements.
Dans un espace 3D, les emplacements peuvent être définis avec des coordonnées [x:y:z]. Si un jeu a une plage prédéfinie, il se peut qu'il ne permette pas aux joueurs d'utiliser un emplacement hors de la plage [0:1000] pour les trois axes, nous sommes donc limités à un espace de 1000 * 1000 * 1000.
D'un autre côté, nous voulons peut-être permettre aux joueurs d'entrer les coordonnées exactes de leur nouvel emplacement - par ex. [1001:2073:4] – et nous voulons que le jeu le traite pour eux.
Nous conserverons une liste de tous les emplacements utilisés dans une instance de notre jeu dans le location
table. Chaque emplacement a son propre nom, mais les noms ne sont pas uniques. Par contre, les coordinates
L'attribut ne doit contenir que des valeurs uniques. Les coordonnées de localisation sont stockées sous forme de valeurs textuelles, nous pouvons donc stocker les coordonnées pour les jeux 3D sous la forme [112:72:235]. Les coordonnées des jeux 2D peuvent être stockées sous la forme <1102:98>.
Dans certains jeux, les emplacements auront un certain nombre de cases utilisées pour abriter des structures ou des unités. Nous conserverons ces informations dans la dimension
attribut, qui est un champ de texte. Une dimension peut être simplement un nombre de carrés dans une grille 2D ou 3D. Le player_id
L'attribut stocke des informations sur le propriétaire actuel de cet emplacement. Il peut être NULL lorsque des emplacements sont prédéfinis et que les joueurs s'affrontent pour les occuper.
La structure
table contient une liste de toutes les structures que nous pouvons construire à différents endroits du jeu. Les structures représentent des améliorations qui nous permettent de produire de meilleures unités, d'effectuer de nouveaux types de recherche, de produire plus de ressources, etc. Chaque structure utilisée dans le jeu a son propre structure_name
. Quelques structure_name
possibles les valeurs sont « ferme », « mine de minerai », « centrale solaire » et « centre de recherche ».
Nous pouvons nous attendre à ce que chaque structure soit mise à niveau plusieurs fois, nous stockerons donc également des informations sur son niveau actuel. Chaque mise à niveau améliore la sortie des structures, donc elle produit plus de ressources ou nous permet d'utiliser de nouvelles fonctionnalités dans le jeu. Nous ne pouvons pas connaître le niveau maximum de mise à niveau à l'avance, nous allons donc définir tous les éléments liés au niveau (coûts, temps de mise à niveau et production) avec des formules. Toutes les formules stockées dans la base de données sont au cœur des mécanismes du jeu, et leur ajustement est crucial pour l'équilibre du jeu et le gameplay en général.
C'est également le cas avec la upgrade_time_formula
attribut. Un exemple de valeur pour ce champ est "
Dans la plupart des cas, certaines conditions doivent être remplies avant que les joueurs ne prennent certaines mesures. Peut-être devons-nous effectuer un certain nombre de recherches avant de pouvoir construire de nouvelles structures ou vice versa. Nous stockerons le niveau de recherche nécessaire pour construire des structures dans le prerequisite_research
table. Les relations et le niveau de structure nécessaire pour démarrer diverses recherches sont conservés dans le prerequisite_structure
table. Dans les deux tables, les clés étrangères research_id
et structure_id
sont appariés pour former une clé unique. Le level_required
l'attribut est la seule valeur.
Ces deux tableaux, prerequisite_research
et prerequisite_structure
, forment également le cœur du jeu.
Pour chaque structure, nous définirons une liste de prérequis :d'autres structures et leurs niveaux minimum que les joueurs doivent avoir pour commencer à construire. Nous stockerons ces données dans le structure_required
table. Ici, structure_id
représente la structure que nous voulons construire; structure_required_id
est une référence à la ou aux structure(s) préalable(s) et au level
est le niveau requis.
La structure_built
La table stocke des informations sur les niveaux de structure actuels à un emplacement donné. Le upgrade_ongoing
l'attribut sera défini uniquement si une mise à niveau est actuellement en cours, tandis que l'attribut upgrade_end_time
l'attribut contiendra un horodatage une fois la mise à niveau terminée.
La structure_formula
tableau relie les structures et les ressources. La paire de clés étrangères de cette table constitue sa clé unique. Cette table possède également deux attributs de texte contenant des formules avec upgrade_time_formula
. Nous en avons besoin car nous devons définir les ressources consacrées à la construction de chaque structure. Nous devons également définir la production de ressources après la mise à niveau, si la structure génère des ressources (c'est-à-dire que la mine de minerai produira
Recherche et ressources
La recherche (ou les technologies) dans les jeux est généralement nécessaire à la création d'autres fonctionnalités. Sans certains niveaux de recherche, de nouvelles structures ou types d'unités ne peuvent pas être construits. La recherche peut aussi avoir ses propres exigences. L'un des plus courants est le niveau d'une structure donnée, généralement appelée « laboratoire de recherche ». Ou peut-être que les joueurs doivent terminer un certain niveau de recherche avant de pouvoir commencer de nouvelles recherches. Toutes ces exigences seront traitées dans cette section. Ci-dessous, nous pouvons trouver le modèle de données pour la recherche et les ressources :
La research
table contient une liste de toutes les actions de recherche possibles dans notre jeu. Il utilise la même logique que la structure
table. Le research_name
est la clé unique de la table, tandis que la upgrade_time_formula
Le champ contient une représentation textuelle de la formule des exigences de temps de recherche, avec upgrade_formula
stocké dans la research_formula
tableau.
Comme pour les structures, nous définirons la liste de toutes les autres recherches et leurs niveaux qui doivent être complétés avant de pouvoir commencer un autre type de recherche. Nous stockerons ces données dans le research_required
table, où research_id
représente la recherche souhaitée; research_required_id
est une référence à la recherche préalable, et level
est le niveau requis.
La recherche est liée aux joueurs individuels, et pour chaque joueur - recherche ch paire, nous devons stocker le niveau de recherche actuel d'un joueur et tout statut de mise à niveau en cours. Nous stockerons ces informations en utilisant le research_level
table de la même manière que nous avons utilisé le structure_built
tableau.
Des ressources telles que le bois, le minerai, les pierres précieuses et l'énergie sont extraites ou collectées et utilisées plus tard pour construire des structures et d'autres améliorations. Nous stockerons une liste de toutes les ressources du jeu dans la resource
dictionnaire. Le seul attribut ici est le resource_name
champ, et c'est aussi la clé unique de la table.
Pour suivre la quantité actuelle de ressources sur chaque emplacement, nous utiliserons le resources_on_location
table. Encore une fois, une paire de clés étrangères (resource_id
et location_id
) forme la clé unique de la table, tandis que le number
l'attribut stocke les valeurs de ressources actuelles.
Unités et Mouvements
Les ressources sont utilisées pour produire des unités. Les unités peuvent être utilisées pour transporter des ressources, attaquer d'autres joueurs ou, en général, piller et brûler.
La liste des types d'unités utilisées dans notre jeu est stockée dans le unit
dictionnaire avec une seule valeur, unit_name
; cet attribut est la clé unique de cette table. Certaines unités de jeu courantes sont "swordsman", "battlecruiser", "griffin", "jet fighter", "tank", etc.
Nous devons décrire chaque unité avec des caractéristiques spécifiques. Une liste de toutes les caractéristiques possibles est stockée dans le characteristic
dictionnaire. Le characteristic_name
champ contient une valeur unique. Les valeurs de ce champ peuvent inclure :"attaque", "défense" et "points de vie". Nous attribuerons des caractéristiques aux unités à l'aide du unit_characteristic
relation. La paire de clés étrangères de unit_id
et characteristic_id
forment la clé unique de la table. Nous n'utiliserons qu'un seul attribut, value
, pour stocker la valeur souhaitée.
L'research_unit
Le tableau contient une liste de toutes les activités de recherche qui doivent être terminées avant que nous puissions commencer la production d'un type d'unité donné. Le unit_cost
table définit les ressources nécessaires pour produire une seule unité. Les deux tables ont des clés uniques composées de la paire de clés étrangères (research_id
ou resources_id
combiné avec unit_id
) et un champ de valeur (cost
et level_required
).
Et maintenant, la partie amusante. La production est amusante, mais déplacer des unités et passer à l'action, c'est encore mieux. Nous avons déjà introduit l'unit
table, mais nous la conserverons ici en raison de sa relation avec d'autres tables.
Soit les unités sont stationnées sur un emplacement, soit elles se déplacent entre les emplacements. Ajout du player_id
champ détermine qui est propriétaire de l'emplacement ou du groupe qui se déplace entre les emplacements.
Si des unités sont simplement stationnées à l'emplacement donné, nous enregistrerons cet emplacement et le nombre d'unités qui y sont stationnées. Pour ce faire, nous utiliserons le units_on_location
tableau.
Lorsque les unités ne sont pas stationnées, elles se déplacent. Nous devrons stocker leur point de départ et leur destination. De plus, nous devons définir les actions possibles lors des mouvements. Toutes ces actions sont stockées dans le movement_type
dictionnaire. Le type_name
l'attribut est unique tandis que le allows_wait
L'attribut détermine si une action permet d'attendre au point de destination.
Nous pouvons déplacer un seul type d'unité, mais dans presque tous les cas, nous déplacerons de nombreuses unités de plusieurs types d'unités différents. Ce groupe partagera des données communes et nous les stockerons dans le group_movement
table. Dans ce tableau, nous définirons les éléments suivants :
- le joueur qui a initié cette action
- le type d'action
- le point de départ
- le point de destination
- l'
arrival_time
à destination - le
return_time
au point de départ - le
wait_time
à destination
Le return_time
l'attribut peut être NULL s'il s'agit d'un aller simple, et wait_time
est défini par le joueur. Les unités appartenant à un groupe sont définies par des valeurs stockées dans le units_in_group
table. La paire de clés étrangères de units_id
et group_moving_id
forme la clé unique de la table. Le nombre d'unités de même type au sein d'un groupe est défini dans le number
attribut.
Chaque mouvement peut transporter des ressources d'un endroit à un autre. Par conséquent, nous allons définir une relation plusieurs à plusieurs entre le group_movement
et les resources
les tables. Outre les clés primaires et étrangères, le resources_in_group
table contient uniquement le number
attribut. Ce champ stocke la quantité de ressources que les joueurs déplacent du point de départ à leur destination.
Dans la plupart des cas, les joueurs peuvent appeler d'autres personnes pour rejoindre leur aventure. Pour cela, nous utiliserons deux tables :allied_movement
et allied_groups
. Un joueur lancera une action conjointe, ce qui créera un nouveau record dans le allied_movement
table. Tous les groupes d'unités qui participent à une action alliée sont définis par des valeurs stockées dans le allied_groups
table. Chaque groupe ne peut être affecté qu'une seule fois à une action alliée, les clés étrangères forment donc la clé unique de cette table.
Ce modèle nous donne la structure de base nécessaire pour construire un jeu de stratégie MMO. Il contient les fonctionnalités de jeu les plus importantes :emplacements, structures, ressources, recherche et unités. Il les relie également, nous permet de définir les prérequis dans la base de données et stocke également la majeure partie de la logique du jeu dans la base de données.
Une fois ces tables remplies, la majeure partie de la logique du jeu est définie et nous ne nous attendons pas à ce que de nouvelles valeurs soient ajoutées. Presque chaque table a une valeur de clé unique, soit un nom de fonctionnalité, soit une paire de clés étrangères. La modification des caractéristiques des unités et des formules de production/coût nous permettra de modifier l'équilibre du jeu dans la couche de base de données.
Comment changeriez-vous ce modèle ? Qu'aimez-vous et que feriez-vous différemment ? Dites-le nous dans la section des commentaires !