How to communicate between game objects.
Over on Richard “PhotonStorm” Davey’s blog he proposed a simple way to communicate between objects in your game using a “Registry” a class with static variables storing all the major systems of your game. So, for example if you wanted to create a spray of blood when an enemy is hit, in the enemy’s hit() function you would include the line Registry.fx.sprayBlood(x, y) and the FX object stored in the Registry.fx variable would create the blood spray and handle updating etc.
In the comments on Richard’s post, I pointed out that this isn’t a very object-oriented approach: these are basically global variables by a different name. I have used a similar approach myself on quite a few games projects, and overall it works well and is a quick way of getting things done. I have, however, encountered two problems with it.
Firstly, you can end up with all you code in one huge blob or “god class”. For example, if the FX class is responsible for handling any possible visual effect you would want to create, it could end up getting very big. But, other than academic notions of “good” and “bad” code, there’s nothing especially wrong with having big classes. They may be a bit harder to maintain and reuse, but nothing to worry about too much.
Secondly, using static/global variables as a communication method means you can be limited to having only one “game” in a single project. Now in most scenarios this wouldn’t be a problem, but it’s normally best not to assume you will only ever have one of a particular class of object. I’ve worked on several Flash projects that were collections of minigames in a single swf. Where I was relying on static variables in some of my base-classes and utility classes, I started to see clashes between the different games.
Ok so how do I handle the same problems? Well, say an enemy needs a reference to the player in order to chase after them. Rather than looking up Registry.player, I would just have a “player” variable in my enemy class and I would pass in the value of player when I create the enemy, or once I know the player exists. Or if the enemy needed more than a couple of different references from game in order to work, I would just pass in a reference to the game itself, and let the object access whatever information it needs. As Richard points out, the downside to this approach is that you end up with a lot of references in different places. This isn’t really a problem if you null your references when destroying objects, but it is more work, and you can leak memory if you’re sloppy with it.
Secondly, pretty much everything in my game extends a base Entity class. I have a main Game class that manages all these entities. Game has a method called addEntity(entity:Entity) which adds any new entities I create to the array of entities. Every frame I loop through all my entities and call their public update() function (to pause the game, I just don’t run this loop).
So say I have a SpaceShip object that creates a Bullet object every time it fires. How would I let the game know? I wouldn’t call Game.instance.addEntity() because I may have more than one mini-game that extends my base Game class in a single project. Instead, the base Entity class has a function addEntity(entity:Entity) which dispatches an AS3Signal with the entity value. My game is listening for this signal, and directs the entity to its own addEntity function. You could also use an event to achieve the same effect. Now any Entity can itself create more entities and add them to the game.
How would I use this to solve the problem of creating a spray of blood? I would create two Entity classes. One class is BloodDrop which is a single particle of blood. Every update() it moves based on its speed and falls with gravity, and after 30 frames it fades out and destroys itself. The other class is BloodSpray, which lives for a single frame, and in that frame just runs a loop creating 100 instances of BloodDrop, setting their initial position and speed, and calling addEntity(bloodDrop). Now to add my spray of blood in the enemy’s hit() function, I simply create a new BloodSpray, set its position to match the enemies position and call addEntity(bloodSpray). All the entities know how to update and destroy themselves and are pooled for reuse.
This won’t solve all the communication problems in your game, but it will solve a big chunk of them, and now your code is neatly organised into distinct Entity classes which each perform a single purpose.
Happy coding! I think my next conference/user group talk might have to be one about organising game code :)
Comments
That said I don't think the registry system works especially well, have a look at a blackboard approach instead. Or even better you could have a map of key->value which is type independent and so you wouldn't need a whole bunch of variables to get and set them. This is possible in C++ but I'm not sure if it is in what you are using.
You could just have something like:
var getRegistryValue(var key)
void setRegistryKey(var key, var value)
bool hasRegistryValue(var key)
You could store whatever you want in this. Reduces code bloat and if objects are missing you can handle it appropriately.
Just a thought :)
Using a *lookup* although it is slower and sometimes feels a bit messy (in terms of OOP) allows much more flexibility which to me is becoming most important when writing the code of games.
Anyway, I think the main take-away from this post should be the Entity stuff :)
Two things:
1) My article was written for flixel specifically. Flixel objects don't (and can't) dispatch events. Which is just fine given the hit you take with events in AS3. With regard to the update / render calls you mention, that's exactly how flixel works. The FlxObject base type being the equivalent of your Entity.
2) To say that using a service wrapper / Registry isn't a very OO way of coding (with added irony given who devised the pattern), yet passing in a reference to an entire games is, is a bit strange imho. It's like DI gone wild.
When the games get large enough, or for multi-game suites especially, it's easily extended out into CommonRegistry, ActorRegistry, FxRegistry, etc. Very little code (if any) resides in them. There is no single "God class". The FX class in my example had just 3 methods in it. Had I needed lots more I would have looked at an FxManager instead, rather than triggering them directly. For that game however it was sufficient.
A true Registry is a list of pointers, nothing more. The classes themselves are responsible for house-keeping, access permissions, etc (just like they are in the DI/local reference approach).
We've used the multi-Registry approach on large game suites quite happily. We could easily have used a single well constructed DI container too.
That's the beauty of coding isn't it. There's never just one "true" way to skin the cat.
http://www.tomseysdavies.com/2011/01/23/entity-systems/
You can call my approach dependency injection if you want, but I would just call it passing references to resources. Dependency injection to me implies using containers, mxml tags, etc.
Re. your point (1) - a Flixel object could still dispatch an AS3Signal. Anything can dispatch them without having to extend EventDispatcher, which is their beauty. But yes, all good frameworks should have a single base GameObject/Entity class.
Re. your point (2) It doesn't matter who came up with the name your approach (appeal to authority?), it's still just some static vars in a class. Passing reference to a game instance rather than using a static class - to me - feels more OOP - you could have any number of games running simultaneously and they wouldn't clash. Obviously people might say it's not good practice to expose so many public properties, etc etc, but I think we're both agreed that this is about what works rather than what is "correct".
Anyway Rich, we both know what we're doing - it's new developers who I think will find this discussion most useful.
My own feeling is that it's easy to get too hung up on this stuff in Flash game dev stuff. In our search for acceptance as 'proper' programmers, we worry about what's 'right' and 'wrong' a bit too much. Or maybe that's just me... ;o)
I've been using the reference passing method for a few years now and while it works well enough I agree the main problem I've run into with it is what another commenter above mentions and that is that during development I find I have to refactor quite often and then I'm left to crawl through a lot of my class files changing or altering all the places I'm passing my references around.
What appeals to me about the Registry idea is how it would (hopefully) greatly reduce this refactoring during rapid development. A lot of the games I work on are less than 100 hour projects to create small scope games and development speed is crucial to being able to support myself. I enjoy any design patterns that help me more or less get the code down fast enough to understand the game and keep it flexible enough to alter as I get new ideas and feedback after reaching the first playable prototype.
I think I'm going to try the Registry on my next few games and see how it goes because that is obviously the only way I'll know if it works out for me.
I too agree that a developer should work with the methods they are comfortable with and that there are many solutions with nothing being really "wrong" or "right." That doesn't mean I don't want to hear these kind of interesting discussions though! I learn something new every time.
I also remember being jumped on by a lot of people for not being oop "enough" ( Iain, think you were in there mate :) ).
Any post like this is going to be abused, as we all have our own variations of "What's right" ( It's like Monopoly, has anyone ever played that with the proper rules ? ).
Coding has so many variations, and a really strict oop approach doesn't really have a place in Flash game dev.
For me the key is to steal the bits you like from other people so you're in a happy place, be that a registry singleton or public vars.
Instead, the base Entity class has a function addEntity(entity:Entity) which dispatches an AS3Signal with the entity value. My game is listening for this signal, and directs the entity to its own addEntity function.
If I understand that right: Bullet extends Entity,
Then Game has to know Bullet before if can listen to the signal dispatch from the addEntity function inherited from Entity. Then Game might as well addEntity(bullet) immediately.
Or is addEntity from Entity a static function?
crookedspoon.
I do my entity updates in an almost identical way, and I'd be curious to see what sort of setup you have for your base engine. Right now I have an Engine class, which handles all of the set up. I have the BaseEntity class, which I can extend for any object I want to add to my game, a World class which handles level/map loading, and some utility classes.
It's still a WIP, and I think it would be cool to see the way you have it set up. I had initially gone with a more Source Engine feel to things, but then adopted FlashPunk naming conventions because of a recent experiment.
Essentially I'm trying to make my engine easier for newer users, as I plan to do a few collaborations.
I really would like to see an article about code organization, communication and probably a simple game in a whole. I still can't make up my mind and not sure how to organize things. Till now I haven't used inheritence but only a bit of composition and you said that you have generic class Entity. What you put in that class and what is the purpose of it? Sorry, for the noobish questions.
Plamen
It really doesn't represent how I code. I'm all for OO and efficiency actually, and I always appreciate reading blogs such as this one - it really gets you thinking.
I've just had a closer look at the registry class on Photon Storm's blog, and it seems like a good system to me.
I'm also always changing my mind about the best ways to do things!
if you use the photon-storm registry approach then on the next game you need to use it again, so you don't need to change to many things.
I wonder how other engines do this (Unreal, Havok, Frostbite, etc) ?
or the big game development studios make their games?