Database
 sql >> Base de données >  >> RDS >> Database

Jeux MMO et conception de bases de données

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_idalliance_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 " * 30 min" , où représente le niveau auquel nous voulons passer.

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 comme paramètre. Nous définirons ces formules, l'une pour les coûts et l'autre pour la génération de ressources, dans la base de données. Ils seront similaires à la 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 * 20 minerai par jour).

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 comme paramètre. Toutes les ressources requises pour les mises à niveau sont définies dans la 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 !