服务器端Box2D入门

Box2D是一个用于游戏的2D刚体仿真库。它的核心目的是为了模拟真实的2D环境,让物体运动更加真实,交互性更好。本文主要介绍一些使用Box2D的基础知识,并且记录如何在没有图形化界面的Linux服务器上使用它。(例子就是我使用的这台:D)。box2d

开始准备安装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施加一个向左的力;

示意图:Box2D一

详细的释义直接附加在代码里:

//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

Leave a Comment

Your email address will not be published.