La meilleure façon de gérer les événements en jeu?

13

Je travaille sur un jeu où certains événements dans le jeu doivent se produire de temps en temps. Un bel exemple serait un tutoriel. Vous démarrez le jeu et à plusieurs moments du jeu, un événement se produit:

  • Vous rencontrez votre premier ennemi, le jeu s'arrête et vous recevez une explication sur la façon de le tuer.
  • Vous avez tué le premier ennemi, vous recevez un message "bon travail".
  • Vous obtenez un nouvel élément, un menu avec la fenêtre contextuelle des statistiques des éléments.
  • etc.

Le jeu sur lequel je travaille est un jeu de puzzle où les règles du jeu sont à peu près les mêmes, il semble donc inefficace de coder en dur tous ces événements dans des niveaux séparés.

Dois-je en quelque sorte définir ces événements dans une source externe, comme XML? Ensuite, écrivez un interprète qui lit le XML et définit les exigences d'événements pour le niveau? Je ne sais pas comment je pourrais définir un événement qui devrait se produire lorsque vous avez tué deux ennemis par exemple.

Pour être clair, je ne recherche pas le meilleur langage de programmation ou de script pour le faire, mais plutôt la meilleure méthode pour gérer cela.

Merci!


Edit: Un deuxième exemple car ma question était assez difficile à comprendre:

Le problème que j'ai est de mettre des actions supplémentaires dans le jeu dans une procédure qui est toujours à peu près la même. Comme une bataille de RPG, tout le monde a son tour, choisit une compétence, etc. - c'est toujours la même chose. Mais que se passe-t-il s'il y avait un cas où je voudrais afficher une cinématique quelque part entre les deux. Modifier toute la structure du jeu pour passer dans une classe de combat modifiée avec la cinématique incluse semble très inefficace. Je me demande comment cela se fait habituellement.

omgnoseat
la source
8
N'essayez pas de trop généraliser les choses, les tutoriels par exemple sont très spécifiques et viennent avec beaucoup de déclencheurs / événements différents. Rien de mal avec le hardcoding / scripting.
Maik Semder
1
@Maik Si vous mettez cela dans une réponse Id +1, il .. Simple et résolu est mieux que joli n'importe quel jour.
James
Votre deuxième exemple montre clairement qu'un système de messagerie abstrait serait une grande victoire. Pour un didacticiel, vous pouvez simplement coder en dur les choses car elles ne se produisent qu'une seule fois au début, mais pour les événements en cours qui peuvent se produire à tout moment pendant toute la durée du jeu, c'est différent.
jhocking le
C'est encore un peu vague, veuillez énumérer au moins 3 déclencheurs pour 3 cinématiques différentes. il est très difficile de répondre en général. Fondamentalement, vous devez trouver un modèle commun pour comprendre comment le mettre en œuvre au mieux.
Maik Semder
Qu'est-ce que tu veux? Vous souhaitez suspendre les actions et faire le supplément, puis annuler les actions?
user712092

Réponses:

7

Cela dépend beaucoup de la façon dont les événements sont réellement communiqués entre les objets de votre jeu. Par exemple, si vous utilisez un système de messagerie central, vous pouvez avoir un module de didacticiel qui écoute certains messages et crée des popups de didacticiel chaque fois qu'il entend certains messages. Ensuite, vous pouvez définir le message à écouter, ainsi que la fenêtre contextuelle à afficher, dans un fichier XML ou quelque chose qui est analysé par le module du didacticiel. En ayant un objet didacticiel distinct qui surveille l'état du jeu et affiche des fenêtres contextuelles du didacticiel lorsqu'il remarque des éléments dans le jeu, vous pouvez modifier l'objet du didacticiel à volonté sans avoir à changer quoi que ce soit d'autre concernant votre jeu. (S'agit-il du modèle Observer? Je ne connais pas tous les modèles de conception.)

Globalement, cela dépend de la complexité de votre tutoriel si cela vaut la peine de vous en préoccuper. Le codage en dur des événements dans votre code et / ou niveaux ne me semble pas très important pour une poignée de popups de tutoriel. Je suis curieux de savoir exactement ce que vous avez en tête qui vous fait penser que ce sera inefficace, car tout ce que vous devriez faire à chaque déclenchement est simplement d'envoyer un message au module du didacticiel, quelque chose comme TutorialModule.show ("1st_kill");

jhocking
la source
Je pense que comme c'est un jeu de puzzle, sa logique est au même endroit pour plusieurs niveaux et ainsi de suite, ce qui rend les vérifications nécessaires si nous faisons un tutoriel pour que ce soit quelque chose qui dure partout. Honnêtement, si c'est un jeu de puzzle, je ne pense pas que cela va prendre un énorme succès, même si ce n'est pas le code le plus joli, et à la fin du code qui fonctionne dans un jeu qui est livré est toujours-toujours- 100% meilleur qu'un joli code qui ne voit jamais le jour;)
James
Je n'ai jamais pensé à quelque chose comme le modèle d'observateur, cela semble être une bonne solution.
Je vais
7

Voici les contraintes de conception telles que je les comprends:

  1. Le code de jeu de base ne se soucie pas des exigences de niveau et ne doit pas être couplé au code qui les traite.

  2. Dans le même temps, c'est le code de jeu de base qui sait quand les événements spécifiques répondant à ces exigences se produisent (obtenir un objet, tuer un ennemi, etc.)

  3. Différents niveaux ont différents ensembles d'exigences et celles-ci doivent être décrites quelque part.

Compte tenu de ceux-ci, je ferais probablement quelque chose comme ceci: Tout d'abord, créez une classe qui représente un niveau de jeu. Il encapsulera l'ensemble des exigences spécifiques d'un niveau. Il a des méthodes qui peuvent être appelées lorsque des événements de jeu se produisent.

Donnez au code de gameplay de base une référence à l'objet de niveau actuel. Lorsque des événements de gameplay se produisent, il va dire au niveau en appelant des méthodes sur elle: enemyKilled, itemPickedUp, etc.

En interne, a Levelbesoin de quelques choses:

  • État pour suivre les événements qui se sont déjà produits. De cette façon, il peut distinguer le premier ennemi tué des autres et sait la première fois que vous avez ramassé un objet donné.
  • Une liste d' LevelRequirementobjets qui décrivent l'ensemble spécifique d'objectifs dont vous avez besoin pour ce niveau.

Lorsque vous entrez dans un niveau, vous créez un Levelavec les bons LevelRequirements, configurez le code de gameplay et lui donnez ce niveau.

Chaque fois qu'un événement de jeu se produit, le gameplay le transmet Level. Celui-ci calcule à son tour des données agrégées (nombre total d'ennemis tués, d'ennemis de ce type tués, etc.). Il parcourt ensuite ses objets d'exigence, en donnant à chacun les données d'aggregegate. Une exigence teste pour voir si elle est remplie, et si c'est le cas, le comportement résultant est approprié (affichage du texte du didacticiel, etc.)

LevelRequirement a essentiellement besoin de deux choses:

  1. Une description d'un test pour dire si l'exigence a été satisfaite. Cela peut simplement être une fonction si votre langue vous facilite la tâche, sinon vous pouvez la modéliser dans les données. (Ie ont unRequirementType énumération avec des trucs comme FIRST_KILLet puis un gros switchqui sait comment vérifier chaque type.)
  2. Une action à effectuer lorsque l'exigence est remplie.

Reste à savoir où ces ensembles d'exigences sont décrits. Vous pouvez faire quelque chose comme XML ou un autre format de fichier texte. C'est utile si:

  1. Les non-programmeurs créeront des niveaux.
  2. Vous souhaitez pouvoir modifier les exigences sans recompiler et / ou redémarrer.

Si aucun de ces cas n'est le cas, je les construirais probablement directement dans le code. Plus simple, c'est toujours mieux.

munificent
la source
Les 3 premiers points sont une description très proche de la méthode que j'utilise maintenant, impressionnant! Oui, la chose avec laquelle je me bats le plus est de savoir où décrire l'exigence et comment la traduire dans le jeu (car cela va probablement être quelque chose d'extérieur). Merci pour l'explication détaillée :)
omgnoseat
5

Je pensais que vous devez savoir comment créer ces événements et le reste du message en est question.Si vous voulez simplement stocker ces événements, utilisez une base de données relationnelle ou décrivez-les par texte et utilisez un langage de script (il fera l'analyse et l'évaluation pour Vous). :)

Ce que vous voulez, c'est reconnaître les événements qui se sont produits (1), puis effectuer certaines actions qui sont exigées par ces événements (imprimer le message, vérifier la pression des touches ...) (2). Vous souhaitez également que ces événements ne se produisent qu'une seule fois (3).

Fondamentalement, vous souhaitez vérifier les conditions, puis planifier un comportement.

Comment reconnaître les événements (1)

  • Vous voulez reconnaître des événements comme ceux-ci "premier ennemi rencontré", "nouvel objet gagné"
  • si une pièce générique se produit, " ennemi rencontré ", " objet gagné " Vous vérifiez la pièce spécifique "d' abord ...", " nouvel objet gagné"

De quoi sont faits les événements

De manière plus générale, chacun de ces événements est composé de:

  • conditions préalables , vous les vérifiez
  • les actions qui seront effectuées lorsque les conditions préalables seront remplies (dites "" Vous avez tué le premier ennemi! ", dites" "créez des combos en appuyant sur les boutons A et B", dites "appuyez sur 'Entrée' pour continuer", appuyez sur la touche "Entrée")

Comment stocker ces événements

Dans certaines structures de données:

  • avoir une liste de conditions préalables (chaînes ou code si vous l'écrivez dans un langage de haut niveau)
  • avoir une liste d'actions (il peut s'agir de chaînes, le moteur Quake utilise des chaînes pour les événements)

Vous pouvez également le stocker dans une base de données relationnelle, bien qu'il semble que ce ne soit pas nécessaire, si vous voulez créer ce jeu en grand, vous devrez peut-être en créer un.

Vous devez ensuite analyser ces chaînes / choses. Ou vous pouvez utiliser un langage de script comme Python ou LUA ou un langage comme LISP, ils peuvent tous l'analyser et l'exécuter pour vous. :)

Comment utiliser ces événements dans la boucle de jeu (2)

Vous aurez besoin de ces deux structures de données:

  • file d'attente d'événements (les événements dont l'exécution est planifiée sont indiqués ici)
  • file d'attente d'actions (actions planifiées, les événements impliquent quelles actions sont effectuées)

Algorithme:

  • Si vous reconnaissez certains des cas de » les conditions préalables sont remplies Vous le mettez dans la file d'événements
  • (3) Ensuite, vous devez vous assurer que cet événement ne s'est produit qu'une seule fois si vous le souhaitez :) (par exemple avec le tableau booléen has_this_event_happened ["premier ennemi rencontré"])
  • (si la file d'attente d'actions est vide, alors) S'il y a un événement dans la file d'attente d'événements Vous mettez ses actions dans la file d'attente d'actions et le supprimez de la file d'attente d'événements
  • S'il y a une action dans la file d'attente d'action Vous commencez à faire ce qui est demandé par elle
  • Si une telle action est effectuée Vous la supprimez de la file d'attente d'actions

Comment réaliser ces actions lui-même (2)

Vous faites la liste des objets, qui ont la fonction "mise à jour". Ils sont parfois appelés entités (dans le moteur Quake) ou acteurs (dans le moteur Unreal).

  1. Vous démarrez ces objets lorsqu'ils doivent démarrer dans la file d'attente d'actions.
  2. ces objets peuvent être utilisés pour d'autres choses telles que d'autres minuteries. Dans Quake, ces entités sont utilisées pour toute la logique du jeu, je vous recommande de lire quelques informations à ce sujet .

Action "dire quelque chose"

  1. Vous imprimez quelque chose à l'écran
  2. Vous voulez que ce message apparaisse pendant quelques secondes
  3. dans "mise à jour":
    • faire la variable remove_me_after et la diminuer par le temps qui s'est écoulé
    • lorsque la variable est 0 Vous supprimez cette action de la file d'attente d'actions
    • Vous supprimez également cet objet (ou planifiez sa suppression ...)

Action "nécessite une clé"

  1. Cela dépend de la façon dont vous voulez le faire, mais je pense que vous faites un message
  2. dans "mise à jour" ":
    • Vous venez de rechercher l'événement de frappe souhaité
    • Vous avez probablement besoin d'un tableau / d'une file d'attente pour contenir les événements de pression de touche
    • alors vous pouvez le supprimer de la file d'attente d'actions et supprimer l'objet

Quelles méthodes apprendre

user712092
la source
-1 à droite, ou il appelle simplement une fonction, sérieusement, OP veut juste une boîte de message quand une certaine condition est remplie
Maik Semder
C'est une très belle écriture, mais pas exactement ce que je cherchais. J'ai eu du mal à expliquer ce que je voulais. J'ai ajouté une deuxième explication: le problème que j'ai est de mettre des actions supplémentaires dans le jeu dans une procédure qui est toujours à peu près la même. Comme une bataille de RPG, tout le monde a son tour, choisit une compétence, etc. - c'est toujours la même chose. Mais que se passe-t-il s'il y avait un cas où je voudrais afficher une cinématique quelque part entre les deux. Modifier l'ensemble de la structure du jeu pour passer dans une classe de combat modifiée avec la coupe incluse semble très inefficace. Je me demande comment cela se fait habituellement?
omgnoseat