ニートが頑張るブログ

ニートが現実逃避するために創作活動など色々とカオスに頑張ってみる
ニートが頑張るブログ TOP  >  ニートメソッド >  油の再発明

油の再発明

 
前回の油のやつ、どうやって作ったのかの話をします。


「自由にくっついたり千切れたりする油を作れ」と言われた時
普通はどういうアプローチをするのだろうか? どういうのを思いつくだろうか?



ところで、昔 wonderflというサイトがあって (今もあるわい)
そこで色んな人が試作品みたいなフラを公開していたもんです。

そういうところで 例えば「water」とかのタグで検索すれば、
色んな人による水を表現しようとする技術・作品が見れたりします。 (コースティクスとか 凄いの)

では、を作っている人もいるかとおもって「oil」で調べたりすると
これが全く見つかりません。


あと、リアルタイムな流体力学を利用したゲームというと殆ど知りません。
ゲームの中で 水滴やスライム油滴の分離や結合を リアルタイムに描いているモノなど、とんと見た記憶がありません。

かろうじて、PSPの「ハイドリウム」というゲームくらいかと思います。


「ロコロコ」とかも、ある程度は「水滴」だと言えるかもしれないけど、
やっぱり、くっつく瞬間・分裂する瞬間はごまかしているのが分かります。

blenderの水とかはどうやってるのだろう。 (でも水はあるけど水の上の油はないよなぁ)


まぁそんな感じ。
それくらい、リアルタイムに油滴を表現している技術というのはあんま知らないワケです。

なんも参考にならんので自分で色々考えました。


今回の自分の油の作り方、まぁ車輪の再発明かもしれませんが、
一応書いておくのです。


二次元配列だけで簡単につくれる油表現です。




 

油の作り方


自分が考えようとした油づくりのアプローチですが、
2つほどくらいしか思いつかなかった。


・一つは 有限の頂点(コントロールポイント) によって囲まれた パスによって描画するという方法。
各頂点、辺、角ごとに色々と計算して、油らしさを表現していこうという方針。
油の再発明


・もう一つは 有限の円を 「油の粒子の最小単位」のように考えて
分子間引力的にくっつきあう そいつらによって表面張力を擬似的に計算し、 (昔つくったマグネットみたいなのイメージ)
何らかの方法によってそいつらの「塊ごと」に 油の周辺を描画する。
油の再発明



この2つね。

で、多分後者の方が質の高いモノが出来そうな予感はするのですが、
マジで難しそうなので、今回採用したのは前者のやり方です。


少しずつ段階を追って作っていったので それを順番に並べていきます。

◆1. まずクリックして歪めることの出来る六角形を作る。

油の再発明

x座標y座標を入れておく配列を6個並べた2次元配列だけで作れます。

ここで、クリックすることで 配列を回転させるような機能を作っておきます。


クリックした場所に一番近い場所を配列の0位置にします。

これはpush とpopをその頂点の番号だけ連続することで簡単に実現できます。

例えば、4番目の頂点をクリックしたとき、

配列の最初の要素を最後にくっつけるのを4回くりかえせば、
配列の0番目が4番目の頂点になるワケです。


・最後に触った場所が0番目。
・任意の点を0番目にすることが出来る。


この機能が後々重要になってきます。


とにかくこれで 「ドラッグでフニャフニャするパス」まで作ることが出来ました。
次は 表面張力を表現し、 形が自動的に「円」に戻るようにします。


◆2. 表面張力を擬似的に表現する

油滴は、ほうっておくと円の形に戻ろうとします。

それは本当は色んな力が働いた末、結果的に表面積を最小にするためにそういう動きが産まれるワケです。

(昔つくったマグネットのやつでは 増やした玉が結果的にハニカム構造状に集まったりする)
(だが意図してハニカム構造になるようにプログラムした訳ではない 結果的にそうなるのが効率的に正しいからそうなるだけ)
分子間引力的な挙動だけをシミュレートした結果、副次的にハニカム構造が産まれるのだ)
(そういうのがモノのありかたということだ)


だから本当は、 油の表面張力とか引力とかを計算した結果、
「結果的に円に近づこうとする」みたいな挙動を表現できたなら、それが理想なのだろうけど、
まぁ今回はそういうことは言ってられないと思った。


もう擬似的だろうが、直接的だろうが、 そのまま「円に戻る」ような動きをさせてしまいます。


これは
2つの方法でやります。

一つは辺の長さ調整。

各頂点の座標差を計算して、
各辺の長さを調整するようにします。


油のボリュームをvolとすると、その油が作る油膜の周辺は√volに比例します。

よって各辺の長さを √vol/頂点数 に近づけることで伸びた油膜、縮んだ油膜の形を戻らせることができます。

油の再発明

↑図のような状態から辺12の長さを√volにするには

x = x2 - x1;
y = y2 - y1;
d = √(x*x + y*y);


で、

x2 = x1 + x * √vol / d;
y2 = y1 + y * √vol / d;

とするだけです。 ノルム。
実際には分数関数的に近づけていかせます。


もう一つは 拡張点ごとに 半径径の位置に調整するやりかたです。

油のボリュームをvolとすると、その油が作る油膜の半径もまた√volに比例します。

よって各頂点の位置を その位置の場所に戻らせることで へこんだ油膜、伸びた油膜の形を円に戻らせることが出来ます。

油の再発明

これもさっきと同じような感じで出来ますね。 さっき以上にシンプルですね。


で、もうこの二つの挙動だけで、油膜のパスが円に近づくようにプルプル動くようになります。




◆3. 油膜同士の衝突判定と結合を出来るようにする

で、ここまで出来たら今度は 油滴のパス同士の結合が出来るようにします。


2つの配列を結合するだけなのですが、ちょっとだけ工夫が必要です。

パスとパスが衝突したとき、 衝突した頂点を 配列の0番めにくるように回転させます。

油の再発明

まず右の油膜の頂点で衝突しているので、を0地点に回転します。

そして次は、左の油膜の中で、の頂点に一番近い頂点を探します。 それがなので、を0地点に回転します。


そうすれば、あとは2つの配列をくっつけるだけで パスがくっついたときのパスの順番になっています。

油の再発明


そして肝心の衝突判定は
各頂点ごとに hittestをしていくしかありません。
これはまぁ処理が重くなるところだけど。




◆4. 分断して千切れる判定を出来るようにする

次に千切れる判定をします。

油の再発明
パスが↑こういう形をとったとき、この頂点を区切りにしてパスを2つに千切れるようにします。
これが出来なければ油ではありません。


これは色々考えましたが、最終的に 辺の交差判定でやることにしました。

点1(x1, y1) 点2(x2, y2)
点3(x3, y3) 点4(x4, y4)

とした場合、
線分12 と 線分34 の交差判定は
a = (x3 - x4) * (y1 - y3) + (y3 - y4) * (x3 - x1);
b = (x3 - x4) * (y2 - y3) + (y3 - y4) * (x3 - x2);
c = (x1 - x2) * (y3 - y1) + (y1 - y2) * (x1 - x3);
d = (x1 - x2) * (y4 - y1) + (y1 - y2) * (x1 - x4);
return c * d < 0 && a * b < 0;



では、この大変そうな計算を
全ての油膜で、
全ての辺と辺の組み合わせ毎に、 
毎フレーム計算するのかというと、


実はそんなことはしなくていいのです。


実際に↑この図のような状況になるのは、
マウスで油膜を掴んで、油の中心を貫通するようにドラッグして、半分に分割しようとしたときだけなのです。

つまり、この図の水色の頂点というのは、かならずマウスで最後に触った「配列の0番目の頂点」になっているということです。

よって、最後に触った油膜だけ、
そして 頂点0を通る 線分01と、各辺の組み合わせ分だけの交差判定だけを計算するだけでよいのです。

(さらに後々のことを考えると、必ず6角系以上で分離させたい場合とかは もっと条件を厳しくしてもいい)


本当にこれでうまくいくのです。

触ってない油膜がほったらかして勝手に千切れる場合はないのか?
勝手にこのような分裂しそうな形になっているパターンはないのか?


それを考えると思いつくのは、◆3で油膜同士をくっつけた直後の油膜の形状は、
たしかにこのような形(ひょうたん型)になっているかもしれません。

でも、くっつけた直後の油膜には分離の処理をしなくてよいことがわかります。
そんなことしてたら、いつまで経っても油膜がくっつかないので。

くっついた直後の油膜にはクールタイムを設けて、交差判定、 分裂判定をしないでよいことが分かります。


まぁそういうわけで、千切れるパターンというのはかならずマウスで触っている頂点でしか発生しないということですね。


◆5. 引っ張って千切れる判定を出来るようにする

もう一つの千切れる判定があります。

二つに分割させるように動かした時だけではなく、
思いっきり引っ張った時もブチっと千切れるのが、油というモノです。

この判定には角度の検出を使うことにしました。

油の再発明


角度はこう
x = x1- x0;
y = y1- y0;
rot = Math.atan2(y,x) * 180 / Math.PI;

x = x11- x0;
y = y11- y0;
rot2 = Math.atan2(y,x) * 180 / Math.PI;
sub = rot2 - rot;
sub -= Math.floor(sub / 360.0) * 360.0;



この角度が、ある程度より急に小さくなったとき、千切れ判定とします。  (今回自分は20度以下としています)


当然、ここでも触っている辺は0地点となっているので、
例えば12角系の油の場合、
0、1、11の3箇所配列のxz座標を要素にもつ配列を作れば、
その三角形を利用して新しい油膜を作ればいいのです。

油の再発明


ここで計算した角度には他の使い方もあります。

千切れないまでも、急に変な形になったとき、
180度を大きくこえた場合なんかも、裏返りそうになってる場合があります。

(今回、この辺があんま上手く機能してないので、ぶっちゃけ裏返った油膜を直せない場合があります)


◆6. 頂点数を倍増して描画する

油の再発明

で、ここらで頂点数が少ない時になめらかに描画する方法について考えます。
(ちぎった場合とかで新しく産まれた3角系の小さい油膜とか)


最終的にこうやって動きつつある6角系の油をどうやって滑らかな油にみせかけるか?

curvetoで描画しようとしても上手く行きませんでした。
かといって、自力でスプライン曲線とか計算するのも重すぎかなぁという感覚がありました。


だから、もっと軽くてシンプルなやり方を考えました。

油膜が8角形以下のとき、角形を二倍にしてなめらかに描画するようにします。

パスの中心点 (平均値で考える) (それが後々に問題になるのだが・・・)
からの各頂点の距離を d1 d2・・・とします

油の再発明

d1 d2はこうなります
x = x1 - x0;
y = y1 - x0;
d1 = √(x*x + y*y);

x = x2 - x0;
y = y2 - x0;
d2 = √(x*x + y*y);


ここで d1 と d2 の平均値 を d3 とします。 
d3 = (d1 + d2)/2;

点1と点2の中点を 点3 とします
x3 = (x1 + x2)/2;
y3 = (y1 + y2)/2;



するともとめるべき点の位置は、
x = x3 - x0;
y = y3 - x0;
d = √(x*x + y*y);

x = x0 + x * d2 / d;
y = y0 + y * d2 / d;



これを各辺ごとに計算すると、まぁ二倍滑らかなパスを描画していくことが出来ます。

油の再発明

6角系の油膜も、これで12角系で表示することができるので、まぁ大体ごまかせます。


こういうののもっと極まった技術がスプライン曲線とかになるんでしょうが、
正直あれを自力で作ると重いだろうし、やってられんだろうと思ったのでこれにしています。


そしてこれを利用すると パスの頂点数を二倍に増やすことも出来ます。


◆7. 頂点数を半減させる機能

パスを◆3の方法でくっつけていくと、パスの頂点数がどんどん増えていきます。

6+6で12
12+12で24
・・・
頂点数は二倍、油膜のボリュームは二倍になっても、辺の長さ、直径の長さは√二倍にしかなりません。

よって、油膜をどんどんくっつけていけば行くほど、パスは無駄に細かくなっていきます。

すると当然処理も無駄に重くなっていくのです。


こういうのを、ある程度のところでカットしてやらねばなりません。


だから 各辺の長さ
つまり
「ボリューム / 頂点数 」 がある程度小さくなったとき、パスの数を半減させる機能が必要になってきます。

これ自体はまぁ、配列の要素を一つ飛びに削除していくだけです。


◆8. 頂点数を二倍にする機能

次に、逆のこと
そうやって ボリュームが増えた油膜の 頂点数を半減・半減、していくこと自体は問題ありません。

が、逆に そういう油膜を 半分に分割したり、 ちぎったりした時にまた問題になってきます。

ボリュームがあるくせに妙にカクカクな3角系とかの油膜がでてくることになります。
◆6の機能で6角形として描画することになりますが、やはり6角形は厳しい)

この時に逆の処理が必要になります。 頂点数を二倍にするのです。

ここで、さっきの 頂点数を二倍あるかのように表示する機能がまた役に立つのです。


◆9. 衝突の条件を考える

で、ぶっちゃけ今回自分がやったことはこれでほぼ終わってます。

あとは色々と微調整しまくるだけです。

3で衝突したとき、すぐにどんどんくっつかないように条件をつけたりします。

油膜と油膜が衝突すればすぐにくっつくわけじゃないのは、ラー油をみていれば分かることです。

油膜と油膜がせりあっている間の境目の部分を、箸でつつくと、そこで急にプチっとくっついたりします。
それを表現したい。

よって油膜は、ある程度のスピードで衝突したとき以外はくっつかず、
拡張点を押し出すようにプルプルするようにさせます。

そして、他の油膜の領域内に衝突した頂点が0地点であるとき、
それはマウスで触ったということなので、そのときはすぐに結合が発動するようにします。

これが、箸で境目をツンとつついた感じの表現となります。


◆10. あとは色々と

というわけでこれでだいたい完成となりました。

あとは油膜にグラデーションのテカリをつけるようにしたり、
お皿とか机の模様とか机の影とか、餃子のタレをかの素材を用意します。


やってることは完全に擬似3Dです。

擬似3Dとして工夫したところは、
油の周辺のテカリをX軸とY軸の傾きに応じて回転させること、
そしてお皿についてるテカリはそれと逆方向に回転させるようにしたことですね。

擬似的だけどなんかそれっぽい感じ。


以上。






今回のやりかたで一番問題があるのは
油のパスの拡張点の平均値を、真ん中だと定めているところです。

このせいで、油がひょんなことから 「C」のような形状になったとき、その凹みを戻すことが出来ません。

あと裏返った油膜を元に戻すことも出来ていません。

これをどうするか。 スライムなら死ねばいいか?


急にやる気が無くなったテキトー文章 オワリ

油の再発明


関連記事
[ 2016/05/18 16:26 ] ニートメソッド | TB(0) | CM(2)
これまででゲームの泡とか気泡ってどれくらい細かく表現されたの?
[ 2016/06/07 21:01 ] [ 編集 ]
コメント返信


> これまででゲームの泡とか気泡ってどれくらい細かく表現されたの?


なんか質問の主語が

自分のゲームのことなのか ゲーム全般のことなのか よく分からなくて
自分が質問の意味を勘違いしているかもしれませんが、



多分こういう話ですかね。


今のところのゲームというのは 大体

水しぶき、 血しぶき、 泡だとか そういうエフェクトは 

リアルタイムに飛沫の挙動を計算しているのとかではなくて、

事前にレンダリングをすませた、つまりプリレンダの2Dエフェクトを出力することで
まぁ大体の 血しぶきだとか 泡だとかを表現しているわけです。


自分が前に作った エフェクトメーカーという奴は、そういうものを自作するためのツールだったりします。
http://nameniku.web.fc2.com/effectmaker/


http://nameniku.web.fc2.com/sozai/awa2half.gif
ゲームに出てくる 泡だとかそういうエフェクトというのは、 
こういうモノを ある程度の物理法則にしたがって出力しているだけ というのが分かってくるようになるかと思います。



いまだに
殆どのゲームがその手法から脱却してないと思います。


だから、血の水滴が表面張力でくっつくだとか

水中の泡が リアルタイムに障害物をよけるだとか 空気がはじけたり くっついたりするのを表現してるようなゲーム、

そういうのは自分は個人的に見たことがありません。



ここまで書いておいて 全然質問の答えになってないようなきもします。



自分が知ってる中で泡が一番細かかったゲームの話とかすればいいのでしょうか?
でもよくわからんのですよね。 

水中ステージがあるゲームというのも限られますが、
それが一番きれいだったゲーム・・・

やっぱよくわからんのですよね。 

なぜかというと、グラフィックに気合入れてるゲームというのは 汚しにも気合を入れているもので
たいてい水中の中は汚くてよくわからんかったりします。

ラスアスなんかでも水中に潜りますが、そのときの気泡がきれいだったか 細かかったか、 
そういうのはあんま意識してなかったワケです。


というわけで 長々と書いたわりによく分らん米返信ですいません。


[ 2016/06/08 20:05 ] [ 編集 ]
コメントの投稿












管理者にだけ表示を許可する
トラックバック
この記事のトラックバックURL

月別カレンダー
02 ≪│2017/03│≫ 03
- - - 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 -