The World/Controller design pattern
Friday, August 26th, 2005One of the most basic things you will always need when making any sort of game is a World - that is, a class object which will track and mediate between your game logic and your display view. In conventional programming, the design pattern Model/View/Controller(MVC) would be popular in this situation - however, due to the unique nature of flash, some things bear consideration:
The World/Controller pattern is an attempt at approaching all of these problems at the same time. Instead of having a seperate model & view, in our game, the model and view will be combined, so that essentially, the model *is* the view - that is, as soon as a change is committed to the model, it will be set to draw on the screen, eliminating the need for a seperate view pass. It also proivides a facade to the stage, allowing us to organise our movieclips in a more intuitive manner, and giving us a single interface to different types of object.
For example:
Suppose we want to create several different types of object on the stage, each of which will behave differently. Let’s keep it simple - let’s sat they all have a different move() method which needs to be called. If we have the move method situated in a controller class object, we’d need to do something like this:
function moveSprites(){
for (i in _root.group1){
if (typeof _root.group1[i] == “movieclip”){
switch (_root.group1[i].type){
case enemy: doEnemyMove(_root.group1[i]);
break;
case bullet: doBulletMove(_root.group1[i]);
break;
}
}
}
As you can see, the sprite’s movieclip path is tightly coupled with the code, and if we decided to change the grouping of the different types, we would have to change a lot of code. However, let’s say we have a class World, which will handle the creation and management of our objects for us - our code would then look something like this:
//world is passed a reference to the current clip scope:
var world:World = new World(this);
world.addGroup("enemies");//create a group callled "enemies"
world.addGroup("bullets");//create a group called "bullets"
//add an 'enemy' type object to the world, inside the 'enemies' group:
world.addObject("enemies","enemy",{x:10,y:20});
var enemies:Array = world.getGroup("enemies");
for (var i in enemies){
enemies[i].move();
}
var bullets:Array = world.getGroup(”bullets”);
for (var i in bullets){
bullets[i].move();
}
As you can see, it would be a matter of just adding an extra line to add another object, and we don’t have to worry about depth, instance names or identifying the type, as the World class does all that for us. It also acts as a factory to create the appropriate clips in the correct levels in the movieclip tree. We don’t ever need to know where they actually reside in the tree, we can just get at them through the World class. Moreover, they could all reside in totally different coordinate systems and movieclips, and our code wouldn’t care.
The grouping is useful when it comes to be time for our collision detection to be worked out, as we’ll need to carefully plan how different objects are grouped - for example, static objects can be placed into lookup trees, and moving objects which don’t need to detect collisions between themselves (such as bullets) can be handled seperately to other objects.
Performance:
As far as performance goes, there is no noticable disadvantage, and possibly even a speed increase, depending on how you handle things - provided you follow a couple of basic rules:
1) Always create a local var reference to the group - otherwise the World class will keep calling the accessor methods and this will make things *slow*.
2) If possible, try and loop through the arrays by number, as this is much faster (up to x3 according to some of my recent tests)
3)Generally, local registers are your friend - they will perform much faster than performing lookups on the tree - var myClip = _parent.myClip will be faster than always calling _parent.myClip - it will also make it easier to use in the debugger.
The World class can be as generic or as specialised as you want it to be - it could handle a number of diverse tasks, such as creating/destroying objects, building a tree hierachy for space partitioning, or even saving game state.