2011年7月27日 星期三

Box2D flash tutorial III–add MovieClip

繼上一篇我們簡單的試範了如何在Box2D的world建立可動及固定的物件

並利用Box2d內建的Debug用的Shape在晝面上顯了結果

在實務上不會是只用那些簡單的形狀就要發佈實作品

這一篇我們來說明如何把設計的圖連結及Box2D的物件

前一篇我們為了簡化Box2D的基本結構未加上任何的使用者操作

這一篇我們加個Space按鈕,按一下我們就增加一個動態的物件(請先focus在flash上)

先來看一下成果(圖片未做去背處理)

程式如下:

  1: package
  2: {
  3: 	import Box2D.Collision.Shapes.b2CircleShape;
  4: 	import Box2D.Collision.Shapes.b2PolygonShape;
  5: 	import Box2D.Common.Math.b2Vec2;
  6: 	import Box2D.Dynamics.Contacts.b2CircleContact;
  7: 	import Box2D.Dynamics.Joints.*;
  8: 	import Box2D.Dynamics.Joints.b2LineJointDef;
  9: 	import Box2D.Dynamics.b2Body;
 10: 	import Box2D.Dynamics.b2BodyDef;
 11: 	import Box2D.Dynamics.b2DebugDraw;
 12: 	import Box2D.Dynamics.b2FixtureDef;
 13: 	import Box2D.Dynamics.b2World;
 14: 	
 15: 	import assets.BlueBirds;
 16: 	import assets.RedBird;
 17: 	
 18: 	import flash.display.MovieClip;
 19: 	import flash.display.Sprite;
 20: 	import flash.events.Event;
 21: 	import flash.events.KeyboardEvent;
 22: 	import flash.ui.Keyboard;
 23: 	
 24: 	public class Box2DMovieClip extends Sprite
 25: 	{
 26: 		private var _world:b2World;
 27: 		private var scale:uint = 1;
 28: 		
 29: 		public function Box2DMovieClip()
 30: 		{
 31: 			super();
 32: 			this.stage.addEventListener(KeyboardEvent.KEY_DOWN,function(event:KeyboardEvent):void{
 33: 				switch(event.keyCode){
 34: 					case Keyboard.SPACE:
 35: 						dynamicRect();
 36: 						break;
 37: 				}
 38: 			});
 39: 			this.addEventListener(Event.ENTER_FRAME,function():void{
 40: 				_world.Step(1/30,10,10);
 41: 				_world.ClearForces();
 42: 				//_world.DrawDebugData();
 43: 				
 44: 				updateDisplayList();
 45: 			});
 46: 			
 47: 			this._world = new b2World(new b2Vec2(0,30),true);
 48: 			
 49: 			//現在套上實際的Shape 故這裡就用不到了
 50: 			/*var dbgDraw:b2DebugDraw = new b2DebugDraw();
 51: 			dbgDraw=new b2DebugDraw();
 52: 			dbgDraw.SetSprite(this);
 53: 			//dbgDraw.SetDrawScale(30.0);
 54: 			dbgDraw.SetFillAlpha(0.5);
 55: 			dbgDraw.SetLineThickness(1.0);
 56: 			dbgDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
 57: 			
 58: 			_world.SetDebugDraw(dbgDraw);*/
 59: 			
 60: 			this.fixRect();
 61: 			this.dynamicRect();
 62: 		}
 63: 		
 64: 		private function fixRect():void
 65: 		{
 66: 			var bird:RedBird = new RedBird();
 67: 			this.addChild(bird);
 68: 			
 69: 			var bd:b2BodyDef=new b2BodyDef();
 70: 			bd.position.Set(150, 100);
 71: 			bd.userData = bird
 72: 			
 73: 			var rect:b2PolygonShape=new b2PolygonShape();
 74: 			rect.SetAsBox(1, 1);
 75: 			
 76: 			var body:b2Body=_world.CreateBody(bd);
 77: 			
 78: 			var fd:b2FixtureDef=new b2FixtureDef();
 79: 			fd.shape=rect;
 80: 			fd.friction=0.3;
 81: 			fd.density=0;
 82: 			
 83: 			body.CreateFixture(fd);
 84: 		}
 85: 		
 86: 		private function dynamicRect():void
 87: 		{
 88: 			var bird:BlueBirds = new BlueBirds();
 89: 			this.addChild(bird);
 90: 			
 91: 			var x:Number = Math.random() *1000;
 92: 			
 93: 			var bd:b2BodyDef=new b2BodyDef();
 94: 			bd.position.Set(x, 50);
 95: 			bd.type=b2Body.b2_dynamicBody;
 96: 			bd.userData =bird;
 97: 			
 98: 			var rect:b2PolygonShape=new b2PolygonShape();
 99: 			rect.SetAsBox(1, 1);
100: 			
101: 			var body:b2Body=_world.CreateBody(bd);
102: 			
103: 			var fd:b2FixtureDef=new b2FixtureDef();
104: 			fd.shape=rect;
105: 			fd.friction=0.3;
106: 			fd.density=0;
107: 			
108: 			body.CreateFixture(fd);
109: 		}
110: 		
111: 		private function updateDisplayList():void
112: 		{
113: 			for (var bb:b2Body=_world.GetBodyList(); bb; bb=bb.GetNext())
114: 			{
115: 				var viewComp:Sprite=bb.GetUserData() as Sprite;
116: 				if (viewComp)
117: 				{
118: 					viewComp.x=bb.GetPosition().x;
119: 					viewComp.y=bb.GetPosition().y;
120: 					viewComp.rotation=bb.GetAngle() * (180 / Math.PI);
121: 				}
122: 			}
123: 		}
124: 
125: 	}
126: }








這次我們因為要用到實際的設計,所以我先把DebugSprite先註解掉(當然不註解掉仍然可以跑,基於效能問題,建議還是若己經實際的設計,就不要再放上了)   line 42 及line50~58









先在fixeRect及dynamicRect的function中加上我們的在flash定義的Symbol(class)並需要先將其加到場景上,不需去設定座標,因為接下的function可以讓Box2D跟我們的設計物件做連動









接著我們在EnterFrame事件裡除了Box2D世界的滾動的呼叫外,最重的就是Box2D跟設計物件連動的function updateDisplayList









加上這個function後即可看到設計物件如何被Box2D帶著走了









P.S.其中line 15~16的類別是定義在flash裡面的,讀者可自行在flash定義自己喜歡的圖片

2011年7月15日 星期五

Box2D flash tutorial II–Add shape

接著我們來我的世界中加上二個物件(Shape)

一個是固定的(b2_staticBody),一個是被地心引力往下吸的(b2_dynamicBody)

首先先來看一下程式

  1: package
  2: {
  3:     import Box2D.Collision.Shapes.b2PolygonShape;
  4:     import Box2D.Common.Math.b2Vec2;
  5:     import Box2D.Dynamics.b2Body;
  6:     import Box2D.Dynamics.b2BodyDef;
  7:     import Box2D.Dynamics.b2DebugDraw;
  8:     import Box2D.Dynamics.b2FixtureDef;
  9:     import Box2D.Dynamics.b2World;
 10: 
 11:     import flash.display.Sprite;
 12:     import flash.events.Event;
 13: 
 14:     public class Initialization extends Sprite
 15:     {
 16:         private var _world:b2World;
 17:         private var scale:uint=30;
 18: 
 19:         public function Initialization()
 20:         {
 21:             super();
 22:             this.addEventListener(Event.ENTER_FRAME, function():void
 23:             {
 24:                 _world.Step(1 / 30, 10, 10);
 25:                 _world.ClearForces();
 26:                 _world.DrawDebugData();
 27:             });
 28: 
 29:             this._world=new b2World(new b2Vec2(0, 1), true);
 30: 
 31: 
 32:             var dbgDraw:b2DebugDraw=new b2DebugDraw();
 33: 
 34:             dbgDraw=new b2DebugDraw();
 35:             dbgDraw.SetSprite(this);
 36:             dbgDraw.SetDrawScale(30.0);
 37:             dbgDraw.SetFillAlpha(0.5);
 38:             dbgDraw.SetLineThickness(1.0);
 39:             dbgDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
 40: 
 41:             _world.SetDebugDraw(dbgDraw);
 42: 
 43:             this.fixRect(); //add fix shape
 44:             this.dinamicRect(); //add dynamic shape
 45:         }
 46: 
 47:         private function fixRect():void
 48:         {
 49:             var bd:b2BodyDef=new b2BodyDef();
 50:             bd.position.Set(10, 10);
 51: 
 52:             var rect:b2PolygonShape=new b2PolygonShape();
 53:             rect.SetAsBox(2, 2);
 54: 
 55:             var body:b2Body=_world.CreateBody(bd);
 56: 
 57:             var fd:b2FixtureDef=new b2FixtureDef();
 58:             fd.shape=rect;
 59:             fd.friction=0.3;
 60:             fd.density=0;
 61: 
 62:             body.CreateFixture(fd);
 63:         }
 64: 
 65:         private function dinamicRect():void
 66:         {
 67:             var bd:b2BodyDef=new b2BodyDef();
 68:             bd.position.Set(5, 5);
 69:             bd.type=b2Body.b2_dynamicBody;
 70: 
 71:             var rect:b2PolygonShape=new b2PolygonShape();
 72:             rect.SetAsBox(2, 2);
 73: 
 74:             var body:b2Body=_world.CreateBody(bd);
 75: 
 76:             var fd:b2FixtureDef=new b2FixtureDef();
 77:             fd.shape=rect;
 78:             fd.friction=0.3;
 79:             fd.density=0;
 80: 
 81:             body.CreateFixture(fd);
 82:         }
 83:     }
 84: }
 85: 


 



在line29我們先把地方引力調小,這樣在往下掉的時侯會掉的慢點



line43加上一個固定的方塊



line44加上一個會隨地心引力的方塊



接著來說明如何把一個物件(shape)加到世界上



要加到世界上主要是有四個動作




  1. b2bodyDef指定其相關資料或屬性 default type:b2_staticBody


  2. 建立 b2XXXShape ,並設定共屬性


  3. 由世界建立一個身體(b2Body)


  4. 設定 b2FixtureDef 的各摩擦等各屬性


  5. 由建立出的身體把固定物(b2FixtureDef )做建立



做完以上的動作即完成物件加到世界上



下一篇我們來把一些自定的flash物件加到整個世界上

Box2D flash tutorial I - Initialization

使用BOX2D由於網上看到很多範例

若沒有促一說明,確實有點點感到複雜

這些說明應該適用於各種不同的版本

那初始主要分為二個部份

  1. 建立世界(b2world),設定其地心引力方向,並設doSleep為true
  2. 重覆執行一步一步(step),並清除力量(force)
  3. 加入測試繪圖資料(b2DebugDraw)

其實初始大概這麼做就夠了,大部份範例主要是使用其內建的繪圖,才會需要第三步驟,實際作業只需要前二步

不過若是只做了前二步,在畫面上是不會有任何,東西在動的,我們先來看一下程式

 

  1: package
  2: {
  3:     import Box2D.Common.Math.b2Vec2;
  4:     import Box2D.Dynamics.b2World;
  5:     import Box2D.Dynamics.b2DebugDraw;
  6: 
  7:     import flash.display.Sprite;
  8:     import flash.events.Event;
  9: 
 10:     public class Initialization extends Sprite
 11:     {
 12:         private var _world:b2World;
 13:         private var scale:uint=30;
 14: 
 15:         public function Initialization()
 16:         {
 17:             super();
 18:             this.addEventListener(Event.ENTER_FRAME, function():void
 19:             {
 20:                 _world.Step(1 / 30, 10, 10);
 21:                 _world.ClearForces();
 22:                 _world.DrawDebugData();
 23:             });
 24: 
 25:             this._world=new b2World(new b2Vec2(0, -30), true);
 26: 
 27: 
 28:             var dbgDraw:b2DebugDraw=new b2DebugDraw();
 29: 
 30:             dbgDraw=new b2DebugDraw();
 31:             dbgDraw.SetSprite(this);
 32:             dbgDraw.SetDrawScale(30.0);
 33:             dbgDraw.SetFillAlpha(0.5);
 34:             dbgDraw.SetLineThickness(1.0);
 35:             dbgDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
 36: 
 37:             _world.SetDebugDraw(dbgDraw);
 38:         }
 39:     }
 40: }
 41: 

 



以上程式算是對物理引擎設置好了基本環境,目前還未加上在世界裡的物件,所以執行時,不會看到任何東西,



物理引擎做的事是在協助我們在畫面上的物作可以做些實際的物理運動,所以當我們做運作時,是會由一些非可視物件關連到



我們的可視物件去做運動的,當我們還沒關連到畫面上的可視物件時,我們就可以先利用DebugDraw(line 28~37)來測試我們希望物理引擎幫我達到的效果(目前試了二個物理引擎,基本都是這樣設計的)



其中28~37為輔助在還未放上所以設計的圖片或DisplayObject時所用的內建繪圖,在整體完成作單時建議是要拿掉的



下個部份我們再來加上一些可運動物件或加一些固定的運件吧



 



2011/07/26 updated



接下來的後續篇及以上所使用的Lib是使用 Box2D 2.0 版的



為了有網友提到可能使用不相同的版本,造成某些資料不同,故提供flex所建的 liberary project及swc給有需的人下載




  1. Library Project:file link


  2. Swc file:file link

2011年7月4日 星期一

Flash Actionscript Physics Engine(APE) III about Roulette

基於上一個拆解  APE的DEMO

右上角的小搖桿,把其想法做了一個   輪子(Wheel)及在上面釘一些點(pin)

並希望這些點可碰撞到一個針   讓這個針可在  被點碰撞到的情況做的小擺動

我們先做一個    由於這些點及輪子會一起連動所以使用了   Composite來把這些解構結合

程式如下:

  1: package comp
  2: {
  3:     import org.cove.ape.*;
  4: 
  5:     import ui.*;
  6: 
  7:     public class Wheel extends Composite
  8:     {
  9:         public function Wheel()
 10:         {
 11:             super();
 12: 
 13:             var wheel:WheelParticle=new WheelParticle(100, 100, 20, true);
 14:             var myWheel:ui.MyWheel;
 15:             wheel.setDisplay(myWheel=new ui.MyWheel());
 16:             wheel.alwaysRepaint=true;
 17:             //wheel.angularVelocity=0.0001;
 18:             //圓轉 結合的釘子不會動
 19: 
 20:             this.addParticle(wheel);
 21: 
 22:             var rCount:uint=0;
 23:             var number:uint=10;
 24:             var r:uint=360 / number;
 25:             for (var i:uint=0; i < number; i++)
 26:             {
 27:                 var X2:uint=Math.cos(rCount / 180 * Math.PI) * 80 + 100;
 28:                 var Y2:uint=Math.sin(rCount / 180 * Math.PI) * 80 + 100;
 29: 
 30:                 rCount+=r;
 31: 
 32:                 var pin:CircleParticle=new CircleParticle(X2, Y2, 2, true);
 33:                 pin.setDisplay(new ui.Pin());
 34:                 pin.alwaysRepaint=true;
 35:                 this.addParticle(pin);
 36:             }
 37:         }
 38:     }
 39: }
 40: 


Line 13~16:使用WheelParticle製作一個 輪子並由flash中指定一個  MovieClip給這個Particle



Line 22~36:在輪子上增加點(pin),目前增加20個點,使用三角涵數環繞擺放,並將flash中的MovieClip指定給各個點



以上即完成我們要的輪子



接著我們要在主場景上,放上一個針,這個針會由一個圓粒子去挓住這個針,讓它不會被地心引力帶著往下掉



  1: //needle start
  2:             var point1:CircleParticle=new CircleParticle(100, 180, 2);
  3:             
  4:             group2.addParticle(point1);
  5: 
  6:             var point2:CircleParticle=new CircleParticle(100, 220, 1, true);
  7:             
  8:             group2.addParticle(point2);
  9: 
 10:             var line:SpringConstraint=new SpringConstraint(point1, point2, 1, false, 0);
 11:             line.setDisplay(new ui.MyNeedle());
 12:             group2.addConstraint(line);
 13:             //needle end
 14: 
 15: 
 16: 
 17:             //Drag line start
 18:             var point_left:CircleParticle=new CircleParticle(100, 165, 0, true);
 19:             
 20:             group2.addParticle(point_left);
 21: 
 22:             var line_left:SpringConstraint=new SpringConstraint(point1, point_left, 0.01, true, 1);
 23:             
 24:             line_left.restLength=2;
 25:             group2.addConstraint(line_left);
 26:             //Drag line end

 



line 2~12為主要的針的解構



line 18~25為挓住針的解構(座標會在針的正上方)



  1: package
  2: {
  3:     import comp.Wheel;
  4: 
  5:     import flash.display.Sprite;
  6:     import flash.events.Event;
  7: 
  8:     import org.cove.ape.*;
  9: 
 10:     import ui.*;
 11: 
 12:     public class MyWheel extends Sprite
 13:     {
 14:         private var wheel:comp.Wheel;
 15: 
 16:         public function MyWheel()
 17:         {
 18:             super();
 19:             this.addEventListener(Event.ENTER_FRAME, run);
 20:             APEngine.init(1 / 4);
 21:             APEngine.container=this;
 22:             APEngine.addForce(new org.cove.ape.Vector(0, 2));
 23: 
 24:             var groupWheel:Group=new Group();
 25:             groupWheel.collideInternal=true;
 26: 
 27:             wheel=new comp.Wheel();
 28: 
 29:             groupWheel.addComposite(wheel);
 30: 
 31: 
 32:             var groupNeedle:Group=new Group();
 33:             groupNeedle.collideInternal=true;
 34: 
 35: 
 36:             //needle start
 37:             var point1:CircleParticle=new CircleParticle(100, 180, 2);
 38:             point1.visible=false;
 39:             groupNeedle.addParticle(point1);
 40: 
 41:             var point2:CircleParticle=new CircleParticle(100, 220, 1, true);
 42:             point2.visible=false;
 43:             groupNeedle.addParticle(point2);
 44: 
 45:             var line:SpringConstraint=new SpringConstraint(point1, point2, 1, false, 0);
 46:             line.setDisplay(new ui.MyNeedle());
 47:             groupNeedle.addConstraint(line);
 48:             //needle end
 49: 
 50: 
 51: 
 52:             //Drag line start
 53:             var point_left:CircleParticle=new CircleParticle(100, 165, 0, true);
 54:             point_left.visible=false;
 55:             groupNeedle.addParticle(point_left);
 56: 
 57:             var line_left:SpringConstraint=new SpringConstraint(point1, point_left, 0.01, true, 1);
 58:             line_left.visible=false;
 59:             line_left.restLength=2;
 60:             groupNeedle.addConstraint(line_left);
 61:             //Drag line end
 62: 
 63:             APEngine.addGroup(groupWheel);
 64:             APEngine.addGroup(groupNeedle);
 65: 
 66:             groupWheel.addCollidable(groupNeedle);
 67:         }
 68: 
 69:         private function run(event:Event):void
 70:         {
 71:             APEngine.step();
 72:             APEngine.paint();
 73:             wheel.rotateByRadian(0.02, new org.cove.ape.Vector(100, 100));
 74:         }
 75:     }
 76: }
 77: 


line 24~29 接下來 把我們做的輪子放上 (groupWheel)



line 32~60 針及拖曳線拼起來(groupNeedle)



line 60   這二個不同的Group知道彼此的存在做碰撞(Collision  detection)



line 73  不停的轉動輪子



P.S Collision detection  only apply for praticle