2Dアクション・テンプレート:物理の対応:設定〜表示確認まで
前回の「こちらでやる作業」を行い、Box2Dの表示確認まで行う。
今回やるのは
の3つ。
他の
- 「Box2Dの物体の位置」と「画像の位置」の同期
- 入力によるコリジョンの移動
に関しては、後に回す。(表示確認は付属のデバッグ機能で行う)
メートルとピクセルの対応付け
メートルとピクセルの対応付けはゲームによって異なる。ここでは「RPGツクール用のキャラ素材」をキャラクターとして使う予定なので、その身長に合わせて設定することにする。
RPGツクール用のキャラの「1マスの高さ」は「32ピクセル」。「キャラクターの身長」はだいたい「1m70cm」くらいと考える。1マスの範囲内にキャラクターは収まっており、上下には若干の余白があるので、だいたい「1マスの高さ」は「2m」とみなしていいだろう。とすると、「2m=32ピクセル」となり、「1m=16ピクセル」となる。
よって、ここでは「1m=16ピクセル」として以下のように設定することにする。
const PIXEL_PER_METER:Number = 16.0 / 1.0;//= ピクセル / メートル
Box2Dの初期化処理(セットアップ)
ライブラリとしてセット
2Dアクション・テンプレート:作成準備 - Master of Noneだけでは、まだ自分のプログラムにBox2Dを埋め込むには不十分。ということで、以下の作業を行う。
まずはソースをコピー。Box2Dのzipを解凍した中にある「Box2D」というフォルダを「flex_sdk_3\frameworks\source」の中にコピーする。
そして、そのソースをコンパイル時に参照できるようにする。flex_sdk_3\frameworks\air-config.xmlの「source-path」に、以下のようにして「source」フォルダを追加する。
<source-path> <path-element>source</path-element> </source-path>
以上で設定は完了。この設定はライブラリのソースを直接使う設定なので、速度面などでは他の設定方法の方が良いかもしれない。が、趣味ベースなので(=面倒なので)気にしない。
import
今回必要な部分をコード側でimportする。本当は「*」は使わない方が色々と良いんだろうけど、あくまで趣味ベースなので(=面倒なので)、この形式でインポートする。
import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; import Box2D.Dynamics.*;
外枠の定義
画面から遠く離れた物体までずっと計算していると負荷が大きくなるので、「どの範囲のコリジョンまで計算するか」を設定する必要がある。「コリジョンを計算する範囲」は、「ステージの大きさ」から一回り大きくしておけばいいので、以下のように設定する。Box2D側に設定する値は「メートル」であることに注意。(画面の幅はGetStageWなどですでに用意されてるものとする)
const PHYS_AABB_BUF:Number = 10.0;//10m分の余白を用意してみる var worldAABB:b2AABB; { worldAABB = new b2AABB(); worldAABB.lowerBound.set(-PHYS_AABB_BUF, -PHYS_AABB_BUF); worldAABB.upperBound.set(GetStageW() + PHYS_AABB_BUF, GetStageH() + PHYS_AABB_BUF); }
重力の定義
重力もこちらで設定する必要がある。通常の重力と同じく9.8に設定しておく。
const DEFAULT_GRAVITY:Number = 9.8; var gravity:b2Vec2; { gravity = new b2Vec2(0.0, DEFAULT_GRAVITY); }
スリープするか
静止している物体の位置計算を毎回行うのは無駄。そこで、「スリープする」という設定にしておけば、静止している物体の位置計算は行われないため、処理負荷の軽減が見込める。ただし、「キャラクター」のように「外部からのコリジョンの接触ではなく、入力で動くもの」に関しては、スリープしてしまうと入力で動かなくなったりする。その場合も、外部からスリープを解除する手段がある(はずな)ので、ここでは「スリープする」という設定にしておく。
var doSleep:Boolean; { doSleep = true; }
セットアップ
以上の設定を実際に反映しつつ、Box2Dの本体を生成する。
m_PhysWorld = new b2World(worldAABB, gravity, doSleep);
これで「物理演算を行う世界」の生成はできたので、あとはここに実際に「コリジョン」を生成して追加し、それに合わせて画像を動かせばいい。
コリジョンの大きさと形状の指定
コリジョンを追加するには、「大きさ・形状の指定」と「位置の指定」がそれぞれ必要になる。この二つはそれぞれ別のクラスで設定する。そのため、同じ「大きさ・形状」を使いまわすことが可能になる。
まず、「大きさ・形状の指定」は以下のように行う。
const FLOOR_W:Number = 10.0;//メートル const FLOOR_H:Number = 1.0;//メートル var shapeDef:b2PolygonDef = new b2PolygonDef(); shapeDef.SetAsBox(0.5*FLOOR_W, 0.5*FLOOR_H);
幅を実際にセットする際に半分にしている(0.5をかけている)のは、「円」などの設計に合わせているためである。「円」では「半径」を指定するので、「四角形」では「半幅」とでも呼ぶべきものを指定する必要があるので、ここでは「幅の半分」を指定している。
「位置の指定」は以下のように行う。Box2Dでの設定は「メートル」を使うので、「ピクセル」から「メートル」になおす処理を行っている点に注意。(画面中央、下のほうにコリジョンができるようにしている)
var posX:Number = 0.5*CAMERA_W/PIXEL_PER_METER;//メートル var posY:Number = 0.8*CAMERA_H/PIXEL_PER_METER;//メートル var bodyDef:b2BodyDef = new b2BodyDef(); bodyDef.position.Set(posX, posY); //固定 bodyDef.massData.mass = 0.0;//解説は下で
以上の指定から、以下のようにして「コリジョン」を生成する。
参考サイトでは「動かないコリジョン」の作成に「CreateStaticBody」を使っていたが、「ver.2.0.1」では存在しなかったので、上の「bodyDef」の方で「mass(密度)」を0にしている。(そもそもデフォルトで0になっているので、わざわざ設定する必要はないが念のため。あと、0で止まるようになっているのは、「密度0の物質」なるものは存在しないため)
var collision:b2Body = m_PhysWorld.CreateBody(bodyDef);//位置 collision.CreateShape(shapeDef);//形状の追加
これで床の指定はできた。玉の指定も同じような感じ+密度の設定でできる。詳細はあとで添付するzipの中のコードに任せる。
実行確認
必要な設定はだいたい以上の通りだが、「物理世界の更新」と「表示」がまだ残っているので、それに対応して実行確認する。
物理世界の更新
物理世界に「何秒経ったか」を知らせることで、現在のコリジョンの位置を更新してもらう。デフォルトではUpdateは「24分の1秒」ごとに呼ばれるようなので、ひとまずその通りに設定しておく。
var deltaTime:Number = 1.0 / 24.0; m_PhysWorld.Step(deltaTime, 10);
デバッグ描画の設定
Box2Dにはコリジョンのデバッグ描画機能があるので、それを利用して表示確認を行う。
var debugDraw:b2DebugDraw = new b2DebugDraw(); debugDraw.m_sprite = this; debugDraw.m_drawScale = PIXEL_PER_METER;//1mあたりのピクセル数 debugDraw.m_fillAlpha = 0.3; // 不透明度 debugDraw.m_lineThickness = 1; // 線の太さ debugDraw.m_drawFlags = b2DebugDraw.e_shapeBit; m_PhysWorld.SetDebugDraw(debugDraw);
実行確認
以上の設定を行えば、実際に床が静止し、その上でボールが跳ねるところまでは確認できる。詳しくは以下に添付のzipにて。