Archive for August, 2005

The World/Controller design pattern

Friday, August 26th, 2005

One 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:

  • As flash itself has a Stage, there is an inherent, built-in View tier, which you have to use - for better or worse
  • One of the overheads in the MVC pattern is the draw phase - the model needs to be analysed, rendered and passed to the screen - however, to an extent, flash already does this for us, and making this a seperate phase is a duplication of work.
  • Although in many ways quite elegant, the flash stage interface can be cumbersome from a programming point of view - e.g. sometimes we experience difficulties traversing the tree, creating/destroying objects, and keeping track of the game’s movieclip structure.
  • 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.

    Blog back up!

    Friday, August 26th, 2005

    PENIS ENLARGEMENT XXX PILLS CHEAP!

    I’ve moved my blog over to the WordPress platform, which seems (so far) to have eliminated all the random comment spam. So after a long hiatus, I’ll be blogging again.

    Yay!

    ADFs - the heart of the Saffron Font engine

    Friday, August 26th, 2005

    I was reading up on the Saffron font engine technology today, and as I was skimming this paper I noticed that the technology seems to use quadtrees and octtrees, which are popular concepts in collision detection space partitioning. Basically, it works on the principle of scanning an object, and analysing the amount of detail in it, and only storing higher resolution data of it if needed. For example, if we have a 200×200 square, and there’s only a 25×25 box in the top right hand corner, we can ignore 3/4 of the scene by dividing the square by four and discarding the empty sections. We can then go further, by dividing the one occupied square by four again, and again, ad infinitum. This lets us draw and query the scene much more quickly, and means that less data is needed to represent it. It also means that the coordinates themselves will be limited to a smaller range, because they only need to be stored in terms of the coordinate space of the cell they are in. This can potentially cut the size of the data in half again. ADF seems to use the corners to represent edges, so it can avoid too many levels of recursion into a shape, while still maintaining the same level of quality.