首页 > 学院 > 开发设计 > 正文

Box2d

2019-11-09 15:47:40
字体:
来源:转载
供稿:网友

一、概述

1、关于

Box2D是个二维刚体仿真库, 用于编写游戏。程序员可以使用它, 让游戏中的物体运动起来更真实, 让 游戏世界更具交互性。以游戏的角度来看,物理引擎只是个程序性动画系统。(PRocedural animation)做动画常有两种方法, 一种是预先准备好动画所需的数据,比如图片,再一帧一帧地播放。另 一种是以一定方法,动态计算出动画所需的数据,根据数据再进行绘图。 从这种角度看,预先准备的,可称为数据性动画,动态计算的可称为程序性动画。这个区别,就类似以前我们做历史题和数学题,做历史题,记忆很重要,也就是答案需要预先准备好的。做数学题,方法就很重要,答案是需要用方法推导出来的。 Box2D就是用物理学的方法,推导出那游戏世界物体的位置,角度等数据。而Box2D也仅仅推导出数 据,至于得到数据之后怎么处理就是程序员自己的事情了。)Box2D用可移植的C++来写成,它定义的大部分类型都有b2前缀, 希望这能有效消除Box2D和你自己 的游戏引擎之间的名字冲突。

2、概念

形状(shape): 2D几何对象, 比如圆形(circle)或多边(polygon)。刚体(rigid body): 十分坚硬的物质, 坚硬得像钻石,它上面任意两点之间的距离都保持不变。在后面的讨论中,我们用 物体(body)来代替刚体。夹具(fixture): fixture将形状绑定到物体之上, 并有一定的材质属性, 比如密度(density), 摩擦(friction)和恢复 (restitution)。约束(constraint): 约束是个物理连接, 用于消除物体的自由度。在2D中, 物体有3个自由度(水平,垂直,旋转)。如果我 们把一个物体钉在墙上(像钟摆那样), 那就把它约束到了墙上。这个时候,此物体就只能绕着钉子旋转 , 所以这个约束消除了它2个自由度。(注:简单的说, 需要用几个参数来确定物体的空间状态, 这个物体就有几个自由度。在二维中,完全 没有约束的条件下, 我们要确定物体的状态, 要有x坐标, y坐标, 旋转角这三个参数, 所以自由度为3。 如果物体被钉在墙上, 只要有旋转角,就可以完全确定物体的状态,有了钉子这个约束,物体自由度 就变成了1。)接触约束(contact constraint): 一种特殊的约束, 设计的目的是为了防止刚体被穿透, 也用于模拟摩擦和恢复。接触约束不用你来创 建, 它们会自动被Box2D生成。关节(joint): 关节就是种约束, 用于将两个或多个body固定到一起。Box2D支持不同的关节类型:转动(revolute),棱 柱(prismatic),距离(distance)等。一些关节可以有限制(limits)和马达(motors)。关节限制(joint limit) : 关节限制限定了一个关节的运动范围。例如人类的胳膊肘只能在某一角度范围内运动。关节马达(joint motor): 根据关节的自由度, 关节马达可以驱动关节所连接的物体。例如, 你可以使用一个马达来驱动一个 肘的旋转。世界(world): 一个物理世界就是各种, 刚体(bodies), 夹具(fixtures), 约束(constraints)相互作用的集合。 Box2D支持创 建多个世界, 但这通常没有必要。

3、单位

Box2D使用浮点数, 所以必须使用一些公差来保证它正常工作。这些公差已经被调谐得适合米-千克-秒(MKS)单位。 尤其是, Box2D被调谐得能良好地处理0.1到10米之间的移动物体。这意味着从罐头盒 到公共汽车大小的对象都能良好地工作。静态的物体就算到50米都没有大问题。

二、应用

1、创建物理世界

1、创建世界: 每个Box2D程序开始时都会创建一个b2World对象。b2World是物理枢纽(physics hub), 用于管理内存 、对象和模拟。根据自己的实际情况, 你可以在栈, 堆或数据区中创建出world。 创建Box2D的world很简单。首先, 我们要定义重力矢量,另外还要告诉world是否允许body在静止时 休眠。休眠中的body不需要任何模拟。 b2Vec2 gravity; //重力 gravity.Set(0.f, -20.f);//竖直向下 现在可以创建world对象了。注意,在这里我们是在栈中创建world, 所以world不能离开它的作用域。 world = new b2World(gravity);//创建一个物理世界 world->SetAllowSleeping(true);//允许睡眠 world->SetContinuousPhysics(true);//允许碰撞2、创建地面盒 2.1: 用位置(position), 阻尼(damping)等来定义body 2.2:通过world对象来创建body 2.3:用形状(shape), 摩擦(friction), 密度(density)等来定义 fixture 2.4:在body上来创建fixture //创建刚体定义 b2BodyDef groundBodyDef;//创建地面 groundBodyDef.position.Set(0.0f, 0.0f);//原点 刚体定义中位置设置 //创建刚体 world创建刚体 b2Body* groundBody = world->CreateBody(&groundBodyDef); b2EdgeShape groundBox; //依次定义盒子的边界 //下 groundBox.Set(b2Vec2(-visabliSize.width / PTM_RATIO, 0.7), b2Vec2(2 * visabliSize.width / PTM_RATIO, 0.7)); groundBody->CreateFixture(&groundBox, 0); //上 groundBox.Set(b2Vec2(-visabliSize.width / PTM_RATIO, visabliSize.height / PTM_RATIO), b2Vec2(2 * visabliSize.width / PTM_RATIO, visabliSize.height / PTM_RATIO)); groundBody->CreateFixture(&groundBox, 0); //左 groundBox.Set(b2Vec2(0 / PTM_RATIO, 0), b2Vec2(0 / PTM_RATIO, visabliSize.height / PTM_RATIO)); groundBody->CreateFixture(&groundBox, 0); //右 groundBox.Set(b2Vec2(visabliSize.width / PTM_RATIO, 0), b2Vec2(visabliSize.width / PTM_RATIO, visabliSize.height / PTM_RATIO)); groundBody->CreateFixture(&groundBox, 0);

2、创建动态刚体,并绑定精灵

1、创建精灵 auto sp = Sprite::create("1.png"); sp->setPosition(240,160); this->addChild(sp); 2、创建刚体,绑定精灵 b2BodyDef ballBodydef; ballBodydef.type = b2_dynamicBody; //动态刚体 ballBodydef.position.Set(sp->getPosition().x / PTM_RATIO, sp->getPosition().y / PTM_RATIO); //设置刚体的位置 ballBodydef.userData = sp; //绑定精灵 b2Body * ballBody = world- >CreateBody(&ballBodydef); //创建刚体 //我们创建一个多边形shapde, 并将它附加到fixture定义上。我们先创建一个box shape: b2PolygonShape blockShape; blockShape.SetAsBox(0.3f, 0.3f); //接下来我们使用box创建一个fixture定义 b2FixtureDef ballShapeDef; ballShapeDef.shape = &blockShape; ballShapeDef.density = 50.0f; //设置密度 ballShapeDef.friction = 0.5f; //设置摩擦 ballShapeDef.restitution = 0.2f; //设置恢复 //刚体添加夹具 ballBody->CreateFixture(&ballShapeDef);

3、模拟(Box2d的)世界

Box2D使用了一个叫积分器(integrator)的数值算法。 积分器在离散的时间点上模拟连续的物理方程。 它与传统的游戏动画循环一同运行。我们需要为Box2D选取一个时间步。通常来说用于游戏的物理 引擎需要至少 60Hz 的速度,也就是 1/60 的时间步。你可以使用更大的时间步,但是你必须更加小心地 为你的世界调整定义。我们也不喜欢时间步变化得太大,所以不要把时间步关联到帧频(除非你真的 必须这样做)。直截了当地,这个就是时间步: float32 timeStep = 1.0f / 60.0f; 除积分器外,Box2D代码还使用了约束求解器(constraint solver)。约束求解器用于解决模拟中的所有约束,一次一个。单个的约束会被完美的求解,然而当我们求解一个约束的时候,我们就会稍微耽误另一 个。要得到良好的解,我们需要多次迭代所有约束。 约束求解有两个阶段:速度、位置。在速度阶段,求解器会计算必要的冲量,使得物体正确运动。而在位置阶段,求解器会调整物体的位置,减少物体之间的重叠。每个阶段都有自己的迭代计数。此外,如果误差已足够小的话,位置阶段的迭代可能会提前退出。 对于速度和位置,建议的Box2D迭代次数都是10次。你可以按自己的喜好去调整这个数字,但要记 得它是性能与精度之间的折中。更少的迭代会增加性能但降低精度,同样地,更多的迭代会降低性 能但能提高模拟质量。对于这个简单示例,我们不需要多次迭代。这是我们选择的迭代次数。 int32 velocityIterations = 10; int32 positionIterations = 10; 完整代码如下: void Box2DTest::update(float dt){ float timeStep = 1.0f / 60.0f;//更新时间(物理世界刷新次数) int32 velocityIterations = 10;//速度迭代次数 int32 positionIterations = 10;//位置迭代次数 //刷新 world->Step(timeStep, velocityIterations, positionIterations); //遍历物理世界中的刚体 for (b2Body *b = world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData()!=nullptr) { //获取刚体绑定的精灵 auto sprite = (Sprite*)b->GetUserData(); //更新刚体绑定的精灵的位置 sprite->setPosition( Vec2( b->GetPosition().x *PTM_RATIO, b->GetPosition().y * PTM_RATIO) ); sprite->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) ); } }}

4、刚体碰撞的监听

代码如下: .h文件:#include <iostream>#include "Box2D/Box2D.h"#include "cocos-ext.h"#include "cocos2d.h"using namespace std;USING_NS_CC_EXT;USING_NS_CC;class MyContactListener:public b2ContactListener{public: b2World* _world; Layer* _layer; MyContactListener(); MyContactListener(b2World* w,Layer* c); ~MyContactListener(); virtual void BeginContact(b2Contact* contact);//碰撞开始 virtual void EndContact(b2Contact* contact);//碰撞结束 virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);//持续接触时响应 //b2Manifold结构含有一个法向量和最多两个的接触点。向量和接触点都是相对于局部坐标系。为方便接触求解器处理,每个接触点都存储了法向冲量和切向(摩擦)冲量。 virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse);//持续接触时响应,调用完preSolve后调用}; .cpp文件:#include "MyContactListener.hpp"MyContactListener::MyContactListener(){}MyContactListener::MyContactListener(b2World* w,Layer* c){ _world = w; _layer = c;}MyContactListener::~MyContactListener(){}void MyContactListener::BeginContact(b2Contact *contact){ log("BegainContact");}void MyContactListener::EndContact(b2Contact* contact){ log("EndContact");}void MyContactListener::PreSolve(b2Contact* contact, const b2Manifold* oldManifold){ log("PreSolve");}void MyContactListener::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse){ log("PostSolve"); //Solve计算完成后调用的函数 float force = impulse->normalImpulses[0];//受到的力 if (force>2) { b2Body* bodyA = contact->GetFixtureA()->GetBody(); b2Body* bodyB = contact->GetFixtureB()->GetBody(); auto spriteA = (Sprite*)bodyA->GetUserData(); auto spriteB = (Sprite*)bodyB->GetUserData(); if (spriteA != nullptr && spriteB != nullptr) { spriteA->setTag(4); spriteB->setTag(4); } }}最后给物理世界设置碰撞监听:listener=new MyContactListener(world,this);world->SetContactListener(listener);
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表