We have a beautiful orange circle, play with its x and y coordinates. If you are bored, we should do something more exciting, let's move the ball.
Create a new class, File menuitem -> New -> Actionscript Class, name it MovingBall, press Enter. MovingCircle.as appeared in Navigator view, and in the source editor also. This class will contain our moving circle-related code.
package
{
import flash.display.Shape;
import flash.events.Event;
public class MovingCircle extends Shape
{
public var xspeed:Number;
public var yspeed:Number;
public function MovingCircle ( )
{
// graphics is an inherited property from Shape
graphics.beginFill( 0xff9933, 1 );
graphics.drawCircle( 0 , 0 , 40 );
}
// initialization, called after parent addChild
public function init ( ):void
{
// x , y and stage are inherited properties
x = Math.random( ) * ( stage.stageWidth );
y = Math.random( ) * ( stage.stageHeight );
xspeed = Math.random( ) * 10;
yspeed = Math.random( ) * 10;
// start step triggering function based on enterframe event
addEventListener( Event.ENTER_FRAME , step );
}
public function step ( event:Event ):void
{
// bounce ball at stage edges
if ( x + xspeed > stage.stageWidth ) xspeed *= -1;
else if ( x + xspeed < 0 ) xspeed *= -1;
if ( y + yspeed > stage.stageHeight ) yspeed *= -1;
else if ( y + yspeed < 0 ) yspeed *= -1;
// set position
x += xspeed;
y += yspeed;
}
}
}
We extend it from Shape class, because we want drawing inside us, and we need repositioning. We have two public properties: x and y component of our speed.
The public function MovingCircle is our constructor, this function runs once when we instantiate the class. Constructor functions don't have returning values, because the returning value of an instantiation is the instance itself, we cannot pass back anything else. But every other function should have a returning value, or void, if it doesn't return a value, like init and step function above. Returning values should be defined after the function name and brackets with a duble dot. Variable types also should be defined by the same way.
In the constructor our orange circle is drawn. In function init, we randomize our starting speed and position, and initialize an event listener for triggering our step function, and function step is for bouncing our ball at stage edges.
Save MovingCircle.as and switch back to FirstCircle.as, a few modifications have to be done here.
package
{
import flash.events.Event;
import flash.display.Sprite;
public class FirstCircle extends Sprite
{
public function FirstCircle()
{
stage.addEventListener( Event.ENTER_FRAME , init );
}
public function init ( event:Event ):void
{
stage.removeEventListener( Event.ENTER_FRAME , init );
var circle:MovingCircle = new MovingCircle( );
addChild( circle );
circle.init( );
}
}
}
In function init we instantiate one MovingCircle class, add it to the display list, and init it.
Run the program, if everything is ok, you should see an orange bouncing ball.
Testing our program in the browser is a little bit annoying, let's use the stand-alone debug player shipped with flex. Project menuitem - Properties - Actionscript compiler menuitem, under HTML wrapper uncheck generate HTML wrapper file, press OK twice, so flex won't start our browser any more.
Let's examine this program deeper, and a few tricks come to light. Why do we have to use an extra init function in MovingCircle class, when we could generate our speed and position in the constructor also? Well, that's an annoying issue of flash player. Until a displayobject is not attached to a display list, its stage property is null, so we couldn't reach it to generate our position, and we couldn't attach any object to the display list before its instantiation. So, we have to instantiate it first, then attach it to display list, then call init function from its parent object.
And why do we have to wait for the first enterframe event of the stage in the main class? This is the other very-very annoying thing of flash player, until the first enterframe event, the stage's width and height is zero, and we cannot use it to calculate a random position.
The other interesting thing is event ( and error ) handling. As you see in the constructor, we initialize an event handler which listens for enterframe events. AS3 has a quite advanced event and error handling model, events can bubble through objects, hold properties, and so on. Errors are special events, they are generated when a runtime exception appears, for example, when we cannot reach an object's property.
Try it. Move the code from init function to the constructor of MovingCircle class, and encapsulate it in a try - catch - finally statement like this:
try
{
x = Math.random( ) * ( stage.stageWidth );
y = Math.random( ) * ( stage.stageHeight );
xspeed = Math.random( ) * 10;
yspeed = Math.random( ) * 10;
}
catch ( error:Error )
{
trace( "Cannot randomize properties, Error: " + error.message );
trace( "I rather randomize the values by myself" );
x = Math.random( ) * 400;
y = Math.random( ) * 400;
xspeed = Math.random( ) * 10;
yspeed = Math.random( ) * 10;
}
finally
{
trace( "Well, the properties are defined" );
}
And run the program. The movie runs smoothly, the circle is moving, but examine the debug console of flex:
Cannot randomize properties, TypeError: Error #1009: Cannot access a property or method of a null object reference.
I rather randomize the values by myself
Well, the properties are defined
So, it runned into the stage == null problem, but our code caught this error, and randomized the numbers from constants.
You can insert as many catch in a "try - catch - finally" statement as many error types you want to listen to.
Play with the code a little : with a for loop create more instances.
FirstCircle.as:
package
{
import flash.events.Event;
import flash.display.Sprite;
public class FirstCircle extends Sprite
{
public function FirstCircle()
{
for ( var a:int = 0 ; a < 10 ; a++ )
{
var circle:MovingCircle = new MovingCircle( );
addChild( circle );
circle.init( );
}
}
}
}
Fantastic, isnt it?