#初めに
今回の記事は
- 円と円をぶつけたときに少しだけ現実の動きに寄せたい!
- ぶつかった後の速さなどの計算がわからない!
という人向けです。
#考え方
####円同士のぶつかった位置を考えない場合(互いの速度ベクトルが同一線上にあるとき)
- 物体の速度ベクトルなどを考える
- 衝突の公式を使って速度を計算する
この2つで求めることができます。
例として、質量Maで右向きに速度Vaで進む円Aと、質量Mbで左向きに速度Vbで進む円Bがぶつかった時を考えます。
この時の反発係数をeとします。
衝突の公式より
衝突した後の円A、円Bの速度は
円Aの衝突後の速度=\frac{(M_a-eM_b)V_a+(1+e)M_bV_b}{M_a+M_b} \\
円Bの衝突後の速度=\frac{(M_b-eM_a)V_b+(1+e)M_aV_a}{M_a+M_b}
これで求められます。
####円同士のぶつかった位置を考える場合(互いの速度ベクトルが同一線上にない場合)
- 互いの円の中心を通る軸(直線)を求める
- 速度ベクトルをさっき求めた軸方向と軸に垂直な方向に分解する
- 分解したベクトルを衝突の公式を使って計算する
基本的には先ほどやったものと同じです。
例)
円Aの中心座標Pa、質量Ma、速度Vaで、円Bの中心座標Pb、質量Mb、速度Vbとします。
まずは軸を求めます。
そうしたら、求めた軸の単位ベクトルを求め、円の互いの速度と内積を求めます。
軸の単位ベクトルと内積を求めると、速度ベクトルの軸方向の力の大きさを求めることができます。
軸方向の力の大きさが求められたらベクトルに戻して衝突の公式で計算すれば衝突後の速度が求められます。
\vec{軸}=P_b-P_a \\
\vec{単位ベクトル}=\frac{\vec{軸}}{|\vec{軸}|} \\
円Aの軸方向の速さ=\vec{単位ベクトル}\cdot \vec{V_a} \\
円Bの軸方向の速さ=\vec{単位ベクトル}\cdot \vec{V_b} \\
衝突の公式などを使う(省略)
#プログラム
実際にプログラムを書いてみます。
struct Vector2
{
float x;
float y;
Vector2& operator+(const Vector2 num) {
x = x + num.x;
y = y + num.y;
return *this;
}
Vector2& operator-(const Vector2 num) {
x = x - num.x;
y = y - num.y;
return *this;
}
};
Vector2 CalcInnerProduct(Vector2 vec, Vector2 UnitVector);
Vector2 CreateVerticalVector(Vector2 vector);
/**
* @brief 衝突の公式、当たったもののベクトル(速度)計算
* @param mass 自分の質量
* @param velocity 自分の速度ベクトル
* @param bounce 反発係数
* @param colMass 当たった物の質量
* @param colVelocity 当たった物の速度ベクトル
* @return 計算後のベクトル(速度)
*/
Vector2 CalcCollisionFormula(float mass, Vector2 velocity, float bounce, float colMass, Vector2 colVelocity)
{
Vector2 resultVelocity;
resultVelocity.x = ((mass - bounce * colMass) * velocity.x + (1 + bounce) * colMass * colVelocity.x) / (mass + colMass);
resultVelocity.y = ((mass - bounce * colMass) * velocity.y + (1 + bounce) * colMass * colVelocity.y) / (mass + colMass);
return resultVelocity;
}
/**
* @brief 当たった物の軸に水平な方向ベクトルの計算
* @param pos 自分の座標
* @param colPos 当たった相手の座標
* @param myVel 分解したいベクトル
* @return 軸に対して水平な方向に分解した速度ベクトル
*/
Vector2 CalcHorizontalVelocity(Vector2 pos, Vector2 colPos, Vector2 myVel)
{
Vector2 dirVector; // 方向ベクトル
// 当たった時の軸方向を求める
dirVector = colPos - pos;
if (dirVector.x == 0 && dirVector.y == 0) {
return { 0,0 };
}
// 単位ベクトルへ変換
float unitVector = 1 / sqrtf(dirVector.x * dirVector.x + dirVector.y * dirVector.y);
dirVector.x *= unitVector;
dirVector.y *= unitVector;
// 当たった者同士の軸方向の内積の大きさのベクトルを求める
Vector2 horizontalVecter = CalcInnerProduct(myVel, dirVector);
return horizontalVecter;
}
/**
* @brief 与えられたベクトル(axis)に対しての垂直ベクトルを求める
* @param axis 方向ベクトル
* @param myVec 分解したいベクトル
* @return 引数axisに対して垂直な方向に分解したベクトル
*/
Vector2 CalcVerticalVelocity(Vector2 axis, Vector2 myVec)
{
Vector2 unitVerticalAxis = CreateVerticalVector(axis);
return CalcInnerProduct(myVec, unitVerticalAxis);
}
/**
* @brief 内積を求める(単位ベクトルを使い軸方向へ分解)
* @param vec 分割したいベクトル
* @param UnitVector 分割したい方向の単位ベクトル
* @return 単位ベクトル方向の内積の大きさのベクトル
*/
Vector2 CalcInnerProduct(Vector2 vec, Vector2 UnitVector)
{
float scalar;
scalar = vec.x * UnitVector.x + vec.y * UnitVector.y;
Vector2 Vector;
Vector.x = UnitVector.x * scalar;
Vector.y = UnitVector.y * scalar;
return Vector;
}
/**
* @brief 引数に対して垂直な単位ベクトルの作成
* @param vector 垂直ベクトルが欲しいベクトル
* @return 引数に対してYが正の垂直な単位ベクトル
*/
Vector2 CreateVerticalVector(Vector2 vector)
{
if (vector.x == 0 && vector.y == 0) {
return { 0,0 };
}
Vector2 verticalVector;
// (-(vector.y * vecY) / vector.x) * (-(vector.y * vecY) / vector.x) + vecY = 1;
verticalVector.y = sqrtf(1 / ((vector.y / vector.x) * (vector.y / vector.x) + 1));
verticalVector.x = -(vector.y * verticalVector.y) / vector.x;
return verticalVector;
}
int main()
{
Vector2 pos1, vec1;
const float mass1 = 1.0f;
Vector2 vec2, pos2;
const float mass2 = 1.0f;
const float bounce = 1;
pos1 = { 1,1 };
vec1 = { 5,5 };
pos2 = { 2,2 };
vec2 = { 1,1 };
Vector2 horizVec1, horizVec2, vertVec1, vertVec2;
horizVec1 = CalcHorizontalVelocity(pos1, pos2, vec1);
vertVec1 = CalcVerticalVelocity(horizVec1, vec1);
horizVec2 = CalcHorizontalVelocity(pos2, pos1, vec2);
vertVec2 = CalcVerticalVelocity(horizVec2, vec2);
Vector2 resultVec1, resultVec2;
resultVec1 = CalcCollisionFormula(mass1, horizVec1, bounce, mass2, horizVec2);
resultVec2 = CalcCollisionFormula(mass2, horizVec2, bounce, mass1, horizVec1);
vec1 = resultVec1 + vertVec1;
vec2 = resultVec2 + vertVec2;
return 0;
}
#最後に
できる限りわかりやすく書いたので伝わっててくれ・・・!
実際に動かさないと理解できない人のために、(多分)動くコードを打ってあるので、プログラムを色々と弄ってみてください。
内積の計算方法や衝突の公式などの詳しい解説は省いているので、もっと詳しく知りたい!と思ったら詳しく説明してくださっているウェブページがたくさんあるのでお任せしています。
誰かの助けになっていれば幸いです。
またね~