今回はXcodeからBox2Dのプロジェクトを作成しそのテンプレートを見てみます(以下の画像参考)。
はじめに
はじめに基本的な概念を。ここは参考記事から抜粋させていただきます。オブジェクトのアニメーションシステム。スプライトを剛体という物理オブジェクトに結びつけ同期する。
ボディには動的と静的がある。
静的ボディ同士は互いに衝突しないが、動的ボディは他の動的・静的ボディと衝突する。
動的ボディのパラメータ。密度(質量)、摩擦、反発
ボディは範囲を決定する1つ以上の図形(シェイプ)で構成される。
シェイプは円、四角形、多角形、頂点、直線。
衝突ごとに交差するポイント接触点が生成される。
ボディはジョイントによってつなぎ合わせることができる。
参考:cocos2d 物理エンジン - プログラミングの魔物
また、Box2Dは元々C++用ライブラリですので、言語もC++で書かれています。
実行環境
今回Box2Dを動かした環境は以下になります。
- Xcode - ver. 4.5.2
- cocos2d - ver. 2.0
ソースコード
今回は作成したテンプレートの中から、Box2Dの実装部分であるHelloWorldLayer.h/mmの2つを見ていきます。説明するソースコードはBox2Dに関係しているものだけを選んでいます。コメントも付けているので参考にしてみてください。
HelloWorldLayer.h
#import <GameKit/GameKit.h>
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
#define PTM_RATIO 32
@interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
CCTexture2D *spriteTexture_;
b2World* world;
GLESDebugDraw *m_debugDraw;
}
+(CCScene *) scene;
@end
ヘッダーファイルでは特殊なことはしていません。必要な変数とsceneメソッドの準備をしています。HelloWorldLayer.mm
続いて実装ファイルになります。ここは少し長いので分けて説明していきます。分けて書くということで見づらく感じる方は、実際にテンプレートからプロジェクトを作成して見比べてみると分かりやすいと思います。
initPhysicsメソッド
Box2Dの初期化メソッドです。Box2Dのワールドの初期化
まずはBox2Dの世界を作ります。画面内に重力を持たせ、私たちが住んでいる世界と同じに変えてあげます。b2Vec2 gravity; // 重力ベクトル変数
gravity.Set(0.0f, -10.0f); // 重力の設定
world = new b2World(gravity); // 物理ワールドの型、b2Worldを初期化
以前は、bool allowBodiesToSleep = true; と変数を準備し、ボディをスリープするかどうか判定していました。ですが、cocos 2.0から生成したBox2Dのプロジェクトにはb2WorldクラスにSetContinuousPhysicsメソッドというのが用意されており、そのメソッドで管理します。スリープ = 微小な動作の演算にフラグを立てて処理しない
これでBox2Dワールド(世界)の作成に成功です。
移動をスクリーン内に制限する
次はオブジェクトが移動できる範囲を定義します。// 衝突可能な静的コンテナをて定義する
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0);
// 辺ポリゴンを使って4つの辺を作成する
b2Body* groundBody = world->CreateBody(&groundBodyDef);
b2EdgeShape groundBox;
// 下辺
groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
// 上辺
groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox,0);
// 左辺
groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox,0);
// 右辺
groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox,0);
まずは、CreateBodyメソッドを使い、空のボディを定義しています。引数には"&"で変数のメモリアドレスを渡しています。b2EdgeShapeクラスについてはこちらを参照してください。
スクリーンの幅と高さをピクセルからメートルに変換するためにPTM_RATIOで割っています。PTM_RATIOはHelloWorldLayer.hに定義されています。定義されている数値がなぜ"32"かというと、慣例的にBox2D内では32pxが1mと扱われているためです。ですので、32px*32pxの箱形のbodyがあればそれは1mの正方形、ということになります。
また、Box2Dは、距離はメートル、質量はキログラム、時間は秒の、MKS(Meter, Kilogram, Second)単位系になっています。
それぞれの辺の定義ではCreateFixtureメソッドを使い辺を作成しています。こちらも"&"で変数のメモリアドレスを渡しています。
drawメソッド
これはデバッグ用という解釈でいいと思います。ここすごく曖昧なので分かる方いましたらコメントいただけると幸いです。
addNewSpriteAtPosition:メソッド
表示するスプライトの定義部分です。CreateBody(&bodyDef);
// ボックスシェイプを定義し、ボディフィクスチャに割り当てる
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(.5f, .5f); // SetAsBoxメソッドは指定された幅と高さの2倍のボックスシェイプを作成する
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox; // 図形
fixtureDef.density = 1.0f; // 密度
fixtureDef.friction = 0.3f; // 摩擦
body->CreateFixture(&fixtureDef);
update:メソッド
cocos2dではおなじみのメソッドですね。定期的に呼ばれ、フィールド内の状況を見ています。int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(dt, velocityIterations, positionIterations);
定期的にフィールド内の状況を見ているのはStepメソッドです。第一引数は直近のstepから何step過ぎた後にワールドを更新するかを決めるパラメータです。velocityIterations(速度)とpositionIterations(位置)については物理シミュレーションの正確さを決める値になります。この部分では参考にしている書籍から気になる記載があったので抜粋します。
これらのパラメータは物理シミュレーションの正確さに加え、ボディの動きを計算するためにかかる時間に左右します。速度か正確さどちらかを選ぶことになります。
位置を反復処理する場合は速度を優先し、反復処理は一回だけでも問題ありません。速度の場合は8からはじめます。10回を超える反復処理はゲーム内でほとんどわからない違いである。だが、1〜4回の反復処理ではシミュレーションを安定させるには不十分。自分でいろいろ試してみるのがいい。
参考:cocos2dで作るiPhone&iPadゲームプログラミング (初版 p. 320)
Box2Dのダウンロード
最後にBox2Dのダウンロード方法です。テンプレートからではなく、例えば既存のプロジェクトにBox2Dを入れたい場合などは以下のリンクからダウンロードしてください。SCM Repositories | box2d - SourceForge
ページ下の「Download GNU tarball」をクリックするとダウンロードできます。
プロジェクトへの導入方法は以下の参考記事を参照してください。
参考記事
cocos2d 物理エンジン - プログラミングの魔物box2dチュートリアル - jarinosuke blog
Cocos2dに最新のBox2Dを入れてみる - ハマケン100%開発
0 件のコメント:
コメントを投稿