Wednesday, December 03, 2008
December already, it really does fly.

I was chatting with our mate Jeff @ 8bitRocket the other week about function pointers / state machines and I thought it may be useful to quickly write something up about it here, as so much of my code relies on them and they do make life easier.

private var mainFunc:Function;

That's pretty much it.

I'm guessing all your routines have some sort of mainloop. Either you're old school and that's running on an enterFrame on each mc you're moving about or it's a public method in an object which you loop through and call every frame.

Let's take a space invaders mainloop as an example ( Psuedo-code ahead )

function mainloop():void{
  if(thisInvaderIsDeadFlag==true){
    return;
  }
  if(areWeDyingFlag==true){
    if(sprite.currentframe==endOfExplosionsFrame){
      thisInvaderIsDeadFlag=true;
    }
    return;
  }

  moveInvader();
  testForShootingAtPlayer();
  testForBeingHitByPlayersBullet();
}


That should hopefully be straight forward enough. In real life we wouldn't want to be testing for thisInvaderIsDeadFlag every time, we'd just remove this invader from our array of active ones, but this is just an example.

Now check this out,

mainFunc=mainloop_normal;

function mainloop():void{
    mainFunc();
}

function mainloop_normal():void{
    moveInvader();
    testForShootingAtPlayer();
    testForBeingHitByPlayersBullet();
}

Here we've set up our function pointer to point to mainloop_normal, and then we just call that one method in our mainloop() method. Cool so far ?

Now we're not checking to see if the invader is dead, or if it's exploding. That's good, because by default the vast majority of the time neither of those are going to be true, so it's a waste to check ( It's like waking up every morning and seeing if it's your birthday ).

At the moment it's more of a function pointer than a state machine as such, so next up is why doing it this way is so sexy...

function testForBeingHitByPlayersBullet():void{
    if(playerBullet.hitTest(thisInvaderSprite)){
       thisInvaderSprite.gotoAndPlay("firstExplodingFrame");
       mainFunc=mainloop_waitingToDie;
    }
}

Here's our testForBeingHitByPlayerBullet method from the main loop. Imagine that's just doing a hitTest, the invader to the player bullet. Bang, it's a collision, but instead of having to set our areWeDyingFlag from the original example, we just change the state machine.

function mainloop_waitingToDie():void{
  if(sprite.currentframe==endOfExplosionsFrame){
//Kill this invader totally, ie remove the mc
      mainFunc=null;
  }
}

In effect, no extra checks are needed every frame, we're only running what we need. We've got the advantage of a slight performance boost, but far more useful than that is the flexibility this gives us.

Say for example you want your invader to teleport in now, at the start instead of:

mainFunc=mainloop_normal;

we can now alter it to,

mainFunc=mainloop_waitingForInvaderToTeleportIn;

And run the code there waiting for your cool teleport effect to finish, and then just alter the mainFunc to carry on with our flow.

Running your code this way means you can chop and change states without having to have lots of extra conditionals in your code ( If the player is teleporting in, but that teleport tween isn't at frame 10 yet, then don't test for collisions with the player bullet etc. etc. ).

Any questions just hit that comment button,

Squize.

Wednesday, December 03, 2008 9:16:54 AM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Thursday, November 06, 2008
Quite a descriptive title for this post for a change.
651_jellyVectors.jpg

In this section of 651 we're running two effects, a bog standard RGB plasma effect and then a "Jelly vector" effect.

To create a plasma you're going to have to suck up to Math.sin, he's your daddy for this.

Firstly we pre-calc a colour table, eg.

        public function ColourTable(){
            colourTable=new Array();
            var cnt:Number=-1;
            var col:Number;
            var r:int;
            var g:int;
            var b:int;
            var offset:Number=3.1415;

//To avoid /4 for each pixel every frame, we just make the colour table 4 times as big
            while(++cnt!=256*4){
                r = 128 + 128 * Math.sin(offset * cnt / 32);
                g = 128 + 128 * Math.sin(offset * cnt / 64);
                b = 128 + 128 * Math.sin(offset * cnt / 128);
                col=(r << 16)+(g << 8)+b;
                colourTable.push(col);
                colourTable.push(col);
                colourTable.push(col);
                colourTable.push(col);
            }
        }

Here we're just creating what is in effect a gradient, so we have an array which smoothly goes from one colour to the last one. When it comes to the look-up when we're plotting we'd need to divide the value by 4, so to avoid this we make the colour table 4 times larger than is really needed ( Often it's a balance between memory usage vs speed. An easy way to think of it is with loops. If your game didn't have any loops and you just copy / pasted the same thing over and over it would run quicker, but take a lot more memory, and be pretty insane ).

Right the colour table is done, next up we create instances of our Pixel class,

            activePixelsStorage=new Array();
            var pixelObj:Pixels;
            var j:int=-1;
            var k:int;
            while(++j!=120){
                k=-1;
                while(++k!=120){
                    pixelObj=new Pixels(new Point(j,k),colourTable);
                    activePixelsStorage.push(pixelObj);
                }
            }    

It's just like doing a tile based engine, each instance of our Pixel class is passed an x/y position so our plasma is 120 pixels wide by 120 high. That's pretty tiny so we double the scale of the sprite in which we're plotting and add a blur filter just to smooth it out. It's a lot less expensive than plotting a 240x240 plasma.

On to the actual Pixel class:

    public class Pixels{
//---------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------
        private var xPos:int;
        private var yPos:int;

        private var cX:Number;
        private var cY:Number;
        
        private var jointDist:Number;
        
        private var offset:int;

        private var cT:Array;
        
//---------------------------------------------------------------------------------------
//Constructor
//---------------------------------------------------------------------------------------
        public function Pixels(pos:Point,colourTableArg:Array):void{
            cX=xPos=pos.x;
            cY=yPos=pos.y;
            var xDist:int=120-cX;            //Distance from the bottom
            var yDist:int=120-cY;

            cT=colourTableArg;
            
            var distance:Number=Math.round((Math.sqrt((xDist*xDist)+(yDist*yDist))/2));
            var distX:Number=256 * Math.sin(distance/8);
            var distY:Number=256 * Math.cos(distance/8);

            jointDist=distX+distY;
       }
        
//---------------------------------------------------------------------------------------
//Public
//---------------------------------------------------------------------------------------
        public function toString():String {
            return "Pixels";
        }        

//---------------------------------------------------------------------------------------
        public function pixelmainloop(x:Number,y:Number,plotbm:BitmapData):void{
            offset = (Math.cos((xPos+x)*0.0525) + Math.sin((yPos+y)*0.0255))*256 + jointDist;

            if(offset<0){
                offset=(offset ^ -1) + 1;
            }
            
            plotbm.setPixel(xPos,yPos,cT[offset]);
        }
    }

I'm not going to go into too much detail with this, as it'll take ages to be honest. The most interesting part is the pixelmainloop, where we pass in the x/y ( As well as the bitmapData we're plotting too, more on that soon ), and from those coords we create an offset into the colour table. To create the smooth curves that makes a plasma look so sexy we use some lovely sin and cos ( That's the bit I'm skipping explaining in any real detail. It takes quite a bit of tweaking to get something looking how you like and different values really give different results, for example:

            offset = (Math.cos((xPos+x)*0.0525) + Math.sin((yPos+y)*0.0255))*64 + jointDist;
            offset=offset>>3;

That's what's used in the credits plasma / kaleidoscope effect, which uses exactly the same colour table values but looks totally different ).

All that's left for the plasma part is the mainloop that we run on the enterFrame.

            sinOffset++;
            var radian:Number = sinOffset/60;
            paletteShiftX = 128-Math.sin(radian)*255;
            paletteShiftY = 128-Math.cos(radian)*255;

            plotbm.lock();

            var pixelObj:Pixels;
            for each(pixelObj in activePixelsStorage){
                pixelObj.pixelmainloop(paletteShiftX,paletteShiftY,plotbm);
            }

            plotbm.unlock();

            if(plotbm==bm1){
                plotbm=bm2;
            } else {
                plotbm=bm1;
            }

            bmData1.bitmapData=plotbm;

Nothing too tricky here. We just increase the position ( Offset ) into the colour table every frame, and then use for...each ( Much quicker ) to loop through all our Pixel instances calling the pixelmainloop and passing the args.
The part that may be of interest is the plotbm var. To increase speed slightly we double buffer the plasma bitmap, so when one bitmapData is being displayed we're plotting to the other one which is no longer being shown.
To try and explain that a little better, we have two bitmapData objects, bm1 and bm2. bmData1 is our bitmap ( I find the difference between the two confusing as hell in as3. It makes total sense, it just doesn't seem to stay in my brain very well ) which is attached to the our holder sprite for the plasma ( The one we doubled in size and added a blur to as mentioned earlier ).
So lets say we have something like this:
holderSprite.bmData1.bm1;
And that's what you see on screen. If you can see bm1 that means we're plotting to bm2, and visa versa.

This is why we pass the currently hidden bitmapData to each instance of the Pixel class every frame rather than just passing one value in during it's construction.

That's plasmas for you. I've only really given the core concept as hopefully a spring board for your own experiments.

The Jelly cube is going to be much more straight forward, because someone else wrote the clever bit. After x amount of time we run a really quick white up over the whole stage, and that's where we remove the plasma all together and replace it with a papervision cube.

Ultra simple, we just rotate him and scale him. The twister code came from the excellent zupko who kindly open sourced it. Now we've got a twisty cube, what about the texture ?

This is another big fat cheat. At best you can get a plasma running the size we have at around 40fps, so there's no way we could do it realtime and run the cube. One idea I had early on was to use draw() on every frame of the plasma and store those away, then update the texture every frame on the cube using those stored away bitmaps.
I didn't go this route as I was concerned about the amount of memory it would use and I was concerned that using draw() may have had a negative performance hit when actually running the plasma ( I'm possibly paranoid about that and it would more than likely be fine, but it felt like quite a bit of data to be copying every frame when you want everything running as quickly as possible ).

The solution ? flv baby. Unless it's youTube the flv format seems to be badly over looked when it can be used for all types of tricks ( I did quite a bit of video work in games at preloaded long before flv came out so I've learnt what the advantages of using video are early on ).
I just ran the plasma for a little while grabbing the frames, cropped them up, created a copy running backwards and then joined the two together, so runs as A > B > A.
All that was left then was to created a flv texture for each side of the cube, and papervision along with Flash did everything for me.

The only thing left to cover off is the black outline on the cube. Again ultra simple, it's just a glow filter. Set it to black, turn up the strength, turn down the blurring and you've got a sexy outline.

Phew. I think this is going to be last in-depth-ish tut on the 651 effects. Not only does it takes ages, but I think the rest of the effects not touched on so far can be summarised in one post.

Squize.


Thursday, November 06, 2008 5:24:29 PM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, November 04, 2008
So what's wrong with this code:

if (this._iDrawLayer & ChipsGame.VIEW_LAYER_A == ChipsGame.VIEW_LAYER_A) {
// draw contents of layer A ...
}

nothing, really. Nonetheless it's not working in CS3 (yet again, prove me wrong).

Basically it's just the unoptimized check if a certain bit is set or not. Let's do some traces:

trace ("with ():", ((this._iDrawLayer & ChipsGame.VIEW_LAYER_A) == ChipsGame.VIEW_LAYER_A))
trace ("without ():", (this._iDrawLayer & ChipsGame.VIEW_LAYER_A == ChipsGame.VIEW_LAYER_A))
trace ("values :", this._iDrawLayer & ChipsGame.VIEW_LAYER_A , ChipsGame.VIEW_LAYER_A)

The result is this:

with (): true
without (): 1
values: 1 1

Interesting, isn't it?
It seems like the compiler chains the & and the == for some reason that escapes me...

So if you get something undesiered with bitwise operators ... use ( and ) around it.

nGFX

Tuesday, November 04, 2008 3:33:54 PM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Wednesday, October 29, 2008
Back again!

After a good deal of time I finally have something to post about - or let's face it moan about.

As the headling slightly might suggest I'm dealing with sound today.
I think that sound handling in AS3 is a nightmare compared to the ease of it in AS1/2 and I'm not the only one asking WTF?

So in order to play your sound you have to instanciate it, if it's exported CS3 kindly creates a class for you so you can easily use it ... (loading it from an external source is another story)

This is what the CS3 help gives us for embeded sounds ("working with embeded sounds"):

var drum:DrumSound = new DrumSound();
var channel:SoundChannel = drum.play();

My first question was: what do I need the SoundChannel for if I just want to play the sound?

Well, the rocket scientists at Adobe thought that it would be a good idea to add a play() command to the Sound, but not a stop(), so in order to stop our sound playing we *need* the SoundChannel - so we better store it for later use.

Anyway, to make my life easier I converted my SoundUtil class from AS2, basically it deals with the sounds so I don't have to think about it, it has a few usefull commands like playSFX (plays a sound effect, once), playMusic (which allows fading), crossfade ...
I usually used attached sounds (or from an external swf, but the SoundUtil dealt with it ...)
So in order to play music for the menu I'd just do:

SoundUtil.getInstance().playMusic("musicName", 2); // 2 would do a 2 sec. fade in

The AS3 version should work the same, although it uses static functions which then call the singleton's method.

Oh wait. We need to have a class to start the embeded sound ...

To get over that I wrote the add method, which basically takes the name of the sound (or the classname) and then does it's magic.

        public function add (strSound:String, bIsMusic:Boolean):void {
            
            var refClass:Class = getDefinitionByName(strSound) as Class;
            var sndTmp:Sound = new refClass();
                        
            var iTmp:int = this._aSound.length;
            
            this._aSound.push(sndTmp);
            this._objSound[strSound] = { id:iTmp, bIsMusic: bIsMusic };
            
            if (bIsMusic) {
                this._objSound[strSound].spDummy = new Sprite();
            }
            
        }

Ha! that was easy ...

As you see the sounds name gets stored in an object (I just use it as dictionairy), I store an Object with some more values along with the name. And you surely might ask WHY on earth I did create a Sprite for music files ...
Well I'm a lamer, I use the Sprite to attach an onEnterFrameTo it for things like fading :)

Fast forward ...

k. Let's say we play some music, and only wont it to play 2 times, after that the sound should be removed from memory. Luckily we have the onSoundComplete Event, it should return (CS3 help): "The Sound object on which a sound has finished playing."

For me it reads like it returns the Sound that is playing. FAIL!

It does however return a SoundChannel, which of course HAS no information (prove me wrong) about the Sound it belongs to ...
So how can I unload/cleanup a Sound when an onSoundComplete occurs, if I don't know which Sound is playing (and don't want to write a seperate Listner for each sound)?

Oh lucky me...

Thank fuck I store a lot of things in my information object (not only what is shown in the add method), for instance I store the SoundChannel I got from Sound's play() command and I store if a Sound is playing ...

After a few hours of using our favorite search engine I came up with something so stupid it might even be brilliant ...

private function onSoundComplete (e:Event):void {
            
      var strKey:String;
            
      for (strKey in this._objSound) {
           if (this._objSound[strKey].bIsMusic) {
               if (this._objSound[strKey].chChannel == e.target) {
                   this._objSound[strKey].chChannel.removeEventListener(Event.SOUND_COMPLETE, this.onSoundComplete);
                   this._objSound[strKey].bIsPlaying = false;
                   // do some cleanup
               }
           }
       }
            
}

Basically I loop over all music "files" that are playing and *compare* their SoundChannel with the one returned by the Event.
That's so insanely stupid! But it works. Sweet.

Maybe it helps some of you ...

nGFX

Wednesday, October 29, 2008 5:46:21 PM (W. Europe Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Thursday, October 23, 2008
Here's another really old trick we used in 651.

651_bobs.jpg

Way back in Amigaland software sprites ( ie, sprites which were plotted by the blitter as opposed to being hardware based, like a mouse pointer ) were called "Bobs" ( Blitter OBjects ). As with everything you could only ever run a certain amount before you started running out of cpu time, so when the first infinite bob effects started appearing in demos every one passed a little bit of involuntary wee.

//------------------------------------------------
// Bob properties
//------------------------------------------------
        private var ball:Sprite;
        
        private var bm1:BitmapData;
        private var bm2:BitmapData;
        private var bm3:BitmapData;
        private var bmData1:Bitmap;
        private var bmData2:Bitmap;
        private var bmData3:Bitmap;

        private var currentBitmapNumber:int;

Just set up 3 bitmaps, and then...

//Set up the sprites
            container=new Sprite();
            stage.addChild(container);
            
            playField=new Sprite();
            container.addChild(playField);

Create a holder sprite + add it to the stage, and then a further sprite within that. Also add your bob to the playField ( Not the container or the stage )

Next up, our mainloop,

//---------------------------------------------------------------------------------------
        private function mainloop(e:Event):void{
            moveBob();
            copyBitmap();
}

moveBob() is however you want to move the bob around the screen, use your nice sin based movement that you've got tucked away. All it's doing is just moving one bob ( ball:Sprite in this case ) around the screen.

The funky bit is the copyBitmap() method,

//---------------------------------------------------------------------------------------
        private function copyBitmap():void{
            container.addChild(this["bmData"+currentBitmapNumber]);
            this["bm"+currentBitmapNumber].draw(playField);

            if(++currentBitmapNumber==4){
                currentBitmapNumber=1;
            }
        }

It just simply loops through all our bitmaps, copying what's in our playField ( ie the ball ) to the next bitmap. Just written down like this it's a bit tricky to grasp, think of it like an old flick book. You move the bob, you take a copy of the whole screen and store that in a bitmap and then display that, you then move the bob again, and take another grab of it and so on. We use 3 bitmaps because the image will be slightly different on all of them, creating the sense of movement ( Otherwise it wouldn't animate and would just look like a trail behind the bob ).

I can recommend giving it a quick play, it'll take 5 mins to set yourself up with a working example and once it's running infront of you it'll click into place how it actually does work.

Squize.

Thursday, October 23, 2008 2:22:11 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Tuesday, October 21, 2008
Now 651 is history I thought it may be of interest to go through how some of the parts work.

Let's start with the boring bit for today, the actual structure. To make testing it easier, and to be able to swap and change the order to make sure it felt right, I used a pretty simple yet modular approach,

//------------------------------------------------
// Demo classes
//------------------------------------------------
        private var logo:Logo;
        private var credits:Credits;
        private var twister:Twister;
        private var vectorBalls:VectorBalls;
        private var pimp:Pimp;
        private var showReel:ShowReel;
        private var water:Water;
        private var fin:Fin;
        
        private var sequenceOrder:Array=new Array("logo","twister","vectorBalls","pimp","credits","showReel","water","fin");
        private var sequenceOffset:int;


The sequenceOrder array kinda speaks for itself. The other part of the code is just as straight forward:

//---------------------------------------------------------------------------------------
        public function sequence():void{
            switch (sequenceOrder[sequenceOffset]){
                case "logo":
                    logo=new Logo();
                    break;    
                case "twister":
                    twister=new Twister();
                    break;    
                case "credits":
                    credits=new Credits();
                    break;    
                case "vectorBalls":
                    vectorBalls=new VectorBalls();
                    break;    
                case "pimp":
                    pimp=new Pimp();
                    break;    
                case "showReel":
                    showReel=new ShowReel();
                    break;    
                case "water":
                    water=new Water();
                    break;    
                case "fin":
                    fin=new Fin();
                    break;    
            }
        }

//---------------------------------------------------------------------------------------
        public function finished():void{
            if(++sequenceOffset==sequenceOrder.length){
//Finished            
            } else {
                sequence();
            }
        }


Each segment is totally independent, ie it has it's own init and housekeeping routines, there's no co-dependency at all. To start the demo the sequenceOffset var is set to 0 and then the sequence() method is called.
When a segment has finished, it calls it's houseKeeping() method to dispose of all the bitmaps and removes all the sprites from the stage, and then calls the finished() method ( Hence it being public ).

That's all there is to the underlying structure which runs the demo, it really doesn't get any more straight forward.

Squize.

Tuesday, October 21, 2008 8:37:29 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, October 02, 2008
Air. The future of RIA. Unless you try and actually use it.

I've been wanting to write a swf encryptor for ages and last night I finally cracked ( As I'm working on something that I really don't want decompiling for various reasons ).
It was a toss up between Zinc and Air, but I opted for AIR 'cause in theory it is the future and therefore should have better support than Zinc.

So after all the hype surrounding Air I should just be able to google around, find out how to drag and drop, save a file and some other basics. I develop in Flex rather than cs3 'cause it's a million times better, but any search for Flex and Air just brings up examples using MXML. That's not great.

Eventually I found a hacky way to create an Air project in actionscript in Flex ( It's so convoluted it's untrue. You create a Flex project as opposed to an AS one as usual, tick the Air box, but on the part where you set the document class you alter the mxml extension to .as and it works ).
Getting there. Published the main class and up popped... nothing. More searching and I found out how to set it up ( A big thanks to Toby for blogging about it, without his words I'd have given up all together ).

Cool, got a window in place now. Close it, try publishing it again, and... nothing. Lot's more searching ( And swearing ) and I found out what the problem was, and the cure. If you don't exit your app correctly ( ie call an exit() after adding a listener to the close button ) then it doesn't actually exit correctly ( I found this out myself after a lot of messing about ).
When you publish an air app it runs something called adl.exe ( Adobe Debugging something. I've had enough air googling for a life time so can't face looking it up ) which runs the swf wrapped in the air api.
If you don't call exit() then when you close the app adl.exe keeps running. Ok, that's not the end of the world. What actually is though, is that you can only run one instance of adl.exe. If it's running after you've closed your app incorrectly, then you can't run any more air apps.
The beautiful thing is, it doesn't tell you. Flex doesn't tell you either. It's like they've ganged up to keep us in the dark.

Until I figured out the whole exit() thing, I was working with task manager open closing it down every time. The only solutions I found online were, yep, work with task manager open and...

Ok it kinda makes sense, and if you've got to call exit() then you've got to call it, but c'mon, this is the future of RIA and I've got task manger open to kill it ?
It all feels very beta-ish, from the hacky way to even create an Air project in Flex to that.

Once I got past these hurdles, I must admit it wasn't that bad. The lack of docs ( I only found this after I'd gone through a lot of pain ) has made it a less pleasant exercise than it should have been ( Oh joy, another mxml example for something I want to do with code ).

One weird thing which I'm putting down to me is that when I drag and drop a swf into my sexy little app it runs the app twice. I don't mean it opens another window, it just runs through all the code twice ( In alcon I was getting,
"wtf ?"
"wtf ?"
which was a bit of a give away ). A little kludgy check cleared that up.

At present we've got a simple little app which you can drag a swf onto, it then encrypts that with blowfish via the very nice Crypto library and you can then save that back out.

Next up ( And what I've been swearing at for the past hour or so ) is the decryption routines. Well, the code is being embedded and decrypted, it's just figuring out how to then make that byteArray run as a swf rather than just sitting there annoying me.

Squize.

Thursday, October 02, 2008 9:20:04 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [7]  |  Trackback
 Sunday, September 07, 2008

I've been silent the last couple of weeks, too silent I reckon, but ... yet again I've been oompha-loompha'ing for various projects.

One of this projects really raised the bar in several ways (I think I'll do a rundown afterward - deadline is tomorrow, really, no way of extending it, even for a few minutes).

So for now let us assume we're dealing with a purely hypothetic project (as this is not about what all went wrong during the assignment).

This fantasy project should contain this:
- a menu, 3 languages, able to load in various games
- multi language support for the games (which have been done by someone else)

As I'm used to work with Flash's strings panel it was a logical choice to use, basically you can set it to "auto" mode and all assigned Textfields will be replaced at runtime with the associated one, you can also set a new language while running and everything is fine.

Or so I thought.

The menu worked fine, but then I got the first game ... oh, now we Imagine the first game ...
Some files ...
- one 150MB fla, called "library" - LIBRARY? yes.
- one 90MB called "game" ...
- a few more files (levels), each around 90MB

OK, library mustn't mean something bad.

Or so I thought.

Library in this case means something bad. BAD. BAD. BAD.
It meant: shared library (for those of you lucky enough to have not heard about it yet ... read about here.)
Oh when you're back from reading about it, you surely think "oh, that is nice"

WAIT! Don't ever even think about thinking "oh, that is nice" when it comes to shared libraries. They are EVIL!
It's the foot and mouth disease of Flash's features.

(imagine using one)
You'll first notice that you lose control about preloading - shared libs work like things exported to the first frame BUT without being able to detect their size.

Fast forward now ...

So you have the menu using Flash's strings panel, working, now it's time to load in the first game ...
I chose to use the good old and trusty loadMovie method instead of the MovieclipLoader class. So it was just a simple load and detect the size, start when done.

OR SO I THOUGHT.

Well, I knew from other project that when loading an swf that uses Locale, the newly loaded swf (using Locale, too) replaces the language data from the holder swf. This is OK and it makes sense if you think about the fact that a static class is assigned to _global in AS2. So the game used the same language files the menu does ... easy.

The dirty details ...

The game loads in a couple of external mp3 files, so I had to take care of that, too - easy enough, so no problem there too..
This is what we see:
- loading starts, use getBytesLoaded / getBytesTotal to see when it's done ...
- tracing shows 140k of swf to load, fair enough.
- after loading that, nothing happened. For 900k nothing happened - Flash was loading in the shared lib.
- then I was able to watch the mp3 files loading and afterward start the game - finally.

The problem is that the user might think that the game isn't loading at all when the shared lib is loaded - THERE IS NO WAY OF WATCHING IT LOADING!!!!!!
First I thought I could trick my way out of it by loading the shared lib FIRST and then hope the cache would kick in when the game is later loaded - nice try buddy. No, it doesn't work (at least not very well).

I ended up by using a faked (and then real) file list to load the game, basically it shows that there are 2 files to load.
First the swf is loaded, then while waiting for it to load the shared lib, I fake a progress and later I add the mp3 files to that list also to watch them loading, too.

Now that this is off the list ...

Yehaa the game is loaded and ready to to be played ...
- level one works ...
- the "level done" screen pops up AND ... it is definitely NOT in the language I've been using in the menu. WTF?

In fact the game was now all in German, whereas I selected English for the menu.

WHY? What happened. I had no idea.

A lengthy search using your fav. search engine did not give us anything.

OK, so we're on our own again (hey, after all this is Flash).

Let the tracing begin ...

First thing I checked was the language set using Locale.getDefault():
- startup: "de" (because of my desktop language)
- set it to "en"
- (checking, yes now everything is "en")
- load game: still "en"
- play game: still "en"
- show level done: still ... "en"

BUT IT FUCKING SHOWS ALL FUCKING STRINGS IN FUCKING GERMAN!

More fast forward ...

A few hours later I have lost a good deal of hair, brain and energy and of course all the trouble is caused by ...  right . the shared library.
It seems like it overwrites the language data with it's own values but doesn't report it to Locale (so you can see it).

And they lived happily ever after ...

I ended up, oh, we "would" (we still imagine the project you know) with loading the game, loading all external files and THEN reload the language file.

nGFX

ps: next time I tell you about the joys of using art based levels that consist of over 900 vector based drawings, movieclips and components ... which take almost 10 minutes to copy and move ... joy.

Sunday, September 07, 2008 2:25:04 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, August 28, 2008
Collisions, they're always a part of my games that I'm never happy with. Not so much the actual this sprite has hit that sprite part ( The narrowphase of the check ), but the broadphase, ie deciding which checks are needed and which we can just ignore.

Different genres require different ways to test for collisions. For a long time now I've been using grid based checks ( As far back as this old beauty ) in arena based shoot'em ups. Simple enough, you split the screen up into overlapping sectors, and store each baddie in the sector it's occupying.
So let's say we've split the screen up into quarters, you check each baddie's position, and store it in one of the four arrays you've set aside for each sector. Then you can run through your bullets and see which sector they're in, so in theory you're only testing the baddies which are nearest to the bullet ( There's no point testing a bullet against a baddie which is on the other side of the screen ) which in an ideal world will reduce the checks by 75%. Not bad.

The problem I've always had with this is that it feels costly to maintain. I've always just cleared the sector arrays at the start of the baddie movement routines, I've never been clever enough to come up with a way to maintain it "properly". Therefore I could have baddies that have only moved a pixel or two since the last frame, there's no way they're going to have changed sectors, but I've had to treat them afresh.
That can't be good, but like I've said, I've never been able to come up with a clever way of negating that, so I've always just done it that big dumb way.

Recently quadtrees ( Check here for a great example, and an overview by the always excellent 8bitrocket can be found here ) and octtrees are very in vogue with Flash developers, so being a bandwagon jumper I thought I'd have a bit of that.

Again, I couldn't think of really good way to maintain the structure every frame, and it felt like you'd need a lot of objects to make it worthwhile ( Or just use it as a generic collision system for every game, but I'm not a fan of that. Collisions are a weird beast where very rarely does one hat fit all ).

One aspect that all the collision methods I mentioned above have, is shown below,

dbB1.png

I'm going to generalise a bit here, but let's say we've drilled down into the correct sector / node / whatever. Our bullet is travelling along that path ( Pick which ever direction you feel more comfortable with, in my head it's going up and right ). Chances are it's never going to hit that baddie ( I know the baddie could in theory move enough to come into collision with it, but we're generalising for a second ).
So we've gone to quite a bit of effort to narrow down our collision checks, and then we're still running a check per bullet every frame when most of the time it's not going to hit ( Think of your accuracy rating at any game that checks such things. 75% is pretty good in a game. That means that 25% of all the bullets are going to miss, yet we're having to test 100% of the bullets a 100% of the time ).

This all felt a bit sucky in my head. A lot of cpu time spent on something that wouldn't happen.

Let's talks about "Distance Based Broadphase". I made that up, it's more than likely already been around for years with a different name and I've just happened across a similar idea, but it explains what it is pretty well.

I've approached it in a different way than how I normally set up the whole bullets / baddies stuff. Using DBB every baddie has an array of all the player bullets ( Well a linked list for speed ), and every time the player shoots that new bullet is shoved into that array.
During the baddies main loop it runs through all the bullets it has in it's array and checks it's distance to it ( The narrowphase checks are just your bog standard circle to circle collisions ). If it's distance has increased, then the bullet is moving away from the baddie, and it won't hit it.

dbB2.png

So looking at that diagram above, lets say the bullet is flying up to the top left. The broadphase will keep checking as the distance from the bullet to the baddie is decreasing every frame, ie it's getting closer. It's possible that it could hit it, so it's worth checking.
Once the bullet goes past the sweet spot, it's moving away from the baddie. It'll never ever hit it, so we just remove it from the array and the baddie won't check for it again.

Whilst there's a possability of a collision it's worth checking, so it's not so costly ( If you're shooting at baddies from a distance then it's going to incur a cost until the bullet goes past the sweet spot, ie 'til the bullet gets to a point where it's not going to hit the baddie. The greater the distance the more the tests as it will take a while to actually get to the sweet spot ).
Going back to the first diagram, the bullet ( If moving to the top right ) is moving away from the baddie right from the start, so it's thrown away.
In effect we're checking the general direction until we get to the point of a hit, or a miss.

Now I've done some generalisation here. In real life your baddie will be flying around throwing some great shapes. For that, you just increase the size of the sweet spot to take it into account. If you have a fixed speed for a baddie you could work out exactly the sweet spot's size ( That is you'd work out if the baddie moving at it's max speed in a straight line to the bullets path how big the area to check would be ), or if you're lazy like me you just increase the size of the sweet spot by subtracting some pixels from the distance and testing it til it stops breaking.

Hopefully I've made some sense, it's proved to be quite a fair bit to explain. As always please feel free to post a comment if you have any questions or if I've got anything wrong. I'm sure I'll be editing this soon enough to clear things up.

Squize.

Thursday, August 28, 2008 2:44:27 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [7]  |  Trackback
 Wednesday, August 27, 2008
In the recent theme of speed testing I thought I'd better double check my atan2 results, and comparing the below method to the built in one I got the following:

runAtan2():  2161
runAtan2Quicker():  1159

( For more about how I test this stuff, please check this recent post )

Phew, something actually working quickly as it's meant to.

The following method is based on code by Jim Shima, http://dspguru.com/comp.dsp/tricks/alg/fxdatan2.htm

private static var coeff_1:Number = Math.PI / 4;
private static var coeff_2:Number;
private static var r:Number;
private static var angle:Number;
private static var absY:Number;

//--------------------------------------------------------------------------
public static function atan2(y:Number,x:Number):Number{
    coeff_2 = 3 * coeff_1;
    absY=y;
    if (absY < 0) absY = -absY;                    //Math.abs
    if(x>=0){
        r= (x - absY) / (x + absY);
        angle = coeff_1 - coeff_1 * r;
    } else {
        r = (x + absY) / (absY - x);
        angle = coeff_2 - coeff_1 * r;
    }
    return y < 0 ? -angle : angle;
}

I've used static properties / method 'cause it's part of my MathX class ( So if you're going to set up a similar way, create a class called MathX and then the usage would be,

var value:Number=MathX.atan2(y,x);

Exactly the same as the built in method ).

Squize.

Wednesday, August 27, 2008 5:05:00 PM (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Saturday, August 23, 2008
I'm becoming a bit of a speed testing slut with as3, and seeing how this function is used in so many games, I thought I'd have a play and see which actually is the quickest way to do this.

Point.distance() seems to have everything going for it, but does it ?

import flash.utils.getTimer;
import flash.geom.Point;

var time:Number;

var point1:Point=new Point(100,100);
var point2:Point=new Point(200,200);

var dist:Number;
var dx:Number;
var dy:Number;

function runPythagorean():void{
    time = getTimer();
    for (var i:int = 0; i < 10000000; i++) {
         dx = point2.x-point1.x;
         dy = point2.y-point1.y;
         dist = Math.sqrt(dx*dx + dy*dy);
    }
    trace("runPythagorean(): ", (getTimer()-time));
}

function runDistance():void{
    time = getTimer();
    for (var i:int = 0; i < 10000000; i++) {
         dist = Point.distance(point1,point2);
    }
    trace("runDistance(): ", (getTimer()-time));
}

runPythagorean();
runDistance();

Slap that into Flash, and be surprised at the huge difference between the two ( For those of you who prefer just to look rather than get involved, on my machine I got the following:

runPythagorean():  1452
runDistance():  10485

Not really neck and neck ).

So the distance method is a none starter ( What the hell does Flash do behind the scenes ? It's only returning a Number, it shouldn't need to be doing anything other that the code in the runPythagorean() method ).

Don't know if I've posted about it here before either, but avoid Rectangle.intersects() for the same reasons too.

Whilst I was testing I thought I'd do the old shortcut of:

var sq:Function=Math.sqrt;
dist = sq(dx*dx + dy*dy);

Again, another surprise,

runPythagorean():  1464
runPythagoreanWithShortCut():  2824

Finally, I finished off with some "quicker" versions of sqrt, ie

//---------------------------------------------------------------------------------------
function sqrt(w:Number):Number{
// Fast Math sqrt function from
// http://osflash.org/as3_speed_optimizations#as3_speed_tests
    var thresh    : Number = 0.002;
    var b:Number = w * 0.25
    var a:Number;
    var c:Number;
    
    if (w == 0) return 0;
    
    do {
        c = w / b;
        b = (b + c) * 0.5;
        a = b - c;
        if (a < 0) a = -a;
    }
    while (a > thresh);
    
    return b;
}
        
//---------------------------------------------------------------------------------------
function fsqrt(w:Number):Number{
// SUPA fast but not very accurate sqrt
// Fast Math sqrt function from
// http://osflash.org/as3_speed_optimizations#as3_speed_tests
    var thresh    : Number = 2;            //1
    var b:Number = w * 0.25
    var a:Number;
    var c:Number;
    
    do {
        c = w / b;
        b