Box2D多边形刚体贴图
在前面的刚体的上衣教程中,我们学会了简单的刚体贴图,确实非常简单,只需要根据b2Body刚体的坐标和角度实时更新userData的坐标和角度就可以了。那么多边形刚体贴图呢?
放心啦,没你想象的那么复杂,和简单刚体的贴图方法是一样一样的,重点是如何绘制和多边形刚体一样的图形,很幸运,我们在用drawPath自由绘制图形中已经学会了,结合运行时创建多边形刚体,我们可以轻松给多边形刚体啦(如果你还不熟练,强烈建议你好好看看这两篇教程)。效果如下:
完整的代码和注释如下:
package { import Box2D.Collision.b2AABB; import Box2D.Collision.Shapes.b2PolygonShape; import Box2D.Common.Math.b2Vec2; import Box2D.Dynamics.b2Body; import Box2D.Dynamics.b2BodyDef; import Box2D.Dynamics.b2DebugDraw; import Box2D.Dynamics.b2FixtureDef; import Box2D.Dynamics.b2World; import Box2DSeparator.b2Separator; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.geom.Matrix; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.display.GraphicsPathCommand; /** * http://www.ladeng6666.com * @author ladeng6666 */ public class MainWithSeparator extends Sprite { private const segmentLength:Number = 20; private var world:b2World; //绘制图形的画布 private var spriteCanvas:Sprite; private var spriteCommand:Vector.<int> = new Vector.<int>(); private var spriteData:Vector.<Number> = new Vector.<Number>(); //绘制图像的纹理 private var woodBmd:BitmapData = new Wood(); private var prePoint:Point = new Point(); private var curPoint:Point = new Point(); private var verticesList:Vector.<b2Vec2> = new Vector.<b2Vec2>(); private var isDrawing:Boolean = false; public function MainWithSeparator() { //创建box2D世界 world = LDEasyBox2D.createWorld(); //创建box2D调试图 addChild(LDEasyBox2D.createDebug(world)); //创建地面 LDEasyBox2D.createWrapWall(world,stage); spriteCanvas = new Sprite(); addChild(spriteCanvas); //侦听事件 addEventListener(Event.ENTER_FRAME, loop); stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp); stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove); } private function onStageMouseMove(e:MouseEvent):void { //如果鼠标没有按下,isDrawing为false,跳出 if(!isDrawing) return; spriteCanvas.graphics.lineTo(mouseX, mouseY); //记录鼠标坐标为当前curPoint,并计算curPoint与上一个点prePoint的距离 curPoint = new Point(mouseX, mouseY); var distance:Number = Point.distance(prePoint, curPoint); //当前后两个点的距离大于线段距离时,添加顶点 if (distance >= segmentLength) { //记录顶点到verticesList数组中 verticesList.push(new b2Vec2(mouseX / 30, mouseY / 30)); prePoint = curPoint.clone(); //存储绘制命令和绘制顶点 spriteCommand.push(GraphicsPathCommand.LINE_TO); spriteData.push(mouseX); spriteData.push(mouseY); } } private function onStageMouseUp(e:MouseEvent):void { spriteCommand.push(GraphicsPathCommand.LINE_TO); spriteData.push(spriteData[0]); spriteData.push(spriteData[1]); //鼠标弹起后,停止绘制 isDrawing = false; //画布里的内容 spriteCanvas.graphics.clear(); //在鼠标位置随机创建一个多边形刚体 var polygonBody:b2Body=createPolygon(); if (polygonBody != null) { createUseData(polygonBody, spriteCommand, spriteData); } //清空存储命令和顶点的数组 spriteCommand = new Vector.<int>(); spriteData = new Vector.<Number>(); //清空存储顶点的Vector数组 verticesList = new Vector.<b2Vec2>(); } /** * 鼠标按下事件侦听 * @param e */ private function onStageMouseDown(e:MouseEvent):void { //鼠标按下后,开始绘制 isDrawing = true; //设置线条样式 spriteCanvas.graphics.lineStyle(2); spriteCanvas.graphics.moveTo(mouseX, mouseY); //存储绘制命令和绘制顶点 spriteCommand.push(GraphicsPathCommand.MOVE_TO); spriteData.push(mouseX); spriteData.push(mouseY); //定义鼠标点为起点 curPoint = new Point(mouseX, mouseY); prePoint = curPoint.clone(); verticesList.push(new b2Vec2(mouseX / 30, mouseY / 30)); } /** * 刷新屏幕 * @param e */ private function loop(e:Event):void { world.Step(1 / 30, 10, 10); world.ClearForces(); world.DrawDebugData(); for (var body:b2Body = world.GetBodyList(); body; body=body.GetNext()) { if (body.GetUserData() != null) { //根据刚体的坐标个角度,更新绑定的userData body.GetUserData().x = body.GetPosition().x * 30; body.GetUserData().y = body.GetPosition().y * 30; body.GetUserData().rotation = body.GetAngle() * 180 / Math.PI; } } } /** * 创建多边形刚体 * @return */ private function createPolygon():b2Body { //1.创建刚体需求b2BodyDef var bodyRequest:b2BodyDef = new b2BodyDef(); bodyRequest.type = b2Body.b2_dynamicBody; bodyRequest.position.Set(0 , 0);//记得米和像素的转换关系 //2.Box2D世界工厂更具需求创建createBody()生产刚体 var body:b2Body=world.CreateBody(bodyRequest); //3.创建敢提形状需求b2ShapeDef的子类 //创建矩形刚体形状需求 var fixtureRequest:b2FixtureDef = new b2FixtureDef(); fixtureRequest.density = 3; fixtureRequest.friction = 0.3; fixtureRequest.restitution = 0.2; //创建一个Separator对象 var separator:b2Separator = new b2Separator(); //验证顶点是否符合创建多边形的标准 var validate:int = separator.Validate(verticesList); //如果是顶点因非顺时针不符标准,则反转数组中的顶点顺序 if (validate == 2) { verticesList.reverse(); }else if (validate != 0) { //如果不符合多边形标准,跳出 return null; } //将顶点分解成多个凸多边形,组合成复杂的多边形 separator.Separate(body, fixtureRequest, verticesList); return body; } /** * 根据存储的路径、顶点绘制多边形图形,并绑定到刚体中 * @param body 要添加贴图的刚体 * @param commandVector 存储绘图命令的刚体 * @param posVector 存储绘图顶点的刚体 */ private function createUseData(body:b2Body, commandVector:Vector.<int>, posVector:Vector.<Number>):void { //创建一个空白的画布 var canvas:Sprite = new Sprite(); canvas.graphics.clear(); canvas.graphics.lineStyle(3, 0x4D370B); //利用存储的命令和顶点绘制并填充图形 canvas.graphics.beginBitmapFill(woodBmd); canvas.graphics.drawPath(commandVector, posVector); canvas.graphics.endFill(); addChild(canvas); //把画布绑定到刚体中 body.SetUserData(canvas); } } }
如上面的代码所示,多边形刚体贴图同样是用body.GetPosition()和body.GetAngle()方法实时更新userData的坐标和角度。
不用的是,刚体和userData的注册点都在舞台的左上角,为什么会这样呢?仔细看一下,你会发现:
第152行: bodyRequest.position.Set(0 , 0);//刚体的坐标是(0,0)
第192行:addChild(canvas);//canvas添加到舞台后,默认的坐标也是(0,0)
所以,未来在项目中给多边形贴图时,要注意这个问题哦。
联系作者
前一段时间,你blog好像上不来,现在ok了?
是的,前两天服务器被攻击了,已经OK了,谢谢你的关注!
请问一下,在于starlingswf结合的时候,b2DebugDraw它需要一个flash sprite,这个要怎么解决?
而且在LDEasyBox2D中还有许多与starling冲突的地方,请问如何解决呢?
恩,调试的时候,还是要基于Sprite的,目前还没有更好的解决方式呢,sorry啦
只能等我学习starling之后,基于starling重写LDEasyBox2D了,另外推荐你使用Nape,这个物理引擎与Flash兼容起来,相对更好些
请问一下在更新userData的时候,贴图在不停的乱动跟什么有关?,刚体是圆形的,但是贴图不是,而刚体的运动的时候贴图会不停的绕着刚体附近的某个点选择……
好吧,这两个问题都解决了,之前那个b2DebugDraw直接用starling自带的Starling.current.nativeOverlay就可以了,而贴图更新角度会乱动是因为starling获取的是弧度,所以跟原生的不一样。
等下个项目我应该会用nape的,但是还是要结合starling,所以到时候可能还会有一些问题。
有问题欢迎随时和我交流,我的微信ladeng6666