fig.1を見てください。ボールとボールとの距離はc_lenで与えられます。これは、それぞれの中心座標をポイントとして、Point.distance()メソッドを使用したり、自身のxと対象となるオブジェクトのxの差であるc_xと同様に求めたc_yの2乗の和のルートを取ったりして取得することが出来ます。Ball.as中には2つの表記方法を用いてみましたので参考にしてください。さて、衝突判定は
if( Point.distance(o_slf, o_obj) < (width/2 + obj.width/2) )
このようにして、中心間の距離が、それぞれの半径の和よりも小さいかどうかで判定出来ます。では衝突後の運動はどのように記述すればいいのでしょうか。高校で"運動量保存則"は習ったでしょうか?質量と速度の積を運動量と呼びますが、2つの物体の衝突に関して運動量の総量は変化しない、といった法則です。
m1*v1 + m2*v2 = m1*v1' + m2 + v2'
こんな感じです。今回は衝突する2つの物体の質量は同じ、と仮定しましたので
v1 + v2 = v1' + v2'
こんな風にかけます。ただしこれでは2変数(衝突後の自身の速度と相手の速度)が分かりませんので、もう1つ何かしらの式が必要です。これには、エネルギー保存の法則の式などが適用できますが、今回は衝突係数=1を用いた式で行いたいと思います。すなわち、壁にボールを投げると同じ速度で跳ね返ってくるのと同じように、ボールが衝突した後は、衝突する前に向かい合っていたスピードで遠ざかっていくという考え方です。これは、
v2 - v1 = -(v2' - v1')
このような式で書くことが出来ます。これらの連立方程式を解くと
v1' = v2
v2' = v1
といった感じです。これより、座標の回転⇒衝突方向についての運動量保存則⇒座標を元に戻すといった3つの作業を行うことによって衝突を記述できます。
// ベクトルをθ回転する
var v_u:Number = v_x*Math.cos(theta) - v_y*Math.sin(theta);
var v_v:Number = v_x*Math.sin(theta) + v_y*Math.cos(theta);
var obj_v_u:Number = obj.v_x*Math.cos(theta) - obj.v_y*Math.sin(theta);
var obj_v_v:Number = obj.v_x*Math.sin(theta) + obj.v_y*Math.cos(theta);
// u方向のみ運動量保存則を適用(質量が同じと仮定し速度交換)
var tmp:Number = v_u/ela;
v_u = obj_v_u/ela;
obj_v_u = tmp;
// ベクトルを -θ回転する
v_x = v_u*Math.cos(theta) + v_v*Math.sin(theta);
v_y = -v_u*Math.sin(theta) + v_v*Math.cos(theta);
obj.v_x = obj_v_u*Math.cos(theta) + obj_v_v*Math.sin(theta);
obj.v_y = -obj_v_u*Math.sin(theta) + obj_v_v*Math.cos(theta);
いかがでしょうか。これを応用して作ったのが今回のサンプルです。for文で指定個数のBallインスタンスを作成し、enterFrameHandlerで衝突判定を行っています。衝突判定には、
for(var m:int=0; m<ball_num; m++) {
for(var n:int=m+1; n<ball_num; n++) {
ball_mc[m].crash(ball_mc[n]);
}
}
このように、ボールの個数の約2乗の半分回(
nC
2)だけ判定しています。なので200個とかやると激重です...。どなたかいい判定方法知っていたら教えてください。
...ちなみに、座標の回転をしなくてもx軸方向、y軸方向それぞれについて運動量保存則を適用しても記述できます。
var tmp:Number;
tmp = v_x;
v_x = obj.v_x;
obj.v_x = tmp;
tmp = v_y;
v_y = obj.v_y;
obj.v_y = tmp;
が、これだと挙動がおかしくなります(例えば少しずらして正面衝突しても、普通に跳ね返るだけです)。試してみてください。
長くなりましたが、今回は以上です。