掉落的苹果——b2Body刚体

上一节中,我们认识了Box2D世界,学习了它的创建方法。今天我们一起来扮演牛顿,认识一下Box2D世界中掉落的苹果。在开始之前,我们想先说明两个内容:
1. Box2D中的计量单位是米

在Box2D中的计量单位是米m,而不是Flash中的像素px,在布置坐标时,要进行一个转换,1米=30个像素。所以Box2D中(a,b)点对于Flash中的(a*30,b*30)的位置,或者说Flash中的(c,d)位置对应Box2D中的(c/30,d/30)位置。

2. Box2D用b2DebugDraw进行模拟调试

Box2D是一个物理引擎,不会向Flash显示列表中添加任何显示对象。不过Box2D中有一个b2DebugDraw类,可以绑定一个显示对象,进行模拟调试。至于如何在Box2D中添加显示对象,在以后的学习中,我们再深入讨论。
b2DebugDraw的用法:

  1. 创建一个空的Sprite对象debugSprite,并添加到舞台中
  2. 创建一个b2DebugDraw对象debugDraw,并设置它的m_sprite属性值为debugSprite.
  3. 用world的SetDebugDraw方法,绑定debugDraw

3. World的更新:

Box2D的和Flash一样,需要实时更新,Flash中有ENTER_FRAME,b2world有step,其参数一表示模拟的时长,二表示限制碰撞后检测程式运行的次数,防止死循环。

刚体与DisplayObject的区别

掉落的苹果实际上是Box2D里的一个刚体b2Body,当然可以把他理解成Flash中的sprite,不过:

A. b2Body

1. 它是Box2D中的一个物理模拟对象,不是可显示对象,但是可以通过b2DebugDraw来进行调试模拟
2. 它的计量单位是米m
3. 它通过b2World.createBody()来创建

B. Sprite

1. 它是显示对象,通过addChild添加即可显示
2. 它的计量单位是像素px
3. 通过new来创建,然后addChild添加后直接显示在舞台上

刚体创建的过程,与AS中new一个对象,addChild到舞台中显示比起来要复杂很多,刚体的创建可以看做是一个工厂模式,根据你的需求(b2BodyDef)生产指定的刚体

刚体的创建过程可以分为两个步骤:

1. 创建刚体需求b2BodyDef。

var bodyRequest:b2BodyDef = new b2BodyDef();

在这个需求中,我们可以包括下面的内容:
a) position:刚体的坐标位置,单位是米m
b) angle:刚体的角度
c) 其他,在以后的学习中我们会陆续介绍

2. b2World工厂用createBody方法创建刚体产品,并自动将其添加到Box2D世界中,然后返回该刚体。

body=world.CreateBody(bodyRequest);

定义刚体形状

到目前位置,我们已经成功添加了一个刚体。
讲个题外话,今天我同时的老婆生了,听到这个消息,你会问的第一个问题是什么?”男孩,女孩?”答对了!
把刚体当成我们的孩子吧。创建一个刚体后,你应该问什么?”方的,圆的?”(这孩子好挫啊!)非常正确。刚体的形状不是与生俱来的,需要用一个b2Shape的子类对象指定。刚体形状的创建过程同样也是一个工厂模式。

这个创建过程与b2Body的创建过程是一样的。我们的形状需求b2ShapeDef可以报考下面的内容:

a) density:质量
b) friction:表面摩擦力
c) restitution:表面张力,这个值越大,刚体越硬
d) SetAsBox:设置刚体为矩形
e) 其他,在以后的学习中我们会陆续介绍

好了创建b2Body刚体的完整过程就讲完了。我们在总结一下:

1. 创建刚体需求b2BodyDef
2. Box2D世界工厂根据需求用createBody方法创建刚体
3. 创建刚体形状需求b2ShapeDef的子类对象
4. B2Body工厂根据需求用createShape方法创建形状

实例练习

掌握了上面的内容,我相信完成下面的这个练习就不是什么难事了。
点击舞台的任意位置创建一个方形刚体。

代码

package
{
	import Box2D.Collision.b2AABB;
	import Box2D.Collision.Shapes.b2PolygonDef;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2DebugDraw;
	import Box2D.Dynamics.b2World;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;

	/**
	 * http://www.ladeng6666.com
	 * @author ladeng6666
	 */
	public class Main extends Sprite
	{
		private var world:b2World;
		private var body:b2Body;

		public function Main()
		{
			createWorld();
			createDebug();
			createBody(stage.stageWidth/2,0);
			createGround();

			addEventListener(Event.ENTER_FRAME, loop);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown);
		}

		private function onStageMouseDown(e:MouseEvent):void
		{
			createBody(mouseX,mouseY);
		}

		private function loop(e:Event):void
		{
			world.Step(1/30, 10);
		}

		private function createWorld():void
		{
			//1.创建一个环境
			var environment:b2AABB = new b2AABB();
			environment.lowerBound = new b2Vec2( -100, -100);
			environment.upperBound = new b2Vec2(100, 100);
			//2.声明重力
			var gravity:b2Vec2 = new b2Vec2(0, 10);
			//3.睡着的对象是否模拟
			var doSleep:Boolean = true;
			//4.创建b2World世界
			world = new b2World(environment, gravity, doSleep);
		}

		private function createDebug():void
		{
			var debugSprite:Sprite = new Sprite();
			addChild(debugSprite);

			var debugDraw:b2DebugDraw = new b2DebugDraw();
			debugDraw.m_sprite = debugSprite;
			debugDraw.m_drawScale = 30.0;
			debugDraw.m_fillAlpha = 0.5;
			debugDraw.m_lineThickness = 1.0;
			debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;

			world.SetDebugDraw(debugDraw);
		}

		private function createBody(posX:Number,posY:Number):void
		{
			//1.创建刚体需求b2BodyDef
			var bodyRequest:b2BodyDef = new b2BodyDef();
			bodyRequest.position.Set(posX / 30, posY / 30);//记得米和像素的转换关系
			//2.Box2D世界工厂更具需求创建createBody()生产刚体
			body=world.CreateBody(bodyRequest);
			//3.创建敢提形状需求b2ShapeDef的子类
			var shapeRequest:b2PolygonDef = new b2PolygonDef();
			//详细说明我们的需求
			shapeRequest.density = 3;
			shapeRequest.friction = 0.3;
			shapeRequest.restitution = 0.2;
			shapeRequest.SetAsBox(1, 1);
			//4.b2Body刚体工厂根据需求createShape生产形状
			body.CreateShape(shapeRequest);
			body.SetMassFromShapes();

		}

		private function createGround():void
		{
			//1.创建刚体需求b2BodyDef
			var bodyRequest:b2BodyDef = new b2BodyDef();
			bodyRequest.position.Set(stage.stageWidth/2 / 30, stage.stageHeight/30);//记得米和像素的转换关系
			//2.Box2D世界工厂更具需求创建createBody()生产刚体
			body=world.CreateBody(bodyRequest);
			//3.创建敢提形状需求b2ShapeDef的子类
			var shapeRequest:b2PolygonDef = new b2PolygonDef();
			//详细说明我们的需求
			shapeRequest.density = 0;
			shapeRequest.friction = 0.3;
			shapeRequest.restitution = 0.2;
			shapeRequest.SetAsBox(stage.stageWidth/30, 1);
			//4.b2Body刚体工厂根据需求createShape生产形状
			body.CreateShape(shapeRequest);
			body.SetMassFromShapes();
		}

	}

}

 

联系作者

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

42 Replies to “掉落的苹果——b2Body刚体”

  1. 2. b2World工厂用createBody方法创建刚体产品,并自动将其添加到Box2D世界中,然后返回该刚体。

    body = world.CreateBody(bodyRequest);

    你这个body是什么啊 也没定义啊

  2. 我不明白
    body在这里只是一个工厂的概念吗?
    如果说每个刚体有一个对应的对象,那对应的对象是bodyRequest吗?
    还有createDebug()应该是控制刚体显示的,但是我看不出来那句话显示了所有刚体啊

    不好意思啊,我菜鸟,才学as一个月,然后上网看到有引擎,所以好奇想学。

  3. 其实我就是想知道愤怒的小鸟这种游戏,小鸟是一个sprite,但是我怎么让这个小鸟变成一个刚体呢?

  4. createDebug是我自定义的一个函数,实际上绘制刚体对象的是b2DebugDraw类。
    你可以把b2BodyDef和b2PolygonDef看成是body必备元素,你所说的对应的对象是只的body.useData,这个属性用来绑定一个显示对象。

  5. 拉登兄你好,请教你一个问题,引擎中的密度到底有什么作用?我分别将同等大小的2个下落的小球的密度设置为不同的值,可是它们的下落速度还是一样..如果我要实现同样大小尺寸的2个物理同时下落,但要求这2个物体的下落速度不一样该怎么做?谢谢,请你务必回答我这2个问题,再次感谢!!

  6. 感谢拉登兄的回复!!
    只是有一点我还不太明白,就是引擎中物体的密度到底有什么作用?它究竟能改变物体的什么?请再回答我这一个问题,再次感谢!!

  7. 密度会影响物体的质量,质量越大,物体的惯性越大,因此物体的动量维持的就越久。
    简单的讲,相同体积的静止的刚体上施加相同的力,密度大的移动的较慢,密度小的移动的较快。
    同样的道理,以相同速度移动的同体积刚体,密度小的停下来的比较早,而密度大的物体停下的较晚。
    不知道,我这么解释你能听明白吗?

  8. private function createBody(posX:Number,posY:Number):void
    private function createGround():void

    这两个写鼠标的那个就行了,上来就创建一个刚体虽然设计上没问题,但是以新手的角度来解读的话,他会以为你的最后一句有另外的含义,起始,只不过就是创建一个刚体而已,没有太对必要的!

  9. 拉登大哥,除了debugDraw其他的都看懂了,谢谢你提供这个好教程,
    请你讲解一下debugDraw的作用,用法,和程序逻辑,最好打个比喻,好理解,
    万分感谢

  10. Box2D只是一个2D物理引擎,它通过内置的算法,对添加的b2Body刚体模拟物理运动,但是b2Body并不是显示对象,也就是说无法通过addChild添加到舞台上并显示。Box2D中通过b2DebugDraw类,绑定一个Sprite对象,并通过调用它的Graphics的绘图API,绘制出b2Body,让开发者可以调试自己的应用。当然如果要添加自己的MovieClip,可以参考我的教程刚体的上衣——b2BodyDef.userData
    这样解释,你可以明白了吗?至于怎么debugDraw的中文怎么说,随便了,知道自己明白就行。

  11. 谢谢,感谢你的耐心及时的回答,再追问一个问题,绘制出b2Body刚体与那个Sprite对象是什么关系,b2Body刚体是Sprite对象的子对象吗,那个Sprite对象在舞台上吗(是作为一个空的对象放到舞台上吗,只是我们看不见?)

  12. 今天研究了一天的box2d,对box2d多少理解了一些,多谢你的教程,你的教程发现是网上写的最好的,建议可以出这方面的书,
    我想问下,box2d较成熟的应用有没有,有没有通过它做出来的游戏什么的

    有几个名称我翻译的,你看怎么样(我觉得哪些类和方法最好有个贴切的中文翻译,毕竟我们的母语是中文,中文好理解,我上次看box2d头都大了,相信很多初学者和我一样的感受,今天硬着头皮看下去,才基本上看懂了):
    aabb:轴对齐边界框, 其实就是矩形边界框,表示wold的有效范围
    debugDraw:调试用绘制刚体

  13. 从事as开发快一年了,对box2d一直是心里的一道坎,一直都过不去,在网上查了很多的资料,要么全是英文,要么不清不楚,现在已看了两章,感觉比网上任何一个helloworld例子让人更易于理解,楼主对于每一个提问者都如此耐心回答,相信别人和我也一样别人提出的问题其实我也一样不清楚,可再看作者的解释脑海里是如此的清晰明了,感谢作者如此无私奉献,相信有了你的这一套教程,我box2d一定可以学好.

  14. 谢谢你的评论,其实我一开始学习box2D时,也是摸不着头脑,官方的教程也很专业,所以自己硬着头皮去研究,然后用傻瓜式的语言重新描述,能给大家带来帮助,我很高兴,也希望你能帮我推广,让更多的网友关注我的教程,谢谢啦!

  15. debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;

    等号右边,中间的那一条 | 是什么意思啊?

发表回复

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