Angry Explanations #2: Spinning a freaking image with PBE!
Thursday, November 19th, 2009Following on from Angry Explanations #1, we will now add some motion to the scene. For kicks and giggles, we’ll cause the image to rotate each frame.
(Note that this information is in reference to PushButton Engine build r625.)
The key takeaway here is that for a component to be tickable, it must implement the ITickedObject interface and then tell the ProcessManager to start and stop calling onTick() every tick.
Oh, there’s one other key takeaway here: how to use PropertyReferences. You get a property of a component by way of the owning entity. You create a PropertyReference instance for each data member that you need to access. In this example, we’re accessing the rotation field on the SpriteRenderer component we added in Angry Explanations #1.
In a bit more detail, the PropertyReference is constructed with a special string: "@Sprite.rotation" (see line 11 in the code below). That means, this is a reference to the component of this entity with the name “Sprite” (recall that we gave it that name when we created the SpriteRenderer component instance), and the field “rotation” of the SpriteRenderer class. It’s quite simple once you realize what it’s doing (but I’m no less angry, mind you!).
I thought that a SpatialComponent might be necessary to alter the sprite’s position, but apparently that component has been removed (since r470, which is the current packaged release). That data seems to now be handled by the renderer component itself.
Add this one line to the end of showLogo() from the previous example:
spriteEntity.addComponent( new SpinController(), "spinner" );
Then add the following new class in a sub-folder (components/controllers/).
package com.yourcompany.yourgame.components.controllers
{
import com.pblabs.engine.core.ITickedObject;
import com.pblabs.engine.core.ProcessManager;
import com.pblabs.engine.entity.EntityComponent;
import com.pblabs.engine.entity.PropertyReference;
public class SpinController extends EntityComponent implements ITickedObject
{
// Cache the PropertyReference instance here to avoid a bunch of temporary allocations.
private var _rotProp:PropertyReference = new PropertyReference( "@Sprite.rotation" );
// Nothing to do in the constructor because this is such a simple example.
public function SpinController()
{
super();
}
// When the component is added to the sprite entity, we want to register to
// get tick updates by the ProcessManager.
protected override function onAdd():void
{
super.onAdd();
ProcessManager.instance.addTickedObject( this );
}
// When the component is removed from the sprite entity, we want to unregister
// from the ProcessManager so tick updates will stop.
protected override function onRemove():void
{
super.onRemove();
ProcessManager.instance.removeTickedObject( this );
}
// Each tick, we just change the rotation of the sprite renderer a little bit.
public function onTick(tickRate:Number):void
{
// Get the current rotation.
var rotation:Number = owner.getProperty( _rotProp ) as Number;
// Add 1 degree to it.
rotation += 1;
// Store the new rotation back in the sprite.
owner.setProperty( _rotProp, rotation );
}
}
}
As an addendum, I should point out that the rotation will probably not be smooth. To fix that, you should have the SpinController implement IAnimatedObject instead of ITickedObject. The registration with the ProcessManager is the same, except you call addAnimatedObject() instead of addTickedObject().
The difference is that IAnimatedObject will update when Flash draws a frame instead of when the ProcessManager ticks, and the motion will appear a lot smoother.
And you’ll want to normalize the rotation based on delta time since the last frame. Er, like this:
public function onFrame( elapsed:Number ):void
{
var rotation:Number = owner.getProperty( _rotProp ) as Number;
// Rotate 15 degrees per second, nice and slow.
rotation += 15 * elapsed;
owner.setProperty( _rotProp, rotation );
}