It’s been a while since we last talked about recording your screen on iOS. You may remember our tutorial on how to capture the Starling stage in a video file. In that tutorial you see how to record the whole screen or a DisplayObject that is attached to the scene and is visible.
In a recent conversation with Amos Laber a question came up: can one record a video of Starling content that is not attached to the stage at all, while the screen is showing something else?
As you can imagine throwing a developer an innocent question like this will turn his curiosity level at max. So this post is the result of our efforts to see if we could teach an old ANE to do new tricks.
Thank you, Amos!
The tutorial in a nutshell
First, this is what we start with:
- we have an already prepared animation, composed of Starling DisplayObject that you want to export to a video file and store it on your iOS device;
- we have an ANE that lets you capture the screen as a series of bitmaps over time and compose a video file out of these.
To get this to work for an invisible Starling DisplayObject, here is the basic idea:
- Create a Sprite that is not attached to the stage.
- Add every Starling display objects you want in the video to that sprite.
- Render the sprite off-screen as Bitmap.
- Send the bitmap to the gameplay recorder ANE in the captureCallback() function that the ANE calls in your app.
Simple, eh?
Time to roll up our sleeves and get down to the code. Ready, steady… GO!
Time:
5-10 minutes
- Step 1: Project Setup
- Step 2: Setup the off-screen sprite
- Step 3: Render the sprite to a bitmap
- Step 4: Feed the bitmap to the ANE
- Step 5: What are you waiting for?
Step 1: Project setup
If you have done the Record Starling gameplay video tutorial, you have already done that and have a project set up. If you haven’t, you can find the tutorial here. Go on, I’ll wait.
Ready?
In this tutorial you will add modifications to the Game.as file only.
Step 2: Set up the off-screen sprite
Open Game.as – this is where you will add some new code and create a Sprite that won’t be in the stage’s display hierarchy.
Declare and instantiate the invisible sprite:
1 2 3 4 5 6 7 8 9 10 |
// This won't be added to the stage. This is where you draw your video frames. private var invisibleSprite : Sprite; private function onAdded ( e:Event ):void { invisibleSprite = new Sprite(); //the rest of the function .... } |
You will use the rotating quads example from the previous tutorial, but this time the rectangles in the four corners will be attached to the invisible sprite:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private function onAdded ( e:Event ):void { ... invisibleSprite.addChild ( quads[0] ); ... invisibleSprite.addChild ( quads[1] ); ... invisibleSprite.addChild ( quads[2] ); ... invisibleSprite.addChild ( quads[3] ); ... } |
*Note: the scene setup in the example is pretty minimal and the sprite animation is done in the onEnterFrame() method of the Game class. If you have a complex scene, where each part of the animation is taken care of by a different sprite or method, you may need to fire events by hand in order to trigger the rendering of the animation, for example:
1 |
invisibleSprite.dispatchEvent( new EnterFrameEvent(EnterFrameEvent.ENTER_FRAME, 0.1/*time between two frames in ms*/)); |
Step 3: Render the sprite to a bitmap
Here, to render the sprite’s content to a bitmap, you will use a slightly modified version of the copyAsBitmapData() function proposed by Jeff in the Starling forum:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public static function copyAsBitmapData( bmp : BitmapData, sprite : starling.display.DisplayObject):void { if (sprite == null) { return; } var context:Context3D = Starling.context; var support:RenderSupport = new RenderSupport(); RenderSupport.clear(); support.setOrthographicProjection(0, 0, bmp.width, bmp.height); support.transformMatrix(sprite); support.pushMatrix(); support.transformMatrix(sprite); sprite.render(support, 1.0); support.popMatrix(); support.finishQuadBatch(); context.drawToBitmapData(bmp); } |
Step 4: Feed the bitmap to the ANE
The last thing to do is to ask invisibleSprite to copy itself to a bitmap every time when the gameplay recording ANE callback is executed:
1 2 3 4 |
private function draw3dCallback( bmp : BitmapData ):void { copyAsBitmapData( bmp, invisibleSprite ); } |
Step 5: What are you waiting for?
Go on, test it.
Leave a Reply