Flexperiment 1: Physics of a Bouncing, throwable ball.
Here is a simple bit of code that demonstrates simulated physics of a ball being thrown (written with my colleague Anthony http://www.creatistblog.com/):
The keystone of this experiment is the ENTER FRAME event function that tracks the motion of the ball, applying horizontal 'drag' and vertical 'gravity' as well as spelling out the boundaries of the ball's movement area. A simple hack forces the ball to stop in its tracks when it is at a certain speed and distance from the 'ground' or bottom of the screen. Understanding the physics of something like a bouncing ball helps me build on top of it with more features (such as a true collision detection system, or adding wind).
package { import flash.display.Graphics; import flash.display.Sprite; import flash.events.*; [ SWF( frameRate="60", width="420", height="420", backgroundColor="0x000000" ) ] public class BouncingBall extends Sprite { private var maxSpeed:Number = 100; private var radius:Number = 25; private var vx:Number; private var vy:Number; private var ball:Sprite; private var previousMouseX:Number=0; private var previousMouseY:Number=0; private var currentMouseX:Number; private var currentMouseY:Number; private var throwX:Number; private var throwY:Number; public function BouncingBall() { ball = new Sprite(); ball.buttonMode=true; ball.addEventListener(MouseEvent.MOUSE_DOWN, ballDownHandler); ball.addEventListener(MouseEvent.MOUSE_UP, ballUpHandler); addChild( ball ); ball.x = Math.random() * ( stage.stageWidth - radius ); ball.y = Math.random() * ( stage.stageHeight - radius ); vx = Math.random() * maxSpeed + 0.5; vy = Math.random() * maxSpeed + 0.5; var g:Graphics = ball.graphics; g.beginFill( 0xFF0000 ); g.drawCircle( 0, 0, radius ); g.endFill(); addEventListener( Event.ENTER_FRAME, onClipEnterFrame, false, 0, true ); } private function onClipEnterFrame( p_event:Event ):void { currentMouseX = this.mouseX; currentMouseY = this.mouseY; ball.x += vx; ball.y += vy; if ( vy != 0 ) vy += 0.5; if ( vx != 0 ) vx += (0-vx)*0.015; if ( ( ball.x <= radius && vx < 0 ) || ( ball.x >= stage.stageWidth - radius && vx > 0 ) ) { vx = -vx * 0.6; } if ( ( ball.y <= radius && vy < 0 ) || ( ball.y >= stage.stageHeight - radius && vy > 0 ) ) { vy = -vy * 0.6; } if ( Math.abs( vx ) < 0.01 ) vx = 0; if ( Math.abs( vy ) < 0.1 && ball.y>=stage.stageHeight-radius-2){ vy = 0; ball.y=stage.stageHeight-radius; } throwX = currentMouseX - previousMouseX; throwY = currentMouseY - previousMouseY; previousMouseX = currentMouseX; previousMouseY = currentMouseY; } public function ballDownHandler(e:MouseEvent):void{ ball.startDrag(true); vy = vx = 0; } public function ballUpHandler(e:MouseEvent):void{ ball.stopDrag(); vx = throwX; vy = throwY - 0.6; } } }
Tween Class Tweens Freezing Halfway Through Animation
When creating multiple Tween instances using the Flash Tween classes, animations randomly freeze.
Error type: Bug
Error class: -
How to Induce error:
Using the built-in Flash Tween classes, create multiple tweens on one or more objects.
When these tweens are all firing , Flash randomly 'cleans up' the tween instance while it is still animating. In effect, the animation appears to freeze.
Further tween instances can unfreeze the animation, but clearly this is too annoying to ignore!
Solution:
We can store the tween instance in an Array, and clear the instance when the tween is no longer required. This forces the Flash garbage collector to stop destroying your tween instance:
var tweensArray:Array = []; var tween_count:Number = 0; function tweenFunc():void{ tweensArray[tween_count] = new Tween(myObj, "alpha", Strong.easeOut, myObj.alpha, 1, 0.5, true); tween_count++; }
//tween class has FREEZING bug, it removes tweens before they complete when many tweens occur - this is RANDOM.
//in order to stop the removal, we store the tweens in an array, and wipe them out on each changeContent call.
//remove this fix at your own risk!
if(currentContentNumber == showWhichNumber){
tweensArray[tween_count] = new Tween(this['content'+ (showWhichNumber).toString()], "alpha", Strong.easeOut, this['content'+ (showWhichNumber).toString()].alpha, 1, 0.5, true);
tween_count++;
}else{
tweensArray[tween_count] = new Tween(this['content'+ (showWhichNumber).toString()], "alpha", Strong.easeOut, this['content'+ showWhichNumber].alpha, 0, 0.5, true);
tween_count++;
}
}
Interface Programming, using ‘implements’ instead of ‘overrides’
Suppose we are a Beastmaster, and have created a class MonsterFarm whose list will contain several species of fantastic creatures. We want to create a Centaur and Human class, and populate the farm with one instance of each:
Human:
package { public class Human { public function Human() { //human here } } }
Centaur:
package { public class Centaur { public function Centaur() { //Centaur here } } }
MonsterFarm:
package { public class MonsterFarm { var monsters:Array = []; public function MonsterFarm() { trace('monster farm is open!'); var centaur:Centaur = new Centaur(); var human:Human = new Human(); monsters.push(centaur); monsters.push(human); } } }
We want to be able to call upon all the creatures within the farm to speak, so both the Human and Centaur will need a 'speak' method. Additionally, the farm will need to treat all creatures similarly, without having to know their Type.
The most straightforward solution would be to extend both the Human and Centaur classes from a common class like Creature, and then move the speak function into that class.
The problem now is that Centaurs and humans speak differently, so we'd probably create another speak() method in both the Centaur and human classes that override the method in Creature.
This is all well and good, but what happens if Centaur didn't extend from Creature, but instead extended from Monster (which in turn extends from EnemyCreature)? Since classes can only extend from a single class, we would need to work out an elaborate encapsulation and overriding method chain for all the speak-able creatures. Worse still, when we call all our creatures to speak() in the farm, we may no longer be able to call all Creatures anymore, but instead call their common parent which can only safely be assumed as Object.
Enter interfaces!
Unlike classes, interfaces define their methods and not their implementation, which means that it is up to each class to implement the methods themselves. This means that a class can implement multiple interfaces.
For our Centaur and Human, we create a new interface named ICharacterActions which will contain a speak() method:
package { public interface ICharacterActions { function speak(); } }
Both the Centaur and Human classes can now 'implement' this interface, and promise to supply its methods similarly to an override (without the 'override' syntax). These must also be public methods!
Human:
package { public class Human implements ICharacterActions { public function Human() { } public function speak() { trace('Good Morrow!'); } } }
Centaur (note that this is extending from a class unrelated to Human!):
package { public class Centaur extends Monster implements ICharacterActions { public function Centaur() { } public function speak() { trace('neigh!'); } } }
Although the Centaur and Human classes have different parents, the MonsterFarm can still call them all to speak() by simply to their common interface: ICharacterActions
//in MonsterFarmfor each(var monster:ICharacterActions in monsters) { monster.speak(); } //traces: //neigh! //Good Morrow!
With interfaces, we have opened up a myriad of possibilities for the MonsterFarm - we can populate it with any kind of creature, with only the speakable ones implementing the respective interface. We can also add additional interfaces and refine them further like 'ISpeak', 'IFight' so that speaking creatures need not also be fighting creatures, and vice-versa. The performance is also much quicker than overrides.
The catch?
Implementing an interface is like setting a contract - a creature that implements the ICharacterActions interface must contain all the methods defined in the interface or it will cause an error:
1044: Interface method * in namespace * not
implemented by class *.
Methods in interfaces must not have any curly braces '{}' that signify their implementation, so no 'default code' can be executed here (which doesn't matter, because you need to implement the method to avoid the 1044 error above anyway).
For a working example of today's files, download the 'Interface Programming Example' file from the files page.
Finding the base URL of your Flash
One of the most useful 'basic' tools of a Flash developer is to be able to find the URL of your Flash application, and it is very simple:
//in timeline trace(loaderInfo.url); //this returns the path of the Flash application stop();
The reason it is useful is because you can make your application behave differently depending on which environment it is in.
Your application may be expecting a flashvar which is only available if the Flash was embedded in an HTML. In order to test your functionality within the Flash IDE instead of creating two separate projects, you create some logic based on the URL of the movie:
//in timeline import flash.display.LoaderInfo; var myVar:String; <pre>var paramObj:Object;</pre> if(loaderInfo.url.indexOf('file://')==0){ //if the URL begins with 'file://' it means we're working locally //locally, flashvar is not going to be available //we'll give it a default value myVar = 'foo'; }else{ //we assume that not working locally means we're working live //get flashvar from HTML paramObj = LoaderInfo(root.loaderInfo).parameters; //set myVar to equal myLiveVar from the flashvar myVar = paramObj.myLiveVar; } stop();