Closed5

Box2D基本1_Worldと基本的なオブジェクト

かじるかじる

Box2Dv3.0系の使い方
(DxLibと組み合わせて使っています)

オススメ書籍
ゲーム開発で学ぶC言語入門 プロのクリエイターが教える基本文法と開発技法
超本格! サンプルで覚えるC言語 3Dゲームプログラミング教室

Step1: 必要なヘッダーファイル

main.cpp
#include "box2d/box2d.h"
#include "box2d/collision.h"
#include "box2d/math_functions.h"

Step2: Box2Dの大元であるWorldを用意する

main.cpp
// Worldの設定
auto worldDef = b2DefaultWorldDef();
worldDef.gravity = (b2Vec2) {0.0f, -10.0f};
// World
auto worldId = b2CreateWorld(&worldDef);
const string result = b2World_IsValid(worldId) ? "Success!!" : "Failed...";
LOGD("Main", "Hello, Box2D: %s", result.c_str());

Step3: update関数等で、Worldを更新する(delayは秒単位)

main.cpp
b2World_Step(worldId, delay, 8);// Step
かじるかじる

Box2Dは、物理演算のみを行う武闘派ライブラリです。
よって、描画処理等からは自力でなんとかしましょうそうしましょう。

Step1: 基底クラスを用意する(ヘッダーは省略)

DrawerBase.cpp
#include "DrawerBase.h"

DrawerBase::DrawerBase(const b2WorldId &worldId,
                       float x, float y) :
        NodeBase(x, y),
        color(GetColor(255, 255, 255)),
        b2dRate(UtilDebug::getInstance()->getGridSize() * 2),
        worldId(worldId) {
    //LOGD("Main", "DrawerBase()");
}

DrawerBase::~DrawerBase() {
    //LOGD("Main", "~DrawerBase()");
}

float DrawerBase::p2m(float pixel) const {
    return pixel / (float) b2dRate;
}

float DrawerBase::m2p(float meter) const {
    return meter * (float) b2dRate;
}
かじるかじる

先ほどの基底クラスを継承して、四角を描画するクラスを用意します

四角を描画するクラス(ヘッダーは省略)

DrawerBox.cpp
#include "DrawerBox.h"

DrawerBox *DrawerBox::create(const b2WorldId &worldId,
                             float x, float y,
                             int w, int h, int deg,
                             const bool dynamicFlg) {
    // New
    auto obj = new DrawerBox(worldId, x, y);
    if (obj && obj->init(w, h, deg, dynamicFlg)) return obj;
    DX_SAFE_DELETE(obj);
    return nullptr;
}

DrawerBox::DrawerBox(const b2WorldId &worldId,
                     float x, float y) : DrawerBase(worldId, x, y),
                                         widthM(0.0f), heightM(0.0f) {
    //LOGD("Main", "DrawerBox()");
}

DrawerBox::~DrawerBox() {
    //LOGD("Main", "~DrawerBox()");
    if (bodyId.index1 != 0) b2DestroyBody(bodyId);
}

bool DrawerBox::init(int w, int h, int deg,
                     const bool dynamicFlg) {

    // Pixel to Meter
    const float mX = this->p2m(pos.x);
    const float mY = this->p2m(pos.y) * -1.0f;
    widthM = this->p2m(w);
    heightM = this->p2m(h);

    // Define the body
    b2BodyDef bodyDef = b2DefaultBodyDef();
    if (dynamicFlg) bodyDef.type = b2_dynamicBody;
    bodyDef.position = (b2Vec2) {mX, mY};
    bodyDef.rotation = b2MakeRot(DEG_TO_RAD * deg);
    bodyId = b2CreateBody(worldId, &bodyDef);

    // ShapeDef
    b2ShapeDef shapeDef = b2DefaultShapeDef();
    shapeDef.isSensor = false;
    shapeDef.enableContactEvents = true;// Important
    shapeDef.userData = this;

    // Polygon
    b2Polygon polygon = b2MakeBox(widthM / 2, heightM / 2);
    b2CreatePolygonShape(bodyId, &shapeDef, &polygon);

    // Points
    points.at(0).x = -w / 2;
    points.at(0).y = -h / 2;
    points.at(1).x = w / 2;
    points.at(1).y = -h / 2;
    points.at(2).x = w / 2;
    points.at(2).y = h / 2;
    points.at(3).x = -w / 2;
    points.at(3).y = h / 2;

    return true;
}

void DrawerBox::update(const float delay) {
    // Draw
    this->draw();
}

void DrawerBox::draw() {

    // Rotation
    const auto position = b2Body_GetPosition(bodyId);
    const auto rotation = b2Body_GetRotation(bodyId);
    const auto angle = b2Rot_GetAngle(rotation);
    const float cosA = cosf(angle);
    const float sinA = sinf(angle);

    // Meter to Pixel
    const float cX = this->m2p(position.x);
    const float cY = this->m2p(position.y);
    for (int i = 0; i < points.size(); i++) {
        const float aX = points.at(i).x;
        const float aY = points.at(i).y;
        const int j = (i + 1) % points.size();
        const float bX = points.at(j).x;
        const float bY = points.at(j).y;
        const float fX = aX * cosA - aY * sinA;
        const float fY = aX * sinA + aY * cosA;
        const float tX = bX * cosA - bY * sinA;
        const float tY = bX * sinA + bY * cosA;
        UtilCamera::getInstance()->drawLine(cX + fX, -(cY + fY),
                                            cX + tX, -(cY + tY),
                                            color, true);
    }
}
かじるかじる

基底クラスを継承して、円を描画するクラスを用意します

円を描画するクラス(ヘッダーファイルは省略)

DrawerCircle.cpp
#include "DrawerCircle.h"

DrawerCircle *DrawerCircle::create(const b2WorldId &worldId,
                                   float x, float y,
                                   int radius,
                                   const bool dynamicFlg) {
    // New
    auto obj = new DrawerCircle(worldId, x, y);
    if (obj && obj->init(radius, dynamicFlg)) return obj;
    DX_SAFE_DELETE(obj);
    return nullptr;
}

DrawerCircle::DrawerCircle(const b2WorldId &worldId,
                           float x, float y) :
        DrawerBase(worldId, x, y), radiusM(0.0f) {
    //LOGD("Main", "DrawerCircle()");
}

DrawerCircle::~DrawerCircle() {
    //LOGD("Main", "~DrawerCircle()");
    if (bodyId.index1 != 0) b2DestroyBody(bodyId);
}

bool DrawerCircle::init(int radius,
                        const bool dynamicFlg) {

    // Pixel to Meter
    const float mX = this->p2m(pos.x);
    const float mY = this->p2m(pos.y) * -1.0f;
    radiusM = this->p2m(radius);

    // Define the body
    b2BodyDef bodyDef = b2DefaultBodyDef();
    if (dynamicFlg) bodyDef.type = b2_dynamicBody;
    bodyDef.position = (b2Vec2) {mX, mY};
    bodyId = b2CreateBody(worldId, &bodyDef);

    // ShapeDef
    b2ShapeDef shapeDef = b2DefaultShapeDef();
    shapeDef.isSensor = false;
    shapeDef.enableContactEvents = true;// Important
    shapeDef.userData = this;

    // Polygon
    b2Circle circle = {0};
    circle.center = (b2Vec2) {0.0f, 0.0f};
    circle.radius = radiusM;
    b2CreateCircleShape(bodyId, &shapeDef, &circle);

    return true;
}

void DrawerCircle::update(const float delay) {
    // Draw
    this->draw();
}

void DrawerCircle::draw() {

    // Rotation
    const auto position = b2Body_GetPosition(bodyId);
    const auto rotation = b2Body_GetRotation(bodyId);
    const auto angle = b2Rot_GetAngle(rotation);

    // Meter to Pixel
    const float cX = this->m2p(position.x);
    const float cY = this->m2p(position.y);
    const float radiusP = this->m2p(radiusM);

    const int segments = 12;
    const float step = M_PI * 2.0f / segments;
    for (int i = 0; i < segments; i++) {
        float angle1 = step * i;
        float angle2 = step * (i + 1);
        float fX = cX + radiusP * cosf(angle1);
        float fY = -(cY + radiusP * sinf(angle1));
        float tX = cX + radiusP * cosf(angle2);
        float tY = -(cY + radiusP * sinf(angle2));
        UtilCamera::getInstance()->drawLine(fX, fY, tX, tY,
                                            color, true);
    }
}
かじるかじる

実際に使ってみるとこういう感じになるかと思います
(重要な箇所だけ抜粋)

SceneBox2d.cpp
#include "SceneBox2d.h"

#include "box2d/box2d.h"
#include "box2d/collision.h"
#include "box2d/math_functions.h"

SceneBox2d::SceneBox2d(int dWidth, int dHeight) : SceneMain(dWidth, dHeight) {
    LOGD("Main", "SceneBox2d()");
}

SceneBox2d::~SceneBox2d() {
    LOGD("Main", "~SceneBox2d()");
    // Delete
    DX_SAFE_DELETE(camMan);
    DX_SAFE_DELETE_VECTOR(bodies);
    // Destroy
    b2DestroyWorld(worldId);
}

bool SceneBox2d::init() {
    LOGD("Main", "init");

    // Center
    const int gSize = UtilDebug::getInstance()->getGridSize();
    const int cX = dWidth * 0.5f;
    const int cY = dHeight * 0.5f;

    // 省略

    // Box2D
    worldDef = b2DefaultWorldDef();
    worldDef.gravity = (b2Vec2) {0.0f, -10.0f};

    // World
    worldId = b2CreateWorld(&worldDef);
    const string result = b2World_IsValid(worldId) ? "Success!!" : "Failed...";
    LOGD("Main", "Hello, Box2D: %s", result.c_str());

    // 地面オブジェクト
    const int deg = UtilMath::getInstance()->getRandom(-1, 1);
    const auto ground = DrawerBox::create(
            worldId, cX, cY, gSize * 24, gSize * 2, deg, false);
    ground->setCameraFlg(true);
    bodies.push_back(ground);

    // 四角オブジェクトをx10
    for (int i = 0; i < 10; i++) {
        const int x = cX + UtilMath::getInstance()->getRandom(gSize * -4, gSize * 4);
        const int y = cY - UtilMath::getInstance()->getRandom(gSize * 18, gSize * 50);
        const auto box = DrawerBox::create(
                worldId, x, y, gSize * 1, gSize * 2, 0, true);
        box->setCameraFlg(true);
        bodies.push_back(box);
    }

    // 円オブジェクトをx10
    for (int i = 0; i < 10; i++) {
        const int x = cX + UtilMath::getInstance()->getRandom(gSize * -4, gSize * 4);
        const int y = cY - UtilMath::getInstance()->getRandom(gSize * 18, gSize * 50);
        const auto circle = DrawerCircle::create(
                worldId, x, y, gSize, true);
        circle->setCameraFlg(true);
        bodies.push_back(circle);
    }
}

void SceneBox2d::update(const float delay) {

    // Update Box2D
    for (auto body: bodies) body->update(delay);// Update
    b2World_Step(worldId, delay, 8);// Step
}

このスクラップは2日前にクローズされました