- 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也比較容易點。