プチメタ3.0

刺激を受けた物事に対する感想や考察、資産運用や英語学習、自己成長に関することなど。


物理エンジンで遊べるゲーム「あそぶつり」ができるまで



フリーソフトとして公開中の
物理エンジンを使ったゲーム「あそぶつり」は
完成に至るまでにいろいろな問題にぶつかった。

物理エンジンを使うときの戸惑い

そもそも海外の実験プログラムや市販ゲームから
「物理エンジン」という言葉を聞くようになり、
いろいろなデモ動画とかを見てはその動きの良さに憧れた。
さらにXbox360を遊んでいると、
実際に物理エンジンが働いている様子が
自分のプレイの上でも感じることができた。

そんな中、実際に自分でも
チャレンジしてみようと思ったのが2008年の3月。

それまで「物理エンジンを使っている」ということは読み取れても
それが一体どういう行為なのか実感が湧いてなかったわけだけど、
一般のゲームプログラムが

 プログラム上でキャラクタの座標を設定
       ↓
 キー操作やAI・重力によって
 キャラクターの座標をその都度、増減。
       ↓
 その座標に合わせてキャラ画像を表示


という流れなのに対して、物理エンジンを使うプログラムは

 ゲーム世界以外に物理エンジン上にも3D世界を作り、
 プログラム上で表示している物体の
 位置・形・大きさ・質量を物理エンジンに登録。
       ↓
 物理エンジン側の時間を進める。
       ↓
 物理エンジンの世界では衝突や重力によって
 物体の位置や向きに変化が生じる。
       ↓
 その位置と向きの情報を取り出し、
 ゲーム世界側の物体の位置と向きに反映。
       ↓
 ゲーム世界側で物体を投げたり移動させたり消したい場合は
 その都度、物理エンジンの世界の対応する物体を同様に処理する。


という流れになる。

今までプログラムが主導だった部分が
ちょこちょこ物理エンジン主導になったり、
と言いつつもやっぱりプログラム主導だったりと、最初はかなり戸惑った。

そのあたりで作れるようになったのが、
2008年4月に作った物理エンジンを使ったデモだ。


これができただけでもかなりの達成感があった。

ドミノ倒しを実現するための見えない苦労

物理エンジンの支配する「物理世界」に作れる物体は
直方体や球、円柱などといった、ごく単純な形が中心となる。
複雑になればなるほど計算の負荷が増すからだ。

つまりゲーム画面上でライフル銃が転がっていたとしても
物理世界では細長い円柱と平たい直方体を組み合わせた立体物だったりする。

逆に言えば、ゲーム中のドラム缶(円柱)や木箱(直方体)や
リンゴ(球)のような単純な形そのままのものはずいぶん楽だ。

さて、物理エンジンがある程度使えるようになると
ドミノ倒しみたいなものはすぐ作れるだろうと思った。
物理世界に直方体を並べ、ボールか何かをぶつけて
ドミノを押せば次々衝突して倒れていくはずだ。

が、これが実はうまくいかない。

ドミノが倒れて次のドミノにぶつかったとき、
倒れかけているドミノは立っているドミノに押される形になるので
接地している方の面が滑って、その場に倒れてしまうのだ。

そこで物体同士が衝突したときの摩擦を高めてみると
今度はドミノがまったく倒れなくなった。



ドミノが地面を滑ることはないものの、
次のドミノとの接触面の滑りが悪くなって
つっかえ棒のようにガッチリとドミノが支えられてしまう。

つまり、摩擦を強くしても弱くしてもドミノは倒れない。
日常生活で簡単にできるドミノ倒しでも
物理世界ではバカ正直すぎて理想通りにならないのだ。

ちゃんとしたドミノ倒しを完成させるためには
どの物体同士の衝突かを見極めて
摩擦係数を変化させる必要がある。



こうすればドミノは次のドミノの表面を
スムーズに滑りながら倒れつつ、
接地している面は滑らずに「前へ」倒れてくれる。

そうして完成したのがドミノ倒しデモ(2008年4月)。


このぐらいまでなら学生でも触れるようになると思う。
物が衝突し、崩れ落ちるさまが表現できるので
「物理エンジンを利用している」という感触は手に入れられる。

しかしこの先の領域に進むには
自分の理想通りの動きをさせるために物理エンジンのプログラムを
細かく改造できる腕が必要になってくる。

物体に力を与えるか、速度を設定するか

2008年4月あたりに物理エンジンを触り始めたものの、
そこから仕事が忙しくなって、
次にちゃんと研究が始められたのが夏休み。2008年8月。

最初、物理エンジンにあらかじめ用意されている処理で
「物体に力を与える処理」と
「物体に速度を設定する処理」の違いがわからなかった。
どちらを使っても物体が移動するからだ。

ここで質量の概念を理解しなければいけなかった。
ここまで物理を意識にするのは高校以来。

質量は重さと混同しやすいが、
重さというものは重力があってこその話。
重いボウリングの玉と軽いサッカーボールでは
同じような大きさでも重さが異なる。
しかし、無重力に持っていけば両方とも0kgである。

では無重力の中ではボウリングの玉もサッカーボールも
同じ動きをするかというと、
やはりボウリングの玉を動かす方が力が必要になるわけだ。
質量が大きいものは動かしにくく、
また、動いている物体は質量が大きい方が
強い力を持っていることになる。

さて、物理エンジンの「物体に速度を設定する処理」を使って
物体の移動速度を時速100km/hに設定すれば
どれだけ質量の大きい物体でも小さい物体でも
次の瞬間に100km/hになっている。

しかし「物体に力を与える処理」を使って
物体に100の力を加えた場合、
質量が大きくて動かしにくい物体はそれほど速度が上がらず、
質量の小さい物体は一気に加速する。ここに違いがある。

ゲーム中で重い砲弾も軽い砲弾も同じ軌道で飛ばしたければ
力を加える処理ではなく
速度を設定する処理を使わなければいけない。
力を加える処理を使ってしまうと重い砲弾がほとんど飛ばなくなるし、
重い砲弾を軽い砲弾と同じように飛ばすために
与える力をどれぐらい増やせばいいのか調整しにくいからだ。

しかし、ゲーム中に風が吹いて物体が吹き飛ばされるような処理では
速度を設定する処理ではなく力を与える処理を使う。
速度を設定する処理を使うと軽い物も重い物も
まったく同じ速さで吹き飛ばされて不自然だからだ。

この違いがわかったとき、
なぜ2つの処理が用意されているかがスパッと理解できた。
(物体の回転に関しても「角速度」と「トルク」と呼ばれる同様の違いがある)

このあたりを踏まえつつ、いろいろな実験を踏まえて
作った物理エンジンのプログラムが以下の動画。



ある一点から一定の距離内にいる物体に対して
ある瞬間に中心から外向きの力を与えると
爆発させたように見せられる。

同じ理屈を使って一定方向に継続的に弱い力を与え続けると
風が吹いたように見せられる。

銃口の位置から発射した直線に最も近くで当たった物体に
当たった位置を基点として力を与えると
銃弾に当たって弾いたように見せられる。

同じ理屈で画面中央の照準にどの物体が合わさっているか算出し、
その物体に対して自機の方向へ力を与えると
物体を吸い寄せているように見せられる。
(Xbox360の「バイオショック」というゲームに影響を受けた)

そういった様々な表現を実験した。

物理エンジンの物理世界の状態を一時保存し、
物体が崩れ落ちる直前にリセットする処理も作った。
これで物理世界の状態をファイルにセーブ・ロードできるようになった。

エディット機能も作った。
物理エンジンの世界を一時的に停止して、
マウスで狙ったところに自由に物体を置けるようになった。

夏休み中の1ヶ月ほどでかなりの研究が進み、
いろいろなものが作れそうな予感がした。

が、ずっと気になっていた問題点があった。

平面の動きのマウスで立体物を操作する難しさ

3D空間に自由に物を置けるエディット機能が付き、
物理エンジンの働く中で物を置いたり投げたり崩したり
いろいろできるソフトはほぼ完成した。
この時点での開発コードネームは「物理エンジンで遊ぼう」。

画面の中で物理的な現象が繰り広げられるのは
眺めているだけで楽しいので
「誰でも物理エンジンを体感できるソフト」にする予定だった。
しかし、それだけではただの実験ソフトなので、
例えば積み上げられた物体の上にプレゼントの箱が置いてあり、
物を崩してプレゼントを地面に着地させればゴール、のような
何かゲーム的な要素を入れようかと思いついた。

ただ、このとき常に感じていた問題が、操作がややこしいこと。
「わかりやすくて直感的な操作=マウス操作」なのだが、
マウスは前後左右という平面上しか動かせない。
縦と横という2軸の移動量しかわからないのだ。

なのに、3D空間内をキャラクターが移動するためには
前後左右に加えて上下という3軸方向があり、
さらに見ている向きを上下左右に変える操作も必要になる。




しかも物体を置いたり手に取ったり回転させたりするなら
マウスだけでは足らず、キーボードのキーまで併用することになる。

3D空間の中を歩き、落ちている立方体をつかんで
別の立方体の上にピッタリ置く、という操作だけでも
かなり難しいし、コツが必要になるのだ。
いくつもの物体をきちんと高く積み上げることを考えると
上下の移動はジャンプのような挙動ではなく、
静止したまま上下にアップダウンできる仕様でなければならない。

ゲームコントローラーなどは直感的な操作に特化しており
こういった面ではかなり優秀なのだが、
マウスでできる操作というのは極端に少ない。
右親指で2つのボタンを同時押ししたり、
左親指でスティックを倒しながら人差し指でLボタンを押す、などという
市販ゲームでよくあるような芸当は真似できないのだ。

さんざん悩んだあげく、
「3D表示ではあるけれど、2Dとして動くようにしよう」
と大きな変更を決断した(2008年9月)。
DSの「Newスーパーマリオブラザーズ」でもあったが、
3Dモデルを使ってはいるが、画面に対して上下左右しか移動せず
操作に関しては古いゲームと同じ2D操作なのだ。

これなら画面に対して前後方向(Z軸)に関する操作を
無視することができ、かなり簡略化される。
それに合わせて物体の回転も
正面に対して右回り左回り(Z軸回転)のみに限定する。

今まで勉強した物理エンジンは3D用なので、
物理世界の動きを毎回チェックし、前後方向にズレた物体や
手前・奥に対して回転した物体を逐一修正する、という方法を取った。
前後に崩れかけた物体を即座に整列させているのでかなり強引だ。




今まで使っていた物体は球やカプセル型だが、
前後に倒れることがなくなった今回の改造によって不自然になったので、
円柱を横倒しにしたような見た目に変更した。




3D空間を映すカメラは前方を真っ直ぐ見るだけに固定し、
向きを変える操作も必要なくなった。

これでずいぶん方向性が決まった。

そんなときPS3で発売された「リトルビッグプラネット」という
物理エンジンを使ったゲームの動画を観ると
同様に上下左右の移動が中心となっていた。
おそらく操作性を追求した結果、
同様にZ軸を捨てる判断をしたのではないだろうか。
惜しむらくはこのゲームの発売前に完成しきれなかったこと。

方向性がいよいよ決まり、あとは完成まで突き進む。

ゲームとして楽しめるルールを考えた

3D表示ではあるけど2D操作ということが決定すると
一気に完成形がイメージできて作業が進んだ。

もともと、映画「バック・トゥ・ザ・フューチャー」の
オープニングに出てくる自動で動く妙なカラクリが大好きで
ボールが転がったりドミノが倒れる動作が次のアイテムに伝わり、
いろいろな結果を引き起こすギミックには昔から憧れていた。

高校1年生の文化祭では教室中にドミノ倒しを作った。

2年生の文化祭では、理科室を借り切って
部屋の中にギミックを張り巡らせるクラスの出し物を企画した。
最初に倒したドミノが重りを落とし、ボールを転がし、
最終的には花火に着火した。
その都度すべてのギミックを復帰させるのだが、
1日数回しかない開催時間には
廊下の窓に観客がひしめき、かなりの人気を呼んだ。

クラスのみんなにも強烈な思い出になったようで、
3年生のときも同じ企画をやった。

最近ではNHKの「ピタゴラスイッチ」という番組に出てくる
ピタゴラ装置がまさにそれで、高校のときの熱気を思い出した。

日常生活でこういったギミックを作ろうすると
かなりの物と手間と広さが必要な上に、
後片付けも大変だし、一回きりしか実行できない。

こういうのが手軽に体験できるソフトを作れれば、と思っていた。
物理エンジンに出会うまでは
2D画像のみでも作れるんじゃないかと思案していた。
シーソーやバネや風船やドミノというアイテムをあらかじめ用意しておき、
マウスでドラッグして好きなように配置できるソフト。
スタートとゴールの方法だけ決めておいて
たくさんのアイテムを絡めてゴール条件を満たした方が得点が高く、
その得点を貯めて新たなアイテムをアンロックするような。

ただ、アイテム同士のアクションをあらかじめ決めておく必要があり、
アイテムごとに判定を作る莫大な労力がかかる割には
動かしたときの結果が予想の範囲を越えないので
面白味に欠けるという想像がついた。
こういうギミックは
「まさかこれがこんな動きをするなんて」というところに感激するものだ。

そんな想いと体得した物理エンジンプログラムが結びついた。
物理エンジンで動いているキャラクターは
あくまで物理現象に従っているだけなので、
開発者の予測を越えるギミックを作ることも可能となる。
あとはそれを使いやすい操作方法で提供するだけで
いろいろなステージを作れる無限の可能性がある。

となれば、より面白そうなギミックを作れるよう
ソフト側で手配してあげればいいのだ。

ジョイントを導入する多大な苦労

いろいろなギミックを作ることを考えると
個々の物体が自由落下するだけでは弱い。

そこでまずはジョイントを導入することにした。
2つの物体をつなぐ「関節」の機能である。
この処理そのものは物理エンジンに備わっているが
実際に導入してみると様々な問題にぶつかった。

今作「あそぶつり」ではアクションボタンを押すまでは
あくまでツールとして自由に物体を配置できる仕様になっており、
物理エンジンは作動していない。物理現象も起こらない。




たとえばジョイントでつながった物体をつかんで移動した場合、
ジョイントでつながっているもう片方を移動しないでいると
物理エンジンを作動させた瞬間に
2つの物体がすごい勢いで引き合う現象が起こる。

いわば腕と胴体がゴムひもでつながった人形の
腕だけを大きく引っ張っているのと同じなわけだ。
物理エンジンが作動するまでは胴体も腕も
マウスで動かしただけ引っ張っていけるが、
物理エンジンが作動した途端、
「ジョイントでつながっているから離れない」という物理現象が働き
離れていた物体たちがギュッと引き合ってしまう。

となると、物体を移動する際は
ジョイントでつながっている側の物体も含めて
ごっそり移動させる必要がある。

そのためには、その物体にジョイントがあるのかどうか、
あるならあるでいくつのジョイントがあって
どの物体とつながっているのかという情報を
きちんと管理していないと実現できない。
これが相当にややこしい。
プログラムで言う「再帰処理」も必須になる。

物体を回転させる場合も同様で
独立した物体を回転させる場合は物体の中心で回転するが、
それがジョイントで別の物体とつながっている場合、
物体を回転させることでジョイント部が離れてしまう。
そうならないように、
ジョイントでつながっている物体を回転させる場合は
ジョイント部を支点として回転するよう処理しなければいけない。
これもなかなかにややこしい。

さらに別のパターン。



物体Aと物体Bがジョイントでつながっており、
物体Bと物体Cもジョイントでつながっている。
ちなみにCは空中に固定されているので移動も回転もしない。

本来、物体同士が衝突するとめり込まないように反発するが、
BとCはジョイントでつながれているので
衝突反応も起こらないようになっている。
どれとどれがジョイントでつながっているかを調べる処理も
物理エンジンには用意されている。




さて、こういった物体の場合、物体Bはジョイント部を支点として
回転できるギミックになる。
こういったギミックは今回のソフトにとって必須だろう。

しかし、最初これがうまくいかなかった。
物体Aと物体Cが衝突してしまうのだ。




ジョイントで直接つながっているAとB同士、
BとC同士は衝突しないものの、
物体Bを介して結果的につながっているAとCは
直接ジョイントでつながっているわけではないため
物理エンジンに用意されている
「両者がジョイントでつながっているか調べる処理」では
チェックできないのだ。
このままだと物体Bが少し回転しただけで止まってしまう。

そこで、ある物体からジョイントを通じてつながっている物体を
次々とたどっていき、
塊となっている物体すべてを同じグループと認識して、
その中では衝突判定が起こらないようにしないといけない。
これも相当にややこしい。

このあたり、物理エンジンを使っていたとしても
結局プログラマ自身の腕と対応力がかなり要求される。
世間で「物理エンジン」の評判が騒がれるようになって以来、
物理エンジンさえ使えば
楽々とリアルな物理現象が再現できる、という妙な誤解がありすぎる。

「物理エンジンを使っているならできて当然」というわけではなく
今までにもあったプログラミングの難しさに加えて
物理エンジン自身の制限にも挟まれることになり、
うまくいかなくて悩んだり解決法が見い出せない苦労を強烈に味わう。

教え子の中にも「流行りの物理エンジンを使ってみたい」的な
ミーハーな気持ちで興味を示す学生がいるだろうが、
やればやるほど地獄を味わうのが物理エンジンだと思う。
毎日のように壁にぶつかり、挫折しそうになる状況を
自分で解決できる力と覚悟がないなら手を出さない方が無難だ。

そのあたりはプロの開発者たちも知っているだろうから
ただ個々の物体が衝突している程度では
それほどの評価は得られないだろう。
そこから先の世界にこそプログラマのレベルが問われるのだ。

自発的に物体を動かすモーターの導入

最後に導入したのが「モーター」だ。
ジョイントが成功したとはいえ、
自由落下による重力のみを動力としたギミックだと
作れるものが限られてくる。

幸いにもモーターを設定する機能は
物理エンジンに搭載されていた。
しかし、実際に使ってみると2つの問題があった。


ひとつは物理エンジンに用意されたモーターは
ジョイントに対して回転力を与える機能だったこと。




ジョイントでつながった2つの物体の
関節部分が回転しているように処理されるわけだが、
この場合、どちらの物体がどう回転しているのかがハッキリしなくなる。




関節部分が回転しているという概念ため、
2つの物体が逆回りする運動をする。
たとえばAの物体が右回りをしているとき、
同時にBの物体は左回りをしている。

ということは、直方体にタイヤがついたような一輪車を作って
それが前進するようにタイヤを回した場合、
車体である直方体も逆回りしてしまう。

さらには、ひとりで勝手に転がっていくタイヤような
他の物体とジョイントでつながっていない物を作りたくても
ジョイントに装着するという前提があるため実現できないのだ。


もうひとつの問題はモーターのパワーが弱いこと。

ギミックを作るために使うことを考えると
リアルな計算を踏まえて力不足で回転しないようなモーターよりも
設定した方向にパワフルに回転する方が面白い。
四角い形のタイヤであっても
強引に跳ねながら進むぐらいのパワーがある方が楽しいのだ。

が、物理エンジンに用意されているモーターは
つながっている物体の質量次第ですぐに出力不足になる。
出力を設定する数値に
極端に大きなものを設定するとエラー落ちするため、
ものすごくデリケートなモーターだった。

ロボット開発のシミュレーションをする場合なら
複数のギアを間に噛まして最終的な出力を上げるのだろうが
ギミックを楽しむソフトでそんなややこしいことはやってられない。

丸2日ほど悩んだものの、結局、モーター機能の使用を諦め、
物体の角速度(回る速さ)を設定する処理で
どんな大きな物体であっても設定した速さで回転するようにした。
内部的には物理エンジンのモーター機能は使っていないが
ユーザーがイメージしやすいよう、「モーター」と呼ぶことにした。

モーターが完成すると俄然、ギミック作りが面白くなった。
今後まだまだいろんな機能を追加していきたいが、
単純な形の物体にジョイントとモーターが加わるだけで
利用の仕方にかなり応用が効くことになった。

そして実際に完成した「あそぶつり」が以下だ。


長きに渡って書いてきたあそぶつりの経緯だが、
それを踏まえて観てもらうと
様々な機能が試行錯誤の上に出来上がっていることがわかると思う。

選んだ物理エンジンと参考になった資料

あそぶつり開発は今まで作ったソフトの中で
一番大変だったように思う。

最初にフリーで使える物理エンジンの中で何を使おうか悩んだが、
PhysX(フィジックス)」は開発者だけでなく
そのソフトをプレイする人のパソコンにも
新たなファイルをインストールしてもらわないといけないため
最終的にフリーソフトとして配布するのが目的の私には
どうにも合わなかった。

ただでさえDirectXがインストールされていないとダメなのに
そういった要インストールファイルが増えて
「手軽に遊ぶ」という路線から外れるのが嫌だった。

そこで「ODE」を使うことに決めた。
これはゲームの実行ファイルと同じフォルダに
dllファイルが入っているだけで動作するため、
ユーザーにとっては別のサイトから何かをダウンロードして
インスートルするような手間がいらないからだ。

さて、ODEを使うことが決まってから大変だったのは
圧倒的な情報不足。
ODE物理エンジンの資料がとにかく限られているのと
仕様を理解しても思い通りの結果にならない部分。
必要な処理をどうやったら実現できるのか
情報集めと試行錯誤の連続だった。

その都度、いろいろな処理をネットで検索したが、
物理エンジンを利用した者として非常に有益な情報が多く、
一番お世話になったサイトと言えるのがbambooflow @Wikiだ。
www10.atwiki.jp
関数資料として綺麗にまとまっていて
関数の仕様を知りたいときにWiki内検索して
書式と利用法を学ぶのに大いに役立った。
ODEを勉強したい人はブックマークしておくことを強くオススメする。

もうひとつ重宝したのが、この本。

後半はほとんどロボットに関する内容なので不要だったが、
前半部分は関数資料として活用したり、
知らなかった機能に気づくことができた。
前述のWikiの方が広く調べる場合は役立つが、
そこで踏み込めていない部分まで載っている情報があった。

物理エンジンを扱えるようになると
誰しも考えることが同じなのか、
2008年中に似たようなブラウザソフトを
ちょくちょく見かける機会があった。

それと同じことしかできないなら存在意義はないので
クライアントソフトということでの描画処理の速さ・滑らかさ、
日本人ユーザーへのとっつきやすさ、
グラフィックソフトのような多機能系ソフトの複雑さは排除して
なるべく手軽さを大切にしたポジションなどで
勝負できればいいな、と思う。

あとはユーザーによる自作ステージがどこまで生まれるかが心配だが、
ソフトの使いやすさに関しては全力を注いだつもりだ。

ソフト名称は「ピタゴラスイッチ」をもじって
「アルキメデスイッチ」とかにしてもよかったのだが、
語呂も悪いので「遊ぶ+物理」で「あそぶつり」になった。
カッコつけて英語にしたりするよりも親しみやすいだろうし。

処理が複雑なだけにまだ不具合が眠っている可能性があるが、
「毎年1本はソフトを作る」という
自分の目標が達成できて、ちょっとホッとできた。

おわり。

総アクセス数