Box2D浮力效果

前些天有人问到Box2D如何实现浮力效果。今天我们就来学习一下。

Box2D浮力效果实现起来并不难。无非就是当刚体接触到水面时,对它施加一个向上的浮力。不过这个浮力受到刚体体积的影响,体积越大,收到的浮力也越大。同时还会因为受力点不在中心位置,而引起角度旋转。

额…。综合这些因素,用b2Body.ApplyForce实现起来似乎有些困难,所以我们要引入一个新的类:b2BuoyancyController。

b2BuoyancyController是b2Controller的一个子类,b2Controller类用来集成一些常用的功能,为用户免去复杂的算法。比如今天我们要学习的浮力效果,就可以用它的子类b2BuoyancyController来实现。

b2BuoyancyController和b2Body刚体不同。它是不可见的Box2D对象,也就是说,b2DebugDraw中不会绘制b2BuoyancyController的实例。但是当它与刚体发生碰撞时,Box2D会自动计算所浮力,并施加到刚体上,进而模拟漂浮效果。

b2BuoyancyController的构造函数非常简单,没有任何参数。

var bc:b2BuoyancyController = new b2BuoyancyController();

它常用的公共属性有:

  • normal:指定水面法向量的方向,也就垂直水面的方向,通常保持默认的(0,-1)不变。
  • offset:指定水面的偏移量。注意它的方向与AS3中的y轴方向相反,也就是说如果我们想设置水面位置的y为200,那么offset属性应该是-200/30。
  • density:设置水的密度。确切的讲,是设置液体介质的密度。我们知道,只有漂浮物的密度小于液体介质的密度时,才可以漂浮在该介质上。(当然,要讲到刚结构的轮船可以漂浮在水上,就要讲到排水量要大于轮船的重量上了,话题就扯远啦,就此打住。)
  • linearDrag:设置漂浮物在水中移动的阻尼值。或者我们可以把它想象成水的粘稠度。这个属性值越大,漂浮物移动的速度越慢。
  • angularDrag:设置漂浮物在水中旋转的阻尼值。
  • velocity:设置水流动的速度。

b2BuoyancyController用起来也很简单,首先创建一个b2BuoyancyController实例。

			//实例化b2BuoyancyController对象
			fuliController = new b2BuoyancyController();

然后设置它们的一些属性,比如水面的位置、方向、密度等等。

			//设置水面的法向量
			fuliController.normal.Set(0, -1);
			//设置水面的位置
			fuliController.offset = -200 / m_physScale;
			//设置水的密度,因为我们创建的刚体密度是3,所以水的密度要大于3
			fuliController.density = 5.0;
			//设置刚体在水中的移动阻尼
			fuliController.linearDrag = 10;
			//设置刚体在水中的旋转阻尼
			fuliController.angularDrag = 6;

然后,将fuliController添加到b2world世界中

			//将水面、浮力控制器添加到世界中
			world.AddController(fuliController);

创建刚体后,如果希望它与fuliController交互,实现漂浮效果,则用b2BanyancyController.AddBody方法,将漂浮物体添加到fuliController中

			//将刚体添加到水面上
			fuliController.AddBody(body);

是不是很简单?在下面的效果中,用鼠标拖动刚体,看看浮力的效果是不是很满意?!

完整的代码和注释如下:

package  
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2World;
	import Box2D.Dynamics.Controllers.b2BuoyancyController;
	import Box2D.Dynamics.Controllers.b2Controller;
	import flash.events.Event;
	import flash.events.MouseEvent;

	import flash.display.Sprite;
	/**
	 * ...
	 * @author ladeng6666
	 */
	public class FuliTest extends Sprite
	{
		//声明世界变量
		private var world:b2World;
		//定义浮力控制器,也就是水面
		private var fuliController:b2BuoyancyController;
		//因为b2BuoyancyController是不可见的,所以我们专门为它定一个图层
		private var controllerLayer:Sprite;
		//像素和米的转换关系
		private const m_physScale:Number = 30;

		public function FuliTest() 
		{
			//创建世界
			initWorld();
			//创建水面,浮力控制器
			createController();
			//创建刚体
			for (var i:int = 0; i < 8; i++) {
				createBodies();
			}

		}

		private function createBodies():void 
		{
			//创建刚体
			var body:b2Body = LDEasyBox2D.createBox(world, Math.random()*400+50, Math.random()*50+30, Math.random()*20+30, Math.random()*20+30);
			//将刚体添加到水面上
			fuliController.AddBody(body);
		}
		//创建水面、即浮力控制器
		private function createController():void 
		{
			//实例化b2BuoyancyController对象
			fuliController = new b2BuoyancyController();
			//设置b2BuoyancyController对象的一些基本属性
			//设置水面的法向量
			fuliController.normal.Set(0, -1);
			//设置水面的位置
			fuliController.offset = -200 / m_physScale;
			//设置水的密度,因为我们创建的刚体密度是3,所以水的密度要大于3
			fuliController.density = 5.0;
			//设置刚体在水中的移动阻尼
			fuliController.linearDrag = 10;
			//设置刚体在水中的旋转阻尼
			fuliController.angularDrag = 6;

			//将水面、浮力控制器添加到世界中
			world.AddController(fuliController);

			//因为b2BuoyancyController是不可见的,所以要为水面添加一个图层,专门绘制水面
			controllerLayer = new Sprite();
			addChild(controllerLayer);

			controllerLayer.graphics.lineStyle(1,0x0000ff,1);
			controllerLayer.graphics.moveTo(0,200);
			controllerLayer.graphics.lineTo(550,200);

			controllerLayer.graphics.lineStyle();
			controllerLayer.graphics.beginFill(0x0000ff,0.2);
			controllerLayer.graphics.drawRect(0, 200, 550, 300);
			controllerLayer.graphics.endFill();
		}

		private function initWorld():void 
		{
			//创建b2World世界
			world = LDEasyBox2D.createWorld();
			addChild(LDEasyBox2D.createDebug(world));
			LDEasyBox2D.createWrapWall(world, this);
			LDEasyBox2D.stage = this;

			addEventListener(Event.ENTER_FRAME, loop);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseEventHandler);
		}
		private function mouseEventHandler(e:MouseEvent):void 
		{
			//鼠标按下后拖动刚体,弹起后释放刚体
			if (e.type == MouseEvent.MOUSE_DOWN) {
				var body:b2Body = LDEasyBox2D.getBodyAtMouse(world);
				if (body != null) {
					LDEasyBox2D.startDragBody(world, body);
				}
			}else if (e.type == MouseEvent.MOUSE_UP) {
				LDEasyBox2D.stopDragBody(world);
			}
		}
		private function loop(e:Event):void 
		{
			LDEasyBox2D.updateWorld(world);
		}

	}

}

源文件下载地址

联系作者

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

17 Replies to “Box2D浮力效果”

  1. 楼主的教程前面都看了,在做游戏的时候遇到问题直接到这里查。写得不错,帮助很大,希望楼主继续写教程,好人一生幸福平安。谢了、请问这里能不能帮忙解答问题的呢?

  2. 风的效果也浮力效果类似,不过风的方向不一定是向上的,它更像是一个在有限区域内浮力效果。
    不过,我还是建议通过添加作用力实现风的效果!

  3. 这样说来,物体放入水中水面应该上升才对是不是也应该考虑进去呢?还有可不可以用许许多多很小的小球来模拟水呢?

回复 wssmrw

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