Testing Starling performance

Get the work files and final example here

This post is just a quick solution to test the performance of Starling. It build upon the previous one called Getting hooked up with Starling, and just adds a lot of the mice and make them react to the mouse.

The process here is simply:

  1. Initialize Starling
  2. Wait for the stage to be ready
  3. Insert a whole lot of images
  4. Make each image react to their position in relation to the mouse.
  5. See, how it goes

Initializing starling and waiting for the stage, was explained in the other tutorial, so I will discuss the other topics here. This is an example of what you will be doing.

Insert more than one image

When dealing with images, it is generally a good idea to define a custom class for them. This way it is easier to manipulate and retain individual information in the individual images. In this example you should go ahead and add a custom class. You can use the Start project to follow along or even better, complete the previous tutorial and use those files :-)

Right click on the default package and select New > ActionScript Class

 

Call the Class CustomImage and make the Image class (starling.display.Image). Declare two public variables called destX and destY as numbers, so the code should look like this:

package
{
  import starling.display.Image;
  import starling.textures.Texture;

  public class CustomImage extends Image
  {
 public var destX:Number;
 public var destY:Number;
    public function CustomImage(texture:Texture)
    {
      super(texture);
    }
  }
}

These variables are used to store the destination coordinates for each image. They are supposed to move to a random new location from time to time, and these variables are taking care of the values.

Open the StarlStage.as file and declare the following variables:

[Embed(source="assets/mouse.png")]
private var mouseBMP:Class;

private const NUM_MICE:uint = 500;
private var miceVector:Vector.<Image> = new Vector.<Image>(NUM_MICE, true);

private var mouseCoord:Point = new Point(); 
private var maxDist:Number;

First, you create a constant that holds the number of mice you want in the test. Change that to suit your need. Then a vector object is created. The first argument is the number of elements in the vector, and the second argument (set to true) is creating a fixed vector, so it isn’t allowed to change size during runtime. These arguments are optional, and only set for performance reasons.

After that a Point object is created to hold the position of the mouse, and a value to hold the maximum distance there can be between two points on the stage (a diagonal line).

Change the onAdded() method, so it looks like this:

private function onAdded(event:Event):void
  {
    q = new Quad(800, 600, 0x880000);
    addChild(q);
    maxDist = Math.sqrt((stage.stageWidth * stage.stageWidth) + (stage.stageHeight * stage.stageHeight));

    var texture:Texture = Texture.fromBitmap(new mouseBMP());

    for(var i:int = 0; i < NUM_MICE; i++)
    {
      var image:CustomImage = new CustomImage(texture);
      image.pivotX = image.width >> 1;
      image.pivotY = image.height >> 1;
      image.alpha = Math.random();
      image.destX = Math.random()*stage.stageWidth;
      image.destY = Math.random()*stage.stageHeight;
      image.x = Math.random()*stage.stageWidth;
      image.y = Math.random()*stage.stageHeight;
      image.rotation = deg2rad(Math.random()*360);

      addChild(image);
      miceVector[i] = image;
    }
}

The first two lines is the colored background, made from the Quad. Next line uses the Pythagorean Theorem to calculate the diagonal based on the width and height of the stage. The value is stored in maxDist for later use.

Then the texture is created, exactly like in the previous tutorial, and a loop is set up to generate the amount of mice, that is declared in NUM_MICE.

Fir you declare a variable called image as the type CustomImage, which is the class you made earlier. pivotX and pivotY is a nice set of properties that Starlings display objects have. They move the origin point inside the object to a new position. In Flash, you are normally moving the object in a negative direction in order to be able to rotate or scale around the middle of the object; With Starling you can just set the pivot point to be any coordinate in the image. The bit shift to the right (>>) is just a fancy way of divide by 2. I could have entered something like image.height/2 but I am told that the performance of this bit shift is a bit faster, so I-ll stick with that :-)

The rest sets a lot of random numbers in the objects properties (alpha, x, y and rotation). It also sets the public variables inside the CustomImage class, with new random coordinates.

degrees to radian conversion

Pay attention to the rotation property. Starling uses radians, when working with rotation. Fortunately, you can use the little helper deg2rad() in Starling. Just remember to import starling.utils.deg2rad 

Finally the image is added to the stage, and places inside the vector, so it can be referenced later on.

Test the project to see a lot of mice with different position, rotation and transparency on a dark red background.

Getting the coordinates from the mouse

The Starling framework do not work with mouse events, but it has (like Flash) an event called TouchEvent, that takes care of most of your needs. Add a listener called onTouch() and update the constructor so it looks like this:

public function StarlStage()
{
  addEventListener(Event.ADDED_TO_STAGE, onAdded);
  addEventListener(TouchEvent.TOUCH, onTouch);
}

private function onTouch(event:TouchEvent):void
{
  var touch:Touch = event.getTouch(stage);
  mouseCoord = touch.getLocation(stage);
}

In the constructor you listen for the TouchEvent (remember to import the Starling version), and set it to call the onTouch method. A touch event can contain multiple touch, so there is a method called getTouch() and one called getTouches(). Here we just take one single touch from stage, and keep it in the object called called touch. These Touch objects holds all relevant information regarding the specific place it points to. Here you use the method getLocation to retrieve a Point objects, that is store in your own mouseCoord variable for later use.

Make the mice move

Not it’s time to move the mice. Start by registering a listener last in the onAdded() method, so the last line should look like this:

private function onAdded(event:Event):void
{
  ...
  //previous code here
  ...
  stage.addEventListener(Event.ENTER_FRAME, draw);
}

Again, remeber to import Starlings own Event class (starling.events.event), instead of the one you are used to in Flash. Create a method called draw, and enter the following:

private function draw(event:Event):void
{
  var distance:Number;
  var tmpImg:CustomImage;
  var ratio:Number;

  for (var i:int = 0; i < NUM_MICE; i++)
  {
    tmpImg = miceVector[i] as CustomImage;
    tmpImg.x -= (tmpImg.x - tmpImg.destX) * .01;
    tmpImg.y -= (tmpImg.y - tmpImg.destY) * .01;
    if(Math.abs(tmpImg.x - tmpImg.destX) < 1 && Math.abs(tmpImg.y - tmpImg.destY) < 1)
    {
      tmpImg.destX = Math.random()*stage.stageWidth;
      tmpImg.destY = Math.random()*stage.stageHeight;
    }

    distance = Point.distance(mouseCoord, new Point(tmpImg.x, tmpImg.y));

    ratio = distance/maxDist
    tmpImg.rotation += 0.2 * ratio;
    tmpImg.scaleX = tmpImg.scaleY = 1 - ratio;
  }
}

That’s a lot, so let’s take it in small chunks. First you create two variables, one to hold the distance between the mouse and the current image. The other is just a temporary instance of the image made outside the loop, so you don’t fill up the memory with hundreds of local instances. This way, you are using the same object over and over again.

Then a loop is set up, that iterates through your pool of images, contained in the vector called miceVector. Inside the loop you first transfer a reference to the current objects to tmpImg. From now on, you can avoid the time consuming process it is for Flash to look up an object in arrays and vectors – it is also a bit easier to read.

tmpImg.x -= (tmpImg.x - tmpImg.destX) * .01;
tmpImg.y -= (tmpImg.y - tmpImg.destY) * .01;

Then each image is moved a bit closer to it’s destination. This is done, by taking the distance between the two points (current and destination) and multiply it with a low number – in this case .01. If you enter a higher value, they will travel faster, but since it is redrawn 60 times per second (if it can), you are working with very low numbers here. In more complex solutions, I would introduce dedicated tween classes, but this should do it, for a quick test.

if(Math.abs(tmpImg.x - tmpImg.destX) < 1 && Math.abs(tmpImg.y - tmpImg.destY) < 1)
{
  tmpImg.destX = Math.random()*stage.stageWidth;
  tmpImg.destY = Math.random()*stage.stageHeight;
}

Then you test if the distance between current position and destination is below 1. If it is, a new destination point is set in the images destX and destY. This is done, because, you would never reach the exact same values when multiplying with .01.

distance = Point.distance(mouseCoord, new Point(tmpImg.x, tmpImg.y));

This one can be a bit tricky. It uses a stetic method in the Point class called distance, to get the distance between two point objects. it actually just does the same as the calculation of maxDist earlier on. Since tmpImage doesn’t have a Point as a property, you create one. I am not sure, that this is very performance effective, but I included it here, so you can see both methods in practice.

ratio = distance/maxDist
tmpImg.rotation += 0.5 * ratio;
tmpImg.scaleX = tmpImg.scaleY = 1 - ratio;

the last lines takes care of scaling and rotation of the objects. First a ratio between the distance variable and the maximum distance is found. This results in a value between 0 and 1. This value is the multiplied with 0.5 to get a number between 0 and 0.5 for the rotation. Remember, that rotation in Starling is in radians, so 0.5 is actually quite a lot :-)

The it scales both x and y on the image based on the same ratio and the value 1 (which mean 100%). The ratio is subtracted from 1. This makes the images smaller the further away from the mouse it is.

Take it for a spin

Try running the application. Fiddle with the values, to achieve other effect. Try to adjust NUM_MICE, to test the performance. There is certainly room for improvements, but I am pretty impressed with the performance. My machine is taking 1000 objects without blinking – I can’t recall doing that earlier, while using square root, and having semi-transparent images rotating and moving on top of each other- Starling is fun :-)



Leave a Comment