自力衝突判定の勘所というか。
haraです。
衝突判定、物理計算とかが絡まず、単純に「ぶつかった・ぶつかってない」だけ取りたいなあ、と思ったのですが、参考書などを見てやってみても、なかなかうまくいかないというかすり抜けまくる!
ということで、僕は衝突判定がとても苦手です。
taroのエントリによると、Box2Dの衝突判定がいいらしいですが。。そりゃ確かに優秀だしsleepとかできてCPUにもやさしいだろうけど、b2Worldとかb2AABBとか、なんかめんどくせーYO・・
と思っていたら、wonderflにいい感じの衝突判定を利用した作品を見つけました。
Box2D使わないでこんなにきれいに衝突取れてる! というわけでコードを読ませていただきました!
このコードを応用すると、こちらの例のように
向かう方向は適当に、衝突だけ取るみたいなことが
そこそこ簡単にできます。
ではでは、動いてるもの同士の衝突判定で、気をつけた方がよさそうなポイントがあったので解説しましょう。
ディスプレイ反映はともかくとして動きの計算はより短間隔で。
コード見ると、1フレームの間に4回も動きの演算をしています。
for (var t:int = 0; t < 4; t++) { var i:int; for(i=0; i<elemNum; i++) { elemList[i].update(); } colManager.checkColision(); } for(i=0; i<elemNum; i++) { elemList[i].render(); }
これはおそらく、1フレームの間に進むスピードが早すぎると、トンネリングといって、演算のタイミングで衝突をキャッチできずにスルーされてしまうために、基本的なスピードを緩くして、その分演算の回数を増やしてキャッチしやすくするってことでしょう。
これの調整だけで、とりあえずトンネリングは回避できるみたいですね。
衝突した時は、何はともあれ反射を返す
ぶつかった時に、「動きを停止する」とかではだめみたいです。
なぜなら、ぶつかった=もうすでにいくらか重なっちゃってる、からです。
ここでの正しい対処は、
ぶつかった時に、「重なった分か、それ以上の力で戻してあげる」
です。
こうすると、うまく輪郭の範囲でかさならずに止まったり、跳ね返って見えます。
public function contact(contactX:Number, contactY:Number, _priority:int, force:Number):void { var tx:Number = contactX - px; var ty:Number = contactY - py; var angle:Number = Math.atan2(ty, tx); var length:Number = Math.sqrt(tx*tx+ty*ty); vx -= (1-length/radious)*0.5*Math.cos(angle); vy -= (1-length/radious)*0.5*Math.sin(angle); if(this.priority < _priority) { this.priority = _priority; cx = tx; cy = ty; } }
処理がかぶらないようにする
これはAS3アニメーションなどにも解説がのっている基本的なことのようですが、
衝突判定をするということは、要素1つ1つについて、全員に対して重なっているかどうかを調べる必要があります。だからといって、
動いている要素の数×動いている要素の数
を素直に演算すると、これは実は見事に2回ずつ計算を重複してしまっています。
なので、ずらして
こうします。
public function checkColision():void { var i:int; for(i=0; i<elemlen-1; i++) { var elem0:Elemet = elemList[i]; for(var j:int=i+1; j<elemlen; j++) { var elem1:Elemet = elemList[j]; elem0.hitTest(elem1); } } }
これで余計な処理は取り除きつつ、負荷も半減というわけです。
こういう衝突判定の動きって、虫とか魚は厳密に衝突を判定しなくても成立するので必須のテクではない気がしますが、こういった車なんかはぶつからないと不自然なので向いてるように思います。(画像の読み込みにちょっと時間かかります、あしからず)
ちょっと理解が進みました。
でも衝突判定の処理ってもっと他にもあるはずなので、さらに調べてみます。