bodyを作ったので、アニメーションのためのループしながら、ワールドから座標を取得してcanvasに書いてみます。
前回
作ったもの
See the Pen box2dを使ってみる(2) by kana (@kanaparty) on CodePen.
作成したbodyのbox2dワールドでの位置と、canvasに描くときの位置が食い違わないように注意する
前回も書きましたがBox2Dはメートル法なので、canvas上では1m何pxなのか決めること。
また、Box2Dではbodyの中心点の座標を使用したり、高さと幅は半分を使用したりします。
canvasに置き換えたときの計算を間違えるとbox2D上ではうまく処理できていても
見た目が変だったりすることがあるので、この辺は確認しながら作っていきます。
以下注意点。
- ボックスとして定義するとき(b2PolygonShape.SetAsBox)は半分の横幅を指定する
リファレンスにも書いてあるけれど...
http://www.box2dflash.org/docs/2.1a/reference/
座標を設定するとき(Box2D.Dynamics.position.Set)にはbodyの中心点の座標を渡す
Box2D内の1mを何pxで書くか決めて、それを変換しながら使うこと
変換用の関数を用意しておくと楽です。
//座標変換用 box2Dワールドからcanvasでの大きさに変えるとき function convertToCanvasScale(box2dWorldscale) { return box2dWorldscale * worldScale; } //座標変換用 canvasからbox2Dワールドでの大きさに変えるとき function convertToWorldScale(canvsScale) { return canvsScale / worldScale; }
- 止まってるかどうか調べる(必要であれば)
b2Body.IsAwake();でtureが返ってきたらまだ動いています。
- 追記:回転するときの軸に注意する。 Box2Dでは物体の中心点からの回転した角度が得られるので、canvasで書くときも、物体の中心を座標の中心に置き換えて書く必要がある。
食い違いがおこらないようにデバッグモードで確認する
b2DebugDrawというのが用意されているので、それを使うと作成したbodyを表示して確認することができます。 表示設定もできます。 まとめておいたほうが便利そうです。
function debugDraw() { var debugDraw = new b2DebugDraw(); //キャンバスの指定です。 //今回は既にdocument.getElementById('canvas').getContext('2d')を変数ctxに入れているのでそれを引数にしてます。 debugDraw.SetSprite(ctx); //スケールの設定。worldを宣言したときに決めたworldScaleを渡して大きく1m30pxで表示させます。 debugDraw.SetDrawScale(worldScale); //見た目の設定 debugDraw.SetFillAlpha(0.5); debugDraw.SetLineThickness(1.0); //シェイプとジョイントの両方を描きます。 debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit); //ワールドに設定 world.SetDebugDraw(debugDraw); }
デバックモードで描くときはアニメーションループの中で以下のように呼び出します。
function loop() { //ワールドの時間を進めます。1 / 60は固定値? world.Step(1 / 60, 10, 10); //デバッグ用のデータを呼び出す world.DrawDebugData(); world.ClearForces(); //さっきまとめておいた処理 debugDraw(); requestAnimationFrame(loop); } loop();
canvasに書いていく
アニメーションのループの中で、worldから位置のリストを取り出していきます。
function loop() { // キャンバスをクリアする ctx.clearRect(0, 0, windowWidth, windowHeight); //ワールドの時間をすすめる world.Step(1 / 60, 10, 10); //worldの中に物体の情報が入っている分だけリピート for (var b = world.GetBodyList(); b; b = b.GetNext()) { var userData = b.GetUserData(); //意図的に生成したものだけ描く if(userData){ var position = b.GetPosition(); var angle = b.GetAngle(); //ボックス if(userData.bodyName === 'box'){ boxArray[userData.order].updateState(position.x, position.y , angle); boxArray[userData.order].draw(); } //障害物 else if(userData.bodyName === 'staticBox'){ staticBox.updateState(position.x, position.y); staticBox.draw(); } //床 else if(userData.bodyName === 'floor'){ floor.updateState(position.x, position.y); floor.draw(); } } else{ } } world.ClearForces(); // world.DrawDebugData(); // debugDraw(); requestAnimationFrame(loop); } loop();
まずworld.Step(1 / 60, 10, 10);でワールドの時間を進めます。
Box2Dは「この物体は、何秒後に ここにいます」という情報を返してくれるので
1フレームごとにその時間を進めます。(引数はよくわかってないんですが固定・・・?)
for (var b = world.GetBodyList(); b; b = b.GetNext()) { //ここにすべてのbodyに対しての処理を書く }
次にworld内にあるbodyの数だけ処理をします。
world.GetBodyList();でbodyのリストを取得、.GetNext()で次の情報を取得。最後まで来るとnullが帰ってきます。
またbodyに作成した順と逆から処理されているみたいです。
(さらにアタッチした数より1つ多く処理が入るんですがこれはworld自身なのかな・・?)
今回は自分が意図的に作成したobjectだけをcanvasに書きたかったので、BodyDef.userDataに名前など保存しておいて、 canvasに書くときにどのオブジェクトなのか判断することにしています。
b.GetPosition();で座標が
b.GetAngle();で中心点の回転角度が得られます。
こうして得られた座標をcanvas用に変換して(前述)書きます。
以上でBox2Dを使って 単純な四角を 静止した単純な四角に ぶつけるアニメーションの完成!
余談 canvasでの回転
canvasで回転させるときは以下のように書く。
saveして状態を保存、座標を変えて回転させて、保存した状態にもどす。
//描く処理 draw() { //いままでのcanvasの設定を保存 ctx.save(); //座標の基準点を移動 ctx.translate(this.leftTopX, this.leftTopY); //移動した後に基準点を中心に回転させる ctx.rotate(this.angle); //四角を書く ctx.fillStyle = this.fillColor; ctx.fillRect(0, 0, this.width, this.height); //先ほど保存した設定に戻す。 ctx.restore(); }
次回