Nape刚体切割

刚体切割就是像切西瓜一样,将鼠标作为刀,手起刀落,把刚体切成。这个效果在游戏中应用的例子也不少,像由大名鼎鼎的nitrome推出的Ice Breaker游戏,它的主题就是通过切割冰块,拯救冰封的公主。今天我们就来学习一下如何用Nape,实现,刚体,切割,效果。

 iceBreaker

之前我曾经翻译过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)

联系作者

公众号:拉小登 | 微博:拉登Dony | B站:拉小登Excel

4 Replies to “Nape刚体切割”

  1. 拉登大师,“filter:交互滤镜,这个我在Nape刚体碰撞检测中曾经介绍过”,是不是这篇文章:http://www.ladeng6666.com/blog/2013/02/20/handling-nape-collision/

    我找了,没看到有介绍呢。我找了你写的Nape的其他文章,都没有找到。请问介绍文章是哪篇?谢谢。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注