Question

Je développe un vrai temps clone de jeu de stratégie sur la plate-forme Java et j'ai quelques questions sur l'endroit où conceptionnelles pour mettre et comment gérer l'état du jeu. Le jeu utilise Swing / Java2D comme rendu. Dans la phase actuelle de développement, pas de simulation et ne AI est présent et que l'utilisateur est en mesure de changer l'état du jeu (par exemple, la construction / démolition d'un bâtiment, ajoutez-supprimer les lignes de production, l'assemblage des flottes et de l'équipement). Par conséquent, la manipulation de l'état du jeu peut être exécuté dans le thread de distribution d'événements sans rendu recherche. L'état du jeu est également utilisé pour afficher diverses informations agrégées à l'utilisateur.

Cependant, comme je l'ai besoin d'introduire la simulation (par exemple, la construction de progrès, les changements démographiques, les mouvements de la flotte, processus de fabrication, etc.), en changeant l'état du jeu dans une minuterie et EDT va sûrement ralentir le rendu.

Disons que l'opération de simulation / AI est réalisée dans toutes les 500ms et je l'utilise SwingWorker pour le calcul d'environ 250 ms de longueur. Comment puis-je veillerai, qu'il n'y a pas de condition de course en ce qui concerne l'état du jeu se lit entre la simulation et l'interaction utilisateur possible?

Je sais que le résultat de la simulation (qui est petite quantité de données) peut être efficace est retourné à l'EDT via l'appel SwingUtilities.invokeLater ().

Le modèle d'état de jeu semble être trop complexe pour être infaisable pour tout en utilisant des classes de valeurs immuables partout.

Y at-il une approche relativement correcte pour éliminer cette lecture condition de course? Peut-être faire un état de jeu complet / partiel clonage sur chaque tick de minuterie ou changer l'espace de vie de l'état du jeu de HAE dans un autre thread?

Mise à jour: (des commentaires que je donnai) Le jeu fonctionne avec 13 joueurs AI contrôlées, 1 joueur humain et a environ 10000 objets de jeu (planètes, bâtiments, équipements, recherche, etc.). Un objet de jeu par exemple, a les attributs suivants:

World (Planets, Players, Fleets, ...)
Planet (location, owner, population, type, 
    map, buildings, taxation, allocation, ...)
Building (location, enabled, energy, worker, health, ...)

Dans un scénario, l'utilisateur construit un nouveau bâtiment sur cette planète. Ceci est réalisé en EDT comme doit être changé la carte et la collecte des bâtiments. Parallèlement à cela, une simulation est exécutée sur toutes les 500ms pour calculer l'allocation énergétique aux bâtiments sur toutes les planètes de jeu, qui doit traverser la collection des bâtiments pour la collecte des statistiques. Si l'allocation est calculée, il est soumis à l'EDT et le champ d'énergie de chaque bâtiment se voit assigner.

interactions joueur humain n'ont cette propriété, car les résultats du calcul AI sont appliquées aux structures EDT de toute façon.

En général, 75% des attributs d'objets sont statiques et utilisés uniquement pour le rendu. Le reste peut être modifiée soit par interaction de l'utilisateur ou d'une décision de simulation / AI. Il est également assuré, qu'aucune nouvelle étape de simulation / AI est démarré jusqu'à ce que le précédent a écrit toutes les modifications.

Mes objectifs sont les suivants:

  • Évitez de retarder l'interaction de l'utilisateur, par exemple utilisateur place le bâtiment sur la planète et seulement après 0.5s obtient la rétroaction visuelle
  • éviter de bloquer le EDT avec calcul, verrouillage attente, etc.
  • Évitez les problèmes de concurrence avec la collecte et la modification traversal, modifications d'attributs

Options:

  • Verrouillage fin objet grain
  • collections Immuable
  • champs volatils
  • Instantané partiel

Tous ces avantages, les inconvénients ont et provoque le modèle et le jeu.

Mise à jour 2: Je parle de ce jeu . Mon clone est . Les captures d'écran peuvent aider à imaginer les interactions modèle de rendu et de données.

Mise à jour 3:

Je vais essayer de donner un petit exemple de code pour clarifier mon problème car il semble d'après les commentaires, il est mal compris:

List<GameObject> largeListOfGameObjects = ...
List<Building> preFilteredListOfBuildings = ...
// In EDT
public void onAddBuildingClicked() {
    Building b = new Building(100 /* kW */);
    largeListOfGameObjects.add(b);
    preFilteredListOfBuildings.add(b);
}
// In EDT
public void paint(Graphics g) {
    int y = 0;
    for (Building b : preFilteredListOfBuildings) {
        g.drawString(Integer.toString(b.powerAssigned), 0, y);
        y += 20;
    }
}
// In EDT
public void assignPowerTo(Building b, int amount) {
    b.powerAssigned = amount;
}
// In simulation thread
public void distributePower() {
    int sum = 0;
    for (Building b : preFilteredListOfBuildings) {
        sum += b.powerRequired;
    }
    final int alloc = sum / (preFilteredListOfBuildings.size() + 1);
    for (final Building b : preFilteredListOfBuildings) {
        SwingUtilities.invokeLater(=> assignPowerTo(b, alloc));            
    }
}

Ainsi, le chevauchement entre le onAddBuildingClicked () et distributePower (). Maintenant, imaginez le cas où vous avez 50 de ce genre de superpositions entre les différentes parties du modèle de jeu.

Était-ce utile?

La solution

Cela sonne comme il pourrait bénéficier d'une approche client / serveur:

Le joueur est un client - l'interactivité et le rendu se produisent sur cette fin. Ainsi, le joueur appuie sur un bouton, la demande va au serveur. La réponse du serveur revient et l'état du joueur est mis à jour. À tout moment entre ces choses qui se passent, l'écran peut être repeint, et elle reflète l'état du jeu que le client connaît actuellement il.

L'AI est également un client - il est l'équivalent d'un bot

.

La simulation est le serveur. Il obtient des mises à jour de ses clients à divers moments et met à jour l'état du monde, puis envoie ces mises à jour à tout le monde, selon le cas. Voici où il lie avec votre situation: La simulation / AI exige un monde statique, et beaucoup de choses se passent à la fois. Le serveur peut simplement la queue des demandes de modification et les appliquer avant d'envoyer les mises à jour au client (s). Pour autant que concerné du serveur, le monde du jeu ne change pas réellement en temps réel, il change à chaque fois que le serveur bien sacrément décide qu'il est.

Enfin, du côté client, vous pouvez empêcher le délai entre la pression sur le bouton et voir un résultat en faisant quelques calculs approximatifs rapides et afficher un résultat (donc le besoin immédiat est atteint), puis d'afficher le résultat plus correct lorsque la serveur se déplace pour vous parler.

Notez que cela ne doit pas être effectivement mis en œuvre dans un réseau TCP / IP over-the-internet sorte de façon, juste qu'il aide à penser en ces termes.

Vous pouvez également placer la responsabilité de garder les données cohérentes au cours de la simulation sur une base de données, car ils sont déjà construits avec verrouillage et à l'esprit de cohérence. Quelque chose comme sqlite pourrait travailler dans le cadre d'une solution non-réseau.

Autres conseils

Je ne sais pas, je comprends parfaitement le comportement que vous recherchez, mais il semble que vous besoin de quelque chose comme un fil / file d'attente de changement d'état de sorte que tous les changements d'état sont gérées par un seul fil.

Créer un api, peut-être comme SwingUtilities.invokeLater () et / ou SwingUtilities.invokeAndWait () pour votre file d'attente de changement d'état pour gérer vos demandes de changement d'état.

Comment cela se reflète dans l'IUG Je pense que dépend du comportement que vous recherchez. Ne peut pas dire retirer de l'argent parce que l'état actuel est de 0 $, ou pop à l'utilisateur que le compte était vide au moment du traitement de la demande se retirer. (Probablement pas avec cette terminologie ;-))

L'approche la plus simple est de faire la simulation assez rapide pour exécuter dans l'EDT. Préférez les programmes qui fonctionnent!

Pour le modèle à deux fils, ce que je suggère est de synchroniser le modèle de domaine avec un modèle de rendu. Le modèle devrait rendre conserver les données sur ce qui est venu du modèle de domaine.

Pour une mise à jour: Dans le fil de simulation verrouiller le modèle de rendu. Traverse la mise à jour du modèle rendu où les choses sont différentes de ce que l'on attend la mise à jour du modèle de rendu. Lorsque vous avez terminé la traversée, déverrouiller le modèle rendu et planifier une repeindre. Notez que dans cette approche, vous n'avez pas besoin d'auditeurs bazillion.

Le rendu modèle peut avoir des profondeurs différentes. À un extrême, il est peut-être une image et l'opération de mise à jour est juste pour remplacer une référence unique avec le nouvel objet d'image (cette poignée coutume, par exemple, le redimensionnement ou toute autre interaction superficielle très bien). Vous ne pourriez pas la peine de vérifier si un élément a changé et simplement mettre à jour eveything.

Si vous changez l'état du jeu est rapide (une fois que vous savez ce qu'il faut changer pour), vous pouvez traiter l'état du jeu comme les autres modèles Swing et seul changement ou afficher l'état dans l'EDT. Si vous changez l'état du jeu n'est pas rapide, vous pouvez synchroniser le changement d'état et de le faire dans travailleur balançoire / minuterie (mais pas l'EDT) ou vous pouvez le faire en fil séparé que vous traitez de façon similaire à l'EDT (à laquelle vous pointez regarder à l'aide d'un BlockingQueue pour traiter les demandes de changement). Le dernier est plus utile si l'interface utilisateur ne doit récupérer les informations de l'état du jeu mais a rendu les changements envoyés par les auditeurs ou les observateurs.

Est-il possible de mettre à jour progressivement l'état du jeu et ont toujours un modèle cohérent? Par exemple recalcule pour un sous-ensemble d'objets planète / lecteur / flotte entre les rend / mises à jour par l'utilisateur.

Si oui, vous pouvez exécuter des mises à jour progressives du EDT qui calculent qu'une petite partie de l'Etat avant de permettre à l'EDT pour traiter les entrées utilisateur et rendu.

Après chaque mise à jour incrémentale dans le EDT vous auriez besoin de se rappeler à quel point le modèle reste à être mis à jour et planifier une nouvelle SwingWorker sur le EDT pour poursuivre ce traitement après des entrées utilisateur en attente et le rendu a été effectué.

Cela devrait vous permettre d'éviter la copie ou verrouiller le modèle de jeu, tout en conservant les interactions utilisateur sensibles.

Je pense que vous ne devriez pas avoir du monde stocker des données ou apporter des modifications à tout objet lui-même, il ne doit être utilisé pour maintenir une référence à un objet et lorsque cet objet doit être changé, ont le joueur qui fait le changement de changement directement. Dans ce cas, la seule chose que vous devez faire est de synchroniser chaque objet dans le monde du jeu de sorte que quand un joueur fait un changement, aucun autre joueur ne peut le faire. Voici un exemple de ce que je pense:

Le joueur A doit savoir sur une planète, il demande mondiale pour que la planète (comment dépend de votre mise en œuvre). Monde renvoie une référence à l'objet Planète joueur A demandé. Le joueur A décide de faire un changement, il le fait. Disons que cela ajoute un bâtiment. La méthode d'ajouter un bâtiment à la planète est synchronisé si un seul joueur peut le faire à la fois. Le bâtiment gardera une trace de son propre temps de construction (le cas échéant) serait donc libéré presque immédiatement la méthode de construction d'ajout de la planète. De cette façon, plusieurs joueurs peuvent demander des informations sur la même planète en même temps sans affecter l'autre et les joueurs peuvent ajouter des bâtiments sans apparence beaucoup de lag presque simultanément. Si deux joueurs sont à la recherche d'un endroit pour mettre le bâtiment (si cela fait partie de votre jeu), puis de vérifier la pertinence d'un emplacement sera une requête pas un changement.

Je suis désolé si cela ne répond pas que vous êtes question, je ne sais pas si je l'ai compris correctement.

Que diriez-vous mettre en œuvre une architecture de tuyaux et filtres. Les tuyaux relient les filtres ensemble et des demandes de file d'attente si le filtre est pas assez rapide. Traitement se produit à l'intérieur des filtres. Le premier filtre est le moteur d'IA tandis que le moteur de rendu est réalisé par un ensemble de filtres suivants.

Sur chaque tic-tac de la minuterie, le nouvel état mondial dynamique est calculé en fonction de toutes les entrées (Le temps est également une entrée) et Copier inséré dans le premier tube.

Dans le cas le plus simple de votre moteur de rendu est implémenté comme un seul filtre. Il faut juste les clichés d'état du tuyau d'entrée et le rend en même temps que l'état statique. Dans un match en direct, le moteur de rendu peut vouloir ignorer les états s'il y a plus d'un dans le tuyau alors que si vous faites une référence ou sortir une vidéo que vous aurez envie de rendre tout le monde.

Les plus filtres vous pouvez décomposer votre moteur de rendu en, mieux le parallélisme sera. Peut-être qu'il est même possible de décomposer le moteur d'IA, par exemple vous pouvez séparer l'état dynamique en évolution rapide et de l'état de changement lent.

Cette architecture vous donne un bon parallélisme sans beaucoup de synchronisation.

Un problème avec cette architecture est que la collecte des ordures va exécuter le gel fréquemment tous les fils chaque fois que possible de tuer un avantage acquis de multi-threading.

On dirait que vous avez besoin d'un PriorityQueue pour mettre les mises à jour du modèle sur, dans lequel l'utilisateur met à jour frmo ont la priorité sur les mises à jour de la simulation et d'autres entrées. Ce que je vous entends dire est que l'utilisateur doit toujours une rétroaction immédiate sur ses actions wheras les autres entrées (simulation, sinon) pourrait avoir des travailleurs qui peuvent prendre plus d'une étape de simulation. Puis synchroniser sur le PriorityQueue.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top