Existe-t-il une technique standard pour gérer les entrées dans les grands jeux? Actuellement, dans mon projet, toute la gestion des entrées se fait dans la boucle de jeu, comme ceci:
while(SDL_PollEvent(&event)){
switch(event.type){
case SDL_QUIT:
exit = 1;
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym){
case SDLK_c:
//do stuff
break;
}
break;
case SDL_MOUSEBUTTONDOWN:
switch(event.button.button){
case SDL_BUTTON_MIDDLE:
//do stuff
break;
}
}
break;
}
(J'utilise SDL, mais je m'attends à ce que la pratique principale s'applique également aux bibliothèques et aux frameworks). Pour un grand projet, cela ne semble pas être la meilleure solution. Je peux avoir plusieurs objets voulant tous savoir sur quoi l'utilisateur a appuyé, il serait donc plus logique que ces objets gèrent les entrées. Cependant, ils ne peuvent pas tous gérer l'entrée, car une fois qu'un événement a été obtenu, il sera poussé hors du tampon d'événements, de sorte qu'un autre objet ne recevra pas cette entrée. Quelle méthode est la plus couramment utilisée pour contrer cela?
Réponses:
Depuis demandé par le starter, je développe sur les gestionnaires d'événements. Je pense que c'est un bon moyen de gérer les entrées dans un jeu.
Un gestionnaire d'événements est une classe globale qui permet à la fois d'enregistrer des fonctions de rappel sur des touches et de déclencher ces rappels. Le gestionnaire d'événements stocke les fonctions enregistrées dans une liste privée regroupée par leur clé. Chaque fois qu'une touche est déclenchée, tous les rappels enregistrés sont exécutés.
Les rappels peuvent être des
std::function
objets pouvant contenir des lambdas. Les clés peuvent être des chaînes. Le gestionnaire étant global, les composants de votre application peuvent s'enregistrer sur des clés déclenchées à partir d'autres composants.Vous pouvez même étendre ce gestionnaire d'événements pour autoriser le passage de valeurs comme arguments supplémentaires. Les modèles C ++ sont parfaits pour cela. Vous pouvez utiliser un tel système pour, par exemple, qu'un
"WindowResize"
événement passe la nouvelle taille de fenêtre, afin que les composants d'écoute n'aient pas besoin de le récupérer eux-mêmes. Cela peut réduire considérablement les dépendances du code.J'ai implémenté un tel gestionnaire d'événements pour mon jeu. Si vous êtes intéressé, je posterai le lien vers le code ici.
À l'aide d'un gestionnaire d'événements, vous pouvez facilement diffuser des informations d'entrée dans votre application. De plus, cela permet une belle façon de laisser l'utilisateur personnaliser les raccourcis clavier. Les composants écoutent les événements sémantiques au lieu des clés directement (
"PlayerJump"
au lieu de"KeyPressedSpace"
). Ensuite, vous pouvez avoir un composant de mappage d'entrée qui écoute"KeyPressedSpace"
et déclenche toute action que l'utilisateur a liée à cette clé.la source
[=]
et les références à toutes les variables locales accessibles à partir du lambda seront copiées. Vous n'avez donc pas à passer un pointeur this ou quelque chose comme ça. Mais notez que vous ne pouvez pas stocker lambdas avec clause de capture dans les anciens pointeurs de fonction C . Cependant, le C ++std::function
fonctionne bien.Divisez cela en plusieurs couches.
Au niveau le plus bas, vous avez des événements d'entrée bruts du système d'exploitation. Entrée de clavier SDL, entrée de souris, entrée de joystick, etc. Vous pouvez avoir plusieurs plates-formes (SDL est un dénominateur le moins commun sans plusieurs formes d'entrée, par exemple, dont vous pourriez vous soucier plus tard).
Vous pouvez les résumer avec un type d'événement personnalisé de très bas niveau, comme "bouton du clavier enfoncé" ou similaire. Lorsque votre couche de plateforme (boucle de jeu SDL) reçoit des entrées, elle doit créer ces événements de bas niveau, puis les transmettre à un gestionnaire d'entrées. Il peut le faire avec des appels de méthode simples, des fonctions de rappel, un système d'événements compliqué, tout ce que vous préférez.
Le système d'entrée a désormais pour tâche de traduire les entrées de bas niveau en événements logiques de haut niveau. La logique du jeu ne se soucie pas du tout de la pression sur SPACE. Il importe que JUMP ait été pressé. Le travail du gestionnaire d'entrées consiste à collecter ces événements d'entrée de bas niveau et à générer des événements d'entrée de haut niveau. Il est responsable de savoir que la barre d'espace et le bouton de la manette de jeu «A» correspondent tous deux à la commande logique Jump. Il traite des commandes de look gamepad vs mouse et ainsi de suite. Il émet des événements logiques de haut niveau qui sont aussi abstraits que possible des contrôles de bas niveau (il y a quelques limitations ici, mais vous pouvez complètement supprimer les choses dans le cas commun).
Votre contrôleur de personnage reçoit ensuite ces événements et traite ces événements d'entrée de haut niveau pour répondre réellement. La couche de plate-forme a envoyé l'événement "Barre d'espace enfoncée". Le système d'entrée a reçu cela, examine ses tables / logiques de mappage, puis envoie l'événement "Saut pressé". La logique de jeu / contrôleur de personnage reçoit cet événement, vérifie que le joueur est réellement autorisé à sauter, puis émet l'événement "Player jumped" (ou provoque directement un saut), que le reste de la logique de jeu utilise pour faire tout .
Tout ce qui dépend de la logique du jeu va dans le contrôleur du joueur. Tout ce qui dépend du système d'exploitation va dans la couche plate-forme. Tout le reste va dans la couche de gestion des entrées.
Voici un art ASCII amateur pour décrire cela:
la source