Skip to content
第05回 プレイヤーを狙う敵弾

2 / 4 発射方向の計算と正規化 ​

このステップの目的

目標位置と発射位置の差から方向ベクトルを求め、正規化によって弾速を一定にそろえる計算方法を理解します。

2点間の差から方向ベクトルを求める ​

敵弾を発射位置(startPosition)からプレイヤーの位置(targetPosition)へ飛ばすには、まず目標がどちらの方向にあるかを求めます。

点Aから点Bへの向きと距離を表すベクトルは、「終点の座標(目標)−始点の座標(発射元)」で求められます。

方向ベクトルの計算cpp
Vector2 direction =
{
    targetPosition.x - startPosition.x,
    targetPosition.y - startPosition.y
};

たとえば、敵の座標が{ 100, 200 }で、プレイヤーの座標が{ 130, 240 }にある場合、差分ベクトルは次のようになります。

direction={130−100, 240−200}={30, 40}

この{ 30, 40 }という値は、発射位置から見た目標の「向き」と「距離」の双方を含んでいます。

ベクトルの正規化(長さを1にする処理) ​

差分ベクトル{ 30, 40 }をそのまま速度に使うと、重大な問題が起きます。
目標が遠いほど値が大きくなって弾が速く進み、近いほど値が小さくなって遅くなります。

位置関係にかかわらず弾速を一定にするには、差分ベクトルの長さを1にそろえ、向きだけを使います。この計算をベクトルの正規化(Normalization)と呼びます。

三平方の定理による長さの算出 ​

直角三角形の斜辺の長さを求める三平方の定理(ピタゴラスの定理)を用いて、差分ベクトルの長さ(2点間の直線距離)を計算します。

length=x2+y2

先ほどの例(x=30,y=40)では、次のように計算されます。

length=302+402=900+1600=2500=50

成分を長さで割り、弾速を掛ける ​

ベクトルの各成分を自身の長さ(50)で割ることで、向きを保ったまま長さをちょうど1.0に変換できます。

正規化後の向き={3050,4050}={0.6, 0.8}

この長さ1の単位ベクトルに、弾速(tuning::enemyShot::Speed)を掛けます。これで、目標方向へ一定の速さで進む速度ベクトルvelocityを求められます。

EnemyShot.cpp の Spawncpp
void EnemyShot::Spawn(const Vector2& startPosition, const Vector2& targetPosition)
{
    position = startPosition;
    Vector2 direction = {
        targetPosition.x - position.x,
        targetPosition.y - position.y
    };
    float distance = std::sqrt(direction.x * direction.x + direction.y * direction.y);

    // 敵とプレイヤーの位置が異なるためdistanceは0にならない
    // 方向を長さ1にそろえ角度によらず同じ速さにする
    velocity = {
        direction.x / distance * tuning::enemyShot::Speed,
        direction.y / distance * tuning::enemyShot::Speed
    };
    isActive = true;
}

ゼロ除算の防止 ​

もし発射位置と目標位置が完全に同一の座標であった場合、距離lengthは0.0fになります。
コンピューターは数値をゼロで割ることができない(ゼロ除算が発生して未定義動作やクラッシュを引き起こす)ため、必ずif (length > 0.0f)の条件チェックを行い、安全性を確保します。

チェックリスト
  • 終点(目標)から始点(発射位置)を引くことで方向ベクトルが求まることを理解している
  • 三平方の定理(std::sqrt)を用いてベクトルの長さを計算できる
  • 長さで割る「正規化」によって、距離にかかわらず弾速を均一に保てる理由を説明できる
  • ゼロ除算を防ぐための条件分岐(length > 0.0f)の必要性を理解している