Box2D是一个用于游戏的2D刚体仿真库。它的核心目的是为了模拟真实的2D环境,让物体运动更加真实,交互性更好。本文主要介绍一些使用Box2D的基础知识,并且记录如何在没有图形化界面的Linux服务器上使用它。(例子就是我使用的这台:D)。
开始准备安装Box2D
github地址:https://github.com/erincatto/Box2D
$git clone https://github.com/erincatto/Box2D
完成之后因为没有图形化界面,去CMakeCache.txt中把BOX2D_BUILD_EXAMPLES设置从ON改成OFF
$cd Box2D/Box2D/Build/ && vim CMakeCache.txt
然后用cmake创建makefile,这里如果有库的缺失补全一下就可以了
$cmake ..
然后直接make会有一处编译错误,找到地方把”nullptr”改成”NULL”或者0
$make $sudo make install $locate Box2D
最后使用locate Box2D查看Box2D的安装情况,默认是在/usr/local/相关目录,至此安装完毕。
一些基础知识
世界(world)
虚拟化的物理时间,里面包含了各类刚体的演算。
刚体(rigid body)
最基础的单位,代表一个不会发生形变的实体,我们通常用body来指代刚体。
夹具(fixture)
个人喜欢叫它为材质,依附在刚体上,约束刚体的形状(shape),密度(density),摩擦(friction),恢复(restitution)等特性。
单位
使用米-千克-秒(MKS)单位制。
场景测试
一个无重力环境的2D场景,正方形40mx40m,正方形边是厚2m的静态围墙,正方形的中心是世界中心;
有bodyA(正方形2mx2m,坐标-10.0f, 10.0f)与bodyB(正方形2mx2m,坐标10.0f, 10.0f),对A施加一个向右的力,对B施加一个向左的力;
详细的释义直接附加在代码里:
//file:myBox2D.cpp #include <stdint.h>; #include <stdio.h>; #include <Box2D/Box2D.h>; int32_t main() { //创建世界 b2Vec2 gravity(0.0f, -0.0f); //无重力,若要设置自然重力为(0.0f, -9.8f) b2World world(gravity); world.SetAllowSleeping(false); //静止的物理也会参与碰撞检测 //构建一个边长40m正方形 //创建下面围墙 b2BodyDef wall; b2PolygonShape wallBox; wall.type = b2_staticBody; wall.position.Set(0.0f, -1.0f); b2Body *wallDown = world.CreateBody(&wall); //下面围墙的形状 wallBox.SetAsBox(20.0f, 1.0f); //设置材质 wallDown->CreateFixture(&wallBox, 0.0f); //创建上面围墙 wall.position.Set(0.0f, 21.0f); b2Body *wallUp = world.CreateBody(&wall); //上面围墙的形状 wallBox.SetAsBox(20.0f, 1.0f); //设置材质 wallUp->CreateFixture(&wallBox, 0.0f); //创建左面的围墙 wall.position.Set(-21.0f, 20.0f); b2Body *wallLeft = world.CreateBody(&wall); //左面围墙的形状 wallBox.SetAsBox(1.0f, 20.0f); wallLeft->CreateFixture(&wallBox, 0.0f); //创建右面的围墙 wall.position.Set(21.0f, 20.0f); b2Body *wallRight = world.CreateBody(&wall); //右面围墙的形状 wallBox.SetAsBox(1.0f, 20.0f); wallRight->CreateFixture(&wallBox, 0.0f); //创建二个动态的body b2BodyDef bodyDefA; bodyDefA.type = b2_dynamicBody; bodyDefA.position.Set(-10.0f, 10.0f); b2Body *bodyA = world.CreateBody(&bodyDefA); b2BodyDef bodyDefB; bodyDefB.type = b2_dynamicBody; bodyDefB.position.Set(10.0f, 10.0f); b2Body *bodyB = world.CreateBody(&bodyDefB); //为这个动态bodyA设置形状 b2PolygonShape dynamicBox; dynamicBox.SetAsBox(1.0f, 1.0f); //为这个动态body设置材质 b2FixtureDef fixtureDef; fixtureDef.shape = &dynamicBox; fixtureDef.density = 1.0f; //密度 fixtureDef.friction = 0.3f; //摩擦系数 fixtureDef.restitution = 1.0f; //恢复系数 bodyA->CreateFixture(&fixtureDef); bodyB->CreateFixture(&fixtureDef); //施加一点力 bodyA->ApplyLinearImpulse(b2Vec2(10, 0), bodyA->GetWorldCenter(), true); bodyB->ApplyLinearImpulse(b2Vec2(-10, 0), bodyB->GetWorldCenter(), true); //迭代的时间间隔,这里是1秒60次 float32 timeStep = 1.0f / 60.0f; int32 velocityIterations = 6; //碰撞时速度迭代 int32 positionIterations = 2; //碰撞时位置迭代 for (int32 i = 0; i < 600; ++i) { world.Step(timeStep, velocityIterations, positionIterations); b2Vec2 positionA = bodyA->GetPosition(); float32 angleA = bodyA->GetAngle(); printf("A %4.2f %4.2f %4.2f\n", positionA.x, positionA.y, angleA); b2Vec2 positionB = bodyB->GetPosition(); float32 angleB = bodyB->GetAngle(); printf("B %4.2f %4.2f %4.2f\n", positionB.x, positionB.y, angleB); for (b2Contact* c = world.GetContactList(); c; c = c->GetNext()) { b2Body *A = c->GetFixtureA()->GetBody(); b2Body *B = c->GetFixtureB()->GetBody(); b2Vec2 positionA = A->GetPosition(); float32 angleA = A->GetAngle(); b2Vec2 positionB = B->GetPosition(); float32 angleB = B->GetAngle(); printf("b2Contact A:%4.2f %4.2f %4.2f|B:%4.2f %4.2f %4.2f\n", positionA.x, positionA.y, angleA, positionB.x, positionB.y, angleB); } } return 0; }
编译后可查看效果
$g++ -o test myBox2D.cpp -lBox2D $./test
关于代码的一些解释
b2BadyDef的type类型:
b2_staticBody:静态物理,质量无穷大,不会因为碰撞移动;不会和其它static或kinematic物体相互碰撞,例如上面样例的围墙;
b2_kinematicBody:可以行动的、质量无穷大的;
b2_dynamicBody:可以受力运动,也可以根据用户指令固定移动;拥有完整的fixtures各种特性;
关于fixtures:
形状(shape),密度(density),摩擦(friction)从字面意思就比较好理解了,说一下恢复(restitution):
官方文档给的释义:恢复可以使对象弹起。恢复的值通常设置在0到1之间。想象一个小球掉落到桌子上,值为0表示着小球不会弹起, 这称为非弹性碰撞。值为1表示小球的速度跟原来一样,只是方向相反,这称为完全弹性碰撞。
若2个均有restitution的刚体碰撞,那么取最大的restitution作为最终计算值。
关于施加力:
ApplyForce:增加力;
ApplyLinearImpulse:增加冲量;具体怎么换算为速度未知,同等单位下,相同时间比ApplyForce运动的更远;
ApplyTorque:角力矩;
ApplyAngularImpulse:角动量;
SetTransform:瞬移物体;
关于迭代:
时间迭代:可以抽象成帧的概念,比如我们需要一秒60帧那么就是float32 timeStep = 1.0f / 60.0f;
碰撞时速度、碰撞时位置迭代:核心是用来检测碰撞物体的相对位置,以及碰撞后的位移;如果值太小,会出现一个物理运动另外一个物理“内部”再碰撞;值越高就越接近真实物理,当然运算负载也越大;
个人觉得Box2D在接口设计上面非常合理,迭代逻辑与业务的主逻辑契合比较紧密(基于帧的概念),看后续有没有机会继续深入使用,若有使用还会更新进阶篇。
(全文结束)
转载文章请注明出处:漫漫路 - lanindex.com