运行时绘制Nape刚体
在创建简单的Nape刚体中,我们学会了简单矩形和圆形刚体的实现方法。今天我们进一步学习,如何在SWF运行时绘制这些刚体。
所谓运行时绘制刚体,就是根据鼠标的坐标与移动,动态的绘制这些刚体。实现这个过程的重点,是计算出刚体的坐标以及尺寸(如矩形的宽高、圆形的半径),而这些坐标和尺寸是根据鼠标的位置和动作计算出来的,大体步骤如下:
- 鼠标按下:记录当前鼠标坐标为刚体的坐标x、y。
- 鼠标滑动:根据鼠标的坐标以及刚体的坐标计算刚体的尺寸,如矩形的宽高w、h。并实时的绘制对于的图形。
- 鼠标弹起:根据前两部计算出来的刚体坐标x、y和尺寸,动态绘制刚体。
大体过程就是这样的啦,看看下面的示例,我想应该可以更加清楚明了。
点击鼠标开始绘制图形,松开鼠标后创建于图形相同形状的刚体。默认绘制的图形是矩形,绘制的同时按下Ctrl键是圆形刚体,按下Shift键是5边形。
[swfobject]689[/swfobject]
完整的代码和注释如下:
package { import flash.display.Graphics; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.geom.Point; import nape.geom.Vec2; import nape.phys.Body; import nape.phys.BodyType; import nape.phys.Material; import nape.shape.Circle; import nape.shape.Polygon; import nape.space.Space; import nape.util.BitmapDebug; [SWF( width="550", height="400", frameRate="60")] public class T7_CreateBodiesOnFly extends Sprite { private var napeWorld:Space; private var debug:BitmapDebug; private var isCtrlDown:Boolean; private var isShiftDown:Boolean; private var bodyOnFly:Object; private var onFlyLayer:Sprite; public function T7_CreateBodiesOnFly() { //1.创建Nape空间,重力,调试视图 createNapeWorld(); //2.添加事件 setUpEvents(); //用createBox()方法创建4个包围舞台的静态刚体 createWall(); //创建一个保存动态刚体信息的对象 bodyOnFly={x:0, y:0, x1:0, y1:0, w:0, h:0, r:0, mouseAngle:0}; //创建一个图层,在鼠标移动时绘制相应的图形 onFlyLayer=new Sprite(); addChild(onFlyLayer); //添加FPS监视器 addChild(new Stats()); } //创房Nape世界 private function createNapeWorld():void { var gravity:Vec2 = new Vec2( 0, 600 ); napeWorld =new Space( gravity ); debug= new BitmapDebug(550, 400, 0xD6D6D6); addChild(debug.display); } //添加事件侦听 private function setUpEvents():void { //listening to the EnterFrame Event to update the Nape world stage.addEventListener(Event.ENTER_FRAME, loop); //add listener to MouseEvent,like mouseDown or MouseUp stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHanlder); stage.addEventListener(MouseEvent.MOUSE_UP, mouseEventHanlder); //侦听键盘事件 stage.addEventListener(KeyboardEvent.KEY_DOWN, keyBoardEventHanlder); stage.addEventListener(KeyboardEvent.KEY_UP, keyBoardEventHanlder); } protected function keyBoardEventHanlder(event:KeyboardEvent):void { //在键盘按下时,记录Ctrl和Shift键的状态 isCtrlDown=event.ctrlKey; isShiftDown=event.shiftKey; } //创建各种刚体 private function createBody(bodyInfo:Object):void{ if(isCtrlDown){ //如果Ctrl按下,绘制圆形刚体 createCircle(bodyInfo.x, bodyInfo.y, bodyInfo.r, BodyType.DYNAMIC); }else if(isShiftDown){ //如果Shift键按下,绘制5边形 createRegular(bodyInfo.x, bodyInfo.y, bodyInfo.r, bodyInfo.mouseAngle, 5, BodyType.DYNAMIC); }else{ //默认绘制矩形刚体 createBox(bodyInfo.x+bodyInfo.w/2, bodyInfo.y+bodyInfo.h/2, bodyInfo.w, bodyInfo.h,BodyType.DYNAMIC); } } //创建矩形刚体,之前我们都已经讲过 private function createBox(posX:Number, posY:Number, w:Number, h:Number, type:BodyType):void{ var box:Body = new Body(type, new Vec2(posX, posY)); var boxShape:Polygon=new Polygon(Polygon.box(w,h), Material.glass()); box.shapes.push(boxShape); box.space= napeWorld; } //创建指定边数的规则多边形刚体 private function createRegular(posX:Number, posY:Number, r:Number, rotation:Number, edgeCount:int, type:BodyType):void{ var regular:Body = new Body(type, new Vec2(posX, posY)); //通过Polygon预定义的regular方法绘制规则的5变形刚体 var regularShape:Polygon=new Polygon(Polygon.regular(r*2,r*2,edgeCount), Material.glass()); regularShape.rotate(rotation); regular.shapes.push(regularShape); regular.space= napeWorld; } //创建圆形刚体 private function createCircle(posX:Number, posY:Number, radius:int, type:BodyType):void { var circle:Body=new Body(type, new Vec2(posX, posY)); var shape:Circle=new Circle(radius,null,Material.glass()); circle.shapes.push(shape); circle.space=napeWorld; } //绘制包围的静态刚体 private function createWall():void{ createBox(stage.stageWidth/2, 0, stage.stageWidth, 10, BodyType.STATIC); createBox(stage.stageWidth/2, stage.stageHeight, stage.stageWidth, 10, BodyType.STATIC); createBox(0, stage.stageHeight/2, 10, stage.stageHeight, BodyType.STATIC); createBox(stage.stageWidth, stage.stageHeight/2, 10,stage.stageWidth, BodyType.STATIC); createBox(250, 405, 600,50, BodyType.STATIC); } protected function mouseEventHanlder(event:MouseEvent):void { switch (event.type) { case MouseEvent.MOUSE_DOWN: //鼠标按下后,添加鼠标移动事件侦听 stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseEventHanlder); //记录鼠标当前坐标,作为刚体的坐标 bodyOnFly.x=mouseX; bodyOnFly.y=mouseY; break; case MouseEvent.MOUSE_UP: //鼠标弹起后,移除鼠标移动事件侦听 stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseEventHanlder); //如果刚体的半径小于1,则绘制预定义的尺寸,防止传入0尺寸,导致Nape计算错误 if(bodyOnFly.r <1){ bodyOnFly.w=4; bodyOnFly.h=3; bodyOnFly.r= Math.sqrt(bodyOnFly.w*bodyOnFly.w + bodyOnFly.h*bodyOnFly.h); } //根据刚体信息bodyOnFly创建刚体 createBody(bodyOnFly); //清楚图形绘制层 onFlyLayer.graphics.clear(); break; case MouseEvent.MOUSE_MOVE: //在鼠标移动时,根据鼠标的坐标计算刚体信息 //矩形刚体的宽和高 bodyOnFly.w= mouseX - bodyOnFly.x; bodyOnFly.h= mouseY - bodyOnFly.y; //5边形刚体的角度 bodyOnFly.mouseAngle=Math.atan2(mouseY-bodyOnFly.y, mouseX-bodyOnFly.x); //圆形刚体的半径 bodyOnFly.r= Math.sqrt(bodyOnFly.w*bodyOnFly.w + bodyOnFly.h*bodyOnFly.h); //适时绘制刚体形状 drawShape(bodyOnFly); break; } } private function drawShape(bodyInfo:Object):void{ var gfc:Graphics=onFlyLayer.graphics; gfc.clear(); gfc.lineStyle(1); gfc.beginFill(0x0000FF, 0.5); if(isCtrlDown){ //Ctrl按下时,绘制圆形 gfc.drawCircle(bodyInfo.x, bodyInfo.y, bodyInfo.r); }else if(isShiftDown){ //Shift按下时,绘制5边形 var mouseAngle:Number=bodyInfo.mouseAngle; var angle:Number = Math.PI*2/5;//每个顶点之间的角度间隔 var x:Number, y:Number;//保存每个顶点的坐标 //移动到第一个顶点 gfc.moveTo(bodyInfo.x+bodyInfo.r*Math.cos(mouseAngle), bodyInfo.y+bodyInfo.r*Math.sin(mouseAngle)); for (var i:int=0; i<5; i++){ //计算每个顶点 x=bodyInfo.x + bodyInfo.r * Math.cos( i*angle+mouseAngle); y=bodyInfo.y + bodyInfo.r * Math.sin( i*angle+mouseAngle); //连线顶点 gfc.lineTo(x,y); } } else{ //默认绘制矩形 gfc.drawRect(bodyInfo.x, bodyInfo.y, bodyInfo.w, bodyInfo.h); } gfc.endFill(); } protected function loop(event:Event):void { napeWorld.step(1/60); debug.clear(); debug.draw(napeWorld); debug.flush(); } } }
值得一提的是第97-104行的createRegular()方法,这里用到了Polygon的静态方法regular()来创建一个规则的5边形刚体,除了regular之外,Polygon的静态方法还是box和rect,它们都会返回一个由顶点组成的数组,然后传递给Polygon对象,创建多边形shape,具体的用法分别如下:
- rect:创建一个注册点在x,y位置的矩形,除了要指定width和height之外,还要指定矩形注册点x,y,
- box:创建一个注册点在中心点的矩形,这个方法,只需要指定刚体width和height即可。相当于rect(0,0,width,height)
- regular:创建宽为width,高为height,顶点数为edgeCount的规则多半形,例如三角形,以及本例中的五边形。这是Box2D没有的功能,在Box2D中需要自定义顶点才能创建类似的多边形。
源文件下载地址
联系作者
膜拜一下大神..嘿嘿。
拉登大哥,给力啊…..
谢谢关注,期待你的继续支持!
BitmapDebug和ShapeDebug有什么区别呢?是将图形转成Bitmap类型,以减少生成矢量图对cpu减少开销么?
Stats()这个类貌似找不到了。。。
今天下午一直在纠结那个五边形的坐标计算。哎,初中的数学都忘光了!悲剧
拉登大叔,我发现一个bug
什么Bug?
画矩形如果画成一条线的话就会出错!
在77行createBody生成刚体前先if一下bodyInfo.w, bodyInfo.h以及bodyInfo.r是不是大于0即可,不然确实会报错。
135行的那段if因此就可以略掉了。
更正一下,w和h应该是判断!=0,负数也可。
只有矩形的情况下需要判断w和h……
谢谢更正,错误难免,还需要大家的监督啊!
感谢拉登大叔!~ 教程太有爱了!
谢谢关注,我会继续写更多Nape教程的!
演示SWF 有个小问题,当我画一个很小很小的东西时,或者直接单击之后就画不出矩形了,能显示出绘制区域,但是松开鼠标之后没有生成矩形
不会吧,我在第135~139行的代码中,有针对绘制尺寸过小时,设置矩形尺寸默认为3*4,所以,理论上不会出现你说的情况。如果我的回答没有解答你的疑问,你可以截图后发邮件到ladeng6666@163.com,谢谢关注!
重新试了一下,是上条评论的层主发的问题,就是我鼠标按下之后水平或者垂直移动时画出来的是一条线,可能获取的宽或者高是0,所以没有画出刚体。之后就生成不了刚体了。