#物理演算エンジンについて
cocos2d-xを使用してゲームを開発していると、
Box2DやChipmunkという言葉をよく耳にするかと思います。
両方共、古典力学的な法則をシュミレーションするゲーム用2D物理演算エンジンです。
記述されている言語に大きな違いがあり、
Box2D = C++
Chipmunk = C言語
となっています。
どちらかと言うとBox2Dの方が日本では人気なのでは無いでしょうか。
よって、まずはBox2Dでの実装例をご紹介します。
その後、Box2Dを使わずにすんげー簡単な方法を説明します。
(長くなってしまったので2つに分割します)
#cocos2d-x 3.0のBox2Dの実装
cocos2d-x 3.0 beta2で実装するとこのようなコードになります。
#import "cocos2d.h"
#import "Box2D/Box2D.h"
#import "cocos-ext.h"
USING_NS_CC;
USING_NS_CC_EXT;
class Box2DScene : public Layer{
private:
b2World* world;
Box2DScene();
~Box2DScene();
static Box2DScene* scene();
void createWorld();
void createSprite();
virtual bool init();
virtual void update(float delta);
public:
static Scene* createScene();
CREATE_FUNC(Box2DScene);
};
#import "Box2dScene.h"
#import "GameConfig.h"
Box2DScene::Box2DScene(){}
Box2DScene::~Box2DScene(){}
Scene* Box2DScene::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = Box2DScene::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
bool Box2DScene::init(){
createWorld();
createSprite();
scheduleUpdate();
return true;
}
/**
* 物理演算の影響を受ける世界全体を生成します。
*/
void Box2DScene::createWorld(){
// 構造体
b2Vec2 gravity;
gravity.Set(0, -10);
world = new b2World(gravity); // 物理演算の全体を管理するワールドを作る
}
/**
* scheduleUpdate()で呼ばれます。
* 物理演算の影響で世界の状態は刻一刻と変化するので、その命令を飛ばす。
* これを行わないと物理演算の影響が行われない。
*/
void Box2DScene::update(float delta){
// 物理シミュレーションの正確さを決定するパラメータ
// (まずはこの値にして、ゲームを見ながら調整を行うと良いらしい)
int velocityIterations = 8;
int positionIterations = 1;
// worldを更新する
world->Step(delta, velocityIterations, positionIterations);
}
void Box2DScene::createSprite(){
Size winSize = Director::getInstance()->getWinSize();
/**
*
剛体は次の手順で作成する。剛体とは物理エンジンの影響をうけることが出来る物質のこと
1. 位置・減衰率などで剛体を定義する。
2. 剛体を作成するためにワールドを使用する。
3. 図形・摩擦・密度などから装備を定義する。
4. 剛体から装備を作成する。
*/
b2Body* body; // 剛体。rigid body。
b2BodyDef bodyDef; // 剛体の定義
b2CircleShape shape; // 装備の形状
b2FixtureDef fixtureDef; // 装備の形状定義
/* 画像の表示(後でも大丈夫だけど、大きさを取るためにここで) */
PhysicsSprite* physicsSprite = PhysicsSprite::create("peri1.png");
// 1. 位置・減衰率などで剛体を定義する。
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set((winSize.width / 2) / PTM_RATIO, (winSize.height) / PTM_RATIO);
bodyDef.userData = physicsSprite; // 画像をこの剛体に紐付ける
// 2. 剛体を作成するためにワールドを使用する。
body = world->CreateBody(&bodyDef);
// 3. 図形・摩擦・密度などから装備を定義する。
shape.m_radius = physicsSprite->getContentSize().width / PTM_RATIO; // 形状の大きさ
fixtureDef.shape = &shape; // 形状を紐付ける
fixtureDef.density = 1; // 密度
fixtureDef.friction = 0.9; // 摩擦
// 4. 剛体から装備を作成する。
body->CreateFixture(&fixtureDef);
/** PhysicsSpriteに各種情報をセットする */
this->addChild(physicsSprite);
physicsSprite->setB2Body(body);
physicsSprite->setPTMRatio(PTM_RATIO);
physicsSprite->setPosition(Point(winSize.width / 2, winSize.height / 2));
}
参考書などを読むとPhysicsSpriteを自作することもありますが、
ここでは、extensions/physics-nodes/CCPhysicsSpriteを使用しています。
以上のようなソースになります。実行してみます。
(写真では分かりづらいですが、落ちてます!)
ソースを見れば分かる通り、地上などを実装しておりませんので、このままではどこまでも落ち続けますが、
ちゃんとBox2Dの恩恵を受けて落下することが確認出来ました。
補足
cocos-extのPhysicsSpriteを使用する時、setPositionなどをsetB2Body前に行うと実行時に落ちます。
ソースを見てみると
void PhysicsSprite::setPosition(const Point &pos)
{
#if CC_ENABLE_CHIPMUNK_INTEGRATION
cpVect cpPos = cpv(pos.x, pos.y);
cpBodySetPos(_CPBody, cpPos);
#elif CC_ENABLE_BOX2D_INTEGRATION
float angle = _pB2Body->GetAngle();
_pB2Body->SetTransform(b2Vec2(pos.x / _PTMRatio, pos.y / _PTMRatio), angle);
#endif
}
このように_pB2Bodyを利用しているためです。
また、setPTMRatioを設定しないと画像が表示されません。ご注意を。
実行時のパラメータにCC_ENABLE_BOX2D_INTEGRATION=1を加えるのも忘れずに!
(デフォルトではCC_ENABLE_CHIPMUNK_INTEGRATION=1になっているので、Box2Dを利用する場合はこれを消してください。)
後編につづく!