【Unity】Sphere Colliderを使って気体分子を作って遊ぶ実験

【Unity】Sphere Colliderを使って気体分子を作って遊ぶ実験

Sphere Colliderのパラメータを紹介する記事を書いていたのですが、Unityをいじっていて気体分子を表現できるかも? と思い立ったので遊んでみましょ。

Sphere Colliderのパラメータ紹介についてはこちら。

 

環境

macOS 10.13 High Sierra

Unity2018.1.0f2

今回作りたいもの

箱の中で気体分子が動き回るようなゲームを作ります。

この時、箱の中には複数の気体分子が飛び回るようにします。

Sphere Collider感を出すために単原子分子でヘリウムとかネオンとかアルゴンとかそんな感じの気体にしましょ。希ガスが充満する空間とか窒息まっしぐらですね。放電したら発光しそう(申し訳程度の化学知識)

気体分子のモデルとして使うのはSphereオブジェクトです。形がSphere Colliderと相性がいいですからね。

RigidbodyコンポーネントのUseGravityはfalseにして自由に動き回れるようにし、ゲーム開始時にランダム方向に力を加えます。

正確な動きのシミュレーションではないので、それっぽく見えればOKにします。

こんな感じの箱に分子が飛び回る
こんな感じの箱に分子が飛び回る

 

物理特性マテリアル

Sphere Colliderを使うので、物理特性マテリアルも設定しておきます。でないと、壁にくっついて終わりになっちゃうんです。

物理特性マテリアルは以下の設定にしておきます。

分かりやすく単純に
分かりやすく単純に

 

摩擦力はどちらも0にして、反発係数を1にします。この物理特性マテリアルは『gas』と名乗りながら気体分子だけでなく壁にも同じものをセットしました。

 

スクリプト

気体分子をインスタンス化するスクリプトも作ってみました。

Prefabにした気体分子オブジェクトのリストを作り、その中からランダムに気体分子を選択してインスタンス化します。作った気体は以下のように色分けして、見た目が変わるようにしています。

気体分子の色分け
気体分子の色分け

 

各オブジェクトにはRigidbodyをアタッチし、衝突検知モードをContinuous Dynamicにしています。壁だけではなく気体分子オブジェクト同士でも衝突してもらうようにしましょ。

AngularDragは初期値の0.05から0に変更し、上で少し触れたUseGravityをfalseにしています。

 

スクリプトの中では、intervalTimeで設定した時間間隔でオブジェクトをインスタンス化しています。どの気体分子をインスタンス化するかは、Random.Rangeを使って選択しています。

このRandom.Rangeってちょっと厄介で、範囲の最小値と最大値を引数で決めるのですが、floatで指定すると最大値を含み、intで指定すると最大値を含まないんです。

うっかりリストのカウント-1で指定したりすると、選ばれないオブジェクトが出てきて「あれ? 乱数だいぶ偏ってない?」なんて心配する羽目になるので注意(1敗)

Update()の中では、気体分子の数をカウントして画面に表示するようにもしています。

 

もいっこ作ったのが気体分子に力を与えるスクリプト。

インスタンス化された気体分子に、指定した範囲の力を与えます。Start()の中で、力をランダムに決めるメソッドを読んでいます。

最初の1回だけ力を加えてあとは何もしないサボりスクリプトになっています。

 

壁の配置

今回の主役はSphere Colliderですが、壁にもBox Colliderをつけておきます。

隙間が無いように置いておけばOKかな。

壁にはRigidbodyはアタッチせず、Static Colliderとして使います。気体分子がぶつかって壁が壊れるとか物理法則を無視するにもほどがありますからね。

いざ、実行

んじゃ実行してみましょ。

気体分子が飛び回る
気体分子が飛び回る

 

よしよし、うまくいってそう。

これで数が増えればそれっぽい動きになるかな。

こんな感じこんな感じ
こんな感じこんな感じ

 

狭い空間をたくさんのオブジェクトが飛び回っているのは見ているだけでも楽しいです。

画像がちっちゃいので分かりにくいですが、飛び回るオブジェクト同士でも衝突して運動の向きが変わっているのが見て取れます。

オブジェクトが増えると……

こうして眺めていたのですが、オブジェクト数が170を過ぎた辺りで異変が。

なんと、1フレームの処理時間が約1000msになっていました。フレームレートは驚異の1FPS。化石パソコンでもなかなかこんな値見ないっす。

Profilerを見るとこんな感じ。

急激に物理処理が上がった
急激に物理処理が上がった

 

FixedUpdateの中身がえらいこっちゃでした。もう上限の15FPSを突き抜けてますね。

原因として思い当たるのはアレ。

RigidbodyコンポーネントのCollision Detectionを『ContinuousDynamic』にしたことです。

『ContinuousDynamic』だと、自分の運動経路上にいるRigidbodyと非Rigidbodyとの衝突を検知してくれるのですが、動き回るオブジェクトに関して自分の経路上に来るかどうか計算しているので、そのコストが重いんです。

衝突検知モードの動作については上の記事で紹介しています。

動くオブジェクトに関して言えば、ちょうど動作が重くなった辺りではオブジェクト数が170個でした。自分の運動経路上に、自分以外のRigidbodyが飛んで来るかどうかの計算をするので、170*170=28900の計算処理が走ることになるのかな。この計算処理はFixedUpdate()が呼ばれるたびに走ることに。

いかんせんRigidbodyのコードは非公開で確認できないのですが、徐々に重くなるというよりは、一気にドカンと重くなる感じだったので、計算量はO(n^2)なのかなーと予想。

Colliderの数が多くなると地獄めいた計算になるのね。

まとめ

Sphere Colliderを使って、狭い空間を飛び回る気体の単原子分子を表現してみました。

Colliderがあるので、ぽんぽんぶつかっていくのを眺めていると楽しくなります。

が、しかし、数が増えると冒涜的で地獄めいた計算量となるので、用法用量を守って楽しくColliderを使いましょ。

     

ゲーム開発の攻略チャートを作りました!

CTA-IMAGE

「ゲームを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験を中心に、ゲーム作りの手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


ゲームを作り始めた時にぶつかる壁である「何をしたら良いのか分からない」という悩みを吹き飛ばしましょう!