jgame.jsでbox2dサポートしてみた
各種モジュールのソースコード管理をどうすりゃいいのかまだ思いつかないので、とりあえずデモだけだけど。
http://jgame-js.sourceforge.jp/module.html
box2dのデモ見ていっつも思うんだけど、「面白そ〜」で終わるって事。
物理世界をトレース出来るのは大変いいんだけど、それ使って何やるかって方が重要なのに、何やるかがなかなか思いつかないんだよね。
まあこのデモ問題は最後でもう一回書くとして、とりあえず実装した内容とかをメモがてら解説から。
jgame.jsにおけるモジュール構造
結局いいモジュール管理のアイデアが思い浮かばなかったので、ただ外部javascriptで適当にとってこいって流れになった。
てことでResourceクラスに.jsの場合scriptタグでとりにいけっていう追加コード入れて、お茶濁させていただきましたよと。。
今回のだとこういう風に書いたTypeScriptを用意して、
module JGBox2D { export class World {
後は
game.preload({box2d:"js/modules/box2d.js"});
とかやってねって事で。
game.loaded後はnew JGBox2D.World();が使えるよと。
box2dモジュールの構造
box2dモジュールは、追加モジュールなので、基本的に「そのモジュールが無ければ無いなりに本体は動く」という理念を持たせた。
だから、enchant.jsのようなSpriteクラス自体を拡張するって形にはしてない。
attach, detachの形にした。
例えばなんかShapeがいっぱいある世界があったとして、それが普通のjgame.jsの世界だとこう。
var game = new Game(320, 320); var objects = []; for (var i=0; i<10; i++) { var shape = new Shape(32, 32)) shape.moveTo(game.width * Math.random(), game.height * Math.random()); objects.push(shape); game.currentScene.append(shape); }
これが物理世界になるとこうなる。
var game = new Game(320, 320); game.preload( _box2d: "js/external/Box2dWeb-2.1.a.3.min.js", box2d: "js/modules/box2d.js" ); game.loaded.handle(function() { var objects = []; for (var i=0; i<10; i++) { var shape = new Shape(32, 32)) shape.moveTo(game.width * Math.random(), game.height * Math.random()); objects.push(shape); game.currentScene.append(shape); } var world = new JGBox2D.World(game); for (var i=0; i<objects.length; i++) { world.attach(objects[i]); } world.start(); }
まあモジュール読まないといけない分どうしてもちょい冗長にはなるけど、基本的にworldをgameに対して作って、物理世界を各シェイプにattachするってやり方。
常に物理世界より先に普通の世界がある。
また物理世界開始後も、attachしなければそれは物理世界から外れたオブジェクトになる。
ということで最初から物理法則を受けるために存在するオブジェクトなんてものは作ってない。
モジュールがあればそのモジュールの影響を受ける、なければないなりに動く、って事でこの形。
もちろん、基本的にこの構造は遅いとか、基本的な動きは物理世界外になるとかの難点はあるけど、拡張モジュールである以上俺はこの形以外考えられん。
いくつか気がついた事
Timelineが使えない件
まず、Timelineが致命的にbox2dと相性が悪い。
box2dにはbox2dなりのルールがあって動いているんだけど、Timelineは自分の管轄外での動作がありえることを想定していないから、例えばmoveByで動かそうとすると引っ張り合いになる。
理想的な動きは、box2dが落とした分だけTimeline側が対象座標を修正してくれる事なんだけど、 moveByだってアニメーション開始時の座標から相対座標をとるけど、一回動き出したら絶対座標なので、そんなこと出来ないんだよね。
これはTimelineのTweenクラスが持つ構造的な問題。
これは正直ちと参った。
基本的なアニメーションのクラスが物理世界で全然使えないとなると、基本物理世界特有の「力を与える」系の動作を加える事になると思うんだけど、なんか、単調になりそう。
完全な相対座標で処理可能なTween2的なクラスを作る方が面白くなりそうではあるけれど。
全体的にパラメータがよくわからん
box2dの実装にあたって、box2dも一通り見てみたんだけど。
こんな?マークだらけ、コメントだらけのコード久々に書いた。
例えばapplyForceってのがあるんだけど、英語のコメントはこうだ。
Apply a force at a world point. If the force is not applied at the center of mass, it will generate a torque and affect the angular velocity. This wakes up the body.
@force The world force vector, usually in Newtons (N).
@point The world position of the point of application.
英語よくわかんないなぁって事で、enchant.jsのプラグインの同一メソッド説明を見てみる。
継続的な力を加える
@param {b2Vec2} force 加える力のベクトル
ふむ、フォースというだけあって継続的な力なのかということで、与えてみるとすぐにフォース無くなる。全然継続しない。これじゃヨーダに怒られちゃうよ。
色々やってみるとapplyImpulseってのが一番数値に対する効果が高くて、 次にSetLinearVelocityってので与えるのが高くて、一番数値辺りの効果が弱いのがapplyForceなんだけど、はて、と。
ということで、正直box2dようわからん。
jointとかも試してるけど全然思うように動かんし。
わからんなりに試行しながら動かす事は出来るけど、完璧に理解したってなれる気がしない。
デモについて
問題は色々ありつつも、一応box2dサポートしたんで、なんかデモ作らんとな。
冒頭に書いたけどbox2d読み込めたよー、ブロックが物理法則に従うよーって、へーすげー以外何もない。
なんかやっぱり一本作んないと問題も見えんと思うし。
物理世界系っていうと、言わずと知れたAngry birdがまずあるけど、俺はどっちかっていうとGravityが好き。
http://senobishiten.cyber-ninja.jp/game.html
未プレーの人は是非。今んとこ俺ん中では物理ゲーの頂点。
GravityもAngry birdも、小さいフィールドってのがポイントだぁね。
マリオみたいなのだとやっぱり演算量が多くなっちゃうからだと思うけど。
とはいえ重力パズルものはもう十分すぎる気がするし、なんかアクションやりたいんだよなぁ。
アイデア降ってきたらすぐ作るんだけど、そこまでが長い。
横重力、ってのが面白いんじゃねぇかなぁと思うんだけど、サンプルだから王道でいくべきかねぇ。