Par exemple, j'ai une classe de jeu et elle en garde une int
qui suit la vie du joueur. J'ai un conditionnel
if ( mLives < 1 ) {
// Do some work.
}
Cependant, cette condition continue de se déclencher et le travail est effectué à plusieurs reprises. Par exemple, je veux régler une minuterie pour quitter le jeu en 5 secondes. Actuellement, il continuera de le régler à 5 secondes à chaque image et le jeu ne se termine jamais.
Ce n'est qu'un exemple, et j'ai le même problème dans plusieurs domaines de mon jeu. Je veux vérifier une condition, puis faire quelque chose une fois et une seule fois, puis ne pas vérifier ou refaire le code dans l'instruction if. Certaines solutions possibles qui viennent à l'esprit sont d'avoir un bool
pour chaque condition et de définir le bool
moment où la condition se déclenche. Cependant, dans la pratique, cela devient très compliqué à gérer bools
, car ils doivent être stockés en tant que champs de classe ou statiquement dans la méthode elle-même.
Quelle est la bonne solution à cela (pas pour mon exemple, mais pour le domaine problématique)? Comment feriez-vous cela dans un jeu?
Réponses:
Je pense que vous pouvez résoudre ce problème simplement en exerçant un contrôle plus attentif sur vos chemins de code possibles. Par exemple, dans le cas où vous vérifiez si le nombre de vies du joueur est tombé en dessous de un, pourquoi ne pas vérifier uniquement lorsque le joueur perd une vie, au lieu de chaque image?
Ceci, à son tour, suppose que
subtractPlayerLife
cela ne serait appelé qu'à des occasions spécifiques, ce qui résulterait peut-être de conditions que vous devez vérifier chaque trame (collisions, peut-être).En contrôlant soigneusement la façon dont votre code s'exécute, vous pouvez éviter les solutions compliquées comme les booléens statiques et également réduire bit à bit la quantité de code exécutée dans une seule trame.
S'il y a quelque chose qui semble impossible à refactoriser, et que vous n'avez vraiment besoin de vérifier qu'une seule fois, alors
enum
des booléens statiques (comme un ) ou statiques sont le chemin à parcourir. Pour éviter de déclarer vous-même la statique, vous pouvez utiliser l'astuce suivante:ce qui rendrait le code suivant:
imprimer
something
etsomething else
une seule fois. Il ne donne pas le meilleur code, mais cela fonctionne. Je suis également sûr qu'il existe des moyens de l'améliorer, peut-être en garantissant mieux les noms de variables uniques. Cela#define
ne fonctionnera cependant qu'une seule fois pendant l'exécution. Il n'y a aucun moyen de le réinitialiser et il n'y a aucun moyen de l'enregistrer.Malgré l'astuce, je recommande fortement de mieux contrôler votre flux de code en premier, et de l'utiliser
#define
en dernier recours, ou à des fins de débogage uniquement.la source
Cela ressemble au cas classique de l'utilisation du modèle d'état. Dans un état, vous vérifiez votre condition pour des vies <1. Si cette condition devient vraie, vous passez à un état de retard. Cet état de délai attend la durée spécifiée, puis passe à votre état de sortie.
Dans l'approche la plus triviale:
Vous pouvez envisager d'utiliser une classe State avec un StateManager / StateMachine pour gérer la modification / poussée / transition entre les états plutôt qu'une instruction switch.
Vous pouvez rendre votre solution de gestion des états aussi sophistiquée que vous le souhaitez, y compris plusieurs états actifs, un seul état parent actif avec de nombreux états enfants actifs en utilisant une certaine hiérarchie, etc. La solution de modèle d'état rendra votre code beaucoup plus réutilisable et plus facile à maintenir et à suivre.
la source
Si vous êtes préoccupé par les performances, les performances de vérification plusieurs fois ne sont généralement pas importantes; idem pour avoir des conditions booléennes.
Si vous êtes intéressé par autre chose, vous pouvez utiliser quelque chose de similaire au modèle de conception nul pour résoudre ce problème.
Dans ce cas, supposons que vous souhaitiez appeler une méthode
foo
s'il ne reste plus de vie au joueur. Vous implémenteriez cela en deux classes, une qui appellefoo
et une qui ne fait rien. Appelons-lesDoNothing
etCallFoo
ainsi:Ceci est un peu un exemple "brut"; les points clés sont:
FooClass
qui peuvent êtreDoNothing
ouCallFoo
. Il peut s'agir d'une classe de base, d'une classe abstraite ou d'une interface.execute
méthode de cette classe.DoNothing
instance).CallFoo
instance).C'est essentiellement comme un mélange de stratégie et de modèles nuls.
la source
if
chèque dans l'FooClass
instance elle-même, il n'est donc exécuté qu'une seule fois, et idem pour l'instanciationCallFoo
.