聲明:歡迎任何人和組織轉載本blog中文章,但必須標記文章原始連結和作者資訊。
本文連結:http://blog.csdn.net/li_007/archive/2009/04/13/4071104.aspx
開拓進取的小烏龜------->CSDN點滴點點滴滴Blog
這段時間在研究Box2DFlash這個開源APE,現在分享一下Box2DFlash中滑鼠實現的方法。在Box2DFlash內建的TestBed中就實現了滑鼠事件和鍵盤事件,其實就是Actionscript 3鍵鼠事件,然後結合一下Box2DFlash。關於怎麼實現滑鼠移動,可以說是對研究APE必須弄清楚的,不然就不要玩APE了。
1、首先肯定就是Actionscript 3的滑鼠監聽事件了,這是基本,不然怎麼交Box2D Flash版啊。在這裡我們需要監聽MOUDSE_DOWN這個事件的,在Box2DFlash中是這樣實現的,當我我們按下滑鼠的時候,以當前滑鼠座標點建立一個Shape(實際上只是一個框框,也就是一個域,範圍),很小很小的shape(0.001有效位);然後遍曆當前World中所有的Body,尋找與剛建立的Shape有重疊的body,返回一個包含這個body的數組(其實只有一個,因為都是body,剛體,這是我的理解);再其次就是迴圈這些body,測試當前滑鼠點是否在此body有效範圍中(也即是確認那個body被點擊了),然後返回合格body,中止迴圈。關於這個功能的實現的GetBodyAtMouse函數實現如下:
private function getBodyAtMouse(includeStatic:Boolean = false):b2Body<br />{<br />// 利用當前滑鼠點位置,建立當前滑鼠向量<br />this._mousePreVector.Set(stage.mouseX / this._phyScale, stage.mouseY / this._phyScale);</p><p>// 利用當前滑鼠向量建立一個很小b2AABB類型的框(感覺叫Shape更好,但其實就是一個範圍,一個域)<br />var _aabb:b2AABB = new b2AABB();<br />_aabb.lowerBound.Set(this._mousePreVector.x - 0.001, this._mousePreVector.y - 0.001);<br />_aabb.upperBound.Set(this._mousePreVector.x + 0.001, this._mousePreVector.y + 0.001);</p><p>// 在World中尋找與剛用滑鼠向量定義的Shape相重疊的Shape,並返回合格所有Shape數組<br />var k_maxCount:int = 10;<br />var _shapes:Array = new Array();<br />var count:int = this._bWorld.Query(_aabb, _shapes, k_maxCount);<br />var _body:b2Body = null;</p><p>// 迴圈數組中的Shape,檢測當前滑鼠是否在當前的Shape所包含的範圍內<br />for (var i:int = 0; i < count; ++i)<br />{<br />// 判斷當前Shape的父Body是否是靜態或者可移動的(這裡要理解b2Body、b2BodyDef和b2Shape<br />// 已經b2Shape子類的關係)。其實查閱源碼會發現GetBody返回的是在b2Shape中儲存的m_body<br />// 這個變數,也就是當前的b2Body對象。<br />if (_shapes[i].GetBody().IsStatic() == false || includeStatic)<br />{<br />trace(_shapes[i]);<br />// 將當前Shape轉化為最初父類b2Shape.<br />var _tShape:b2Shape = _shapes[i] as b2Shape;</p><p>// 測試當前滑鼠向量是否在合格當前Shape範圍內。注意在這裡需要將當前body的座標轉化<br />// 全局座標系,也即當前Sprite繪製情境。<br />var inside:Boolean = _tShape.TestPoint(_tShape.GetBody().GetXForm(), _mousePreVector);<br />if (inside)<br />{<br />// 返回符號條件的body,並終止迴圈。<br />_body = _tShape.GetBody();<br />break;<br />}<br />_tShape = null;<br />}<br />}<br />_aabb = null;<br />_shapes = null;<br />return _body;<br />}
2、當我們得到了當前點擊的body後,通過建立一個b2MouseJoint對象,在這裡翻譯成“滑鼠關節”吧,讓這個滑鼠關節的一個物體為從b2World的GetGroundBody得到的沒有碰撞檢測形狀的靜態body,問另一個body則串連上被點擊的body,然後不但更新這個滑鼠關節的target位置為當前滑鼠位置,這樣就間接實現了物體被滑鼠拖動了。代碼如下:
var _tempBody:b2Body = getBodyAtMouse();</p><p>if (_tempBody)<br />{<br />// 建立滑鼠節點定義<br />var _mouseJointDef:b2MouseJointDef = new b2MouseJointDef();<br />// 設定body1為無碰撞檢測形狀的靜態剛體<br />_mouseJointDef.body1 = this._bWorld.GetGroundBody();<br />// 設定body2為當前被檢測到被點擊的剛體<br />_mouseJointDef.body2 = _tempBody;<br />// 設定滑鼠節點的最初世界位置<br />_mouseJointDef.target.Set(this._mousePreVector.x, this._mousePreVector.y);<br />_mouseJointDef.maxForce = 5000 * _tempBody.GetMass();<br />_mouseJointDef.timeStep = this._timeStep;<br />// 在世界中建立這個b2MouseJoint對象<br />this._mouseJoint = this._bWorld.CreateJoint(_mouseJointDef) as b2MouseJoint;</p><p>// 如果當前body處於休眠狀態,則喚醒。<br />_tempBody.WakeUp();<br />_mouseJointDef = null;<br />_tempBody = null;<br />}
3、好了,然後我們不但更新這個b2MouseJoint對象的座標點就可以了,其實就是隨著時間步中更新。
整個工程代碼如下:(直接建立Actionscript 3的工程,用下面代碼做文檔類即可看到結果)
//<br />// Box2DEvent.as<br />//<br />// the entry of application<br />//<br />// Written by Leezhm, 10st April, 2009<br />// Contact : leezhm@126.com<br />// Last Modified by Leezhm@126.com on 11st April, 2009<br />//</p><p>package Srcs<br />{<br />import Box2D.Collision.b2AABB;<br />import Box2D.Collision.Shapes.b2CircleDef;<br />import Box2D.Collision.Shapes.b2PolygonDef;<br />import Box2D.Collision.Shapes.b2Shape;<br />import Box2D.Collision.Shapes.b2ShapeDef;<br />import Box2D.Common.Math.b2Vec2;<br />import Box2D.Dynamics.b2Body;<br />import Box2D.Dynamics.b2BodyDef;<br />import Box2D.Dynamics.b2DebugDraw;<br />import Box2D.Dynamics.b2World;<br />import Box2D.Dynamics.Joints.b2MouseJoint;<br />import Box2D.Dynamics.Joints.b2MouseJointDef;<br />import flash.events.MouseEvent;</p><p>import flash.display.Sprite;<br />import flash.events.Event;</p><p>public class Box2DEvent extends Sprite<br />{<br />private var _bWorld:b2World;<br />private var _iterations:uint = 10;<br />private var _timeStep:Number = 1.0 / 60.0;</p><p>private var _phyScale:Number = 30.0;</p><p>private var _dbgSprite:Sprite;<br />private var _mouseJoint:b2MouseJoint;<br />private var _mousePreVector:b2Vec2;<br />private var _mouseDown:Boolean = false;</p><p>public function Box2DEvent()<br />{<br />InitWorld();</p><p>addDynamicBodies();</p><p>this.addEventListener(Event.ENTER_FRAME, OnUpdateWorld, false, 0, true);<br />this.addEventListener(MouseEvent.MOUSE_UP, OnMouseUpHandler, false, 0, true);<br />this.addEventListener(MouseEvent.MOUSE_DOWN, OnMouseDownHandler, false, 0, true);<br />}</p><p>private function InitWorld():void<br />{<br />// define a AABB coordination<br />var _bAABB:b2AABB = new b2AABB();<br />_bAABB.lowerBound.Set( -100.0, -100.0);<br />_bAABB.upperBound.Set( 100.0, 100.0);</p><p>// define the gravity vector<br />var _gravity:b2Vec2 = new b2Vec2(0.0, 10.0);</p><p>// allow the bodies to sleep<br />var _bSleep:Boolean = true;</p><p>// create the world object<br />if (null == this._bWorld)<br />{<br />this._bWorld = new b2World(_bAABB, _gravity, _bSleep);<br />}</p><p>// define a b2DebugDraw class object to draw all bodies when you<br />// debug this application<br />var _b2Dbg:b2DebugDraw = new b2DebugDraw();<br />if (null == this._dbgSprite)<br />{<br />this._dbgSprite = new Sprite();<br />this.addChild(this._dbgSprite);<br />}<br />_b2Dbg.m_sprite = this._dbgSprite;<br />_b2Dbg.m_fillAlpha = 0.6;<br />_b2Dbg.m_lineThickness = 0.4;<br />_b2Dbg.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;<br />_b2Dbg.m_drawScale = this._phyScale</p><p>this._bWorld.SetDebugDraw(_b2Dbg);</p><p>// define the ground body<br />var _bodyDef:b2BodyDef = new b2BodyDef();<br />_bodyDef.position.Set(10, 19);</p><p>var _rectangle:b2PolygonDef = new b2PolygonDef();<br />_rectangle.SetAsBox(800 / _phyScale, 50 / _phyScale);<br />_rectangle.friction = 0.5;<br />_rectangle.density = 0;<br />_rectangle.restitution = 0.6;</p><p>var _body:b2Body = this._bWorld.CreateBody(_bodyDef);<br />_body.CreateShape(_rectangle);<br />_body.SetMassFromShapes();</p><p>// left<br />_bodyDef.position.Set( -2, 1);<br />_rectangle.SetAsBox(80 / _phyScale, 800 / _phyScale);<br />_rectangle.restitution = 0.6;<br />_body = this._bWorld.CreateBody(_bodyDef);<br />_body.CreateShape(_rectangle);<br />_body.SetMassFromShapes();</p><p>// right<br />_bodyDef.position.Set( 28, 1);<br />_rectangle.SetAsBox(80 / _phyScale, 800 / _phyScale);<br />_rectangle.restitution = 0.6;<br />_body = this._bWorld.CreateBody(_bodyDef);<br />_body.CreateShape(_rectangle);<br />_body.SetMassFromShapes();</p><p>// top<br />_bodyDef.position.Set( 1, -2);<br />_rectangle.SetAsBox(800 / _phyScale, 80 / _phyScale);<br />_rectangle.restitution = 0.6;<br />_body = this._bWorld.CreateBody(_bodyDef);<br />_body.CreateShape(_rectangle);<br />_body.SetMassFromShapes();</p><p>_bAABB = null;<br />_gravity = null;<br />_dbgSprite = null;<br />_b2Dbg = null;<br />_rectangle = null;<br />_bodyDef = null;<br />_body = null;<br />}</p><p>private function addDynamicBodies():void<br />{<br />for (var i:uint = 0; i < 6; i ++)<br />{<br />var _bodyDef:b2BodyDef = new b2BodyDef();<br />_bodyDef.position.Set(Math.random() * _phyScale, Math.random());</p><p>var _circleDef:b2CircleDef = new b2CircleDef();<br />_circleDef.radius = Math.random() + 1.0;<br />_circleDef.friction = 0.3;<br />_circleDef.density = 3 * Math.random();<br />_circleDef.restitution = 0.8;</p><p>var _body:b2Body = this._bWorld.CreateBody(_bodyDef);<br />_body.CreateShape(_circleDef);<br />_body.SetMassFromShapes();</p><p>_bodyDef = null;<br />_circleDef = null;<br />_body = null;<br />}<br />}</p><p>private function OnUpdateWorld(evt:Event = null):void<br />{<br />this._bWorld.Step(this._timeStep, this._iterations);</p><p>if (this._mouseJoint)<br />{<br />this._mousePreVector = new b2Vec2(stage.mouseX / this._phyScale, stage.mouseY / this._phyScale);<br />this._mouseJoint.SetTarget(this._mousePreVector);<br />}</p><p>for (var bb:b2Body = this._bWorld.m_bodyList; bb; bb = bb.GetNext())<br />{<br />if (bb.m_userData is Sprite)<br />{<br />bb.m_userData.x = bb.GetPosition().x * 30;<br />bb.m_userData.y = bb.GetPosition().y * 30;<br />bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI);<br />}<br />}<br />}</p><p>private function OnMouseUpHandler(evt:MouseEvent = null):void<br />{<br />if (this._mouseJoint)<br />{<br />this._bWorld.DestroyJoint(this._mouseJoint);<br />this._mouseJoint = null;<br />}<br />}</p><p>private function OnMouseDownHandler(evt:MouseEvent = null):void<br />{<br />if (null == this._mousePreVector)<br />{<br />this._mousePreVector = new b2Vec2();<br />}</p><p>var _tempBody:b2Body = getBodyAtMouse();</p><p>if (_tempBody)<br />{<br />// 建立滑鼠節點定義<br />var _mouseJointDef:b2MouseJointDef = new b2MouseJointDef();<br />// 設定body1為無碰撞檢測形狀的靜態剛體<br />_mouseJointDef.body1 = this._bWorld.GetGroundBody();<br />// 設定body2為當前被檢測到被點擊的剛體<br />_mouseJointDef.body2 = _tempBody;<br />// 設定滑鼠節點的最初世界位置<br />_mouseJointDef.target.Set(this._mousePreVector.x, this._mousePreVector.y);<br />_mouseJointDef.maxForce = 5000 * _tempBody.GetMass();<br />_mouseJointDef.timeStep = this._timeStep;<br />// 在世界中建立這個b2MouseJoint對象<br />this._mouseJoint = this._bWorld.CreateJoint(_mouseJointDef) as b2MouseJoint;</p><p>// 如果當前body處於休眠狀態,則喚醒。<br />_tempBody.WakeUp();<br />_mouseJointDef = null;<br />_tempBody = null;<br />}<br />}</p><p>private function getBodyAtMouse(includeStatic:Boolean = false):b2Body<br />{<br />// 利用當前滑鼠點位置,建立當前滑鼠向量<br />this._mousePreVector.Set(stage.mouseX / this._phyScale, stage.mouseY / this._phyScale);</p><p>// 利用當前滑鼠向量建立一個很小b2AABB類型的框(感覺叫Shape更好,但其實就是一個範圍,一個域)<br />var _aabb:b2AABB = new b2AABB();<br />_aabb.lowerBound.Set(this._mousePreVector.x - 0.001, this._mousePreVector.y - 0.001);<br />_aabb.upperBound.Set(this._mousePreVector.x + 0.001, this._mousePreVector.y + 0.001);</p><p>// 在World中尋找與剛用滑鼠向量定義的Shape相重疊的Shape,並返回合格所有Shape數組<br />var k_maxCount:int = 10;<br />var _shapes:Array = new Array();<br />var count:int = this._bWorld.Query(_aabb, _shapes, k_maxCount);<br />var _body:b2Body = null;</p><p>// 迴圈數組中的Shape,檢測當前滑鼠是否在當前的Shape所包含的範圍內<br />for (var i:int = 0; i < count; ++i)<br />{<br />// 判斷當前Shape的父Body是否是靜態或者可移動的(這裡要理解b2Body、b2BodyDef和b2Shape<br />// 已經b2Shape子類的關係)。其實查閱源碼會發現GetBody返回的是在b2Shape中儲存的m_body<br />// 這個變數,也就是當前的b2Body對象。<br />if (_shapes[i].GetBody().IsStatic() == false || includeStatic)<br />{<br />trace(_shapes[i]);<br />// 將當前Shape轉化為最初父類b2Shape.<br />var _tShape:b2Shape = _shapes[i] as b2Shape;</p><p>// 測試當前滑鼠向量是否在合格當前Shape範圍內。注意在這裡需要將當前body的座標轉化<br />// 全局座標系,也即當前Sprite繪製情境。<br />var inside:Boolean = _tShape.TestPoint(_tShape.GetBody().GetXForm(), _mousePreVector);<br />if (inside)<br />{<br />// 返回符號條件的body,並終止迴圈。<br />_body = _tShape.GetBody();<br />break;<br />}<br />_tShape = null;<br />}<br />}<br />_aabb = null;<br />_shapes = null;<br />return _body;<br />}<br />}</p><p>}
BTW:不知道麼時候CSDN可以直接上傳SWF到blog中。歡迎大家指正錯誤,以及討論這些知識。