Welcome to the first of Colin Northway’s “I’m Too Busy Writing Games to Write Technical Blog Posts So I’ll Just Write a Shoddy Technical Blog Post Really Quickly Instead”.
This Shoddy blog post is the first of two on migrating an existing project to Starling and Falsh’s Stage3d. You can find part 2 here.
Well Flash is now all jiggy with the graphics card and even though you started your project a year ago you’re definitely going to want to get all jiggy too. Putting graphics on the graphics card is way faster and you get all those extra CPU cycles to do cool things like physics. Plus you can play with neat things like Pixel Shaders which I’ll talk about in a future post.
This is all based on my experience converting Incredipede and is really just intended to provide a vague roadmap through the experience. This first post will merely be about getting your project swapped over and compiling. The next one will be about making your Stage3d code suck less.
First thing’s first: go download Starling. Starling is a 2d Stage3d framework being written by Daniel Sperl. It wraps the flexible but verbose Stage3d stuff into a Display-List-like interface. It’s open source, Daniel is constantly working on it, the community is growing and helpful, and it’s quite good so it’s prolly the way to go for 2d Flash games from now on. Also, it closely replicates the way Flash did things before Stage3d existed so it’s going to make it much easier to convert your existing project.
To get you going, you’ll need to grab the newest versions of the Flex SDK, either the AIR SDK or playerglobal.swc, and the Starling SDK, point your project at them and add “-swf-version=13” to your compiler options. Read the first part of the quite good Starling Introduction by Thibault Imbert, and come back when you call starling.start();
Now that you have Starling all up and running it’s time to do some mass search and replaces. This would be a really really good time to back up your code. It’s pretty hard to undo the changes we’re going to make so please back it up in case you get half way through and decide I’m an idiot who led you astray.
Now to work: time to grab the Flash display list, yank out the spine, and then stuff Starling down in the hole left behind.
Search and replace the following:
“import flash.display.DisplayObject;” with “import starling.display.DisplayObject;”
“import flash.display.Sprite;” with “import starling.display.Sprite;”
“import flash.display.MovieClip;” with “import starling.display.MovieClip;”
Now everything is going to be broken and you’re going to get a lot of Compile Time errors, mostly where you add graphics to the game. Less’ fix ’em!
There are three ways you might be getting graphics into your game at the moment. You might be embedding images, you might be embedding MovieClips and you might be using the flash 2d graphics drawing API. Incredipede uses all 3 so I’ve got a system in place to deal with all of them. Let’s deal with embedding images and MovieClips first.
Most of your compile time errors are going to be where you .addChild(new Graphic()); because starling’s Sprite.addChild method takes a starling.display.DisplayObject not a flash.display.DisplayObject.
It’s just going to take a little grunt work to make these errors go away. We’re going to do it in a very easy, very bad way and then after you have everything running we’ll go back and make everything suck less (in the next post).
First let’s make up a very simple, static, texture manager class:
public class TextureManager { public static function imageFromSprite(sprite:DisplayObject):Image { var rect:Rectangle = sprite.getBounds(sprite); var image:Image = new Image(textureFromSprite(sprite)); image.x = rect.x; image.y = rect.y; return image; } public static function textureFromSprite(sprite:DisplayObject):Texture { var rect:Rectangle = sprite.getBounds(sprite); if ( rect.width > 2048 ) { rect.width = 2048; trace("Bitmap too big, shrinking"); } if ( rect.height > 2048 ) { rect.height = 2048; trace("Bitmap too big, shrinking"); } //red box means invalid texture if ( rect.width <= 2 || rect.height <= 2 ) { var tempSprite:flash.display.Sprite = new flash.display.Sprite(); tempSprite.graphics.beginFill(0xFF0000); tempSprite.graphics.drawRect(0, 0, 50, 50); sprite = tempSprite; rect = sprite.getBounds(sprite); } var bmd:BitmapData = new BitmapData( rect.width, rect.height, true, 0x0000000000); var matrix:Matrix = new Matrix(); matrix.translate( -rect.x, -rect.y); // draw the shape on the bitmap bmd.draw(sprite, matrix); var texture:Texture = Texture.fromBitmapData(bitmapData); return texture; } }
This takes whatever DisplayObject you just passed in, converts it to a bitmap and then uploads it to the graphics card. Now you can go replace all of your:
addChild(new Graphic())
with this call:
addChild(TextureManager.imageFromSprite(new Graphic()));
That should work for Sprites and MovieClips (alhtough they wont animate) and .PNGs and anything you might embed.
So that will have killed a lot of errors but there are still a lot left. Among them are all those graphics.beginFill calls which now make no sense.
To make those errors go away just draw to an intermediate sprite and pass that to imageFromSprite() like this:
var sp:Sprite = new Sprite(); sp.graphics.stuff sp.graphics.etc... addChild(TextureManager.imageFromSprite(sp));
WARNING: you are now leaking horribly into texture memory. Read part 2 if you are interested in how I stopped this.
Now all your graphics problems should be gone. You’re close to having things at least running. Well, if all those Event errors weren’t staring at you pretty menacingly you’d be close.
Starling replaces the Flash event model with its own. I’m not totally in love with this decision but whatever, we can work with it. More search and replace!
replace “import flash.events.Event;” with “import starling.events.Event;”
Now all your event classes extend Starling.event.Event so you have to go remove all the clone() methods from them. That’s one upside to Starlings events. One of the down sides is that all your MouseEvents just stopped working.
That sucks pretty hard right? Well, if you step into this back alley I have some candy you might like. Just don’t tell anyone you got this from me…
Starling handles mouse events in a totally different way than Flash. Which is too bad because Incredipede was written to use Flash’s mouse events and changing the way everything works is going to kind of suck. But if we put this method in a Utils class:
public static function addMouseListener(mouseEvent:String, target:starling.display.DisplayObject, callback:Function):void { target.addEventListener( TouchEvent.TOUCH, function( event:TouchEvent ):void { var touch:Touch = event.getTouch(target.stage); var newEvent:MouseEvent; if ( mouseEvent == MouseEvent.MOUSE_DOWN && touch.phase == TouchPhase.BEGAN ) { newEvent = new MouseEvent(MouseEvent.MOUSE_DOWN, false); }else if ( mouseEvent == MouseEvent.MOUSE_MOVE && touch.phase == TouchPhase.MOVED ) { newEvent = new MouseEvent(MouseEvent.MOUSE_MOVE, false); }else if ( (mouseEvent == MouseEvent.MOUSE_UP || mouseEvent == MouseEvent.CLICK) && touch.phase == TouchPhase.ENDED ) { newEvent = new MouseEvent(MouseEvent.MOUSE_UP, false); } if ( newEvent != null ) { callback(newEvent); } }); }
Then we can replace
button.addEventListener(MouseEvent.MOUSE_DOWN, loadMain);
with
Utils.addMouseListener(MouseEvent.MOUSE_DOWN, button, loadMain);
Of course this is pretty limited. You can’t get the mouse position from the event or anything (although you could make position work) but it’s a quick and dirty way to make your code compile.
In fact at this point your game should be running! On the graphics card! Really fast! Or actually, you might be uploading a lot of textures to the graphics card every frame depending on how your game works so it might instead be Really slow! Which we’ll fix next time.
I welcome other suggestions if you’ve found better ways to do some of this stuff. Stay tuned for my next post when I discuss “how to make your Stage3d code suck less”. See you then!
I apologize for being a pedantic engineering dweeb that harps on something you surely know, but “back up your code”? We should have a version control party when you’re in town. Git’s super easy to use just locally, and adds much of the benefit of version control even without its distributed aspects.
Sarah and I have a subversion server in BC we back everything up to. You’d have to be truly crazy to keep the only copy of your code on a small laptop sitting on a patio table in Costa Rica.
You never know who migt be reading though.
Thanks for the post, theres a link I think you’ll be interested in but I can’t find it in my bookmarks at the moment. Will come back when I remember it.
You should mention how AMAZING the results look, and post the example you showed me, or link a youtube video of it.
I’d definitely checking this out once I finish my current project.
Hello, thank you for this nice tips.
Can you post an example code?
Thank you
In your TextureManager class it seems that ‘bounds’ is undefined in the textureFromSprite method
You mean Sprite.getBounds is returning null somehow?
No these lines
rect.height = bounds.height;
rect.width = bounds.width;
bounds is never defined anywhere. Am I being a thicko here?!
Oh yeah, you’re right. Those lines shouldn’t be in there. I’ll remove them.
This is hacked out of the more complete texture manager in Part 2: http://northwaygames.com/wp-content/uploads/TextureManager.as
Thanks a lot for this post !
One small thing, in the texture manager :
var texture:Texture = Texture.fromBitmapData(bitmapData);
should be probably
var texture:Texture = Texture.fromBitmapData(bmd);
ok
Hello I need a little help,, the question is:where are migrating starlings I will be very grateful for who help me please help … urgent
Hi, what about using Flash CS6?
I got:
5000: The class ‘Button’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.
In all clases. Help please?