赞
踩
我们在游戏中,有时需要模拟真实的物理世界,让游戏世界中的物体都按照物理世界的规律进行运动。而要实现这种功能,就需要用到物理引擎。
在cocos2d-x中集成了2个物理引擎,一个是Chipmunk,一个是Box2D,Box2D的使用人数更多,文档,资料更加完成,所以我们一般选择Box2D。
cocos2d-x 3.0之后的版本对Box2D进行了封装,但是我们学习的时候还是最好了解着更加全面一点。
Box2D中有几个常见的概念。
世界代表一个遵循物理规律的空间,所有的物体都在世界中运动,世界具有创建销毁刚体,创建销毁关节等功能。
一块非常坚硬的物质,它上面的任何两点之间的距离都是完全不变的。刚体具体划分为静态刚体,动态刚体和菱柱刚体。
一块严格依附于物体(body)的2D碰撞几何结构(collision geometry)。形状通过关联附加到刚体上,这样刚体就具备了视觉上的外形。
它是一种用于把两个或者多个物体固定到一起的约束。Box2D支持的关节类型有:旋转,菱柱,距离等等。关节支持限制(limits)和马达(motors)。
一个关节限制限定了一个关节的运动范围。例如人类的胳膊肘只能做某一范围角度的运动。
一个关节马达能依照关节的自由度来驱动所连接的物体。例如,你可以使用一个马达来驱动一个肘的旋转。
世界代表了一个遵守某些物理定律的空间。
创建世界需要两个步骤,首先生成重力向量,然后根据重力向量生成世界。
//重力参数,向量
b2Vec2 gravity;
//设置水平方向和垂直方向上的加速度
gravity.Set(0.0f, -10.0f);
//创建世界
world = new b2World(gravity);
//是否允许物体休眠,如果允许休眠,可以提高物理世界的处理效率,只有在发生碰撞时才唤醒对象,节省CPU
world->SetAllowSleeping(true);
//是否开始连续碰撞,计算机智能把一段连续的时间分成许多离散的时间点,在对每个时间点之间的行为进行机选,如果时间点分割得不够细致,速度较快的两个物体碰撞时就可能产生碰撞,连续碰撞将启用特殊的算法避免该现象。
world->SetContinuousPhysics(true);
最后程序结束的时候,别忘了销毁世界哟。
//销毁世界
delete world;
世界类用于驱动模拟。你需要指定一个时间步和一个迭代次数。例如:
float timeStep = 1/60.0f;
int32 velocityIterations = 10;//速度迭代次数
int32 positioniterations = 1;//位置迭代次数
world->Step(timeStep, velocityIterations, positioniterations);
Box2D使用一个称为积分器(integrator)的算法。在离散的时间点上模拟物理方程。
Box2D还使用了大量的代码来调用约束求解器(constraint solver)。约束求解器可以解决约束问题。单个约束可以被完美的解决。但是有的时候,我们求解一个约束的同时会影响到另一个约束。为了解决所有问题,我们需要多次遍历所有约束。
约束求解器中有两个阶段:速度阶段和位置阶段。在速度阶段,求解器为使物体能够正确的移动需要计算必要的冲量。在位置阶段,求解器调整物体的位置来降低物体之间的重叠和连接的分离程度。
Box2D建议的迭代次数是速度阶段8次,位置阶段3次。我们可以根据自身的喜好改变这个数字。用更少的迭代次数会提高效率,但会影响精度。同样,用更多的迭代次数会降低效率但会提升模拟质量。
世界就是物体和关节的容器。我们可以获取世界中所有物体和关节,并遍历他们。
for (b2Body* a = world->GetBodyList(); a; a->GetNext()) {
if (a->GetUserData() != nullptr) {
Sprite* sp = (Sprite* )a->GetUserData();
sp->setPosition(Vec2(a->GetPosition().x*PTM_PATIO, a->GetPosition().y*PTM_PATIO));
sp->setRotation(-1*CC_RADIANS_TO_DEGREES(a->GetAngle()));
}
}
我们已经知道了刚体的概念,那么该怎么创建一个刚体呢?
创建刚体需要以下几个步骤。
1.创建一个刚体定义bodyDef
2.世界根据刚体定义创建刚体
3.用形状(shape),摩擦(friction),密度(density)等来定义fixture
4.在body上来创建fixture
auto sprite = Sprite::create("zh.png");
sprite->setPosition(Vec2(p.x, p.y));
addChild(sprite);
//物体定义
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;//物体类型为动态刚体
bodyDef.position.Set(p.x/PTM_PATIO, p.y/PTM_PATIO);
b2Body* body = world->CreateBody(&bodyDef);//创建物体
body->SetUserData(sprite);//将精灵放置到物体的UserData属性中,这样便于我们从物体中获取相关联的物体
//定义两米见方的盒子形状
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(1, 1);//中心点为1,1,盒子左下角为0,0.这样大小可以确定
//夹具定义
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
//设置密度
fixtureDef.density = 1.0f;
//设置摩擦系数
fixtureDef.friction = 0.4f;
//设置弹性系数
fixtureDef.restitution = 0.5f;
//使用夹具固定形状到物体
body->CreateFixture(&fixtureDef);
上面创建的是一个动态刚体,如果只需要一个静态刚体,可以去掉
bodyDef.type = b2_dynamicBody;//物体类型为动态刚体
默认情况下,body 是静态的。同时,密度,摩擦系数,弹性系数也没有意义了。
物体的创建和摧毁都是通过世界来完成的。世界创建物体,摧毁物体,完成内存的分配和回收。物体有动态和静态的。静态的物理没有质量,动态的物体有质量。
无论物体是静态还是动态的。摧毁方式都是一样的。
world->DestroyBody(body);
body = NULL;
形状就是物体上的碰撞几何结构。另外形状也可以用于定义物体的质量。也就是说,我们指定密度,Box2D根据形状可以计算出质量。
形状具有摩擦和恢复的性质。形状用于属于某物体,单个物体可以拥有多个形状。形状是抽象类,所以在Box2D中可以实现许多类型的形状。
形状定义用于创建形状。通用的形状数据会保存在b2Shape中,特殊的形状数据会保存在其派生类中。其派生类有b2PolygonShape,b2EdgeShape,b2CircleShape等,我们可以直接使用意见定义好的形状。
1)摩擦和恢复
摩擦可以使对象逼真地沿着其他对象滑动。摩擦参数设置在0到1之间。0意味着没有摩擦,1会产生强摩擦。
恢复可以使对象弹起。恢复的值也是0到1之间。0的意思是物体不会弹起,这称为非弹性碰撞。1的意思是物体的速度会得到精确的反射。这称为完全弹性碰撞。恢复是通过这样的公式计算的。
2)筛选
碰撞筛选是为了防止某些形状发生碰撞的系统。Box2D通过种群和组支持了这样的碰撞筛选。
Box2D支持16个种群,对于任何一个形状你都可以指定它属于哪个种群。你还可以指定这个形状可以和其他那些种群发生碰撞。例如,你可以在一个多人游戏中指定玩家之间不会碰撞,怪物之间也不会碰撞,但是玩家和怪物会发生碰撞。这是通过掩码来完成的。
playerShapeDef.filter.categoryBits = 0x0002;
monsterShapeDef.filter.categoryBits = 0x0004;
playerShape.filter.maskBits = 0x0004;
monsterShapeDef.filter.maskBits = 0x0002;
碰撞组可以让你指定一个整数的组索引。你可以让同一个组的所有形状总是相互碰撞(正索引)或者永远不碰撞(负索引)。组索引通常用于一些以某种方式关联的事物。就像汽车的部件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。