存档

‘box2D’ 分类的存档

Box2D物理引擎模拟炸弹爆炸效果

2015年1月25日 没有评论

今天咱们来模拟炸弹效果。于是问题一来了:“为什么要模仿这么暴力的效果呢?莫非几日不见,Yorhom人品煞变?”

其实玩过愤怒的小鸟的同学都应该对这种效果似曾相识,因为据非官方报道,第二厉害的小鸟——黑色鸟的特技就是自爆。问题二出现了:“那第一厉害的小鸟是哪一种呢?”据Yorhom我本人测试,那只红色大鸟应该是最厉害的,不过貌似没有特技?愤怒的小鸟这种肤浅的游戏,Y某我最擅长了,以后有时间会专门写写这个游戏的攻略。这两种鸟的靓照如下:

Box2D物理引擎模拟炸弹爆炸效果Box2D物理引擎模拟炸弹爆炸效果

敷衍了问问题二的同学,问题三就来了:“到底如何实现这个效果呢?” 这个问题不好敷衍,于是接下来的文章,就由我向大家介绍如何使用Box2D实现这种效果。

在学习本章前,你需要先了解一下Box2D这款很实用,很出名的物理引擎。凭借我个人的学习经验,我向大家推荐拉登大叔的博客,其博客Box2D栏目地址如下:

http://www.ladeng6666.com/blog/category/box2d/

一,炸弹效果原理

在实现这个效果的时候,我想到了两种方案。

方案一:在炸弹刚体爆炸时,由炸弹刚体为起点,向四周喷散小刚体,这些小刚体会被喷射到附近的刚体上,对附近的刚体施加力,然后力是物体运动状态改变的原因(摘自高中物理必修一),然后爆炸效果就可以完成了。这个方法比较简单,但是我觉得有点dirty way。像我这种耳机标有R的必须带右边,标有L的必须带左边的人,怎么可能就此满足了呢?当然,感兴趣的朋友可以自己尝试一下这种方法~

方案二:首先想办法把炸弹刚体周围的其他刚体找到,然后对它们施加一个力,我们只用控制好力的方向就可以实现爆炸效果了。嗯~不错。似乎这种方法更漂亮~

这两种方案显而易见的区别就是方案二比方案一字数要少一点。这意味着什么呢?我想,大家都应该做过至少6年的数学题了吧,据我大约9.5年来总结的经验,题目字数越少的数学题就越难,因为提供的信息量少。所以说,虽然方案二效果相对更好,但是实现起来可能要复杂很多。

方案二之所以比较难以实现,是在于我们不知道如何查找周围的刚体。为此,我将我所了解到的Box2D中的功能都涉猎了一遍,发现我从来没有用过只是听说过的一个功能——射线投射可能会派上用场。不过辛好网上相关的资料挺多的,所以我就此学习了一下b2RayCastInput,b2RayCastOutput这两个类。

上述的两个类很重要,所以大家必须先了解这两个类,具体的教程为:http://ohcoder.com/blog/2012/12/10/ray-casting/,教程中用的是Box2D C++,不过无论是C++版还是Web版,用法都是一样的。

b2RayCastInput,b2RayCastOutput这两个类到底有什么用的?从上面提供的教程可以看出,这两个类的实例一般作为参数传入b2Fixture的RayCast函数中,这个函数用来检查某个射线是否于刚体相交。所以我们只用以炸弹刚体位置为始点,然后向周围喷射射线。然后循环世界里的刚体,判断循环得到的刚体是否与射线相交,如果相交则对当前刚体施加一个力。这样一来,炸弹效果的原理基本就被弄清除了。

二,构建一个物理世界

先把最基础的世界创建出来把。于是,我们在index.html里加入以下代码:

<!DOCTYPE html>
<html>
<head>
	<title>Box2d Bomb</title>
	<script type="text/javascript" src="./Box2dWeb-2.1.a.3.min.js"></script>
	<script type="text/javascript" src="./Main.js"></script>
</head>
<body>
	<canvas id="mycanvas" width="600" height="400"></canvas>	
</body>
</html>

html代码不是重点,重点是Main.js:

window.addEventListener("load", main, false);

var b2Vec2 = Box2D.Common.Math.b2Vec2
, b2AABB = Box2D.Collision.b2AABB
, b2BodyDef = Box2D.Dynamics.b2BodyDef
, b2Body = Box2D.Dynamics.b2Body
, b2FixtureDef = Box2D.Dynamics.b2FixtureDef
, b2Fixture = Box2D.Dynamics.b2Fixture
, b2World = Box2D.Dynamics.b2World
, b2MassData = Box2D.Collision.Shapes.b2MassData
, b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
, b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
, b2DebugDraw = Box2D.Dynamics.b2DebugDraw
, b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
, b2RayCastInput = Box2D.Collision.b2RayCastInput
, b2RayCastOutput = Box2D.Collision.b2RayCastOutput;

var world, fixDef, bodyDef;

var bomb = null;

var timer = 0;

function main () {
	world = new b2World(new b2Vec2(0, 9.8), true);

	fixDef = new b2FixtureDef();
	fixDef.density = 1.0;
	fixDef.friction = 0.5;
	fixDef.restitution = 0.2;

	bodyDef = new b2BodyDef();

	createGround();
	createBlocks();
	createBomb();
	createDebugDraw();
	setInterval(update, 1000 / 60);
}

function createGround () {
	fixDef.shape = new b2PolygonShape();
	fixDef.shape.SetAsBox(20, 1);
	
	bodyDef.type = b2Body.b2_staticBody;
	bodyDef.position.Set(10, 13);

	world.CreateBody(bodyDef).CreateFixture(fixDef);
}

function createBlocks () {
	var list = [
		{width : 120, height : 30, x : 140, y : 330},
		{width : 20, height : 100, x : 50, y : 200},
		{width : 20, height : 100, x : 230, y : 200},
		{width : 120, height : 20, x : 140, y : 80},
		{width : 120, height : 30, x : 440, y : 330},
		{width : 20, height : 100, x : 350, y : 200},
		{width : 20, height : 100, x : 530, y : 200},
		{width : 120, height : 20, x : 440, y : 80}
	];

	for (var i = 0; i < list.length; i++) {
		var data = list[i];

		fixDef.shape = new b2PolygonShape();
		fixDef.shape.SetAsBox(data.width / 30, data.height / 30);
		
		bodyDef.type = b2Body.b2_dynamicBody;
		bodyDef.position.Set(data.x / 30, data.y / 30);

		world.CreateBody(bodyDef).CreateFixture(fixDef);
	}
}

function createBomb () {
	fixDef.shape = new b2CircleShape(0.5);

	bodyDef.type = b2Body.b2_dynamicBody;
	bodyDef.position.Set(9.7, 1);

	bomb = world.CreateBody(bodyDef);
	bomb.userData = "iambomb";
	bomb.CreateFixture(fixDef);
}

function createDebugDraw () {
	var debugDraw = new b2DebugDraw();
	debugDraw.SetSprite(document.getElementById("mycanvas").getContext("2d"));
	debugDraw.SetDrawScale(30.0);
	debugDraw.SetFillAlpha(0.5);
	debugDraw.SetLineThickness(1.0);
	debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit | b2DebugDraw.e_controllerBit | b2DebugDraw.e_pairBit);
	world.SetDebugDraw(debugDraw);
}

function update () {
	world.Step(1 / 60, 10, 10);
	world.DrawDebugData();
	world.ClearForces();

	if (timer++ == 100) {
		detonated();
	}
}

值得注意的是timer这个变量,这个变量是用来为爆炸计时的。在update函数里我们调用了dentonated这个函数,我暂时不把这个函数的代码公布,这个函数是引爆炸弹用的。
运行代码,我们得到如图的物理世界:

Box2D物理引擎模拟炸弹爆炸效果

图中的小球实际上就是一颗炸弹。

三,引爆炸弹

上面提到过dentonated这个函数,这里,我们就来学习一下里面的代码,这也是本次开发的核心部分。

function detonated () {
	var position = bomb.GetWorldCenter();

	var range = 3;

	for (var i = 0; i <= 100; i++) {
		var angle = 360 / 100 * i;

		var input = new b2RayCastInput();
		input.p1 = position;
		input.p2.Set(position.x + range * Math.cos(angle), position.y + range * Math.sin(angle));
		input.maxFraction = 1;

		var output = new b2RayCastOutput();

		for (var currentBody = world.GetBodyList(); currentBody; currentBody = currentBody.GetNext()) {
			if (currentBody.userData == bomb.userData) {
				continue;
			}

			var fix = currentBody.GetFixtureList();

			if (!fix) {
				continue;
			}
			
			var isHit = fix.RayCast(output, input);

			if (isHit) {
				var p1 = input.p1.Copy();
				var p2 = input.p2.Copy();
				p2.Subtract(p1);
				p2.Multiply(output.fraction);
				
				p1.Add(p2);

				var hitPoint = p1.Copy();
				
				hitPoint.Subtract(position);

				currentBody.ApplyForce(new b2Vec2(hitPoint.x * (1 - output.fraction) * 300, hitPoint.y * (1 - output.fraction) * 300), hitPoint);
			}

		}
	}

	world.DestroyBody(bomb);
}

在这个函数里,我首先用GetWorldCenter获取到炸弹刚体的位置,然后设定一个爆炸范围变量range。然后通过循环开始向四周创建射线。这里我设定一共要喷出100条射线,所以,每条射线间的夹角为3.6度。知道角度后,我们就可以计算每条射线向量的终点了,并将终点和始点传入b2RayCastInput中。

一系列射线方面的准备完毕后,就要开始循环世界里的刚体了。然后将循环得到的刚体获取刚形(一个b2Fixture对象),并调用刚型的RayCast来判断刚才创建的射线是否与刚体相交。

if (isHit) {
	var p1 = input.p1.Copy();
	var p2 = input.p2.Copy();
	p2.Subtract(p1);
	p2.Multiply(output.fraction);
	
	p1.Add(p2);

	var hitPoint = p1.Copy();
	
	hitPoint.Subtract(position);

	currentBody.ApplyForce(new b2Vec2(hitPoint.x * (1 - output.fraction) * 300, hitPoint.y * (1 - output.fraction) * 300), hitPoint);
}

以上几行代码负责计算射线与刚体相交的位置所对应的向量,并在该位置上施加一个力。那么如何计算射线与刚体相交的位置所对应的向量呢?我们不妨来看张图:Box2D物理引擎模拟炸弹爆炸效果

现在,让我们调动起高中数学必修四里平面向量的知识。于是数学问题来了:已知向量Op1Op2坐标,且p1,p2,p3三点共线,p1p3 = n p1p2,求Op3

要解这道题,可以用定比分点等知识,用向量加减,数乘运算也能解决问题。以下是我的解答:

解 设p1(x1, y1),p2(x2, y2)

Op3 = Op1 + p1p3 = np1p2 + Op1 = n(x2 – x1, y2 – y1) + (x1, y1) = (nx2 – nx1 + x1, ny2 – ny1 + y1)

这是数学写法,换到程序代码里就是:

var p1 = input.p1.Copy();
var p2 = input.p2.Copy();
p2.Subtract(p1);
p2.Multiply(output.fraction);

p1.Add(p2);

var hitPoint = p1.Copy();

这里的output.fraction就相当于上题所给的n。Copy,Substact,Add,Multiply分别是用来复制当前向量,向量减法,向量加法,向量数乘运算。

计算完了碰撞位置,我们给刚体用ApplyForce施加一个力,然后刚体就类似于被一股爆炸产生的气流冲击了似的,飞了出去~

炸弹爆炸了炸弹本身肯定不负存在了,所以在给周围的刚体施加完力后,用DestroyBody销毁掉这个炸弹刚体~

运行代码,得到下图:

Box2D物理引擎模拟炸弹爆炸效果

在线测试地址:http://wyh.wjjsoft.com/demo/box2d_bomb/

源代码下载地址:http://wyh.wjjsoft.com/downloads/box2d_bomb.zip

本章就到此结束了,欢迎大家留言~

—————————————————————-

欢迎大家转载我的文章。

转载请注明:转自Yorhom’s Game Box

http://blog.csdn.net/yorhomwang

欢迎继续关注我的博客

分类: box2D 标签:

【HTML5物理小Demo】用Box2dWeb实现锁链+弹簧效果

2014年2月5日 没有评论

最近开始研究Box2dweb,Box2dweb是一款物理引擎,主要是对物理刚体和关节连接进行了封装,box2dweb很强大当然也有些复杂,不过幸好lufylegend.js做了这方面的封装,在制作时如果用lufylegend配合Box2dweb,那就简单多了。要学习box2dWeb我还是给大家推荐拉登大叔的博客,地址:http://www.ladeng6666.com/blog,写得相当好,话说他的文章中还运用了相当多的修辞手法呢,看他的文章边学技术,边学写作,哈哈。顺便也提一提,本次实现的效果也是模仿拉登大叔一篇文章中的效果,不过大叔的是ActionScript版本的,我是Js版的。

最后还是祝大家新年快乐吧~虽然这祝福来晚了,不过还是满含我的诚意……

好了,费话不多说,直接进入正题。

首先看截图吧,如下:

【HTML5物理小Demo】用Box2dWeb实现锁链+弹簧效果

【HTML5物理小Demo】用Box2dWeb实现锁链+弹簧效果

测试链接:http://game.h5stars.com/20141952ee70ee9c117/

接下来就来讲一讲实现步骤。

 

一,准备工作

首先你需要下载lufylegend和box2dweb 这两个引擎。

box2dweb可以到这里下载:

http://code.google.com/p/box2dweb/downloads/list

lufylegend可以到这里:

http://lufylegend.com/lufylegend

关于lufylegend怎么用,可以到这里看看API文档:

http://lufylegend.com/lufylegend/api

Box2dWeb怎么用?其实我也不太清楚,这次主要用lufylegend封装的API,用到原生API的时候我自己来讲讲吧,讲的差不要骂喔~

 

二,原理

这个小demo的原来其实很简单,就是将几块矩形小刚体用旋转关节连接起来。当然,说起来很容易,其实做起来还是要深究的。在我看到拉登大叔的文章之前,其实我也没有想到什么方法,于是就借鉴了大叔的文章,原理不是我独创的,所以呢,不好直说,免得大叔看到了不高兴,大家看一看拉登大叔的文章就能明白原理了。

文章地址如下:http://www.ladeng6666.com/blog/2012/11/25/create-box2d-linkage-or-bridge-effect-using-b2joint/

 

三,含详细注释的源代码

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>box2d demo</title>
	<script type="text/javascript" src="./Box2dWeb-2.1.a.3.min.js"></script>
	<script type="text/javascript" src="./lufylegend-1.8.7.min.js"></script>
	
	<script type="text/javascript">
		init(50,"mylegend",600,400,pageInit);
		function pageInit(){
			LStage.setDebug(true);
			LStage.box2d = new LBox2d();

			if(LStage.canTouch == true){
				document.body.style.margin = "0px 0px";
				LStage.stageScale = LStageScaleMode.SHOW_ALL;
				LSystem.screen(LStage.FULL_SCREEN);
			}

			var mainObj = new Main();
			addChild(mainObj);
		}

		/**
		 *Main
		 *@author: Yorhom
		 *@http://blog.csdn.net/yorhomwang
		*/
		function Main(){
			var s = this;
			base(s,LSprite,[]);

			/**加入围墙*/
			s.addWall();
			/**加入锁链桥*/
			s.addBridge();
			/**随机加入其他物体*/
			s.addRandomObj();
		}
		Main.prototype.addWall = function(){
			var s = this;
			
			//设置围墙大小
			var wallSize = 10;
			//设置围墙数据
			var wallList = [
				//左边
				[wallSize*0.5, LStage.height*0.5, wallSize, LStage.height],
				//右边
				[LStage.width-wallSize*0.5, LStage.height*0.5, wallSize, LStage.height],
				//上面
				[LStage.width*0.5, wallSize*0.5, LStage.width, wallSize],				
				//下面
				[LStage.width*0.5, LStage.height-wallSize*0.5, LStage.width, wallSize],
			];
			//通过遍历围墙数据,添加四面围墙
			for(var key in wallList){
				//获取数据
				var item = wallList[key];
				//创建围墙对象
				var wallLayer = new LSprite();
				//设定对象位置
				wallLayer.x = item[0];
				wallLayer.y = item[1];
				//加入刚体
				wallLayer.addBodyPolygon(item[2],item[3],0);
				//加入显示列表
				s.addChild(wallLayer);
			}
		};
		Main.prototype.addBridge = function(){
			var s = this;

			//关节向量
			var vec = new LStage.box2d.b2Vec2();
			//添加对象数量
			var amount = 6;
			//设置对象宽度和高度
			var bw=50,bh=15;
			//获取锁链桥总长度
			var bridgeWidth = bw*amount;
			//设置锁链桥开始位置
			var initX=(LStage.width-bridgeWidth)*0.5,
				initY=(LStage.height-bh)*0.5+30;
			//设置用于固定锁链桥的定点半径
			var anchorR = 15;

			/**用于固定锁链桥的定点A*/
			var anchorA = new LSprite();
			anchorA.x = initX-anchorR;
			anchorA.y = initY-anchorR;
			anchorA.addBodyCircle(anchorR,anchorR,anchorR,0);
			s.addChild(anchorA);
			/**用于固定锁链桥的定点B*/
			var anchorB = new LSprite();
			anchorB.x = bridgeWidth+initX-anchorR;
			anchorB.y = initY-anchorR;
			anchorB.addBodyCircle(anchorR,anchorR,anchorR,0);
			s.addChild(anchorB);

			//上一个对象
			var previousBlock = anchorA;

			/**循环添加刚体*/
			for(var i=0; i<amount; i++){
				//实例化对象
				var block = new LSprite();
				//设定对象位置
				block.x = initX+i*bw+bw*0.5;
				block.y = initY;
				//加入刚体
				block.addBodyPolygon(bw,bh,1);
				//设置鼠标拖动
				block.setBodyMouseJoint(true);
				//加入显示列表
				s.addChild(block);
				//加入关节
				var revoluteJoint = new LStage.box2d.b2RevoluteJointDef();
				vec.Set((initX+i*bw)/30, initY/30);
				revoluteJoint.Initialize(previousBlock.box2dBody,block.box2dBody,vec);
				LStage.box2d.world.CreateJoint(revoluteJoint);
				//更改上一个对象
				previousBlock = block;
			}
			//将最后一个刚体固定
			var revoluteJoint = new LStage.box2d.b2RevoluteJointDef();
			vec.Set((initX+i*bw)/30, initY/30);
			revoluteJoint.Initialize(previousBlock.box2dBody,anchorB.box2dBody,vec);
			LStage.box2d.world.CreateJoint(revoluteJoint);
		};
		Main.prototype.addRandomObj = function(){
			var s = this;
			for(var i=0; i<10; i++){
				//创建对象
				var obj = new LSprite();
				//设置对象位置
				obj.x = Math.floor(Math.random()*(400-200+1)+200);
				obj.y = 0;
				//加入显示列表
				s.addChild(obj);
				//根据随机数添加不同的刚体
				if(Math.random() > 0.5){
					//获取随机宽度和高度
					var w = Math.floor(Math.random()*10)+25;
					var h = Math.floor(Math.random()*10)+25;
					//重现设置y坐标
					obj.y += h*0.5;
					//添加矩形刚体
					obj.addBodyPolygon(w,h,1);
				}else{
					//获取随机半径
					var r = Math.floor(Math.random()*20)+5;
					//重现设置y坐标
					obj.y += r;
					//添加圆形刚体
					obj.addBodyCircle(r,r,r,1);
				}
				//设置鼠标拖动
				obj.setBodyMouseJoint(true);
			}
		};
	</script>
</head>
<body>
	<div id="mylegend"></div>
</body>
</html>

所有代码都在这里了,还是很少的,对吧。看了拉登大叔的原理讲解,再来看我写的js代码就非常容易了。主要是注意以下几个地方。

1,添加刚体

在box2dweb中添加刚体超级麻烦,把整个创建过程告诉大家估计大家都会觉得不耐烦,所以,我用到了lufylegend.js添加刚体,这样一来添加刚体就被简化成一步了。非常方便,不是吗?在lufylegend中添加刚体一共有这几个函数:

addBodyCircle() 添加原形刚体
addBodyPolygon() 添加矩形刚体
addBodyVertices() 添加不规则图形刚体

具体的参数说明和使用举例可以参见lufylegend的API文档中LSprite的API。文档地址已在文章的准备工作一栏写出。

2,添加关节

在lufylegend虽然也有添加关节的封装,但是不能设置关节点。所以我还是用了box2dweb原生方法。添加关节的代码如下:

var revoluteJoint = new LStage.box2d.b2RevoluteJointDef();
vec.Set((initX+i*bw)/30, initY/30);
revoluteJoint.Initialize(previousBlock.box2dBody,block.box2dBody,vec);
LStage.box2d.world.CreateJoint(revoluteJoint);

其他的好理解,主要是vec这个变量,它在这里是一个向量对象,实例化代码如下:

var vec = new LStage.box2d.b2Vec2();

其实这个b2Vec2这个类可以传参数,和它的成员函数Set的参数是一样的。但是这里又多个关节,所以就没有直接设置,而是在后面用Set方法设置。这个向量可以看成一个json吧,传的参数就是设定关节的位置(x,y)。

在创建了关节之后,千万别忘记下面的代码:

LStage.box2d.world.CreateJoint(revoluteJoint);

另外,Initialize方法传的参数是box2dweb的刚体对象,在LSprite中的 box2dBody就可以获取该LSprite添加的刚体。

3,固定动态刚体

在box2dweb中,刚体分动态和静态。静态刚体比较老实,就呆在原地不动;动态刚体很活泼,不停地在做运动。

拉登大叔其实也写过相关的文章,但是我没怎么看,于是就自己想了一个方法:首先在锁链的两端建立两个圆形静态刚体,然后把锁链两端的两块矩形刚体分别连在附近的圆形静态刚体上,圆形静态刚体就会帮忙把锁链拽着,不让它乱跑。但由于锁链中每个矩形刚体是动态的,所以他们还是可以互相牵扯的。

4,如果你自己在用lufylegend+box2dweb编写时看不到图象,该怎么办?

首先你要检测一下是否在最底层上加了一个LSprite,并且用这个LSprite的graphics画了一个不透明的背景。如果是的话,你可以把这个LSprite去掉,然后再看看有没有。如果还没有,可能有以下几种原因:(1)没有在用之前加入LStage.setDebug(true); (2)没有在使用box2dweb之前加入LStage.box2d = new LBox2d();

 

源代码在上面已经全部给出了,运行时需要配置一下引擎box2dweb和lufylegend,然后把引擎的压缩版本(文件名含.min)放在与html同级的文件目录下即可运行,测试愉快~

分类: box2D, html5 标签:

Cocos2d-x 3.0 开发(九)使用Physicals代替Box2D和chipmunk

2013年11月4日 没有评论

1、   概述

    游戏中模拟真实的世界是个比较麻烦的事情,通常这种事情都是交给物理引擎来做。首屈一指的是Box2D了,它几乎能模拟所有的物理效果。而chipmunk则是个更轻量的引擎,能够满足简单的物理需求,比如最常用的的碰撞检测等。这些引擎在使用的过程中有个令人讨厌的地方,它们参数太多了。通常为了初始化一个简单的场景要写很多代码。在cocos2d-x 3.0版本中,出现了一个新类族——physicals。它将Box2D或者chipmunk做了一层封装,使我们的上层调用有更友好的接口。它通过宏来切换使用哪种物理引擎,目前的版本只有chipmunk的实现,Box2D的实现没有写,所以手动将宏切换的话是不行的。另外,当前版本还是有bug的,下面会提到,先看效果图吧:

Cocos2d-x 3.0 开发(九)使用Physicals代替Box2D和chipmunk

2、 原理分析

    相信大家都对物理引擎的使用有所了解,篇幅有限,一些基本概念就不复述了。如果你曾经用过Box2D或者chipmunk,再使用这套封装,你只会有一种爽到爆的感觉。

    在这个版本中,物理世界的概念被加入到Scene中,即当创建一个场景时,就可以指定这个场景是否使用物理引擎。相对应的,每一个Sprite中也有body的概念。可以直接将body关联到Sprite上。Listener当然也不需要再弄一套东西来监听,只要注册到场景中就可以了。

    不知你听到这个改动有和感想,反正我是震惊了。

    接下来我们动手做一个吧。


3、创建场景

    首先,运行脚本创建一个新工程:testNewPhy,编译运行确保一切正常。 

    找到 CreateScene函数,更改scene的初始化。

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::createWithPhysics();
    scene->getPhysicsWorld()->setDebugDraw(true); //此句仅3.0 alpha0 有效
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);

    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

    更改create,创建一个支持物理的世界,打开debugDraw。两行就可以搞定了。

    打开debugDrawMask,两行就可以搞定了。DrawMask参数可以选择打开绘制哪些部分比如,Joint、Shape等等。    

    接下来,我们要将这个World传到Layer中。所以我们在HelloWorld类中加入一个函数。将这个world存起来。 

//……
	 void setPhyWorld(PhysicsWorld* world){m_world = world;}
private:
	PhysicsWorld* m_world;
}

    同时在creatScene创建layer完成后,将这个值设定上。

 // ……
    auto layer = HelloWorld::create();
    layer->setPhyWorld(scene->getPhysicsWorld());
// ……

    另外,我们更改一下menuItem的响应,来控制debugDraw的绘制:


  此函数已在3.0 alpha1中失效

void HelloWorld::menuCloseCallback(Object* pSender)
{
	if(m_world->isDebugDraw())
	{
		 m_world->setDebugDraw(false);
	}
	else
	{
		m_world->setDebugDraw(true);
	}
}


    使用如下函数

void HelloWorld::menuCloseCallback(Object* pSender)
{
	if(m_world->getDebugDrawMask() != PhysicsWorld::DEBUGDRAW_NONE)
	{
		 m_world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_NONE);
	}
	else
	{
		m_world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
	}

}

    

4、创建边界

    创建了物理世界,还要有东西才行。接下来,我们着手创建一个边界。我们可以方便的使用PhysicalsBody的create方法创建自己想要的物体。 

    在init中进行更改: 

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Point origin = Director::getInstance()->getVisibleOrigin();

    /////////////////////////////
   
	auto edgeSp = Sprite::create();
	auto body = PhysicsBody::createEdgeBox(visibleSize,3); //此句仅3.0 alpha0 有效
        auto body = PhysicsBody::createEdgeBox(visibleSize,PHYSICSBODY_MATERIAL_DEFAULT,3);
        edgeSp->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
        edgeSp->setPhysicsBody(body);this->addChild(edgeSp);edgeSp->setTag(0);
        return true;
}

    其中,PHYSICSBODY_MATERIAL_DEFAULT宏表示的是创建的Body的默认材质,3是边线宽度。编译运行我们会看到场景边上有红色的边界。

Cocos2d-x 3.0 开发(九)使用Physicals代替Box2D和chipmunk

5、添加元素

    我们先将点击响应搭建起来,在init中将touchEnable设置为true,重新onTouchesEnd方法:

void HelloWorld::onTouchesEnded(const std::vector<Touch*>& touches, Event *event)
{
	for(auto touch:touches)
	{
		auto location = touch->getLocation();
		addNewSpriteAtPosition(location);
	}
}

    然后我们来实现addNewSpriteAtPosition函数。关联body与sprite从未如此简单,我们只需创建一个body,创建一个sprite然后将body设置为sprite的body即可。 

void HelloWorld::addNewSpriteAtPosition(Point p)
{    
    auto sp = Sprite::create("1.png");
    sp->setTag(1);
    auto body = PhysicsBody::createBox(Size(80, 40));
    sp->setPhysicsBody(body);	
    sp->setPosition(p);
    this->addChild(sp);
}

    在这其中,当前版本的cocos2d-x 3.0有一个小问题。关联的时候,并未将body相应的owner设置为对应的sprite,我们需要修改sprite.cpp中的setPhysicsBody这个函数。增加最后一行。此bug已修复

void Sprite::setPhysicsBody(PhysicsBody* body)
{
    _physicsBody = body;
    _physicsBody->retain();
    _physicsBody->setPosition(getPosition());
    _physicsBody->setRotation(getRotation());
    _physicsBody->_owner = this;
}

     编译运行,我们点击屏幕即可动态创建元素了。

Cocos2d-x 3.0 开发(九)使用Physicals代替Box2D和chipmunk

6、碰撞检测

    碰撞检测的回调是在world中注册函数来实现的。首先我们在HelloWorld中声明一个变量。并重写OnEnter方法。

    碰撞检测的回调是在Scene中注册Listener来实现的。当有碰撞发生时,就会调用对应的Listener。所有的碰撞都使用EventListenerPhysicsContact类。我们可以通过重写它的onContactBegin、onContactPreSolve、onContactPostSolve、onContactSeperate方法来更改它的行为。

    下面函数已失效

//声明
PhysicsContactListener m_listener;

//实现

void HelloWorld::onEnter()
{
	Layer::onEnter();

	m_listener.onContactBegin = [=](const PhysicsContact& contact)
	{
		auto cnt = const_cast<PhysicsContact*>(&contact);

		auto sp = cnt->getShapeA()->getBody()->getOwner();
		int tag = sp->getTag();
		if(tag == 1)
		{
			Texture2D *texture = TextureCache::getInstance()->addImage("2.png");
			sp->setTexture(texture);
		}

		sp = cnt->getShapeB()->getBody()->getOwner();
		tag = sp->getTag();
		if(tag == 1)
		{
			Texture2D *texture = TextureCache::getInstance()->addImage("1.png");
			sp->setTexture(texture);
		}
		return true;
	};
	m_world->registerContactListener(&m_listener);
}

    应使用:

void HelloWorld::onEnter()
{
	Layer::onEnter();
	auto listener = EventListenerPhysicsContact::create();
	listener->onContactBegin = [=](EventCustom* event, const PhysicsContact& contact)
	{
		auto sp = (Sprite*)contact.getShapeA()->getBody()->getNode();
		int tag = sp->getTag();
		if(tag == 1)
		{
			Texture2D *texture = TextureCache::getInstance()->addImage("2.png");
			sp->setTexture(texture);
		}

		sp = (Sprite*)contact.getShapeB()->getBody()->getNode();
		tag = sp->getTag();
		if(tag == 1)
		{
			Texture2D *texture = TextureCache::getInstance()->addImage("1.png");
			sp->setTexture(texture);
		}
		return true;
	};	
	Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listener,10); //第二个参数是优先级,10是随意写的
}

    其中,我们将listener的onContactBegin方法重写。并通过shape->body->owner的方式来取到sprite。更改它的显示。最后将listener注册到m_world中。

    编译运行,然后点击menuItem,将debugDrow关闭,即可。

7、总结

    通过创建一个支持Physicals的场景,来创建物理系统。将body创建出来,并调用sprite的setPhysicsBody来为一个sprite设定body。通过PhysicsContactListener来创建一个Listener并通过registerContactListener将其注册,来处理碰撞。

 

    Demo下载:http://download.csdn.net/detail/fansongy/6502401

    Demo 下载(3.0 alpha1版本):http://download.csdn.net/detail/fansongy/6652089

    本篇博客出自阿修罗道,转载请注明出处,禁止用于商业用途http://blog.csdn.net/fansongy/article/details/14142323  


分类: box2D, chipmunk, cocos2d, 未分类 标签: