Nape刚体切割
刚体切割就是像切西瓜一样,将鼠标作为刀,手起刀落,把刚体切成。这个效果在游戏中应用的例子也不少,像由大名鼎鼎的nitrome推出的Ice Breaker游戏,它的主题就是通过切割冰块,拯救冰封的公主。今天我们就来学习一下如何用Nape,实现,刚体,切割,效果。
之前我曾经翻译过emanueleferonato的Box2D刚体切割教程,那还是2011年的事儿了。转眼两年过去了,岁月催人老啊!回顾一下过去的两年,我的梦想依旧没有实现…
Sorry,非常sorry,跑题儿了。回到刚体切割上来。读过我的Box2D刚体切割译文(第1节,第2节,第3节)的同学都知道,在Box2D实现这个效果是个非常复杂的过程,emanueleferonato用了3节的篇幅才讲完。跟Box2D比起来,在Nape中实现起来就简单多了。
在学习之前,我们先了解一下,切割刚体时需要用到的几个方法:
Space.rayCast()
ray在英语里是”射线”的意思,如果用一个线段表示射线的话,那么rayCast()的作用就是检测Space空间中与这个线段发生碰撞的形状,然后将检测到的形状存储到一个RayResult类型的变量中,并返回。
Ray是Nape中的一个类,用来表示射线,这是一个很简单的类,只要定义射线的起始点origin和方向direction即可完成实例化,也可以通过fromSegment()方法,通过一个起始点start和一个结束点end来创建射线对象。具体请参考Ray类的官方文档。
Space.rayCast()方法的结构如下:
function rayCast( ray : Ray, inner : Boolean = false, filter : InteractionFilter = null):RayResult
每个参数说明如下:
- ray:Space空间中要检测的射线
- inner:指定检测形状的内边界是否进行交互,默认为FALSE
- filter:交互滤镜,这个我在Nape刚体碰撞检测中曾经介绍过
GeomPoly.cut()
从名字就能看的出来,这个函数是用来切割刚体的,切割之后的片段会保存到一个GeomPolyList类型的变量中,并返回。这个方法是切割的核心函数,其结构如下:
function cut( start:Vec2, end:Vec2, boundedStart:Bool = false, boundedEnd:Bool = false, output:GeomPolyList = null):GeomPolyList
每个参数说明如下:
- start:切割功能也需要用一个类似于射线的线段来对刚体进行检测,决定切割后的顶点。这个start表示该线段的起始点。
- end:表示切割线段的终点。
- boundedStart:当起点在刚体形状内部时,不沿线段方向进行延伸,默认为false。你可以在下面的示例代码中修改为true看看效果。
- boundedEnd:当终点在刚体形状内部时,不沿线段方向进行延伸,默认为false。你可以在下面的示例代码中修改为true看看效果。
- output:将一个GeomPolyList对象传递给该变量,切割后的片段会自动存储到这个GeomPolyList对象中。
了解完主要的函数,下面我们看一下具体的效果。在下面的示例中,有一个矩形的刚体,点击鼠标并拉出一条红线作为ray射线,松开鼠标切割刚体。
[swfobject]926[/swfobject]
完整的代码如下:
package learnNape { import nape.phys.BodyType; import nape.geom.RayResult; import nape.geom.Ray; import flash.events.Event; import nape.phys.Material; import nape.shape.Polygon; import nape.geom.GeomPolyList; import nape.geom.GeomPoly; import flash.display.Sprite; import nape.geom.Vec2; import flash.events.MouseEvent; import ldEasyNape.LDEasyNape; import nape.phys.Body; [SWF( frameRate="60", width="550", height="400")] public class T14_CutBody extends AbstractNapeTest { public function T14_CutBody(){} private var box : Body; private var sp : Vec2 , ep : Vec2; private var layer : Sprite; private var isDrawing : Boolean = false; private var geomPoly : GeomPoly; override protected function onNapeWorldReady() : void { sp = new Vec2(); ep = new Vec2(); layer = new Sprite(); addChild(layer); box = LDEasyNape.createBox(275, 200, 200, 200, true); } override protected function mouseEventHanlder(event : MouseEvent) : void { super.mouseEventHanlder(event); switch(event.type){ case MouseEvent.MOUSE_DOWN: if(LDEasyNape.getBodyAtMouse()!=null) return; isDrawing = true; sp = new Vec2(mouseX,mouseY); break; case MouseEvent.MOUSE_UP: if(isDrawing){ isDrawing = false; ep = new Vec2(mouseX,mouseY); if(Vec2.distance(sp, ep)>10){ cutBody(); } layer.graphics.clear(); } break; } } override protected function loop(event : Event) : void { super.loop(event); if(isDrawing){ layer.graphics.clear(); layer.graphics.lineStyle(2,0xff0000); layer.graphics.moveTo(sp.x, sp.y); layer.graphics.lineTo(mouseX, mouseY); } } private function cutBody():void{ var ray : Ray = Ray.fromSegment(sp, ep); var rayResult : RayResult = napeWorld.rayCast(ray); if(rayResult!=null){ geomPoly = new GeomPoly((rayResult.shape as Polygon).worldVerts); var geomPolyList : GeomPolyList = geomPoly.cut(sp, ep,true,true); geomPolyList.foreach(function (s:*):void{ var body : Body = new Body(); body.shapes.push(new Polygon(s,Material.ice())); body.align(); if(body.contains(Vec2.weak(275,200))){ body.type = BodyType.STATIC; } body.space = napeWorld; }); rayResult.shape.body.space = null; } } } }
第28~29行:定义切割时绘制线段的起点和终点。
第48~53行:当切割线段绘制完毕鼠标弹起时,且起点和终点距离大于10时(防止距离为0而引发Nape计算错误),开始切割刚体cutBody
第71~72行:根据起点sp和终点ep定义射线Ray对象,并检测napeWorld中与之碰撞的形状,将检测结果保存到rayResult变量中。
第76行:用检测到的形状顶点创建一个GeomPoly对象,用来调用cut方法。
第77行:调用cut方法切割刚体,将切割结果保存到geomPolyList中。
第79~87行:遍历geomPolyList中的形状,分别创建切割后的刚体。其中第84行将原刚体位置的片段定义为静态刚体。
第88行:删除原刚体。
点击下载源文件(Download source code)
联系作者
没仔细发现cut方法中true和false的区别
刚刚试过知道true和false的区别了
拉登大师,“filter:交互滤镜,这个我在Nape刚体碰撞检测中曾经介绍过”,是不是这篇文章:http://www.ladeng6666.com/blog/2013/02/20/handling-nape-collision/
我找了,没看到有介绍呢。我找了你写的Nape的其他文章,都没有找到。请问介绍文章是哪篇?谢谢。
是那篇文章,不过貌似真的没有提及,sorry,回头我在写一篇文章来介绍filter。不过它和Box2D的filterData是类似的,您先凑活着看一下Box2D的教程http://www.ladeng6666.com/blog/2012/11/02/filterdata-to-separate-the-box2d-collision/,抽时间我把Nape的filter教程补上