Je fais un jeu simple et j'ai décidé d'essayer d'implémenter un système de messagerie.
Le système ressemble essentiellement à ceci:
L'entité génère un message -> le message est publié dans la file d'attente de messages globale -> messageManager notifie chaque objet du nouveau message via onMessageReceived (Message msg) -> si l'objet le souhaite, il agit sur le message.
La façon dont je crée des objets de message est la suivante:
//base message class, never actually instantiated
abstract class Message{
Entity sender;
}
PlayerDiedMessage extends Message{
int livesLeft;
}
Maintenant, mon SoundManagerEntity peut faire quelque chose comme ça dans sa méthode onMessageReceived ()
public void messageReceived(Message msg){
if(msg instanceof PlayerDiedMessage){
PlayerDiedMessage diedMessage = (PlayerDiedMessage) msg;
if(diedMessage.livesLeft == 0)
playSound(SOUND_DEATH);
}
}
Les avantages de cette approche:
- Très simple et facile à mettre en œuvre
- Le message peut contenir autant d'informations que vous le souhaitez, car vous pouvez simplement créer une nouvelle sous-classe Message contenant les informations nécessaires.
Les inconvénients:
- Je ne peux pas comprendre comment je peux recycler des objets Message dans un pool d'objets , sauf si j'ai un pool différent pour chaque sous-classe de Message. J'ai donc beaucoup, beaucoup de création d'objets / d'allocation de mémoire au fil du temps.
- Impossible d'envoyer un message à un destinataire spécifique, mais je n'en ai pas encore eu besoin dans mon jeu, donc cela ne me dérange pas trop.
Qu'est-ce que j'oublie ici? Il doit y avoir une meilleure implémentation ou une idée que je manque.
Réponses:
La réponse simple est que vous ne voulez pas recycler les messages. On recycle des objets qui sont soit créés (et supprimés) par milliers à chaque image, comme des particules, soit très lourds (dizaines de propriétés, long processus d'initialisation).
Si vous avez une particule de neige avec un bitmap, des vitesses, des attributs physiques associés qui vient de descendre sous votre vue, vous pouvez la recycler au lieu de créer une nouvelle particule et d'initialiser à nouveau le bitmap et de randomiser les attributs. Dans votre exemple, il n'y a rien d'utile dans le Message pour le conserver, et vous gaspillerez plus de ressources pour supprimer ses propriétés spécifiques à l'instance que vous ne le feriez pour créer un nouvel objet Message.
la source
Dans mon jeu basé sur Java, j'ai trouvé une solution très similaire, sauf que mon code de gestion des messages ressemble à ceci:
J'utilise des annotations pour marquer une méthode comme gestionnaire d'événements. Ensuite, au début du jeu, j'utilise la réflexion pour calculer un mappage (ou, comme je l'appelle, "piping") de messages - quelle méthode invoquer sur quel objet lorsqu'un événement d'une classe spécifique est envoyé. Cela fonctionne ... génial.
Le calcul de la tuyauterie peut devenir un peu compliqué si vous souhaitez ajouter des interfaces et des sous-classes au mélange, mais c'est néanmoins assez simple. Il y a un léger ralentissement pendant le chargement du jeu lorsque toutes les classes doivent être analysées, mais cela n'est fait qu'une seule fois (et peut probablement être déplacé vers un thread séparé). Ce qui est gagné à la place, c'est que l'envoi réel de messages est moins cher - je n'ai pas à invoquer toutes les méthodes "messageReceived" dans la base de code juste pour qu'il vérifie si l'événement est de bonne classe.
la source
EDIT La réponse de Liosan est plus sexy. Regardez-y.
Comme l'a dit Markus, les messages ne sont pas un candidat courant pour les pools d'objets. Ne vous embêtez pas pour les raisons qu'il a mentionnées. Si votre jeu envoie des tonnes de messages de types spécifiques, alors cela en vaudrait peut-être la peine. Mais à ce stade, je vous suggère de passer aux appels de méthode directs.
En parlant de ça,
Si vous connaissez un destinataire spécifique auquel vous souhaitez l'envoyer, ne serait-il pas assez facile d'obtenir une référence à cet objet et de faire un appel direct à la méthode? Vous pouvez également masquer des objets spécifiques derrière les classes de gestionnaire pour un accès plus facile entre les systèmes.
Il y a un inconvénient à votre implémentation, et c'est qu'il y a le potentiel pour beaucoup d'objets d'obtenir des messages dont ils ne se soucient pas vraiment. Idéalement, vos classes ne recevraient que des messages qui leur tiennent à cœur.
Vous pouvez utiliser un
HashMap<Class, LinkedList<Messagable>>
pour associer des types de message à une liste d'objets qui souhaitent recevoir ce type de message.L'abonnement à un type de message ressemblerait à ceci:
Vous pouvez implémenter
subscribe
comme ça (pardonnez-moi quelques erreurs, je n'ai pas écrit java depuis quelques années):Et puis vous pouvez diffuser un message comme celui-ci:
Et
broadcastMessage
pourrait être implémenté comme ceci:Vous pouvez également vous abonner à un message de telle sorte que vous puissiez diviser votre gestion des messages en différentes fonctions anonymes:
C'est un peu bavard, mais aide à l'organisation. Vous pouvez également implémenter une deuxième fonction,
subscribeAll
qui gardera une liste séparée deMessagable
s qui veulent entendre tout.la source
1 . Non.
Les messages sont peu coûteux. Je suis d'accord avec Markus von Broady. GC les récupérera rapidement. Essayez de ne pas joindre beaucoup d'informations supplémentaires pour les messages. Ce ne sont que des messages. La recherche d'une instance de est une info en soi. Utilisez-le (comme vous l'avez fait dans votre exemple).
2 . Vous pouvez essayer d'utiliser MessageDispatcher .
Contrairement à la première solution, vous pourriez avoir un répartiteur de messages centralisé qui traite les messages et les transmet aux objets voulus.
la source