- Motion
Download: BallMotion.zip
原先只是要簡單介紹get/set function以及interface的運用,結果卻寫了一個不小的範例,畫面如下:
球體的產生程式碼如下:
/* Ball.as */
package xinyu.geom{
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.display.DisplayObject;
import flash.display.Shape;
import xinyu.science.Science;
import xinyu.motions.Motions;
import xinyu.motions.CircleMotion2D;
public class Ball extends Sprite{
private var shape:Shape;
public var motion:Motions;
public function Ball(innCol:int, outCol:int, radius:Number = 20, degree:Number = 0){
shape = new Shape();
var fillType:String = GradientType.RADIAL;
var colors:Array = [innCol, outCol];
var alphas:Array = [1, 1];
var ratios:Array = [0x00, 0xAA];
var matr:Matrix = new Matrix();
var spreadMethod:String = SpreadMethod.PAD;
var radian:Number = Science.degToRad(degree);
matr.createGradientBox(4*radius, 4*radius, 0, 0.5*radius*Math.cos(radian), 0.5*radius*Math.sin(radian));
shape.graphics.beginGradientFill(fillType, colors, alphas, ratios, matr, spreadMethod);
shape.graphics.drawCircle(2*radius,2*radius,radius);
shape.graphics.endFill();
shape.x = -2*radius;
shape.y = -2*radius;
addChild(shape);
}
public function setMotion(m:Motions){
if(motion != null){
motion.stop();
removeChild(motion as DisplayObject);
}
motion = m;
addChild(motion as DisplayObject);
}
}
}
主要的重點是將球體繪製在shape裡,setMotion()由設計者自己決定是否要讓motion特效重疊,這裡我設定為單一DisplayObject僅能有一個Motions Object,Motions為一個Interface,為了不要跟fl.motion相衝,所以將名稱宣告為Motions如下:
/* Motions.as */
package xinyu.motions{
public interface Motions {
function run():void;
function stop():void;
function get running():Boolean;
}
}
run()跟stop()是控制motion內的timer的啟動與關閉,而get running()則是回傳timer內的running的值,我設定這三個function為時做Motion必要的函式。
實做Motion的二維球體運動程式碼:
/* CircleMotion2D.as */
package xinyu.motions{
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.TimerEvent
import xinyu.motions.Motions;
public class CircleMotion2D extends Sprite implements Motions{
private var timer:Timer;
private var rad:Number = 0;
private var radius:Number;
private var speed:Number;
private var dir:int;
private var xOffset:Number;
private var yOffset:Number;
public static const CW:int = 1;
public static const CCW:int = -1;
public function CircleMotion2D(xOffset:Number = 0 ,yOffset:Number = 0, radius:Number = 50, speed:Number = 0.1, fps:Number = 25, dir:Number = CircleMotion2D.CW){
this.radius = radius;
this.speed = Math.min(1, Math.max(speed, 0.001));
this.dir = dir;
this.xOffset = xOffset;
this.yOffset = yOffset;
timer = new Timer(1000/fps);
timer.addEventListener(TimerEvent.TIMER, onTimer);
}
private function onTimer(event:TimerEvent):void{
this.parent.x = 100+radius*Math.cos(rad) + xOffset;
this.parent.y = 100+radius*Math.sin(rad) + yOffset;
rad += dir*speed;
if(rad > 2*Math.PI){
rad = 0;
}
}
public function run():void{
timer.start();
}
public function stop():void{
timer.stop();
}
public function get running():Boolean{
return timer.running;
}
}
}
實做Motion的二維自由運動程式碼:
/* SpaceMotion2D */
package xinyu.motions{
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.TimerEvent
import flash.geom.Rectangle;
import xinyu.motions.Motions;
import xinyu.science.Science;
public class SpaceMotion2D extends Sprite implements Motions{
private var timer:Timer;
private var x_dir:int;
private var y_dir:int;
private var speed:Number;
private var range:Rectangle;
public function SpaceMotion2D(range:Rectangle, speed:Number = 0.1, fps:Number = 25){
this.range = range;
this.speed = Math.min(20, Math.max(speed, 0.001));
do{
x_dir = Science.randInt(-1,1);
}while(x_dir == 0);
do{
y_dir = Science.randInt(-1,1);
}while(y_dir == 0);
timer = new Timer(1000/fps);
timer.addEventListener(TimerEvent.TIMER, onTimer);
}
private function onTimer(event:TimerEvent):void{
if(this.parent.x + this.parent.width/2 > range.width){
x_dir = -1;
}
if(this.parent.x - this.parent.width/2 < range.x){
x_dir = 1;
}
if(this.parent.y + this.parent.height/2 > range.height){
y_dir = -1;
}
if(this.parent.y - this.parent.height/2 < range.y){
y_dir = 1;
}
this.parent.x += x_dir*speed;
this.parent.y += y_dir*speed;
}
public function run():void{
timer.start();
}
public function stop():void{
timer.stop();
}
public function get running():Boolean{
return timer.running;
}
}
}
這些運動函式多半會需要用到基本的數學物理的轉換,我把他寫在一個Class底下:
/* Science */
package xinyu.science{
public class Science{
public function Science(){
}
public static function radToDeg(rad:Number):Number{
return rad/Math.PI*180;
}
public static function degToRad(deg:Number):Number{
return deg/180*Math.PI;
}
public static function randInt(from:int = 0, end:int = 100):int{
if(from > end){
var temp:int = end;
end = from;
from = temp;
}
return Math.round(Math.random()*(end-from))-Math.abs(from);
}
}
}
最後在fla裡的第一個影格加上如下程式碼即可編譯執行:
import xinyu.geom.Ball;
import xinyu.motions.CircleMotion2D;
import xinyu.motions.SpaceMotion2D;
var ball1:Ball = new Ball(0xff0000, 0x0000ff, 20, -45);
addChild(ball1);
ball1.x = 100;
ball1.y = 70;
ball1.setMotion(new CircleMotion2D(ball1.x, ball1.y, 100, 0.1, 30, CircleMotion2D.CCW));
ball1.motion.run();
var ball2:Ball = new Ball(0x00ff00, 0x0000ff, 20, -45);
addChild(ball2);
ball2.x = 100;
ball2.y = 100;
ball2.setMotion(new SpaceMotion2D(new Rectangle(0,0,stage.stageWidth, stage.stageHeight), 5, 30));
ball2.motion.run();
這樣的寫法目的是要讓每個物件都可以套用已經設計好的Motion函式,而不必對每一個顯示物件重新設計過動作,如果要增加新的Motion也比較容易點。