この記事は「初心者でも作れるUnity簡単2Dゲームの作り方講座」の第4回です。
前回は進む方向に合わせてプレイヤーの向きを変え、左右キーを押しているときだけ歩きアニメーションが再生されるようにしました。
また、Main Camera が Player の動きに合わせて横方向に追従するようにし、横スクロールゲームらしく画面が動くところまで作成しました。
前回の記事:

今回はジャンプアクションで踏みつけるターゲットになる敵キャラクターを作成していきます。
まずは敵キャラをステージ上に配置し、左右に動くようにします。そのあと、敵を上から踏んだら倒せる処理を作成します。
左右に動く敵キャラクターを作ろう
まず敵の動きだけを作成してみましょう。
敵のオブジェクトを用意する
Project ウィンドウから Assets > 横スクロールアクションゲーム画像素材 を開き、その中にある「スライム」を Scene ビューにドラッグ & ドロップしましょう。

次に、Hierarchy から「スライム」を選択し、Inspector 上部の名前を「Enemy」に変更します。

Transform の Position を以下のように設定しておきましょう。

これで敵キャラをステージ上に配置できました。
次に、Enemy に物理を適用します。
Player と同じように、Enemy にも Rigidbody2D を追加します。Inspector の Add Component から Physics 2D > Rigidbody2D を選択しましょう。

また、Player と同じように、Rigidbody2D の Constraints にある Freeze Rotation の Z にチェックを入れておきましょう。

これを設定しておくと、Enemy が地面やプレイヤーにぶつかったときに、勝手に回転しなくなります。
次に当たり判定を追加します。
Inspector の Add Component から Physics 2D > Capsule Collider 2D を選択しましょう。

これで敵キャラのUnityエディタ上での準備は完了です。
Unity C#スクリプトを書いて敵キャラの行動を作ろう
次に、敵を左右に動かすスクリプトを作成します。今回は指定した範囲をひたすら往復するだけのシンプルな敵を作成します。
Project ウィンドウから Assets 内の Scripts フォルダを開きます。Scripts フォルダの中で右クリックし、Create > MonoBehaviour Script を選択しましょう。作成したスクリプトの名前は「EnemyPatrol2D」にします。

作成したスクリプトをダブルクリックで開きましょう。開いたら、EnemyPatrol2D の中身を以下のように書き換えます。
EnemyPatrol2D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using UnityEngine; public class EnemyPatrol2D : MonoBehaviour { [Header("Move Settings")] public float moveSpeed = 2f; public float moveDistance = 3f; private Vector2 startPosition; private int moveDirection = 1; private SpriteRenderer enemySprite; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { //最初の位置を保存 startPosition = transform.position; //スライムのSpriteRendererを取得 enemySprite = GetComponent<SpriteRenderer>(); } // Update is called once per frame void Update() { //敵を動かす transform.Translate(Vector2.right * moveDirection * moveSpeed * Time.deltaTime); //反復移動させる if (transform.position.x > startPosition.x + moveDistance) { moveDirection = -1; enemySprite.flipX = true; } else if (transform.position.x < startPosition.x - moveDistance) { moveDirection = 1; enemySprite.flipX = false; } } } |
スクリプトの解説
Start で Enemy の最初の位置を保存しています。
|
1 2 |
//最初の位置を保存 startPosition = transform.position; |
この位置を基準にして、右にどこまで進むか、左にどこまで進むかを決めます。
例えば、moveDistance が3 のとき、Enemy は最初の位置から右に3マス、左に3マスの範囲で動きます。
次に、Enemy の SpriteRenderer を取得しています。
|
1 2 |
//スライムのSpriteRendererを取得 enemySprite = GetComponent<SpriteRenderer>(); |
SpriteRenderer は、画像の表示を担当しているコンポーネントです。

今回は enemySprite.flipX を使って、Enemy の見た目を左右に反転させるために取得しています。
次はUpdate の中を見ていきましょう。
|
1 2 |
//敵を動かす transform.Translate(Vector2.right * moveDirection * moveSpeed * Time.deltaTime); |
この 1 行で、Enemy を左右に動かしています。Translate は、オブジェクトの位置を今の場所から少しだけ移動させる処理です。今回のコードでは、右方向を表す Vector2.right に、進む向き、移動速度、時間をかけています。
それぞれの役割は以下の通りです。
| Vector2.right | 右方向 |
| moveDirection | 進む向きを決める |
| moveSpeed | 移動速度を決める |
| Time.deltaTime | フレームごとの動きの差を少なくする |
Time.deltaTime とは?
ここで使っている Time.deltaTime は、「前のフレームから今のフレームまでにかかった時間」です。
Update は毎フレーム呼ばれますが、1 秒間に何回呼ばれるかは環境によって変わります。たとえば、動作が軽い環境では 1 秒間にたくさん Update が呼ばれます。逆に、動作が重い環境では Update が呼ばれる回数が少なくなります。
もし Time.deltaTime を使わずに移動させると、Update が多く呼ばれる環境では速く動き、Update が少ない環境では遅く動いてしまいます。

そこで Time.deltaTime をかけることで、「1 フレームごとにどれだけ動くか」ではなく、「1 秒間でどれだけ動くか」という考え方にできます。つまり、Time.deltaTime は、ゲームの動きを環境に左右されにくくするために使います。
| 動作環境 | Update の回数 | Time.deltaTime の目安 |
| 60 FPS | 1 秒に 60 回 | 約 0.016 |
| 30 FPS | 1 秒に 30 回 | 約 0.033 |
moveDirection は、Enemy が右に進むか、左に進むかを決めるための変数です。
|
1 |
private int moveDirection = 1; |
moveDirection が 1 のときは右に進みます。
moveDirection が -1 のときは左に進みます。
つまり、moveDirection の値を 1 と -1 で切り替えることで、Enemy の進む向きを変えています。
次に、Enemy が右端まで進んだかを判定しています。
|
1 2 3 4 5 6 |
//反復移動させる if (transform.position.x > startPosition.x + moveDistance) { moveDirection = -1; enemySprite.flipX = true; } |
transform.position.x は、Enemy の現在の X 座標です。startPosition.x + moveDistance より右に進んだら、右端まで来たことになります。右端まで来たら、moveDirection を -1 にして、今度は左に進むようにします。同時に enemySprite.flipX を true にして、Enemy の見た目も左向きに反転させています。
次の else if では、その逆を行っています。
|
1 2 3 4 5 |
else if (transform.position.x < startPosition.x - moveDistance) { moveDirection = 1; enemySprite.flipX = false; } |
Enemy が左端まで進んだら、moveDirection を 1 にして今度は右に進むようにします。
同時に enemySprite.flipX を false にして、Enemy の見た目を右向きに戻しています。
この処理が毎フレーム繰り返されることで、Enemy が決められた範囲を左右に往復するようになります。
動作を確認しよう
スクリプトを書き終わったら、Ctrl + S で必ず保存して Unity に戻りましょう。
次に、Project ウィンドウから Scripts フォルダを開き、作成した EnemyPatrol2D を Hierarchy の Enemy にドラッグ & ドロップしてアタッチしましょう。

ここまでできたら、一度ゲームを再生して動きを確認します。

再生してみると地面の位置などによってはEnemy が地面の上からそのまま下に落ちてしまうかもしれません。その場合はEnemyの位置を修正しましょう。
Hierarchy から Enemy を選択し、Inspector の Transform から Position を以下のように調整します。

また、最初の設定だと左右に動く範囲が少し狭く感じると思います。EnemyPatrol2D の Move Distance を 5 に変更しましょう。

設定できたら、もう一度ゲームを再生します。

Enemy が地面の上に乗ったまま、左右に往復していれば成功です。
スーパーマリオのように敵キャラを踏んだら倒せるようにしよう
ここからはEnemy を踏んだら倒せるようにしていきます。
大切なのは、Enemy に触れたらいつでも倒せるのではなく、Player が上から踏んだときだけ倒せるようにすることです。ではどのように判定するのか確かめていきましょう。
敵キャラを踏みつける処理をC#スクリプトで書こう
前章で作成した EnemyPatrol2D をダブルクリックで開きましょう。

開けたら、以下のように追記します。
EnemyPatrol2D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
using UnityEngine; public class EnemyPatrol2D : MonoBehaviour { [Header("Move Settings")] public float moveSpeed = 2f; public float moveDistance = 3f; [Header("Stomp Settings")] public float stompHeight = 0.3f; private Vector2 startPosition; private int moveDirection = 1; private SpriteRenderer enemySprite; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { //最初の位置を保存 startPosition = transform.position; //スライムのSpriteRendererを取得 enemySprite = GetComponent<SpriteRenderer>(); } // Update is called once per frame void Update() { //敵を動かす transform.Translate(Vector2.right * moveDirection * moveSpeed * Time.deltaTime); //反復移動させる if (transform.position.x > startPosition.x + moveDistance) { moveDirection = -1; enemySprite.flipX = true; } else if (transform.position.x < startPosition.x - moveDistance) { moveDirection = 1; enemySprite.flipX = false; } } void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Player")) { if (IsStompedByPlayer(collision.gameObject)) { Destroy(gameObject); } } } bool IsStompedByPlayer(GameObject player) { Rigidbody2D playerRigidbody = player.GetComponent<Rigidbody2D>(); bool playerIsAboveEnemy = player.transform.position.y > transform.position.y + stompHeight; bool playerIsFalling = playerRigidbody.linearVelocity.y <= 0f; return playerIsAboveEnemy && playerIsFalling; } } |
スクリプトの解説
今回追加した処理では、Enemy に Player が当たったときに、上から踏んだのかを確認しています。
まずは OnCollisionEnter2D から見ていきましょう。
|
1 2 3 4 5 6 7 8 9 10 |
void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Player")) { if (IsStompedByPlayer(collision.gameObject)) { Destroy(gameObject); } } } |
OnCollisionEnter2D は、自分の Collider 2D に、別の Collider 2D がぶつかった瞬間に呼ばれる関数です。今回の場合、Enemy に Player がぶつかった瞬間に、この中の処理が実行されます。
ぶつかった相手の情報は、引数の collision に入っています。
|
1 |
if (collision.gameObject.CompareTag("Player")) |
この部分では、ぶつかった相手に Player という Tag が付いているかを確認しています。つまり、「ぶつかった相手が Player なら、次の処理へ進む」という意味です。
この判定を使うために後で Player に Player Tag を設定します。
次に、こちらの処理です。
|
1 |
if (IsStompedByPlayer(collision.gameObject)) |
ここでは、Player が Enemy を踏んだのかを確認しています。IsStompedByPlayer は、下で作成している自分用の関数です。この関数の結果が true なら「踏んだ」と判断し、false なら「踏んでいない」と判断します。
|
1 |
Destroy(gameObject); |
Destroy は、GameObject を削除する処理です。ここでは gameObject と書いているので、このスクリプトが付いている Enemy 自身を削除します。つまり、踏まれた Enemy を消すことで、Enemy を倒したように見せています。
OnCollisionEnter2D とは?
OnCollisionEnter2D は、Collider 2D 同士がぶつかった瞬間に呼ばれる関数です。
2D アクションゲームでは、Player と Enemy、Player と地面、Player と障害物などが触れたかどうかを調べるときによく使います。
似たような関数には、以下のようなものがあります。
| OnCollisionEnter2D | ぶつかった瞬間 |
| OnCollisionStay2D | ぶつかっている間 |
| OnCollisionExit2D | 離れた瞬間 |
今回は「Enemy に触れた瞬間だけ判定できればよい」ので、OnCollisionEnter2D を使っています。
次に、踏んだかどうかを判定している IsStompedByPlayer を見ていきましょう。
|
1 2 3 4 5 6 7 8 9 |
bool IsStompedByPlayer(GameObject player) { Rigidbody2D playerRigidbody = player.GetComponent<Rigidbody2D>(); bool playerIsAboveEnemy = player.transform.position.y > transform.position.y + stompHeight; bool playerIsFalling = playerRigidbody.linearVelocity.y <= 0f; return playerIsAboveEnemy && playerIsFalling; } |
先頭に bool と書いてあるので、この関数は true か false を返します。ここではtrue なら「踏んだ」、false なら「踏んでいない」という結果になります。また、GameObject player と書くことで、判定したい Player オブジェクトをこの関数の中で使えるようにしています。
|
1 |
Rigidbody2D playerRigidbody = player.GetComponent<Rigidbody2D>(); |
ここでは、Player に付いている Rigidbody2D を取得しています。Rigidbody2D を取得している理由は、Player が上に移動しているのか、下に落ちているのかを調べるためです。
次に、踏んだ判定に使う条件を作っています。
|
1 |
bool playerIsAboveEnemy = player.transform.position.y > transform.position.y + stompHeight; |
これは、Player が Enemy より上にいるかを調べています。transform.position.y は、オブジェクトの縦方向の位置です。Player の Y 位置が、Enemy の Y 位置より stompHeight 分だけ高ければ、Player は Enemy の上にいると判断します。
|
1 2 |
[Header("Stomp Settings")] public float stompHeight = 0.3f; |
stompHeight は、「どれくらい上にいれば踏んだとみなすか」を調整する値です。
|
1 |
bool playerIsFalling = playerRigidbody.linearVelocity.y <= 0f; |
ここではPlayer が落下中かどうかを調べています。linearVelocity.y は、Player の縦方向の速度です。この値が 0 以下なら、Player は上昇しておらず、下に落ちている状態です。
この条件を入れている理由は、ジャンプで上に向かっている途中に Enemy に触れただけで倒せてしまうのを防ぐためです。
最後に、この 2 つの条件をまとめています。
|
1 |
return playerIsAboveEnemy && playerIsFalling; |
&& は「両方の条件が true なら true」という意味です。つまり、
- Player が Enemy より上にいる
- Player が落下中である
この 2 つが両方そろったときだけ、Enemy を踏んだと判定します。どちらか片方でも条件を満たしていない場合は false になり、Enemy は倒れません。
動作を確認しよう
スクリプトを書き終わったら、Ctrl + S で必ず保存して Unity に戻りましょう。次に、忘れずに Player に Tag を設定します。今回のスクリプトでは、Enemy にぶつかった相手が Player かどうかを Tag で判定しています。
|
1 |
if (collision.gameObject.CompareTag("Player")) |
そのため、Player に Player Tag が設定されていないと、Enemy を踏んでも処理が実行されません。Hierarchy から Player を選択しましょう。

Inspector 上部にある Tag から Player を選択します。

ここまでできたら、ゲームを再生して確認してみましょう。

Player が Enemy を上から踏んだときだけ Enemy が消えれば成功です。ただし、今の状態では「倒した」というより、Enemy がその場で消えただけに見えます。
そこで、Enemy を踏んだときに Player が上に跳ねるようにして、倒した感触をわかりやすくしていきます。
倒したらバウンドするようにしよう
EnemyPatrol2D をダブルクリックで開きましょう。

開けたら、以下のように追記します。
EnemyPatrol2D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
using UnityEngine; public class EnemyPatrol2D : MonoBehaviour { [Header("Move Settings")] public float moveSpeed = 2f; public float moveDistance = 3f; [Header("Stomp Settings")] public float stompHeight = 0.3f; public float playerBouncePower = 8f; private Vector2 startPosition; private int moveDirection = 1; private SpriteRenderer enemySprite; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { //最初の位置を保存 startPosition = transform.position; //スライムのSpriteRendererを取得 enemySprite = GetComponent<SpriteRenderer>(); } // Update is called once per frame void Update() { //敵を動かす transform.Translate(Vector2.right * moveDirection * moveSpeed * Time.deltaTime); //反復移動させる if (transform.position.x > startPosition.x + moveDistance) { moveDirection = -1; enemySprite.flipX = true; } else if (transform.position.x < startPosition.x - moveDistance) { moveDirection = 1; enemySprite.flipX = false; } } void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Player")) { if (IsStompedByPlayer(collision.gameObject)) { Destroy(gameObject); Rigidbody2D playerRigidbody = collision.gameObject.GetComponent<Rigidbody2D>(); playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, playerBouncePower); } } } bool IsStompedByPlayer(GameObject player) { Rigidbody2D playerRigidbody = player.GetComponent<Rigidbody2D>(); bool playerIsAboveEnemy = player.transform.position.y > transform.position.y + stompHeight; bool playerIsFalling = playerRigidbody.linearVelocity.y <= 0f; return playerIsAboveEnemy && playerIsFalling; } } |
スクリプトの解説
まず、Player を上に跳ねさせる力として、playerBouncePower を用意しています。
|
1 2 3 |
[Header("Stomp Settings")] public float stompHeight = 0.3f; public float playerBouncePower = 8f; |
public にしているので、Inspector から数値を調整できます。数値を大きくすると、Enemy を踏んだあとに Player が高く跳ねます。
次に、Enemy を踏んだと判定されたときの処理に、以下の 2 行を追加しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.CompareTag("Player")) { if (IsStompedByPlayer(collision.gameObject)) { Destroy(gameObject); Rigidbody2D playerRigidbody = collision.gameObject.GetComponent<Rigidbody2D>(); playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, playerBouncePower); } } } |
1 行目では、Player の Rigidbody2D を取得しています。
|
1 |
Rigidbody2D playerRigidbody = collision.gameObject.GetComponent<Rigidbody2D>(); |
ここで使っている collision は、Enemy にぶつかった相手との接触情報です。今回の処理では、先にこのように確認しています。
|
1 |
if (collision.gameObject.CompareTag("Player")) |
つまり、この if 文の中に入っている時点で、collision.gameObject は Player Tag が付いたオブジェクトです。そのため、collision.gameObject.GetComponent<Rigidbody2D>() と書くことで、Player に付いている Rigidbody2D を取得できます。
次に、取得した Rigidbody2D の速度を変更しています。
|
1 |
playerRigidbody.linearVelocity = new Vector2(playerRigidbody.linearVelocity.x, playerBouncePower); |
linearVelocity は、Rigidbody2D の現在の速度です。
new Vector2 の中では、横方向の速度と縦方向の速度を指定しています。
|
1 |
new Vector2(横方向の速度, 縦方向の速度) |
今回のコードでは
|
1 |
new Vector2(playerRigidbody.linearVelocity.x, playerBouncePower) |
と指定しています。
|
1 |
new Vector2(そのままの速度, 速度をplayerBouncePowerに); |
つまり、Enemy を踏んだ瞬間に、Player の縦方向の速度を上向きに変えているということです。これにより、Enemy を倒したあとに Player が少し跳ねるようになります。
動作を確認しよう
ここまでできたら、Ctrl + S で必ず保存して Unityに戻りましょう。Unityに戻ったら、ゲームを再生して動きを確認します。

プレイヤーが敵を踏んだ時に少し上に跳ねれば成功です。横から敵キャラに触れたときに Enemyオブジェクトが消えないことも確認しておきましょう。
プロジェクトファイルのダウンロード
今回の講座で作ったプロジェクトファイルのダウンロードリンクを用意しました。
まとめ
今回は敵キャラクターを作成し、プレイヤーが上から踏むと倒せるようにしました。
これで敵らしい動きはできましたが、敵にずっと横から当たっても敵を押し続けられましたよね。
これでは敵をジャンプで踏みつぶすことができても、敵から当たられたりジャンプを失敗したときに何のペナルティもありません。
そこで、次回は敵に横や下から触れたときにゲームオーバーになる処理を作成します。
さらに、落下したとき、危険な障害物に触れたときにもゲームオーバーになる仕組みを実装していきます。
次の記事:

現場レベルのゲーム制作が、すべてここで学べます。






コメント