はじめに
前回は自機から銃弾を発射できるようにしました。
今回は敵を実装していきます。
敵の実装
大まかに実装することは以下の通り
- 弾が当たるとHPが減少
- HPが0になれば消滅
- 自機に向かって飛んでくる
- 自機に当たると自機を減らして消滅
以下の仕様で実装したいです。
・弾が当たるとHPが減少
・HPが0になれば消滅
・自機に向かって飛んでくる
・自機に当たると自機を減らして消滅
🔧 敵キャラの仕様
項目 | 内容 |
---|---|
HP | 弾が当たるたびに減少、0で消滅 |
移動 | 自機(Player)に向かって直進 |
自機との衝突 | 自機にダメージを与えて自爆消滅 |
弾との衝突 | HPを減らす、HPが0であれば消滅 |
🧩 必要なスクリプト
① Enemy.cs(敵の挙動とHP管理)
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float speed = 3f;
public int maxHP = 3;
private int currentHP;
private Transform player;
void Start()
{
currentHP = maxHP;
player = GameObject.FindGameObjectWithTag("Player")?.transform;
}
void Update()
{
if (player != null)
{
Vector2 direction = (player.position - transform.position).normalized;
transform.position += (Vector3)direction * speed * Time.deltaTime;
}
}
public void TakeDamage(int damage)
{
currentHP -= damage;
if (currentHP <= 0)
{
Destroy(gameObject);
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Bullet"))
{
TakeDamage(1);
Destroy(collision.gameObject);
}
else if (collision.CompareTag("Player"))
{
PlayerController pc = collision.GetComponent<PlayerController>();
if (pc != null)
{
pc.TakeDamage(1);
}
Destroy(gameObject);
}
}
}
② Bullet の設定(タグ付き)
- Bullet プレハブに
Tag: "Bullet"
を設定 Collider2D
はIsTrigger = ON
Rigidbody2D
にGravity Scale = 0
③ PlayerController にダメージ処理(仮)
public class PlayerController : MonoBehaviour
{
public int life = 3;
public void TakeDamage(int damage)
{
life -= damage;
if (life <= 0)
{
Debug.Log("プレイヤーがやられた!");
// GameOver処理など
}
}
}
パッと見た感じまたRigidbodyが付いていないコードになっていますね……
自機や弾との接触でめり込む可能性があるので直してもらいましょう。
Rigidbody2D を使う前提で、敵の挙動や当たり判定をUnityの物理エンジンに準拠した形にリファクタリングして提供します。
🔧 更新後の敵キャラの仕様(Rigidbody2D対応)
項目 | 内容 |
---|---|
HP管理 | 弾が当たるたびに減少、0で消滅 |
移動 | 自機に向かって直進(Rigidbody2D.MovePosition() 使用) |
自機との衝突 | 自機にダメージを与えて敵は自爆消滅 |
弾との衝突 | HPを減らし、0で消滅。弾も同時に消滅 |
🧩 必要スクリプト(Rigidbody2D版)
✅ Enemy.cs
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D), typeof(Collider2D))]
public class Enemy : MonoBehaviour
{
public float speed = 3f;
public int maxHP = 3;
private int currentHP;
private Transform player;
private Rigidbody2D rb;
void Start()
{
currentHP = maxHP;
rb = GetComponent<Rigidbody2D>();
player = GameObject.FindGameObjectWithTag("Player")?.transform;
// Rigidbody設定(念のためコードで上書き)
rb.gravityScale = 0;
rb.isKinematic = true; // MovePositionを使うならKinematic推奨
}
void FixedUpdate()
{
if (player == null) return;
Vector2 direction = ((Vector2)player.position - rb.position).normalized;
rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime);
}
public void TakeDamage(int damage)
{
currentHP -= damage;
if (currentHP <= 0)
{
Destroy(gameObject);
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Bullet"))
{
TakeDamage(1);
Destroy(collision.gameObject);
}
else if (collision.CompareTag("Player"))
{
PlayerController pc = collision.GetComponent<PlayerController>();
if (pc != null)
{
pc.TakeDamage(1); // プレイヤーにダメージ
}
Destroy(gameObject); // 敵も消滅
}
}
}
✅ 弾(Bullet)設定
Tag: "Bullet"
Rigidbody2D:
Gravity Scale = 0
isKinematic = true または velocity 移動なら isKinematic = false
Collider2D:
Is Trigger = ON
✅ プレイヤー(PlayerController.cs)
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public int life = 3;
public void TakeDamage(int damage)
{
life -= damage;
if (life <= 0)
{
Debug.Log("プレイヤーがやられた!");
// ゲームオーバー処理をここに追加
}
}
}
スクリプトはそのままで入れていきますが、
意味は理解できるようにしておきたいところです。
Enemy.csの中身
{
if (player == null) return;
Vector2 direction = ((Vector2)player.position - rb.position).normalized;
rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime);
}
このコードがいまいち分かりません。教えてもらいましょう。
以下ではこのコード:
void FixedUpdate()
{
if (player == null) return;
Vector2 direction = ((Vector2)player.position - rb.position).normalized;
rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime);
}
について、C#の構文的な意味とUnityエンジン上での処理の流れの両面から、細かく丁寧に分解して解説します。
🔁 FixedUpdate() とは?
・Unityの物理演算専用の更新関数(通常は毎秒50回、一定間隔で呼ばれる)
・Rigidbody2D に関わる処理(移動・加速度・力の適用など)は FixedUpdate() 内で実行すべきとされている
・Update() はフレームごと(時間がバラバラ)、FixedUpdate() は時間が一定(物理と同期)
👇 分解して1行ずつ解説
① if (player == null) return;
✔️ C# の意味
player が未設定(null)なら、このフレームの処理を中止する
✔️ Unity的な意義
プレイヤーがまだシーンに存在しないとき(例:シーン開始直後など)にエラー回避のため
GameObject.FindWithTag("Player") に失敗した場合の保険
② Vector2 direction = ((Vector2)player.position - rb.position).normalized;
💡 処理の目的
→ 敵からプレイヤーへ向かう方向ベクトルを求める
🔍 詳細分解
処理 | 内容 |
---|---|
player.position | Transform.position は Vector3 型。プレイヤーのワールド座標 |
(Vector2)player.position | z座標を無視し、Vector2(x, y) に変換(2D用) |
rb.position | Rigidbody2D.position は元々 Vector2(なのでキャスト不要) |
player.position - rb.position | 敵 → プレイヤー への「差分ベクトル」 |
.normalized | 長さを1にして「方向」だけを取り出す(速度の大きさと切り離せる) |
🔁 例:
player.position = (5, 2)
enemy.position = (2, 1)
→ 差分 = (3, 1)
→ normalized = (0.948, 0.316)
③ rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime);
💡 処理の目的
→ 「今の位置」に「1フレーム分の移動量」を加算して、次の位置にスムーズに移動する。
🔍 詳細分解
処理 | 内容 |
---|---|
direction | 進むべき向き(単位ベクトル) |
* speed | 移動の「速さ」(1秒あたり何単位移動するか) |
* Time.fixedDeltaTime | この1フレームにおける経過時間(例:0.02秒) |
rb.position + ... | 現在位置に「そのぶんの移動量」を加算 |
rb.MovePosition(...) | Rigidbody2D を「物理的に正しい」形で次の位置に移動させる |
📌 MovePosition()
は 力や加速度なしにワープせず、コリジョン(当たり判定)を維持したままスムーズに移動する メソッド
✅ 処理全体のまとめ
この FixedUpdate() 内の処理は:
「プレイヤーが存在するなら、その方向に向かって毎フレーム一定速度で物理的に滑らかに移動する」という動きを実現します。
- FiexedUpdate:Rigidbodyを付ける場合のUpdateで用いる
- Vector:ベクトルで処理したいときは変数前に付ける
- (Vector2)player.position - rb.position:敵から自機の方向ベクトルを特定する
- .normalized:ベクトルのデータを長さ1にする(後で計算しやすくする)
- + direction * speed * Time.fixedDeltaTime:1フレーム後の位置を算出して前の位置に加算する
といった感じでしょうか。
複雑そうに見えますが、"自機に向かって飛んでいく敵"を実装していることを覚えておけば大丈夫そうです。
他のコードについてはHPのやり取りに関する内容っぽいので必要になったら確認することにします。
敵へのスクリプト反映
さて、スクリプトが完成したもののこれをどこに貼り付ければいいのか分かりません。
自分でprefabを作れば良さそうですかね?
以下の手順に沿って「敵のPrefab化とスクリプト設定」を行えば、敵キャラが正常に動作するようになります。
✅ 最終ステップ:敵のPrefab作成と設定
① 敵の GameObject を作成
Hierarchy で右クリック → 2D Object > Sprite(例: 名前を Enemy に変更)
Sprite画像を設定(任意の敵画像)
必要に応じて Scale や Position を調整
② コンポーネント追加(重要)
コンポーネント | 設定内容 |
---|---|
Rigidbody2D | ✅ Gravity Scale = 0 ✅ Body Type = Kinematic |
Collider2D(例: Circle, Boxなど) | ✅ Is Trigger = ON |
Enemy.cs スクリプト | 追加(Inspector の「Add Component」で検索) |
🔧 Rigidbody2D の設定は以下の通り:
Body Type: Kinematic
Gravity Scale: 0
Interpolate: Interpolate(推奨)
③ Tag の設定
Enemy にしてもいいですが、衝突判定には使っていないので任意です
④ プレハブ化(Prefab)
Enemy オブジェクトを Project ビューの任意のフォルダにドラッグ
プレハブ化されたら、シーンから元のEnemyオブジェクトは削除してOK
⑤ 敵をシーンに出す方法(テスト)
一旦、手動で敵を出したい場合:
Hierarchyにプレハブをドラッグして配置
ゲームを再生して動くか確認
🔄 あとで自動出現にしたい場合
EnemySpawner.cs のようなスクリプトで一定時間ごとに Instantiate() すればOKです(必要なら作成例を出します)
✅ 念のための確認チェックリスト
項目 | チェック |
---|---|
Rigidbody2D 付いてるか | ✅ Gravity 0、Kinematic |
Collider2D 設定 | ✅ Trigger ON |
スクリプト貼り忘れなし | ✅ Enemy.cs 追加済み |
プレイヤーに "Player" タグがあるか | ✅ タグ設定必須 |
弾に "Bullet" タグがあるか | ✅ タグ設定必須 |
プレイヤー側の PlayerController.cs に TakeDamage() があるか | ✅ 敵が触れた時に必要 |
大体の設定は出来ましたが、
タグの設定方法が分からないので聞いておきます。
Unityでオブジェクトにタグ(Tag)を設定する方法を、画像なしでもわかるようにステップ形式で丁寧に解説します。
🏷 タグ(Tag)の設定とは?
タグは「このオブジェクトは〇〇です」と識別するためのラベルです。
例えば「Bullet」「Player」などのタグを使って、CompareTag("Bullet")
のようにスクリプトで判別します。
✅ タグの設定手順(例:Bullet に "Bullet" タグをつける)
【Step 1】対象のオブジェクトを選択
Hierarchy でタグを設定したいオブジェクト(例:Bullet プレハブ or シーン上のオブジェクト)をクリック
【Step 2】Inspector ウィンドウ上部の「Tag」フィールドを探す
「Transform」の上あたりに「Tag: Untagged ▼」というドロップダウンがあります。
【Step 3】既存タグの中に目的のタグがあるか確認
クリックして、Player や Bullet などがあるかチェック
【Step 4】ない場合 → 「Add Tag...」を選択
Tag: Untagged ▼ をクリック → 一番下の「Add Tag...」をクリック
Tags リストが表示される(新規タグ作成画面)
【Step 5】新しいタグを追加
上部の「+」ボタンをクリック
新しいタグ名(例:Bullet)を入力
入力が終わったら Enter またはクリックで確定
【Step 6】もう一度対象オブジェクトに戻り、追加したタグを選択
Hierarchy に戻ってオブジェクトを選択し直す
「Tag」フィールドをクリックすると、今追加したタグが表示される
それを選べば完了!
🔁 よく使うタグ一覧(今回のプロジェクト向け)
タグ名 | 使い道 |
---|---|
Player | プレイヤー識別用 |
Bullet | 弾の衝突判定 |
Enemy | (任意)敵識別用 |
Item | (任意)アイテム衝突用 |
Inspector上部のフィールドから
新規作成して
選択
設定できました。敵は数種類作るつもりなのでタグがあると便利そうです。
ここでテストをしてみます。
弾が当たると敵が消えるのはOKですが、敵が動いていませんね。
特にエラーなどは発生していませんがとりあえずEnemyのスクリプトを全文貼り付けて問題ないか聞いてみます。
Enemyのinspectorを添付します。
以下はEnemyのスクリプトです。
もしそれでも動かない場合は、Playerに "Player" タグがついているかも合わせてチェックしてください。
質問に対してチェックリストを長々と作ってくれましたが、最後のおまけみたいな1文に答えを載せてくれていました。Playerにタグを設定していませんでしたね。
これでPlayerを認識して敵が自機に向かって動くようになりました。
しかし、敵がぶつかっても何も発生しません。
これまた色々と聞いてみてチェックリストを作ってもらいますが
分かりませんでした。
しかし、色々と見ていると何やら黄色の注意マークが見つかりました。
どこからどうみてもエラーです。
エラーの画像をそのまま貼り付けて問題がないか確認してみます。
"The collider did not create any collision shapes as they all failed verification..."
は、Collider が正しく生成されなかったことを示しています。
原因と対策を以下にまとめます。
🚨 原因(主に以下のどれか)
原因 | 詳細 |
---|---|
スケールが極端に小さい | Transform.scale が 0.001 や 0 などになっていると Collider の形が作れない |
Collider のサイズが小さすぎる | Radius や Size が 0.0001 など極小だと不正と判定される |
回転による頂点のズレ | Rotation が極端だと内部計算で不正と見なされることがある |
Spriteが未設定 or Null | 自動で形状を検出できず、Colliderが機能しない |
使用している Collider の種類と Sprite の形が合っていない | PolygonCollider2D で不正な形など |
✅ 対処方法
🔹 ① Transform のスケールを確認する
該当オブジェクトの Transform の Scale を見てください
正常例:(1, 1, 1) 〜 (0.5, 0.5, 1) 程度
❌ NG:(0.0001, 0.0001, 1) など
Inspectorを確認するとSizeがx,yともに0.0001という数値になっています。
しっかりとした当たり判定がありそうな大きさに変更ということで
x = 0.6
y = 0.8
としました。自機はちょっと小さいくらいが好きです。
自機が体力でやられる設定になっているので消えませんが、ちゃんと動いていますね!
長くなったのでここまでで一旦区切りたいと思います。
敵が生成される仕組みや敵の種類ごとの挙動などまだまだかなり実装できることが多い気がします。
それでは次回に続きます。ありがとうございました。