Friday, June 1, 2012

Starling TextField performance issue resolved

Starling is popular pure ActionScript 3 framework which provides a lot of features for creating 2D games or any other graphical applications. We've decided to use this framework in our game as a primary game engine. While creating stopwatch component which uses starling.text.TextField class for rendering text we've faced with performance issue and memory leaks. Issue was resolved by creating new starling.text.TextField instance each time we need to render new time. Sounds strange, but this approach works much faster (without any memory leaks) than just updating text property of existing starling.text.TextField instance. Below describes step-by-step flow which we've discovered during stopwatch component implementation.


As a first step we've created instance of starling.text.TextField class for timer rendering.
timerTxt = new TextField(480, 50, text, "Arial", 80, 0xff0000, true);
timerTxt.autoScale = true;
timerTxt.pivotX = timerTxt.width/2;
timerTxt.x = stageWidth/2;
timerTxt.y = stageHeight/2;
timerTxt.hAlign = HAlign.LEFT;
timerTxt.vAlign = VAlign.CENTER;
addChildAt(timerTxt, 0);
The next thing what we did is added animation for our stopwatch component. Starling framework allows create different animations in a very simple way.
tween = new Tween(this, 60);
tween.delay = 0.25;
tween.animate("currentTime", 6000);
tween.onStart = function():void{_currentTime = 0;};
Starling.juggler.add(tween);
Logically the next thing will be handling changes of the timer (by invoking currentTime setter function) and update text property of already created starling.text.TextField instance.
private var _currentTime:Number;

public function get currentTime():Number
{
 return _currentTime;
}

public function set currentTime(value:Number):void
{
 _currentTime = value;
 var seconds:String = int(_currentTime/100).toString();
 seconds =  seconds.length < 2 ? "0" + seconds : seconds;
 var miliseconds:String = int(_currentTime%100).toString();
 miliseconds = miliseconds.length < 2 ? "0" + miliseconds : miliseconds;
 var text:String = seconds + ":" + miliseconds;
 
 // Update timer text field
 timerTxt.text = timerTxt;
}
And on this step by profiling our application we've faced with performance issue and memory leaks. Memory 133Mb and continuously growing.

We decided to create new starling.text.TextField instance on each timer update.
public function set currentTime(value:Number):void
{
 _currentTime = value;
 var seconds:String = int(_currentTime/100).toString();
 seconds =  seconds.length < 2 ? "0" + seconds : seconds;
 var miliseconds:String = int(_currentTime%100).toString();
 miliseconds = miliseconds.length < 2 ? "0" + miliseconds : miliseconds;
 var text:String = seconds + ":" + miliseconds;
 
 // Delete old timer text field
 if(timerTxt)
 {
  timerTxt.dispose();
  removeChild(timerTxt);
 }
 
 // Add new timer text field
 timerTxt = new TextField(480, 50, text, "Arial", 80, 0xff0000, true);
 timerTxt.autoScale = true;
 timerTxt.pivotX = timerTxt.width/2;
 timerTxt.x = stageWidth/2;
 timerTxt.y = stageHeight/2;
 timerTxt.hAlign = HAlign.LEFT;
 timerTxt.vAlign = VAlign.CENTER;
 addChildAt(timerTxt, 0);
}
And vu a la, no memory leaks and performance looks pretty good. Memory constantly 24Mb.

Additionally call to the system garbage collector was added on the final phase.
System.gc();

We've used this approach in our first game.

Optimized approach (please use this one for your games/applications).
In our next game we've decided to use Bitmap Fonts approach - optimized from the performance standpoint of view. In this case we just updating text property of existing starling.text.TextField instance. And it works beautifully.

Please share your thoughts below.

1 comment:

  1. very informative article.And very well explained about different protocols.
    keep posting like good content posts.For more details about oracle fusion please visit our website.
    Oracle Fusion Training Institute

    ReplyDelete