Système de composants d'entité - progression du jeu


8

Je suis tout à fait nouveau dans le développement de jeux (mais pas dans la programmation) et j'essaie de comprendre quelle serait la meilleure façon de gérer la communication inter-mondiale. Je veux dire ceci:

J'ai lu sur les systèmes de composants d'entité (ECS) et comment les gens suggèrent d'utiliser différents mondes / espaces ( http://gamedevelopment.tutsplus.com/tutorials/spaces-useful-game-object-containers--gamedev-14091 ) pour une sous-partie d'un jeu. Par exemple, un HUD, un inventaire ou un combat / mouvement obtiennent chacun un monde / espace séparé (car ils ont des graphiques différents et une logique sous-jacente.

Cependant, je me demandais comment l'inventaire ou le HUD connaît la santé d'un joueur lorsque la santé est gérée par un espace / monde différent, par exemple en combat?

Cela s'applique également à la progression du jeu en général, par exemple le dialogue avec le PNJ (un dialogue serait un espace séparé car il s'agit d'un écran contextuel), mais comment transmettriez-vous les choix effectués dans (ou l'état) du dialogue à d'autres espaces / mondes . Ou fondamentalement tout autre type d'événement qui influence la progression du jeu dans différents espaces / mondes (santé, mana, quêtes, dialogue, combat, inventaire, hud, etc.)

Comment gérer ce type de design? A-t-il besoin d'un objet singleton (dans l'implémentation) qui contient tout ce type d'informations? Ce serait bizarre car alors la componentsnécessité de transmettre chaque changement à cet objet singleton qui donne envie de faire les choses deux fois (aller à l'encontre du DRY principal de la programmation) ...

Je suis un peu perdu ici en termes de design, des conseils?


---ÉDITER---

J'ai donc lu quelques autres articles suggérés par des commentaires et j'ai eu une idée générale des possibilités, mais chacun d'entre eux semble avoir un inconvénient majeur qui ne les rend pas corrects. Il est très possible que je supervise des détails qui pourraient résoudre ces inconvénients, alors n'hésitez pas à me corriger. Je vais essayer de donner un aperçu ainsi que des réponses à certaines questions.

Je vois trois options principales pour «partager» les données entre les espaces. Même si la plupart des articles concernent le partage de données entre systèmes, j'ai l'impression que la même chose peut être appliquée au partage de données entre systèmes.

1. Interrogation

Exemple : Si le monde HUD a besoin de connaître la santé actuelle du joueur, il peut interroger un autre monde et demander la santé actuelle.

Inconvénient : les mondes doivent se connaître, ce qui est un problème de dépendance majeur et va à l'encontre du découplage.

2: Messagerie directe (synchronisation et async)

Exemple : si pendant le combat la santé d'un joueur change, il peut envoyer des messages (synchronisation et async, tout ce qui est nécessaire) à d'autres mondes qui ont besoin de connaître ce changement.

Inconvénient : toujours le problème du découplage: les mondes doivent se connaître.

3: Messagerie indirecte (synchronisation et async) <- meilleure option

Exemple : si pendant le combat la santé d'un joueur change, il peut envoyer des messages (synchronisation et async, tout ce qui est nécessaire) au hub de messages général. D'autres mondes / systèmes qui ont besoin de connaître ce changement sont abonnés au canal de message particulier et lisent les messages.

Côté supérieur : complètement découplé, facilement gérable et extensible.

Inconvénient / peu clair : quand le canal de message sait-il que les messages doivent être supprimés? Ou peut-être que le système auquel vous êtes abonné marque (uniquement pour lui-même) le message comme lu et attend de nouveaux messages -> messagebox devient énorme après un certain temps. Comment les mondes / systèmes gèrent-ils l'ordre? Par exemple, pendant une trame: si le HUD a déjà interrogé le message de santé et après que la santé change, la prochaine trame du HUD est mise à jour. Pour certaines applications, ce n'est peut-être pas la bonne façon.

Q: Un seul objet de jeu peut exister dans plusieurs espaces

J'utilise le framework Artemis ECS qui est livré avec des espaces intégrés (appelés mondes). Chaque entité (et avec elle, les données sous forme de composants) est créée sur un monde et ne peut donc pas être partagée entre des mondes.


La messagerie est l'approche standard ici: gamedev.stackexchange.com/questions/23834/…
MichaelHouse

D'après ce que je peux lire dans l'article lié, un seul objet de jeu peut exister sous plusieurs espaces. Si vous avez des graphiques ou une logique différents entre les espaces, séparez les données des graphiques et de la logique. Partagez l'objet de jeu de données à travers les espaces et agrégez-le avec différents graphiques et objets de jeu de logique.
Andreas

Cette réponse que j'ai donnée sur les systèmes de messagerie peut également vous aider: gamedev.stackexchange.com/questions/7718/…
James

J'ai mis en œuvre les trois solutions (interrogation, directe et indirecte) dans ma vie gamedev. Et je peux dire que la troisième option me convient le mieux. Vous pouvez facilement découpler les systèmes et exécuter leur logique en parallèle. Le seul inconvénient est que vous devez effectuer 9 appels de fonction pour acheminer chaque message / événement d'un système à un autre. De toute évidence, vous pouvez l'optimiser et le gros avantage est que vous n'avez pas besoin de mutex ou de singletons dans cette approche.
Gregory

@Gregory Merci pour votre imput, je m'attendais à ce que les messages indirects soient la meilleure option. Je n'étais pas au courant des 9 appels de fonction, mais en planifiant ce messagehub, je me suis rendu compte que ce serait en fait pas mal d'appels. Avez-vous déjà trouvé une bonne solution / alternative à la suppression de messages quand aucun système n'en a plus besoin?
Tim

Réponses:


1

Une façon de voir les choses est que vous mettez peut-être trop dans vos objets de jeu.

Il n'y a aucune raison que le code qui connecte réellement le HUD à votre jeu dans le monde doive être dans un composant / système qui vit dans un espace particulier. Ce code sera peut-être mieux de vivre dans un gestionnaire central ou un script global qui a accès à tous les espaces et à tous les objets, et peut ensuite interagir avec le code qui sait quand créer réellement un espace et quoi y mettre (par exemple, le code qui engendre le joueur, enregistre son état entre les niveaux, etc.).

Vous pouvez également avoir un "espace maître" qui contient des objets de jeu avec une logique ou des données qui doivent persister ou manipuler les espaces utilisés pour les niveaux et l'interface utilisateur. Cette approche est courante dans les moteurs qui obligent les développeurs à mettre tous les scripts / logiques sur les composants / objets (par exemple, dans Unity, vous créez un objet principal global et le définissez pour qu'il persiste pendant le déchargement de la scène; si Unity avait réellement des espaces, vous '' d utilisez ceux au lieu du drapeau).

N'oubliez pas, abuser de votre ECS revient à abuser des modèles de conception; ce n'est pas parce que vous avez un nouvel outil astucieux que vous êtes censé l'utiliser pour résoudre tous les problèmes que vous rencontrez. Évaluez votre espace problématique et sélectionnez la solution la plus adaptée, même si c'est la vieille chose terne que vos ancêtres utilisaient dans les âges sombres des années 90. : p


0

J'ai créé quelques prototypes mais rien de trop grand et la façon dont j'ai utilisé pour gérer plusieurs espaces était simplement de créer un objet de jeu qui contient, un monde, un joueur, etc. et que je configurerais certaines propriétés requises par d'autres espaces, par exemple la santé , dans l'objet de jeu

Chaque fois qu'il est appelé, il obtient la santé du joueur. de cette façon, je pourrais l'envoyer au HUD et afficher la barre de santé.

Ce n'est pas le plus propre, mais il fait le travail, j'ai fait des tests de performance en 2013 et tout semblait bien fonctionner. pour éviter de telles dépendances, vous pouvez toujours laisser tomber la barre de santé lorsque la santé du joueur est nulle.

Habituellement, lorsque le lecteur n'existe pas, cela signifie que l'utilisateur se trouve dans un menu ou dans une cinématique.

Exemple de code:

public float PlayerHealth {
  get {
    if (player !+ null) 
      return player.Health;
    return -1;
  }
}

J'espère que c'est ce que vous cherchiez.


0

c'est quelque chose sur lequel je travaille en fait depuis quelques semaines. Je travaille sur ma propre bibliothèque ECS (je voulais le faire par expérience et juste pour l'essayer, parce que je voulais le faire depuis un certain temps).

Ceci est le lien github: https://github.com/gioragutt/xna-ecs

Pour votre problème, j'ai toujours écrit une petite bibliothèque de pubsub, que vous pouvez voir ici

Fondamentalement, j'ai une EmsClientclasse, dont les choses peuvent dériver. Actuellement, mes composants ne le font pas, mais les classes de niveau supérieur, bien qu'il n'y ait aucune raison de ne pas le faire. Je souscris aux noms des messages, et fournir un rappel avec la signature suivante: Action<JObject>. Comme vous l'avez déjà compris, j'utilise des objets Json comme moyen de transférer des messages. Je l'ai fait après avoir utilisé juste avant byte[], et j'ai trouvé que j'avais besoin de quelque chose de plus général, et puisque je suis habitué à quelque chose comme ça depuis mon lieu de travail (nous avons un IPCD qui fonctionne de manière similaire, sauf le rappel est toujours la même, car nous séparons généralement la responsabilité des différents gestionnaires).

Il y en a un EmsServer(un sur le serveur et un sur chaque client) qui est responsable du déplacement des messages entre EmsClientsur son domaine ( EmsServercôté serveur déplace les messages entre EmsClientscôté serveur, et vice versa pour le côté client).

Pour la messagerie entre le client et le serveur, j'ai créé un EmsServerEndpointqui est un EmsClientlui-même, il fait juste la logique de tamponner les messages envoyés sur son domaine, et de les vider vers d'autres domaines (FE le client envoie le message au serveur, alors que lorsque le serveur transfère chaque message à tous les clients connectés.

Vous pouvez voir l' utilisation dans beaucoup d'endroits, fe: ClientGameManager, ServerGameManager.

Alors que, pour votre exemple, si je veux ajouter un composant GUI pour un joueur, vous pouvez regarder ICI , les méthodes BeginAllocateLocalet BeginAllocateRemote, qui sont responsables de la construction GameObjectsdes joueurs. Chacun GameObjectcontient un Entity(de la bibliothèque ECS) et un IComponentContainer(qui l'est aussi, de la bibliothèque ECS). Chacun GameObjectobtient automatiquement une transformation (comme dans Unity, dont je me suis inspiré).

Mon code parle à peu près de lui-même, et si vous y arrivez, je suis à la recherche de critiques, donc je voudrais des critiques constructives :)

J'espère que mon code vous aidera avec quelques idées!


0

Considérez les composants de modèle d'observateur / sujet pour vos composants de jeu et les composants d'interface utilisateur. Vous les branchez ensemble lors de la création / du chargement, puis vous les oubliez. Si la santé du personnage change, il avertit tous les observateurs, qui peuvent faire ce qu'ils veulent avec les informations.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.