円筒状の瓶をモデリングして、その中にビー玉を無造作に入れてみます。
瓶は、円筒状の瓶にすることにしました。作者が愛用しているインスタントコーヒーの瓶を採寸して、デフォルメしました。透明なガラス瓶の属性を与え、内部が透けて見えるようにしたいので、「論理演算のいじめパタン」でも説明したように、瓶全体が1つの物体になる様に作ります。
「ワープアウトしてくるキャンディポット」で使ったテクニックを用いて、瓶を半分に切り取り、断面を確認することもお忘れなく。
ビー玉については、「ビー玉がスロープを跳ね落ちる」で作ったモデルを使い回します。
実は、今回のメインはビー玉の配置方法について、です。ある空間に球体を詰める時、なるべく密度が濃くなるように配置することを「球の最密充填」と言います。今回は、これをやりたい訳ではなく、無造作に、ころころころっと流し込んだような感じにしたい。
まあ、実際に、瓶とビー玉を用意して瓶にビー玉を入れ、少し小刻みに振動を与えてあげると、結果としては最密充填に近い状態になりそうな気はしますが……。逆に、規則的に並べる方が簡単なので、それは興味のある方にお任せします。
さてそれでは、「無造作」とは、どうすれば良いでしょうか?
「金魚鉢」で、金魚鉢の底に玉石を並べる時に、玉石をランダムに並べる方法について考察しました。単純に乱数で座標を決めてしまうと、以下図の様に、玉石同士がくっついてしまって、見栄えが良くないのでしたね?
玉石同士がくっつかないように制御した、以下図の状態で「金魚鉢」のデータは作成したのですが、この状態で、何層も重ねて瓶に詰めると、スカスカに空間が空いてしまって、宙に浮いた玉石なども登場してしまいそうです。
もう一工夫必要、という事です。
そこで今回は、水平方向の座標はランダムに決定しますが、座標を算出した後で、他のビー玉と接触するか、または、地面と接触するまで、落下させてみます。
上の図はダメな例です。最初に、1のビー玉が地面まで落下、その後算出した座標から2のビー玉が落下して、1のビー玉とギリギリのところで接触、そこで止まってしまいます。同様に、3と4のビー玉が、非常にギリギリのところで、それぞれ2と3のビー玉に接触して止まってしまうと、ビー玉が宙に浮いてしまいます。
そこでさらに、他のビー玉と接触した時に接触したビー玉を避けて通るように、座標をずらす処理を加えます(下図)。
2のビー玉が1のビー玉と接触した時点で、少し左にずれて(赤矢印)、そのまま落下を続けた結果、1のビー玉のすぐ隣に落ち着く様子です。
横にずれた結果、さらに他のビー玉と接触してしまい、動けなくなったら、その時点で落下を中止して座標を確定します。
スクリプトでは、最初に接触したビー玉と一対一の判定しかしていないため、横にずれて避ける動作は二次元の判定となっています。このため、普通では止まらないような場所でも止まってしまうケースがあるようです。例えば、下図の様なケース。
1と2のビー玉が地面に到達したところで、3のビー玉が落下してきます。最初に2のビー玉と接触して、今度は少し右にずれます。細かく図示していませんが、右にずれたことで次は1のビー玉と接触して左へずれます。するとまた2のビー玉と接触して右へ……。こうして左・右に振動しながら、上図の様に1と2のビー玉の間に落ち着いて止まります。
実際のビー玉かつ三次元で考えてみると、3のビー玉は1、2のビー玉と2点だけで接しており、非常に不安定な状態です。通常であれば、図の面に対して手前か奥へ倒れて落下するでしょうが、スクリプトではこの様な状態で止まってしまうことがあるようです(三次元で計算した場合でも…)。これ以上処理を複雑にしても計算時間が増加するだけなので、「OK」という事にしてありますm(1__)m。
テスト用に、奥行きを考えず、二次元で配置してみた動画がこちら(↓)。
他のビー玉と接触した時に、避けながら落下していく軌跡を赤い線で描画しています。
これを三次元で実行して、座標ファイルを起こしました。
後は動きを付けて完成。
完成したデータと動画です。
1) | Xi-TRACEデータ | bee_in_bin.xitr |
2) | 座標データ | bee_in_bin_pos.txt |
3) | 座標計算スクリプト | bee_in_bin_pos.xitr |
1)と2)でレイトレーシングを実行します。3)は、2)の座標ファイルを生成するスクリプトです。結構計算時間がかかるので、別スクリプトにしてあります。座標ファイルをご自身で生成しない場合は不要です。
決定した座標に向かって、次々とビー玉が落下してくるような動画になっています。毎度のことながら、自由落下させると速すぎるので、今回は開き直って、等速直線運動にしてあります。