Gaming Your Way

May contain nuts.

Unity, Mecanim ... the easy way ... for dummies ... like me.

If you happen to be old enough to know a world without youtube and like the written word (like a tutorial), tough time for you.

So if you want to learn, say about using Unity's main animation tool Mecanim, but also happen to *HATE* tutorial videos (because they waste your precious time) you're in deep shit. I'm not saying that Unity's tutorial videos are shit, but watching 30 minutes of someone talking away for an information that fits on 2 lines of written text (and maybe a screenshot) ... well, you get my point.

Fear not, we have written words (and screenshots)

Let's start by wasting some space with the basic problem: You have a character, animated him (or her) and want to use it in Unity. I might need to point out that I'm NOT using Blender (so things may be a bit different for you, but the basics still apply).


That's Violet and the odd 850 frames needed to animate her.

Let's fast forward the export and import process and jump right to the part you'd do if you haven't watched any tutorial video on setting up anims or re-targeting them in Unity.

After you're done setting up the animation clips you might end up with something like this:

(taken from a character that needs updating)

In a way you're set and can start setting up the animator's state machine, but what if you change the model or need to update / change the animation? Well you end up re-creating all the clips. Joy.
After the second time it becomes some sort of a curse.

Oh my.

If you have at least read the Unity manual on this (which is [again] very much lacking useful examples), you should know that you can re-target animations between models that share the same bone structure. But you may have (like me) overlooked the fact that this also solves the problem of re-doing the clips over and over again when you update your character.

Exporting the character to use with Mecanim

(This headline is just there to ensure google finds this post)

This process might be a bit more work when starting, but it saves a shit load afterwards. Let's start with exporting the "base" (and NOT animated character). In my case I select the skin and bones and hit "export selected" and get this dialog:


I should have exported the base model in T-pose and without animations, though (makes it easier to create a ragdoll).

Anyway, that's done, go to Unity and import and setup the base model:

Note that there are no animations to be imported.

Exporting animations and setting them up in Unity to be used with the base model (and re-target them)

Now it's time to swap the tedious process of setting up the animation clips in Unity with the tedious process of exporting the animation clips from our 3D app (although, this is the by far better choice in the long run).


The trick is, that this time I only select the bones when clicking "export selected" and the important part of this dialog is "bake animations" and the option to give a start and end frame (hint, hint). 
Actually, you could of export the skin again, but there really is no need to do so.

And after (what felt like an hour) I've got my clips exported and in Unity.

Preparing for re-targeting the animations

The tutorial video needs about 17 minutes to explain all that shit, but we can get the important information from a single screenshot:

When importing the animation, we copy the rig from an existing model (the base model) and click apply. Now set up the animation clip by changing to "Animations" and because we only exported the needed frames it's just changing the name from "Take 0001" to something meaningful (and maybe go over the other options of the clip, like looping).


That's it. Now you can just setup the animator state machine as before. Violet's looks like this right now:


Viola! A bit of text, some screenshots and no need to watch 30 minutes of video. Mhm, but then it took me about 2 hours to write this ...
... I think I better start making videos. 

-- Olli

Unhappy meals - and that's OK

If you come here often you might have seen the screenshots of the "random mine" game, well this one won't make it to a full game.
There's a good reason of course (actually there are a good number of reasons).
I still like the appeal of random created levels, but this one didn't quite work as well as I thought. The early alpha version showed that a jump & run game needs carefully designed levels in order to be fun and you can't fake that by adding elements from other genres.
That was something I discovered pretty early in development and changed the idea from "really random" to "random pieces" or blocks.
This created levels that were playable, but in worst case quite repetitive. The obvious thing to do was to create more variations of the basic tiles (or parts). 

 
These are the basic blocks the generator used.

 
A version of the 0101 block.

I create a set of variations for the blocks (each one uses 30*20 ingame tiles) which worked well at a first glance.  But due to the random nature could appear multiple times in a large map. That drained the fun more than I expected. In the end it proved as much work to create varied and fun blocks as to create a whole level.
Tough decission, but I didn't want to design a jump & run game, I wanted to write code that does that for me.

Zipped and stored away for later use.

 

So while Squize works on an AAA title I'm back at the beginning of a new project.

 

You've seen Knuckles (I think he might change a bit in the end), the main character in the Unity game I decided to do (that mine game should have been done in Unity in the first place, too - but flash seemed to be make a faster development possible).

The Idea for Nuckle's game evolved from a 2d sidescroller into a 3d game, so using Unity seems the better choice - even though there's the promissing stage3d api around.

Right now I'm focussing on the controls, aiming at a mouse only controlable game, but adding optional keys for movement. I think point 'n' click controls are what I'll use in the end.
But first there's a lot of stuff to code for the first level and I need to find ways to wire things in the game (like alarms and lights) to create an working environment.
If that proves to be "fun", more things can be added for the later levels (guards, cameras, more types of alarms and security).

nGFX

Friends and foes

For anyone who's interrested: it's shit weather outside. And cold. And grey. And it rains.

So Nuts and Bolts getting near gold status every minute I thought I might explain how lazy I was this time with the level editor. So lazy in fact, that there is no such thing. As Nuts and Bolts is a puzzle game and is using a fairly simple map structure I thought I could get away with doing the maps in the Flash IDE.

The basic idea was to place Movieclips on a grid and read out the names and positions at runtime.

Here's a screenshot of the first level:
nb_post_ide_howto_00.jpg



The squares and circles are in the background so that I know where to align my Movieclips, you can see the textboxes which provide addtional informations for the parsing routine (name is not used in the game anymore, but I was to lazy to remove it and the last box contains the help screens that should be displayed).

Each of the level elements got a name I use to build the map, so the yellow circles with the white "p" are normal platforms (name:"pl"), the green "e" is the exit and so on. Luckily we can use the same name more than once in as3 so I could just copy and paste elements once I had decided what name they should use...

Here's another screenshot of a later level:
nb_post_ide_howto_01.jpg

All we have to do now is to get a copy of the Movieclip (the level MCs are exported for runtime use) with this:

private function getLevelMC (iLevel:int):MovieClip {
	var myClass:Class = getDefinitionByName("mcLevel_" + iLevel.toString()) as Class;
	var mcReturn:MovieClip = new myClass() as MovieClip;
	return mcReturn;
}

And the run the parser of that MC (just a part of the code, though):

// find platforms 
for (i = 0; i < mcMapTemplate.numChildren; i++) {          
	mcLevel = mcMapTemplate.getChildAt(i) as MovieClip;          
	bVisible = false;     
	x = Math.floor(mcLevel.x / 60);     
	y = Math.floor(mcLevel.y / 60);          
	
	switch (mcLevel.name) {         
		case "pl": // normal platform             
			this._aMap[x][y].p = PLATFORM_NUTS;             
			bVisible = true;             
			break;                     
		case "start":         
		case "start_0":         
		case "start_1":        
		case "start_2":         
		case "start_3":             
			this._aMap[x][y].p = PLATFORM_START; // we'll set the dir later when adding beam's data             
			this._aMap[x][y].d = 0; 
			this._pCurrent = new Point(x, y);
			this._pNuts = new Point(x, y);                          
			if (StrUtil.contains(mcLevel.name, "_")) {                 
				this._aMap[x][y].d = StrUtil.cInt(StrUtil.part(mcLevel.name, "_", 1));             
			}                          
			bVisible = true;             
			break;                      
		// [Skipped a lot of code ...]     
	}      
}

Then the Movieclip can be removed again and I can plot the level from the resulting map data array ...
2011_nb_01.jpg

For this game this was hazzle free way of doing (and changing) the 30 levels.

And with that, I'm getting back to the game, I have some sounds to add before it becomes gold ...

nGFX

Blitter engine: Benchmarks

Ok, before anyone says "You said you weren't going to do any benchmarks, you said. Wanker" I had a rush of inspiration the other night to improve the blitter engine, so I figured I need to have my own benchmark test to make sure I'm not just wasting my time.

And because I'm a giver rather than a taker, I figured it wouldn't kill me to do a pointless sprite based one just to show how slow they [ Sprites ] are.
Before we go any further, I'm busy working with our new favourite client ( We may even have a game to show next week. Yeah, actually producing something rather than talking about it ), which means I'm knackered, which in turn means the examples are as ugly as you like.
Also it's not a case of getting them all running as fast as possible, it's to show the relative speed between them. We're not trying to break records here, it doesn't matter how high we piss, more how the three approaches compare to each other.

The first test just uses sprites with a bitmap for the scrolling background ( ie, the background isn't wrapped in a sprite, it's just a bitmap added directly to the stage ). For this test we only run a 1000 sprites ( Ha, only. In Flash5 that really would have been a record ).

Test1

Yeah, ugly and not even embedded, but it's only for us to see. All the tests can take a little while, once all the sprites are active you'll see "Finished" and then it's worth checking out the fps.

Next up it's a test using bitmaps, like the scroller they are added straight to the screen. 3000 of the bad boys this time.

Test2

You should roughly be getting the same fps as the sprite test, but with 2000 more objects running ( I get around 31-35, but let's not get too hung up on the actual frame rate, as I'm running snow leopard on a macbook pro and using Firefox as my browser. You more than likely won't be, it's all about the relative speeds ).

Final test, 3000 bobs a blitting, and the scroller background is also being blitted.

Test3

Now for me it runs slightly quicker than test2 in the browser ( I'm not running the debug version in the browser ), but locally the bitmap version ran faster by around 5fps or so.

I'm not overly advocating the blitter approach compared to bitmaps, I'm not trying to sell the blitter engine to you. Bitmaps give you a lot more of everything, you get all the lovely blendmodes, rotation, alpha etc. All the good stuff you don't get in a straight forward copyPixels() engine.
The advantage of the blitter engine is that it's better suited to mobile devices, it will run better on an iPhone than a bitmap approach. Moot point I know, but don't forget we're all going to be writing Android games real soon.
Also the framework for dealing with layers is there and working, if you're using bitmaps then you've got to write your own framework for such things ( And if you do, please post it on your blog so I can get a copy ).

Let's recap as it's late, bitmaps are great, the blitter engine is pretty sweet and sprites are like poking a stick in your eye.

Squize.

Blitter engine

I thought I may as well open source this as it may be useful for some of you.

Let's start with the all important caveats.
I'm not going to be showing you a load [ Or any in fact ] of benchmarks or examples, I can think of nothing more boring to do. Either take it on faith that it's fast, or just use it to look through and nick the bits that may be helpful to you. I'm not trying to prove a point with this that my code is so l33t. I've found it useful, if you do to then cool, we're both winners. If not, well you've not lost anything have you ?

Again another life's too short thing, I really can't be bothered with sorting out a license for this, is it MIT or is it GPL ? I don't really care, it's not something I hold close to my heart, it's a bit of code to do a job. If you do tinker with it and make it better it would be nice to share what you've done, if you make a game using it and you make a ton of money, great. A credit or even a shout about it would be cool, but if not, then I'm not going to hunt you down with an angry mob.
The only thing which would piss me off is people passing it off as they're own, but we've got a wanker free readership here so it's not going to be any of you guys, and there's not a lot you can do about the sort of idiots who would do that anyway.

There may be bugs, there was some experimental iPhone and pixel bender stuff in there which I've just ripped out. If you spot any thing dumb please flag it up, it would be nice to have a fixed version available here for people coming to this further down the line.

Ok, let's go over the usage.

The static Canvas class is the main part of it, it's what makes it tick. To set things up just do a simple,

            Canvas.init(stage,new Rectangle(0,0,600,500),true);
            stage.addChild(Canvas.viewport);
 
stage is your stage ref ( Surprisingly enough ), next up is the size of the canvas ( The canvas is the bitmap which everything will be blitted into, the viewport property ), and are we going to be transparent or using a background ? You decide with a boolean. You can also pass a fill colour as the final parameter if you don't want to use a bitmap as the background.

Next in our hierarchy is the PlayField. Think of this as your container sprite. I don't know about you, but I miss working with layers in as1 via the timeline, so I've always simulated them with container sprites. PlayFields are pretty much that, eg

            var defRect:Rectangle=new Rectangle(0,0,600,500);
            gridPlayField=new PlayField(defRect);
            baddiePlayField=new PlayField(defRect);
            playerPlayField=new PlayField(defRect);

            Canvas.addChild(gridPlayField);
            Canvas.addChild(baddiePlayField);
            Canvas.addChild(playerPlayField);

From memory I don't think the engine actually does anything with the rectangle we pass to it, I think I was planning to do some simple clipping, but never got round to it, but pass it in anyway it doesn't do any harm.

When it comes to rendering the display, the Canvas class loops through all the PlayFields in the order they are added, so the gridPlayField in that example will be plotted first, then the baddiePlayField and so on. It's a simple way of simulating layers / depth.

Finally we're onto the Bob class. These are your Blitter OBjects, your software sprites. To create one,

            var temp:Bitmap=new playerBM();
            bob=new Bob(temp.bitmapData);
            playerPlayField.addChild(bob);

You just pass it the bitmapData you want, and then add it to your PlayField. The Bob class has all the properties you'd expect, and supports animation too ( So bob.gotoAndStop(bob.currentFrame+1) works, check through the class on how to pass it an animated sprite sheet ).
Where possible I've tried to make things as close to the display list as possible, there's no need to be forcing a new syntax onto yourself.

Like the PlayFields these are plotted in order ( The PlayField is basically just a list of its children ), so add them in the depth order you want, exactly like adding children to a sprite.

As with all blitter engines there are restrictions due to using copyPixels, so forget all about rotation and alpha and blendModes. A pity, and I did start with a ComplexBob class that used Draw() for those things, but to be honest it was a real arse ache, and it's quicker to attach the bitmap directly than to use draw ( The downside to that being the bitmaps have to be above the canvas, there's no way to slide them in there between PlayFields ).

There are quite a lot of properties and methods available to you in these classes, auto-complete is going to be your friend with this.

One last thing, the Canvas uses the RENDER event to refresh the screen, so there's no need to call anything in your mainloop, just by altering a bob's x position it'll refresh for you ( If you're not going to use the actual package, I'd recommend having a look at the Render event stuff, you should find it quite useful if you're doing something similar ).

Guess I should just link to it now, download me here.

As always abuse the comments. Just to sound a grumpy shit for one last time this post, this isn't some big open source project that I plan to expand and maintain, if you find bugs or even better come up with fixes, then yeah they'll be added with a great big bag of thanks from me, but this isn't my life's work. Feature requests are going to be ignored to be honest, it is what it is.

Squize.

Embedding a text file

I've started work on game 2 for game'88, and it's going to be a tile based scroller. Unbelievably after doing literally dozens of tile based games in as2, I've still not done one in as3.

First up I fired up Mappy, did some an ultra simple test map and then exported it using the lua based exporter I wrote way back. It's a nice simple format, eg

smf!|10|200|0,0|0,0|0,0|0,0|

We've got a bit of a header there, "Stimunation Map Format", now that's going back some years, the width and height of the map ( 10 tiles wide, 200 high ), and then a combination of the tile image|tile attribute.

In the good old days we'd just #include that and hit it up that way, but as3 doesn't give us include, so it's a swanky new byteArray approach for us.

As always reading code on a blog is just boring, so open up another tab and get some filth in there to flick over to when your eyes start to glaze, or maybe a youTube video of a cute cat.

package Classes.Game2.Scroller {
    import flash.utils.ByteArray;
    
    public class G2_LevelData {

//---------------------------------------------------------------------------------------
// Assets
//---------------------------------------------------------------------------------------
        [Embed("/_assets/G2_level1.txt",mimeType="application/octet-stream")]
        private var level1Data:Class;
        
//---------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------
        public var map1:Array;

        private var mapData:ByteArray;
                
//---------------------------------------------------------------------------------------
// Constructor
//---------------------------------------------------------------------------------------
        public function G2_LevelData() {
            mapData=new level1Data();
            var mapDataArray:Array=convertToArray(mapData);
        }

//---------------------------------------------------------------------------------------
// Private
//---------------------------------------------------------------------------------------
        private function convertToArray(source:ByteArray):Array{
            var tempString:String=source.toString();
            var tempArray:Array=tempString.split("|");
            return tempArray;        
        }
        
//---------------------------------------------------------------------------------------
    }
}

And that's it, almost too simple to share, but if it saves you a bit of hunting further down the line then it's not a bad thing.

Squize.

Simple Star Wars wipe in as3

I've done this effect to death, used it again in the upcoming DAC ( Whose launch has been delayed due to having to add achievements, luckily someone else is doing that ) so I figured it's time to put it out to pasture and share it around.

'cause we've not been posting as often here, I thought I'd treat everyone to some nice grabs to break up the code slightly.

Basically we want to create a simple gradient rectangle movieclip, like so

swatch_grab.png

And we end up with something looking like this,

gradient_grab.png

A simple rectangle that's slightly bigger than your stage, that fades out to one side. It needs to be bigger than your stage 'cause we want all the gradient part with the alpha blending to be off-screen when we first apply it as a mask.

Next up, the boring old code bit.

package Classes {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.display.Stage;
    
    import gs.TweenLite;
    
    public class Transition {

//---------------------------------------------------------------------------------------
// Assets
//---------------------------------------------------------------------------------------
        [Embed("../_assets/assets.swf",symbol="transitionMaskMC")]
        private var transitionMaskMC:Class;

//---------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------
        private var playField:Sprite;

        private var grabBitmapData:BitmapData;
        private var grabBitmap:Bitmap;

        private var transitionMask:Sprite;
            
//------------------------------------------------
// System
//------------------------------------------------
        private var main:Main;
        private var mainMovie:DisplayObject;
        private var stage:Stage;

        private var callBack:Function;
        
//---------------------------------------------------------------------------------------
//Constructor
//---------------------------------------------------------------------------------------
        public function Transition(){
            main=Main.getInstance();
            mainMovie=main.getMainMovie();
            stage=main.getStage();

            grabBitmapData=new BitmapData(640,480,false,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.cacheAsBitmap=true;
            
            transitionMask=new transitionMaskMC();
            transitionMask.cacheAsBitmap=true;
        }
        
//---------------------------------------------------------------------------------------
// Public
//---------------------------------------------------------------------------------------
        public function toString():String {
            return "Transition";
        }        

//---------------------------------------------------------------------------------------
        public function grabScreen(callBackArg:Function):void{
            callBack=callBackArg;

            if(playField==null){
                playField=main.getInitObj().getPlayField().transition;
            }

            grabBitmapData.draw(stage);
            transitionMask.x=-160;
            grabBitmap.mask=transitionMask;
            playField.addChild(grabBitmap);
            playField.addChild(transitionMask);

            callBackArg();

            TweenLite.to(transitionMask, 1.5, {x:640, delay:0.2,onComplete:houseKeeping});

        }

//---------------------------------------------------------------------------------------
        public function houseKeeping():void{
            playField.removeChild(transitionMask);
            playField.removeChild(grabBitmap);
        }

//---------------------------------------------------------------------------------------
    }
}

There's really no way to make code look interesting in a blog is there ?

            grabBitmapData=new BitmapData(640,480,false,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.cacheAsBitmap=true;
            
            transitionMask=new transitionMaskMC();
            transitionMask.cacheAsBitmap=true;

Here in our constructor we're just creating a bitmap which we're going to use when we take a snap shot of the whole screen, and create our transisitonMask ( ie, the gradient mc we've just made ) instance.
The key thing is to turn cacheAsBitmap=true on both, otherwise the gradient mask won't work, it'll just be a boring old normal gradient free mask.

            grabBitmapData.draw(stage);
            transitionMask.x=-160;
            grabBitmap.mask=transitionMask;
            playField.addChild(grabBitmap);
            playField.addChild(transitionMask);

            callBackArg();

            TweenLite.to(transitionMask, 1.5, {x:640, delay:0.2,onComplete:houseKeeping});

This is the guts of what we're doing. Firstly use the always sexy draw() to grab the whole screen, then position the mask so our alpha blended bit is off screen. We want the whole snap shot bitmap to be masked normally at first.
We then simply add both the bitmap and mask to the screen. You'll notice the playField reference there. What I do, as I'm a layer loving kinda guy, is create a simple class that creates all the holders for all the things in a game at the depths I want. It's like codey version of using layers that way. So for your transition "layer", that should be on top of everything else ( You could also add it directly to the stage, same thing, I'm just paranoid that I'll need a layer above the transition for some reason ).
We've got the bitmap, the mask, the bitmap is now masked so all we need to do is cheat, and use the very lovely TweenLite. That just slides our mask all the way across the screen, in effect removing our mask, but with the nice alpha blending on the edge it makes it appear soft. Kinda like in Star Wars.

Notice the callback and the slight delay in the tween in there ? What we do is call the transition method, and then jump back to our main code where we're setting up the screen "beneath" the transition, so it wipes away to reveal our next screen.

[Update: Destroy all Cars is now ( Finally ) live, so you can see it action and see if you want to go to the effort of using it yourself ]

Squize.

Hello Pixel Bender

Played with Pixel Bender yet ? For Ionic ( My TD game which is taking an age to finish ) I wanted to use a similar effect to the transition in cronusX, the rgb split / tv interference thing. In the transition it didn't matter how long it took, but to use it in-game with God knows how many sprites along with collisions running it's got to be a lot lot quicker. Yeah you know where this is going.

ionic_rgbGrab.jpg

After firing up the Pixel Bender toolkit and swearing and looking through docs and examples for an hour or so, I kinda got it, so I thought I'd share what little I know. Just to warn you, this is going to be code heavy and no example to even look at, dry reading ahead. Maybe open up some porn in another tab and just flick between the two when your eyes start glazing over.

<languageVersion : 1.0;>

kernel RGBDistortion
< namespace : "RGB Distort";
vendor : "www.gamingyourway.com";
version : 1;
description : "Pixel Bender version of RGB distort";
>
{
  input image3 src;
  output pixel3 dst;

  parameter float rOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;

  parameter float gOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;

  parameter float bOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;
  void

  evaluatePixel()
  {
    float2 outCoords=outCoord();

    float2 redPos=outCoords;
    redPos[0]+=rOffset;
    float3 inputColorR = sample(src,redPos);
    dst.r = inputColorR.r;

    float2 greenPos=outCoords;
    greenPos[0]+=gOffset;
    float3 inputColorG = sample(src,greenPos);
    dst.g = inputColorG.g;

    float2 bluePos=outCoords;
    bluePos[0]+=bOffset;
    float3 inputColorB = sample(src,bluePos);
    dst.b = inputColorB.b;
  }
}

I'll try and break it down quickly in my usual lazy not really explaining things style. At the start is simple metadata, nothing you can't figure out there.

   input image3 src;
   output pixel3 dst;


A bit more interesting. input is the image you're going to send to the kernal, image3 being the datatype ( Because we don't want to worry about the alpha channel the datatype is image3 ( RGB ), if you want alpha it would be image4 ( ARGB )), the output is the bitmap we're going to plot the result to, which is a pixel3 datatype ( Which is the same as image3 ).

  parameter float rOffset
<
   minValue:float(-50.0);
   maxValue:float(50.0);
   defaultValue:float(0.0);
>;

This how you set up an argument to pass to the kernal. It's a float and refers to the red channel offset when we jiggle things about. The min/max and default values are more for the toolkit itself. When you run it it'll give you a slider to play with, and those are the values for the slider ( It'll make sense when you copy the code into the toolkit. Also you can drop a description in there as more metadata, but this is for Flash so we don't really need it ).

   evaluatePixel()

This is our main loop. Everything in this method is run on each and every pixel in the image.

    float2 outCoords=outCoord();

The outCoord() method returns this pixels x/y as two floating point numbers, in the form of an array ( Kinda ). We just store that in a var out of habit and speed.

    float2 redPos=outCoords;
    redPos[0]+=rOffset;
    float3 inputColorR = sample(src,redPos);
    dst.r = inputColorR.r;

redPos is our x,y position. We then add the new x position to redPos.x ( redPos[0] ), with the new x position being our parameter from above.
inputColorR is quite a chunky little line. sample grabs the pixel data at ( inputSource, position ), in effect we're setting the float3 ( RGB ) variable inputColorR = sourceImage[x+our offset][y]
After doing that, we make this pixel in the r(ed channel ) in our destination this colour. Basically we're doing this:

var rgb=sourceBitmap.getPixel(x+offset,y);
destBitmap.setPixel(x,y,rgb);

( But just on the red channel ).

The rest of the kernal is just more of the same, just for the other two channels.

After that pick the Export Filter for Flash Player option and we're ready to get back to normal coding.

package Classes {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.MovieClip;
    import flash.display.Shader;
    import flash.display.ShaderJob;
    import flash.display.ShaderParameter;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.events.ShaderEvent;
    import flash.filters.ShaderFilter;
    
    public class Transition {
        
//---------------------------------------------------------------------------------------
// Assets
//---------------------------------------------------------------------------------------
        [Embed(source="_shaders/distortion.pbj", mimeType="application/octet-stream")]
        private var shader_distortPBJ:Class;

        [Embed("/_assets/assets.swf",symbol="staticMC")]
        private var staticMC:Class;

//---------------------------------------------------------------------------------------
// Properties
//---------------------------------------------------------------------------------------
        private var playField:Sprite;

        private var shaderBitmapData:BitmapData;
        private var grabBitmapData:BitmapData;
        private var grabBitmap:Bitmap;

        private var shader_distort:Shader;
        private var shaderFilter_distort:ShaderFilter;
        
        private var distortCnt:int;
        private var rSin:Number;

        private var staticEffect:Sprite;
        private var staticAnim:MovieClip;
                
//------------------------------------------------
// System
//------------------------------------------------
        private var main:Main;
        private var mainMovie:DisplayObject;
        private var stage:Stage;

//---------------------------------------------------------------------------------------
//Constructor
//---------------------------------------------------------------------------------------
        public function Transition(){
            main=Main.getInstance();
            mainMovie=main.getMainMovie();
            stage=main.getStage();

//Let's make our bitmaps where we're going to plot our data too
            shaderBitmapData=new BitmapData(700,380,false,0);
            
            grabBitmapData=new BitmapData(700,380,true,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.alpha=0.4;
//Create our shader, sexy
            shader_distort=new Shader(new shader_distortPBJ());
            shader_distort.data.src.input=shaderBitmapData;

            staticEffect=new staticMC();
            staticAnim=staticEffect["anim"];
            staticAnim.gotoAndStop(1);
        }

//---------------------------------------------------------------------------------------
// Public
//---------------------------------------------------------------------------------------
        public function toString():String {
            return "Transition";
        }        

//---------------------------------------------------------------------------------------
        public function init():void{
            playField=main.getInitObj().getPlayfield().transitionPlayField;
            rSin=0;
        }
        
//---------------------------------------------------------------------------------------
        public function distortInit():void{
            if(grabBitmap.parent!=null){
                return;    
            }

            distortCnt=0;
            shaderBitmapData.draw(stage);
            playField.addChild(grabBitmap);

            staticEffect.height=int(Math.random()*100)+10;
            staticEffect.y=int(Math.random()*300)+50;
            staticAnim.gotoAndPlay(1);
            playField.addChild(staticEffect);
            
            startJob();
        }

//---------------------------------------------------------------------------------------
// Private
//---------------------------------------------------------------------------------------
        private function startJob():void{
            rSin++;
            rSin++;
            ShaderParameter(shader_distort.data.rOffset).value=[Math.sin(rSin)*12];
            ShaderParameter(shader_distort.data.gOffset).value=[Math.cos(rSin)*12];
            ShaderParameter(shader_distort.data.bOffset).value=[Math.sin(-rSin)*12];
            
            var job:ShaderJob = new ShaderJob(shader_distort,grabBitmapData);
            job.addEventListener(ShaderEvent.COMPLETE,shaderCompleted);
            job.start();
        }
        
//---------------------------------------------------------------------------------------
        private function shaderCompleted(e:ShaderEvent):void{
            if(++distortCnt==6){
                staticAnim.gotoAndStop(1);
                playField.removeChild(staticEffect);
                playField.removeChild(grabBitmap);
                return;
            }

            staticEffect.height=int(Math.random()*100)+10;
            staticEffect.y=int(Math.random()*300)+50;

            startJob();
        }

//---------------------------------------------------------------------------------------
    }
}

Sigh, that's a ton of boring code to look at, let's just pick out the fun stuff ( Equating as3 to fun, fuck me that's tragic ).

        [Embed(source="_shaders/distortion.pbj", mimeType="application/octet-stream")]
        private var shader_distortPBJ:Class;

That's how to embed the kernal.

//Let's make our bitmaps where we're going to plot our data too
            shaderBitmapData=new BitmapData(700,380,false,0);
            
            grabBitmapData=new BitmapData(700,380,true,0);
            grabBitmap=new Bitmap(grabBitmapData);
            grabBitmap.alpha=0.4;

What we're going to do is grab the screen when we want to run the effect, but we just want it faint ( alpha=0.4 ). shaderBitmapData is our source, with grabBitmapData being our dest. You can use the same bitmap for both, but Flash has to create a temp working copy, so it's slower.

//Create our shader, sexy
            shader_distort=new Shader(new shader_distortPBJ());
            shader_distort.data.src.input=shaderBitmapData;

Finally we're creating our actual shader, with the second line defining our source bitmap, which can do here as it won't change.

The distortInit() just grabs the screen, resets any vars we need and sends up the static effect ( Which has nothing to do with the pixel bender stuff, so we're going to ignore that ). Once that's all done we call the startJob() method which does the magic.

        private function startJob():void{
            rSin++;
            rSin++;
            ShaderParameter(shader_distort.data.rOffset).value=[Math.sin(rSin)*12];
            ShaderParameter(shader_distort.data.gOffset).value=[Math.cos(rSin)*12];
            ShaderParameter(shader_distort.data.bOffset).value=[Math.sin(-rSin)*12];
            
            var job:ShaderJob = new ShaderJob(shader_distort,grabBitmapData);
            job.addEventListener(ShaderEvent.COMPLETE,shaderCompleted);
            job.start();
        }

K, so we're using sin and cos to create our r,g and b position offsets, nothing new there. The ShaderParameter is how we change our parameter values in the kernal. You can hit the values directly, and they will be modified, but it doesn't actually do anything. I lost over an hour to that before finding out about ShaderParameter. Hopefully those lines should make sense.

Next up, we create a ShaderJob. We're doing this as opposed to the adding it as a filter approach, as this is the async magic that we've gone to all this effort for ( Runs on it's own thread etc. etc. Let's face it, that's the only thing we all know about PB ). The ShaderJob just wants to know which kernal we're using, and the destination bitmap.
Finally we just add a listener to that, and call the start() method, and it'll go and do it's thing and won't affect our other code. There's no waiting around here.

        private function shaderCompleted(e:ShaderEvent):void{
            if(++distortCnt==6){
                staticAnim.gotoAndStop(1);
                playField.removeChild(staticEffect);
                playField.removeChild(grabBitmap);
                return;
            }

            staticEffect.height=int(Math.random()*100)+10;
            staticEffect.y=int(Math.random()*300)+50;

            startJob();
        }

We're just been recursive bad boys here. Have we run the effect 6 times ? Yes so we're done, clear everything up, nope, so call the startJob() method again.

Hardly an indepth technical breakdown, but hopefully it's a starting point. This was a fair few hours of swearing for me, but I'm old so new things take longer to make sense, so it shouldn't take you more than a couple of hours to get PB doing cool things.

Squize.

Mochibot and BitmapData.draw() issues

Nice descriptive title for a change, mainly 'cause I've spent ages hunting around for a solution myself, so if you're coming here for just this very reason, I understand your pain.

We had issues with mochibot on Invaders Must Die, but by then I'd passed it over to Mousebreaker so they had to deal with it in-house. It's the first time I've hit it face on so to speak.

To set the scene and get you in the mood, I've just finished the viral version of cronusX and wanted to try the version control that mochi offer, as that was one of the best things about gameJacket, plus I've added a twitter option to the game which I know is going to bite me on the arse with people bitching it's spyware etc. etc. so I wanted the option to rip that out if it was becoming too much of a ball ache.
I've also included mochi-bot tracking so Candystand can see how it's doing out in the wild, so I've dropped that in too.

Everything worked first time ( That's where it differs from gameJacket and it's constant security check failures ), gave it a quick test, all good. Ah, not all good, the transition craps out to a black screen.

Testing it locally ( Turns out I've not installed the browsers debug version of FP10 on the pc, how good am I ? ) it spat out an error,

Error # 2121: Sandbox Security Violation: BitmapData.draw

etc.

Joy.

Lots of googling ( To no real avail, hence this post ), seeing the only advice on the mochi forums that I could find was someone copying out some text from the Flash help ( Cheers for that, solves everything ) it turns out that the mochi-bot can't be on your root display object, as you get all the security violations when trying to use draw(); that you would if you tried loading in say an image and using draw(); on that ( It's a cross domain thing. To be honest life is too short to go into it, I could just copy from the as3 docs, but... ).

Basically, long story cut shorter, if you do something like:

MochiBot.track(this,"yourMagicNumber");

In your preloader ( Assuming that "this" is your main preloader class than extends MovieClip ) then you can't use draw(); in your main code.

To get around this, give it a movieclip instead, eg.

mochiBotHolder=new MovieClip();
stage.addChild(mochiBotHolder);
mochiBotHolder.addEventListener(Event.ADDED_TO_STAGE,triggerMochiBotTracking);

private function triggerMochiBotTracking(e:Event):void{
    mochiBotHolder.removeEventListener(Event.ADDED_TO_STAGE,triggerMochiBotTracking);
    Security.allowDomain("http://core.mochibot.com/my/core.swf");
    MochiBot.track(mochiBotHolder,"yourMagicNumber");
}

Now I'm not sure if you need the allowDomain(), but it works with it, and that's good enough for me after losing a good hour and a half to this little gem of a problem.
Because the mochibot code needs to hit loaderInfo.loaderURL via the clip you pass it, you have to add your holder mc to the stage, and then wait until it's actually there before calling MochiBot.track, otherwise you're in the land of null properties.

Now I'm not sure if you can kill that holder mc of after calling the track() method, it's like the allowDomain(), it works as it is, and I'm happy with that.

This has proved to be a fairly techy / geeky post, quite rare from me.

Squize.

Random Dynamic Levels - Part 3

In the last two articles  we made a maze and then destroyed it by adding a lot of free space. In part 3 of this articles I'm going to add some rooms to the empty space and add some doors ...

Part 3 - part 1 - why seperate things and make rooms?

At a first glance it might not be necessary to seperate data into dungeon, room and cell, but thinking ahead a bit it should make sense ...
My idea is that you can have the dungeon which holds the complete map (with the basic room data rendered into it) and the rooms so you access them easier and most important do some magic with them later. One of the neat things is that you could use a different tiling for rooms making them more detailed or use the additional map data for skinning (when rendering the dungeon into a tile based map).

The main difference between a room and a dungeon is that the room consists of empty cells and has walls along it's boundaries. So the first additional method we'd add to the Room class will be the init method, which simply sets all cells so they form a rectangular room.

public function initCells ():void {
            
    var x:uint;
    var y:uint;
    var cellTmp:Cell;
            
    for (x = 0; x < this.iWidth; x++) {
        for (y = 0; y < this.iHeight; y++) {
                    
            cellTmp = this.cell2(x, y);
            cellTmp.setWalls(WallType.OPEN);
                  
            if (x == 0) cellTmp.setWall(Dir.WEST, WallType.WALL);
            if (x == (this.iWidth - 1)) cellTmp.setWall(Dir.EAST, WallType.WALL);
            if (y == 0) cellTmp.setWall(Dir.NORTH, WallType.WALL);
            if (y == (this.iHeight - 1)) cellTmp.setWall(Dir.SOUTH, WallType.WALL);
        }
    }
}

We could later add methods to create different rooms (ie. two overlapping rectangles, circular rooms), but for now that will do ...

I also added a getter setter for Offset to the room, so we can modify the x and y pos of the bounding rect that we stored in the map class (you'll see later what's that for).

So we now have a room, but how do we get that sucker into the map, or (as just putting it into the map isn't really an issue) where can we place it *best*.

There are a few things that I want to watch when placing the rooms ...
- a room should not overlap any existing room, we rather don't at it
- placing a room at a place where it doesn't touch anything, is something we don't want, too
- rooms overlapping corridors should be avoided
- rooms touching dead ends is something we want (what's nice than finding a room after a long winded corridor?)
- rooms touching any sort of wall is OK, too

Yet again we (as humans) could just look at the map and say "here, there and there" and done, but that stupic piece of plastic cannot... we need to apply some sort of scoring to the whole placement mess.

Here's a bit of pseudo code ...

  • start with a VERY high best score, lets say 999999999999 and set current score to 0 ...
  • Loop over every cell in the dungeon
    • at any given position check if the new room overlaps any rooms already in there
      if so, we add 5000 to our current score, otherwise we add nothing
    • now loop over every cell in the room and compare it with the current dungeon cell (offsetting the room to the current position)
      • if the current room cell touches an empty cell (in the dungeon), add 10
      • if we touch a wall, add 3
      • if we touch a dead end, add 1
    • if the final current score is lower than the best score, sreplace the best score and store the current position as the best possible location
  • if the score is higher than the "room overlaps room" score, assume that it only can be placed overlapping a room and drop it, otherwise add it to the dungeon

Here is the scoring code:

private const TOUCH_DEADEND:int = 1;
private const TOUCH_WALL:int = 3;
private const TOUCH_EMPTY:int = 10;
private const OVERLAP_ROOM:int = 5000;
private const OVERLAP_CORRIDOR:int = 100;
        
public function fitMazeRoom (myRoom:Room):Boolean {
    
    var iBestScore:int = 999999999;
    var iScore:int = 0;
    var pBestPos:Point = new Point(0, 0);
    var pOffset:Point = new Point(0, 0);
    
    var cellDungeon:Cell;
    var cellNext:Cell;
    
    var rectTmp:Rectangle = myRoom.rectBound.clone();
    
    var i:uint;
    
    var x:int;
    var y:int;
    
    var xx:int;
    var yy:int;
    
    var iRoomID:uint;
    
    var bAddRoom:Boolean = false;
    
    // loop over map (- roomsize)
    for (y = 0; y < (this.iHeight - myRoom.iHeight + 1); y++) {
        for (x = 0; x < (this.iWidth - myRoom.iWidth + 1); x++) {
            
            // do the scoring ...
            iScore = 0;
            
            // check room/room overlapping
            rectTmp.x = x;
            rectTmp.y = y;
            for (i = 0; i < this._aRoom.length; i++) {
                if ((this._aRoom[i] as Room).rectBound.intersects(rectTmp)) {
                    iScore += OVERLAP_ROOM;
                }
            }
            
            // check room/dungeon overlapping
            for (yy = 0; yy < myRoom.iHeight; yy++) {
                for (xx = 0; xx < myRoom.iWidth; xx++) {
                    pOffset.x = (x + xx);
                    pOffset.y = (y + yy);
                    
                    cellDungeon = this.cell(pOffset);
                    if (cellDungeon.iType == RoomType.CORRIDOR) iScore += OVERLAP_CORRIDOR;
                    
                    if (yy == 0) {
                        iScore += this.getCellScore(this.cell(this.getNextPos(pOffset, Dir.NORTH)), Dir.NORTH);
                    }
                    if (xx == (myRoom.iWidth - 1)) {
                        iScore += this.getCellScore(this.cell(this.getNextPos(pOffset, Dir.EAST)), Dir.EAST);
                    }
                    if (yy == (myRoom.iHeight - 1)) {
                        iScore += this.getCellScore(this.cell(this.getNextPos(pOffset, Dir.SOUTH)), Dir.SOUTH);
                    }
                    if (xx == 0) {
                        iScore += this.getCellScore(this.cell(this.getNextPos(pOffset, Dir.WEST)), Dir.WEST);
                    }
                }
            }
            
            if (iScore < iBestScore) {
                iBestScore = iScore;
                pBestPos = new Point(x, y);
            }
            
        }
    }
    
    // add to dungeon if it doesn't overlap any other rooms
    if (iBestScore < OVERLAP_ROOM) {
        myRoom.pOffset = new Point(pBestPos.x, pBestPos.y);            
        bAddRoom = true;
    }
    
    return bAddRoom;
    
}

private function getCellScore (cellNext:Cell, iDir:int):int {
    
    var iScore:int = 0;
    
    if (cellNext.iType == RoomType.CORRIDOR) {
        if (cellNext.isDeadEnd) {
            iScore += TOUCH_DEADEND;
        } else if (cellNext.hasWall(Dir.getOppositeDir(iDir))) {
            iScore += TOUCH_WALL;
        } else {
            iScore += TOUCH_EMPTY;
        }
    } else {
        if (cellNext.iType == RoomType.ROOM) {
            if (cellNext.hasWall(Dir.getOppositeDir(iDir))) {
                iScore += TOUCH_WALL;
            } else {
                iScore += TOUCH_EMPTY;
            }
        } else {
            iScore += TOUCH_EMPTY;    
        }
    }
    
    return iScore;
    
}

That's ugly and not very fast, but it works.

Some additional info about adding the room to the dungeon: whenever we place a room cell in the dungeon map it's a good idea to check if it overwrites a corridor and if it's a cell on the outer bounds of the room add a new wall to the touching cell (if it's not empty) ...

So far so good, we have rooms in the map, but they cannot yet be reached because we're missing doors ...

Part 3 - part 2 - adding doors and cleaning up

You might ask why I haven't added the doors as soon as I've added the room to the dungeon (and I might just reply that I just didn't mention it), but nope, I didn't add doors - that's the next step.

The reason is quite simple, though. I don't want doors cluttered all over the space and because of that I added another (optional) thing to the room data: hasDoorInDirection ... this way we make sure that there is only one door per wall / room when we add doors ...

Yet again we loop over all rooms and over their outer bounding cells, if we touch another cell, store the current position as possible door location. Then pick a random one per direction and check if it touches another room and if this room might already have a door ...
I guess that's easier to explain with some more code:

private function createDoors (myDungeon:Dungeon, bOneDoorPerRoom:Boolean = true):void {
    
    var rnd:MersenneTwister = MersenneTwister.getInstance();
    
    var i:uint;
    var j:uint;
    
    var x:uint;
    var y:uint;

    var cellTouch:Cell;
    var myRoom:Room;
    
    var aDoor:Array;
    var pDoor:Point;
    var pNext:Point;
    
    for (i = 0; i < myDungeon.aRoom.length; i++) {
        
        myRoom = (myDungeon.aRoom[i] as Room);
        
        aDoor = [[], [], [], []];

        // collect possible door locations ...
        for (y = 0; y < myRoom.iHeight; y++) {
            for (x = 0; x < myRoom.iWidth; x++) {
                
                pDoor = new Point(myRoom.pOffset.x + x, myRoom.pOffset.y + y);
                
                if (y == 0 && pDoor.y > 0) {
                    pNext = myDungeon.getNextPos(pDoor, Dir.NORTH);
                    cellTouch = myDungeon.cell(pNext);
                    if (!cellTouch.isUnused || cellTouch.iType == RoomType.CORRIDOR) {    // the check for a cooridor is needed because they might be just one cell long ...
                        aDoor[Dir.NORTH].push(pDoor);
                        if (cellTouch.isDeadEnd) aDoor[Dir.NORTH].push(pDoor); // double chances for dead ends ...
                    }
                }
                
                if (x == (myRoom.iWidth - 1) && pDoor.x < (myDungeon.iWidth - 1)) {
                    pNext = myDungeon.getNextPos(pDoor, Dir.EAST);
                    cellTouch = myDungeon.cell(pNext);
                    if (!cellTouch.isUnused || cellTouch.iType == RoomType.CORRIDOR) {
                        aDoor[Dir.EAST].push(pDoor);
                        if (cellTouch.isDeadEnd) aDoor[Dir.EAST].push(pDoor); // double chances for dead ends ...
                    }
                }
                
                if (y == (myRoom.iHeight - 1) && pDoor.y < (myDungeon.iHeight - 1)) {
                    pNext = myDungeon.getNextPos(pDoor, Dir.SOUTH);
                    cellTouch = myDungeon.cell(pNext);
                    if (!cellTouch.isUnused || cellTouch.iType == RoomType.CORRIDOR) {
                        aDoor[Dir.SOUTH].push(pDoor);
                        if (cellTouch.isDeadEnd) aDoor[Dir.SOUTH].push(pDoor); // double chances for dead ends ...
                    }
                }
                
                if (x == 0 && pDoor.x > 0) {
                    pNext = myDungeon.getNextPos(pDoor, Dir.WEST);
                    cellTouch = myDungeon.cell(pNext);
                    if (!cellTouch.isUnused || cellTouch.iType == RoomType.CORRIDOR) {
                        aDoor[Dir.WEST].push(pDoor);
                        if (cellTouch.isDeadEnd) aDoor[Dir.WEST].push(pDoor); // double chances for dead ends ...
                    }
                }
            }
        }
        
        // now just pick one door per side ...
        for (j = 0; j < Dir.NUM_BASEDIR; j++) {
            
            if (aDoor[j].length > 0) {
                
                pDoor = aDoor[j][rnd.Range(0, (aDoor[j].length - 1))];
                pNext = myDungeon.getNextPos(pDoor, j);
                
                if (!myRoom.hasDoor(j)) {
                    myRoom.setDoor(j, pDoor);
                    
                    if (bOneDoorPerRoom && myDungeon.cell(pNext).iType == RoomType.ROOM) {
                        myDungeon.getRoom(myDungeon.cell(pNext).iValue).setDoor(Dir.getOppositeDir(j), pNext);
                    }
                    
                    myDungeon.cell(pDoor).setWall(j, WallType.DOOR);
                    myDungeon.cell(pNext).setWall(Dir.getOppositeDir(j), WallType.DOOR);
                }
            }
        }
    }
}

Viola done ... but wait one more thing, cleaning up ...

The last step might not be needed, but imho it makes some nice dungeons: after we've added all the rooms and doors, we remove all remeaning dead ends. This way there will be no corridors just ending somewhere and the map looks nicer.

So we just run the removeDeadEnds method again, this time with 100% ... now:done.

As with the last parts, the link to a working demo of the whole mess is here or here.

nGFX