1. はじめに
シューティングゲームにおける敵キャラの動きは、ゲームの難易度や面白さに大きな影響を与える要素である。
敵キャラの動き方には、直線的な追跡から、円形運動、放物線、さらにはランダムな動きまで、さまざまな種類がある。
本記事では、これらの移動パターンを数学的な公式と共に紹介し、ゲーム開発でどのように実装できるかを解説する。
2. ベクトルによる直線追尾
概要
プレイヤーや指定したターゲットに対して、敵キャラが「一直線で近づく」動き。シューティングにおいてもっとも基本で、プレイヤーの位置に応じて常に方向を更新するため、操作性と緊張感がある動きになる。
数学的背景(ベクトル演算)
プレイヤーの位置を ((x_p, y_p))、敵キャラの位置を ((x_e, y_e)) としたとき、ターゲットへの方向ベクトルは次のように求められる:
$$ \Delta x = x_p - x_e,\quad \Delta y = y_p - y_e $$
速度の大きさ(スピード)を (s) として、正規化ベクトルを使った移動量は:
$$ \text{norm} = \sqrt{\Delta x^2 + \Delta y^2} $$
$$ v_x = s \times \frac{\Delta x}{\text{norm}}, \quad v_y = s \times \frac{\Delta y}{\text{norm}} $$
これを毎フレーム足すだけで、敵キャラはプレイヤーに向かって動き続ける。
実装例(JavaScript)
function updateChaserEnemy(enemy, player, speed) {
const dx = player.x - enemy.x;
const dy = player.y - enemy.y;
const dist = Math.hypot(dx, dy);
if (dist === 0) return; // 同位置なら動かさない
enemy.x += (dx / dist) * speed;
enemy.y += (dy / dist) * speed;
}
応用アイデア
- 近づき過ぎたら停止 or 回避 「一定距離以内になったら止める」「軸ずらしを入れて回避してくる」などのバリエーション。
- 加速度を使って慣性のある動き 徐々に加速 → 最終速度に達するようにすれば、より自然で重量感ある挙動に。
3. 円周運動(回転軌道)
概要
円や楕円軌道を描きながら回転する動き。特にボスや中ボス、避けづらい弾幕系の敵でよく使われる。
数学的背景(三角関数)
中心点を ((c_x, c_y))、半径を (R)、角度を (\theta(t))(時間に応じて増加)とすれば、位置は:
$$ x(t) = c_x + R \cos \theta(t) $$
$$ y(t) = c_y + R \sin \theta(t) $$
$$ \theta(t)$$ の増加速度を $$ \omega $$ とすれば、以下のように時間経過で回る:
$$ \theta(t) = \omega t + \theta_0 $$
実装例(JavaScript)
function updateOrbitEnemy(enemy, centerX, centerY, radius, omega) {
enemy.angle += omega; // 例: omega = 0.05 (ラジアン/フレーム)
enemy.x = centerX + radius * Math.cos(enemy.angle);
enemy.y = centerY + radius * Math.sin(enemy.angle);
}
応用アイデア
- 楕円にする:x軸と y軸で異なる半径を使うことで。
- 中心の移動:回転の中心を徐々にプレイヤー方向に動かすことで、回転+追尾混合の複雑な動きに。
- 角度変化の加速/減速:スムーズな回転 → 突如速度上げる → 普通に戻す、などで“捻り”を加える。
4. 放物線運動(弾道運動)
概要
銃弾や砲弾、パラシュート型の敵の降下などに使われるアーチ状の軌道。重力や重力に相当する引力、あるいは単純な “抛物線的弾道” を表現するのに使われる。
数学的背景(物理法則 + 二次関数)
初速度を (v_0)、水平方向の速度を (v_{0x})、鉛直方向の初速度を (v_{0y})、重力加速度を (g) としたとき、時間 (t) における座標は:
$$ x(t) = x_0 + v_{0x},t $$
$$ y(t) = y_0 + v_{0y},t - \frac{1}{2} g t^2 $$
また、角度 (\theta) を指定して放つときは、
$$ v_{0x} = v_0 \cos \theta,\quad v_{0y} = v_0 \sin \theta $$
とすることで、任意の方向に弾道を飛ばせる。
実装例(JavaScript)
function createProjectile(x0, y0, speed, angle, gravity = 0.2) {
return {
x: x0,
y: y0,
vx: speed * Math.cos(angle),
vy: -speed * Math.sin(angle), // 上が負方向,注意キャンバス座標系によって符号変更
g: gravity,
time: 0
};
}
function updateProjectile(proj) {
proj.time += 1;
proj.x += proj.vx;
proj.y += proj.vy + 0.5 * proj.g * proj.time;
}
応用アイデア
- 重力を変化させる:重力値を徐々に変えることで、落下が加速/減速する弾道。
- 風の影響を追加:横への微小な加速度をランダムまたは時間変化させる。
- 画面端で反射 or 画面外消滅:落下しきる前に消える、または壁で跳ね返る。
5. ランダム & 擬似ランダム運動
概要
動きが予測しにくい敵や、雑魚が複数散らばる際に効果的な動き。プレイヤー側に“読みづらさ”や“混乱”を与える。
数学的背景(乱数・角度のランダム化)
角度 (\theta) をランダムに生成し、その方向に進むことでランダム移動になる:
$$ \theta = 2\pi \times \mathrm{rand}(), \quad \mathrm{rand}(): 0〜1 の乱数 $$
$$ v_x = s \cos \theta,\quad v_y = s \sin \theta $$
実装例(JavaScript)
function updateWanderingEnemy(enemy, speed) {
if (enemy.timer <= 0) {
const angle = Math.random() * Math.PI * 2;
enemy.vx = speed * Math.cos(angle);
enemy.vy = speed * Math.sin(angle);
enemy.timer = 60 + Math.random() * 120; // 次の方向変更までのフレーム
}
enemy.x += enemy.vx;
enemy.y += enemy.vy;
enemy.timer--;
}
応用アイデア
- 一定時間後に方向変更:ランダムで方向を変える周期を設定。
- 画面端で反射 or ワープ:壁や画面端で跳ね返る or ランダムリポップさせる。
- プレイヤーに近づく傾向を残す:完全ランダムではなく、60%ランダム + 40%プレイヤー追尾 の混合型。
6. 複合パターンとスプライン / カスタム軌道
概要
単一の動きだけではなく、複数の軌道や物理効果、時間経過で変化する挙動を組み合わせることで、より複雑で不規則、かつ “読ませない” 敵の動きを作る。スプライン曲線(ベジェなど)や補間、またはステートマシンで軌道を切り替えるのもこの層に入る。
手法の例
-
Bézier曲線 / スプライン補間 制御点を複数設定し、滑らかな曲線を計算。
t = 0 → 1の値で補間。 -
ステート遷移型動作
- 初期:画面外から放物線で降下
- 中盤:円運動やワープ+弾の放出
- 終盤:ターゲット追尾+高速移動
-
時間経過やプレイヤーの行動による分岐 プレイヤーの位置やスコアに応じて、敵の動きパターンを変更。
実装例 イメージ
// 敵の状態管理
const STATE = { ENTRY: 0, PATTERN_A: 1, PATTERN_B: 2, CHASE: 3 };
function updateComplexEnemy(enemy, player) {
switch (enemy.state) {
case STATE.ENTRY:
updateProjectile(enemy); // 放物線で降下
if (enemy.y > canvas.height * 0.3) {
enemy.state = STATE.PATTERN_A;
enemy.timer = 0;
}
break;
case STATE.PATTERN_A:
updateOrbitEnemy(enemy, enemy.centerX, enemy.centerY, enemy.radius, 0.04);
enemy.timer++;
if (enemy.timer > 300) enemy.state = STATE.PATTERN_B;
break;
case STATE.PATTERN_B:
updateWanderingEnemy(enemy, 1.5);
if (distToPlayer(enemy, player) < 200) enemy.state = STATE.CHASE;
break;
case STATE.CHASE:
updateChaserEnemy(enemy, player, 2.2);
break;
}
}
6. スプライン曲線(曲線運動)
・そもそもスプライン/ベジェとは
- Bézier 曲線は「制御点 (control points)」を使って滑らかな曲線を描く方法。始点 (P₀) と終点 (Pₙ) に加え、中間の制御点が“曲がり”を決める。 (ウィキペディア)
- 例えば 4 点 (P₀, P₁, P₂, P₃) を使う 三次 (cubic) Bézier なら、0 ≤ t ≤ 1 でパラメータ t に応じて滑らかに位置が変わる。曲線の式は次の通り:
$$ P(t) = (1 - t)^3 P_0 + 3 (1 - t)^2 t P_1 + 3 (1 - t) t^2 P_2 + t^3 P_3 $$
-
Bézier をつなぎあわせたものを 複合 Bézier 曲線 (composite Bezier / Bezier‑spline) と呼ぶ。複数区間に分割して滑らかさを維持しつつ複雑な経路を描ける。 (ウィキペディア)
-
スプライン全般 (Bézier も含め) は、キャラクター、カメラ、敵の移動経路、弾道などを滑らかに描く際に広く使われる。ゲームでも定番。 (unitycodemonkey.com)
実装アイデア:敵キャラをスプラインで動かす
以下は JS/Canvas (あるいは類似の環境) で使える実装例または設計案。
1. 制御点で軌道を定義する
// 例: 4 点で三次ベジェ
const P0 = {x: x0, y: y0};
const P1 = {x: cx1, y: cy1};
const P2 = {x: cx2, y: cy2};
const P3 = {x: x3, y: y3};
// t: 0〜1 の値
function cubicBezier(P0, P1, P2, P3, t) {
const u = 1 - t;
const tt = t * t;
const uu = u * u;
const uuu = uu * u;
const ttt = tt * t;
return {
x: uuu * P0.x + 3 * uu * t * P1.x + 3 * u * tt * P2.x + ttt * P3.x,
y: uuu * P0.y + 3 * uu * t * P1.y + 3 * u * tt * P2.y + ttt * P3.y,
};
}
- 毎フレーム (あるいは一定時間ごと) に t を進めて cubicBezier(…) を呼べば、滑らかに曲線に沿って移動する敵を作れる。
2. 複数区間で長い軌道を作る
- 一つの Bézier で終点・制御点の組み合わせに限界があるので、「複合 Bézier」または スプライン (Spline) を使う。
- たとえば、区間ごとに Bézier を定義し、それぞれを順につなげることで複雑で自然な長い軌道を作れる。 (ウィキペディア)
3. 等速に移動させるには要注意
- t を一定速度で増やすだけだと、曲線のある部分は速く、直線寄りの部分は遅くなる。これだと速度が不自然。
- 解決には「曲線全体の長さを近似 → そこからたどる距離に応じた t を求める」か、曲線を小さな直線セグメントに分割する方法がある。 (redblobgames.com)
4. スプラインの種類を使い分ける
- Bézier (cubic) は制御点で自由にカーブを作れるが、通過点を「必ず通る」とは限らない。
- Catmull‑Rom spline や Hermite スプライン などは「複数のキーポイント (waypoints) を滑らかに通る」経路を定義しやすい。自然で予測可能な軌道向き。 (ウィキペディア)
適用イメージ — 敵キャラの動きパターンとして
| パターン名 | 特徴 & 使い所 |
|---|---|
| 滑らかな蛇行移動 | スプラインで画面端から端、あるいは左右に蛇行しながら進む雑魚。軌道を明示できるので避けやすさ/バランス調整も容易。 |
| ボスの複雑な弾幕軌道 | 弾の軌道やボスの移動を Bézier / スプラインで制御。直線・放物線・円運動を組み合わせた複雑な弾幕に。 |
| ワープ & 転回を含む中速軌道 | 通過点 (waypoints) を Catmull‑Rom などで滑らかにつなぎ、途中で向き変更や速度変化を入れる。 |
| 自動スクロール背景やカメラの軌道 | スプラインによる滑らかな移動で、背景スクロールやカメラワークにも応用。 |
注意点と限界
- Bézier などでは「速度一定」「距離一定」で移動させるのがやや面倒。単に t をインクリメントするだけだと速度ムラが出る → セグメント分割 or 長さ補正が必要。
- スプライン同士の接続 (複合スプライン) で「滑らかさ (連続性)」を保ちたいなら、制御点や接線 (tangent) を正しく調整する必要がある。特に C¹ 継続 を意識するなら要注意。 (ウィキペディア)
- 計算コストがやや増える (特に多くの敵/弾を曲線で動かす場合) — 必要に応じて最適化 or 近似手法を検討。
スプライン/曲線運動は、「見た目の滑らかさ」と「経路の明示性」を両立できる強力な手段。 特に雑魚〜ボス、背景、弾幕、カメラなど、多方面で活用可能。既に「直線・放物線・円運動」が理解できているなら、次はぜひスプラインにも挑戦してみる価値あり。
7. 敵キャラの挙動を実装するために必要な数学的知識
シューティングゲームにおける敵キャラの挙動は、ゲームの難易度やプレイヤーの体験に大きく影響します。ここでは、敵キャラの挙動を実装するために重要となる数学的な知識を紹介します。これらの技術は、ゲーム内で敵の動きを自然にし、プレイヤーに挑戦を与える要素となります。
1. ベクトル演算
ベクトル演算は、敵キャラの進行方向や速さを計算するために必須の数学的な道具です。敵キャラがプレイヤーを追尾する際、または一定の速度で進むときに使用されます。ベクトルの基本的な計算を理解しておくと、敵キャラの動きを簡単に制御できるようになります。
- 方向ベクトルの計算 例えば、敵キャラがプレイヤーに向かって進む場合、プレイヤーの位置 (P(x_p, y_p)) と敵キャラの位置 (E(x_e, y_e)) を用いて、進行方向ベクトルを計算します。
$$ \Delta x = x_p - x_e $$
$$ \Delta y = y_p - y_e $$
そして、ベクトルの長さ(速度)の大きさを保持しつつ、正規化した方向ベクトルを計算します。
$$ \text{方向ベクトル} = \left( \frac{\Delta x}{\text{norm}}, \frac{\Delta y}{\text{norm}} \right) $$
これに速度 (v) を掛け合わせて、移動量を決定します。
- 進行方向の更新 毎フレーム、敵キャラの位置を以下のように更新します。
$$ \text{新しい位置} = (\text{現在の位置}) + (\text{速度} \times \text{方向ベクトル}) $$
2. 三角関数(sin, cos)
三角関数は、円周運動や回転運動を計算する際に使用します。円を描いて移動する敵キャラや、回転する敵キャラの動きに欠かせません。特に sin と cos を使って、敵キャラの位置を時間経過に応じて更新します。
- 円周運動 敵キャラが円軌道を描いて移動する場合、位置は次のように更新されます。円の中心を ((c_x, c_y))、半径を (r)、角度を (\theta(t)) とすると、
$$ x(t) = c_x + r \cdot \cos(\theta(t)) $$
$$ y(t) = c_y + r \cdot \sin(\theta(t)) $$
ここで、(\theta(t)) は時間に応じて増加します。例えば、一定速度で回転させる場合、(\theta(t)) は次のように計算できます。
$$ \theta(t) = \omega \cdot t $$
ここで、(\omega) は回転の角速度です。
- 放物線運動 放物線運動をシミュレートする際にも三角関数を利用します。例えば、初速 (v_0) で発射された弾の運動を計算するために、放物線の水平方向と垂直方向の動きは次のように表されます。
$$ x(t) = v_0 \cdot \cos(\theta) \cdot t $$
$$ y(t) = v_0 \cdot \sin(\theta) \cdot t - \frac{1}{2} g t^2 $$
ここで、(g) は重力加速度です。
3. 物理の基礎
物理学を応用してゲーム内の挙動をリアルにするためには、速度や加速度、重力などの基本的な物理の概念を理解しておく必要があります。シューティングゲームにおいて、物理をシミュレートすることで、敵キャラの動きや弾道がより自然に見えます。
-
放物線運動 弾や敵キャラが放物線を描いて動く場合、その動きには重力の影響を加えます。ゲーム内での弾道を計算する際に、時間経過に合わせて位置を更新する必要があります。
-
加速度と速度 敵キャラや弾の速度や加速度を変化させることで、よりリアルな動きを実現できます。たとえば、弾が最初はゆっくり動き、時間とともに速くなるようにすることができます。
4. 乱数
敵キャラのランダムな動きや行動を作るために、乱数を使用します。特にシューティングゲームでは、敵キャラの動きが予測できるとゲームが単調になりがちです。そのため、敵の動きに ランダム性 を加えることで、プレイヤーにとって予測できない動きを作り出し、ゲームに深みを持たせます。
- ランダムな角度や速度 敵キャラの進行方向や速度をランダムに設定することで、毎回異なる動きが実現できます。例えば、一定の範囲内でランダムに方向を設定する場合:
$$ \text{角度} = \text{Math.random()} \times 2\pi $$
これを進行方向に適用することで、予測不可能な動きになります。
8. まとめ
- 敵キャラの動きは、ゲームの難易度や戦略を大きく左右する。
- 数学や物理を使ったシンプルな公式を利用すれば、リアルで面白い動きが簡単に実現できる。
- ベクトル演算や三角関数を理解すれば、敵キャラの移動パターンは無限に広がり、よりダイナミックなゲームを作ることができる。
- スプラインやランダムな動きの導入など、複雑な動きも数学的に理論立てて設計でき、プレイヤーの予測を超えるゲーム体験を提供できる。
- 物理シミュレーション(重力、加速度、衝突判定など)を活用することで、ゲーム内の動きが自然に感じられ、リアルさを追求できる。
これらの知識を駆使すれば、シューティングゲームの敵キャラや弾幕、さらにはボス戦の動きも非常に多彩で面白いものに仕上げることができ、プレイヤーにとって予測不可能で魅力的な体験を提供できるでしょう。
💬 コメント