現場レベルのゲーム制作が、すべてここで学べます。
倉庫番ゲーム制作講座の第3回となる今回はいよいよC#スクリプトを書いて配置したプレイヤー(ひよこ)を実際に動かしていきます!
前回の記事:

ただし、普通のアクションゲームのように滑らかに動かすのではなく、
キー入力で1マスずつきれいに移動する「倉庫番ならではの動き」を作っていきます。
プログラミングと聞くと「難しそう…」と感じるかもしれませんが、基本の移動からスタートして少しずつコードを付け足していくので安心してください。
さらに今回は、移動に合わせて「足音を鳴らす」、「ひよこの向きを移動方向に変える」、そしてゲームに欠かせない「壁にぶつかったら止まる当たり判定」まで一気に実装します。
どんどんゲームらしくなっていく過程を楽しみながら、UnityとC#プログラミングの基礎を学んでいきましょう!
プレイヤーを1マスずつ動かそう
この章では、キーボード入力でプレイヤーを上下左右に動かせるようにしていきます。
ただし、普通のアクションゲームのようにスムーズに移動するのではなく、倉庫番ゲームらしく 1マスずつ移動するようにします。
たとえば、”右キーを1回押したら右に1マス移動”し、”上キーを1回押したら上に1マス移動する”といった動きです。
スクリプトを用意しよう
まずは、プレイヤーの操作を書くためのスクリプトを用意します。
ProjectウィンドウのAssetsフォルダ内で右クリックし、Create > Folder を選択します。作成されたフォルダの名前は Scripts にしましょう。

次に、作成したScriptsフォルダを開きます。フォルダ内で右クリックし、Create > MonoBehaviour Script を選択します。作成されたスクリプトの名前を PlayerController2D に変更しましょう。

このPlayerController2Dの中に、プレイヤーを動かすための処理を書いていきます。
ですがその前にスクリプトを作成できたら、まずはHierarchyウィンドウにある Player にアタッチします。Projectウィンドウ内のPlayerController2Dを、HierarchyのPlayerにドラッグ&ドロップしてください。

これで、PlayerにPlayerController2Dスクリプトを付けることができます。

↑右側のインスペクター画面でPlayerオブジェクトにPlayerController2Dが表示されていればOKです。
次に、Projectウィンドウ内の PlayerController2D をダブルクリックして開きましょう。

Start、Updateとは?
PlayerController2Dを開くと、任意のコードエディタが起動します。環境によってはVisual StudioやVisual Studio Codeなどが開きます。
作成したばかりのスクリプトは、最初は以下のような状態になっていると思います。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using UnityEngine; public class PlayerController2D : MonoBehaviour { // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { } // Update is called once per frame void Update() { } } |
まずは、この中にあるStartとUpdateについて確認しておきましょう。
初心者のうちは、最初からすべての仕組みを覚える必要はありません。今回の講座では、まずStartとUpdateの2つを覚えておけば大丈夫です。
|
1 2 3 4 |
void Start() { } |
Startは、ゲームが始まったときに最初の1回だけ実行される関数です。たとえば、ゲーム開始時に初期設定をしたい場合などに使います。
|
1 2 3 4 |
void Update() { } |
一方で、Updateはゲーム中に何度も実行される関数です。正確には、1フレームごとに実行されます。プレイヤーの入力を確認したり、キャラクターを動かしたりする処理は、このUpdateの中に書くことが多いです。
今回のプレイヤー移動では、「キーボードの矢印キーが押されたか」を毎回確認したいので、Updateの中に処理を書いていきます。
Unityには他にもいろいろな関数がありますが、最初から全部覚える必要はありません。まずは、以下のように考えておけば大丈夫です。
| Start | 最初に1回だけ実行される |
| Update | ゲーム中に何度も実行される |

今回は、このStartとUpdateを使って、プレイヤーを1マスずつ動かす処理を作っていきましょう。
キーボードの入力をスクリプトから受け取ろう
まずは、キーボードの入力をスクリプトから判定する方法を確認していきます。
Updateの中に、以下のように記述してみましょう。黄色のハイライトがついてる部分が追記したプログラムです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using UnityEngine; public class PlayerController2D : MonoBehaviour { // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.A)) { Debug.Log("Hello World!"); } } } |
このスクリプトは、Aキーが押された瞬間に、Debug.Log(“Hello World!”);を実行する処理です。少し分けて見ると、次のような意味になります。
|
1 |
if |
これは「もし」という意味です。「もし条件を満たしたら、中の処理を実行する」というときに使います。
|
1 |
Input.GetKeyDown(KeyCode.A) |
これは「Aキーが押された瞬間」を調べています。つまり、このif文は、もしAキーが押された瞬間ならという意味になります。
GetKey・GetKeyDown・GetKeyUpについて
Unityには、キー入力を判定するための書き方がいくつかあります。
| Input.GetKey(KeyCode.A) | Aキーが押されている間、ずっと反応します。 |
| Input.GetKeyDown(KeyCode.A) | Aキーが押された瞬間だけ反応します。 |
| Input.GetKeyUp(KeyCode.A) | Aキーが離された瞬間だけ反応します。 |
今回の倉庫番ゲームでは、1回キーを押したら1マスだけ動かしたいので、Input.GetKeyDownを使います。もしInput.GetKeyを使ってしまうと、キーを押している間ずっと反応してしまい、プレイヤーが一気に何マスも動いてしまいます。
そのため、今回のような1マスずつ動くゲームでは、GetKeyDownが向いています。
|
1 |
Debug.Log("Hello World!"); |
こちらは、ConsoleにHello World!と表示する処理です。そのため、今回のコード全体は、もしAキーが押されたら、ConsoleにHello World!と表示するという処理になります。
Debug.Logとは?
Debug.Logは、Consoleに文字や数値を表示するための機能です。ゲーム画面に表示されるわけではなく、開発者が確認するためのメッセージとして使います。
|
1 |
Debug.Log("Hello World!"); |
こちらでは、ConsoleにHello World!と表示されます。ゲーム開発に直接必要な見た目の処理ではありませんが、スクリプトがちゃんと動いているかを確認するときにとても便利です。たとえば、Aキーを押したときに本当に処理が実行されているのかを確認したい場合、Debug.Logを使うことでConsoleに結果を表示できます。
Debug.Logで表示できるもの
Debug.Logでは、文字だけでなく、数値や変数の中身も表示できます。例えば、以下のようなものを確認できます。
文字を表示
|
1 |
Debug.Log("Hello World!"); |
数値を表示
|
1 |
Debug.Log(10); |
オブジェクトの現在位置を表示
|
1 |
Debug.Log(transform.position); |
オブジェクトのX座標だけを表示
|
1 |
Debug.Log(transform.position.x); |
文字と数値を組み合わせて表示
|
1 |
Debug.Log("現在のX座標:" + transform.position.x); |
開発中に思った通りに動かないときは、Debug.Logで数値や状態を確認すると、原因を見つけやすくなります。例えば、プレイヤーが動かない場合でも、Consoleに「Aキーが押されました」と表示されていれば、キー入力まではできていると分かります。逆に表示されない場合は、入力判定やスクリプトのアタッチに問題があるかもしれません。
ここまで書けたら、コードエディタで保存します。Windowsの場合は、Ctrl + Sで保存できます。保存できたらUnityに戻りましょう。Unityの上部にある再生ボタンを押して、ゲームを実行します。

Gameビュー内を一度クリックしてから、Aキーを押してみましょう。ConsoleウィンドウにHello World!と表示されれば成功です。


これで、スクリプトからキーボード入力を受け取ることができました。次は、この仕組みを使って、Playerを1マスずつ動かしていきます。
キーボードの入力でプレイヤーを移動させよう
では、いよいよキーボード入力でプレイヤーを移動させていきます。
まずは”上矢印キーを押したときに、プレイヤーが上に1マス移動する処理”から作成していきましょう。
PlayerController2Dを開き、以下のように変更してください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using UnityEngine; public class PlayerController2D : MonoBehaviour { // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.UpArrow)) { float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; } } } |
一気に新しいコードが出てきたので少し難しく見えるかもしれませんが、1つずつ分けて見るとやっていることはかなりシンプルです。
まずは、この部分です。
|
1 2 3 4 |
if (Input.GetKeyDown(KeyCode.UpArrow)) { } |
これは前の章で学んだキー入力の判定です。KeyCode.UpArrow は、上矢印キーのことです。つまり、このif文は「もし上矢印キーが押されたら、中の処理を実行する」という意味になります。
次に、if文の中を見ていきましょう。
|
1 2 |
float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; |
transform.position.x は、現在のPlayerのX座標(横方向の座標)です。
transform.position.y は、現在のPlayerのY座標(縦方向の座標)です。
Unityのヒエラルキー画面でPlayerを選択すると、インスペクター画面のTransformコンポーネントの中にPositionがあることが確認できます。
そのPositionのXが横の位置、Yが縦の位置です。

今回は上に移動させたいので、X座標はそのままにして、Y座標だけを1増やしています。

|
1 2 |
float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; |
つまり、X座標を取得し、Y座標を取得してさらに1を足すという意味です。
変数とは?
ここで出てきた nextPlayerX や nextPlayerY は、変数と呼ばれるものです。変数とは、簡単に言うと「数字や文字などを一時的に入れておく箱」のようなものです。たとえば、次のコードを見てみましょう。
|
1 |
float nextPlayerY = transform.position.y + 1; |
これは、プレイヤーの現在のY座標に1を足した結果を、nextPlayerY という名前の箱に入れているイメージです。
なぜわざわざ変数を使うのかというと、あとからその値を使いやすくするためです。
今回の場合、プレイヤーの移動先を作るために、まずX座標とY座標を別々に用意しています。
|
1 2 |
float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; |
nextPlayerXやnextPlayerYのように、変数名は自分で決めることができますが、後で見たときに意味が分かる名前にしておくことが大切です。
変数の種類について
変数には、入れるデータの種類によっていくつかの型があります。型とは、「この変数にはどんな種類のデータを入れるのか」を決めるものです。例えば、数字を入れたいのか、文字を入れたいのか、true/falseのような判定を入れたいのかによって、使う型が変わります。Unityのスクリプトでよく使う変数には、以下のようなものがあります。
| 型 | 入れられるもの | 例 | よく使う場面 |
| int | 整数 | int count = 10; | 個数、回数、スコアなど |
| float | 小数を含む数値 | float speed = 3.5f; | 位置、速度、時間、距離など |
| string | 文字 | string message = “Hello”; | 表示する文章、名前、メッセージなど |
| bool | true または false | bool isClear = false; | クリアしたか、動けるか、ゲーム中かどうかなど |
| Vector2 | XとYの2つの数値 | Vector2 position = new Vector2(1, 2); | 2Dの位置や方向 |
| Vector3 | X、Y、Zの3つの数値 | Vector3 position = new Vector3(1, 2, 0); | 3Dの位置、またはUnity上のTransformの位置 |
| GameObject | Unity上のオブジェクト | GameObject player; | Player、Box、Wallなどのオブジェクトを扱う |
| Transform | 位置・回転・大きさの情報 | Transform playerTransform; | オブジェクトのPositionやScaleを操作する |
次に、この部分を見ていきます。
|
1 |
Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); |
ここでは、先ほど用意したX座標とY座標をまとめて、プレイヤーの移動先の座標を作っています。
Unityの2Dゲームでは、位置を表すときにXとYの2つの数値を使います。Xは横方向、Yは縦方向です。その2つをまとめたものが Vector2 です。
今回の場合は、nextPlayerX と nextPlayerY を使って、次の移動先を作っています。
最後に、この部分です。
|
1 |
transform.position = nextPlayerPosition; |
これは、Playerの現在位置を、先ほど作った nextPlayerPosition に変更しています。つまり、プレイヤーを実際に移動させているのはこの行です。
ここまでの流れをまとめると、次のようになります。
- 上矢印キーが押されたか確認する
- 現在のX座標をそのまま使う
- 現在のY座標に1を足す
- X座標とY座標をまとめて移動先を作る
- Playerの位置を移動先に変更する
ここまで書けたら、コードエディタで Ctrl + S を押して保存しましょう。保存できたらUnityに戻ります。Unityの上部にある再生ボタンを押して、ゲームを実行します。
Gameビューをクリックしてから、上矢印キーを押してみましょう。

Playerが上に移動すれば成功です。ただし、現時点では壁にぶつかったときに止まる処理はまだ作っていません。
そのため、上矢印キーを押し続けると、Playerは壁をすり抜けて移動してしまいます。
これは失敗ではありません。
今はまだ「キーボード入力でPlayerを動かす」処理だけを作っている段階です。
上下左右に移動できるようにしよう
次はプレイヤーを上下左右に移動できるようにしていきます。先ほどと同じような方法で上下左右に移動できるようにしていきます。
先ほどの内容をしっかり理解できている方であれば、ここから先を見なくてもプログラムを作成できるかもしれません。自信のある方はぜひ一度ご自身で作成してみてください。
それでは、PlayerController2Dをダブルクリックで開き、以下のようにコードを追加しましょう。
|
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 |
using UnityEngine; public class PlayerController2D : MonoBehaviour { // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.UpArrow)) { float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; } if (Input.GetKeyDown(KeyCode.DownArrow)) { float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y - 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; } if (Input.GetKeyDown(KeyCode.RightArrow)) { float nextPlayerX = transform.position.x + 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; } if (Input.GetKeyDown(KeyCode.LeftArrow)) { float nextPlayerX = transform.position.x - 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; } } } |
追加した内容はとても単純で、前回作成した「上に移動する処理」を、下・右・左にもそれぞれ作成しただけです。まず、上に移動する処理は以下のようになっていました。
|
1 2 3 4 5 6 7 8 9 |
if (Input.GetKeyDown(KeyCode.UpArrow)) { float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; } |
上に移動したい場合は、Y座標を増やします。そのため、上矢印キーでは以下のようにしています。
|
1 |
float nextPlayerY = transform.position.y + 1; |
下に移動したい場合は、Y座標を減らします。
|
1 |
float nextPlayerY = transform.position.y - 1; |
右に移動したい場合は、X座標を増やします。
|
1 |
float nextPlayerX = transform.position.x + 1; |
左に移動したい場合は、X座標を減らします。
|
1 |
float nextPlayerX = transform.position.x - 1; |
つまり、方向ごとの考え方は以下のようになります。
| 上に移動 | Yを+1 |
| 下に移動 | Yを-1 |
| 右に移動 | Xを+1 |
| 左に移動 | Xを-1 |
このように、if文の中で判定するキーをそれぞれの方向キーに変更し、移動させたい方向に合わせてXまたはYの数値を変更しています。
ここまでできたら、Ctrl + Sでスクリプトを保存してUnityに戻りましょう。Unityの再生ボタンを押して、矢印キーを入力してみてください。

プレイヤーが上下左右に1マスずつ移動できれば成功です。
足音をつけてみよう
ここまで操作してみると、ひよこを操作しているというより、ただ画像を動かしている感じが強いかもしれません。そこで、ひよこが移動したときに歩く音を鳴らしてみましょう。
まずは、HierarchyからPlayerを選択します。

InspectorのAdd Componentをクリックし、Audio > Audio Source を選択して追加しましょう。

Audio Sourceは、Unityで音を再生するためのコンポーネントです。これをPlayerに追加することで、Playerから音を鳴らせるようになります。
次に、音素材を用意します。以下のリンクから「ぺタっ.mp3」をダウンロードしましょう。
ダウンロードできたら、ProjectウィンドウのAssetsフォルダ内で右クリックし、Create > Folder を選択します。作成されたフォルダの名前を Sounds に変更しましょう。

Soundsフォルダを開き、先ほどダウンロードした「ぺタっ.mp3」をドラッグ&ドロップしてインポートします。

これで音素材の準備は完了です。
次は、プレイヤーが歩いた瞬間に音が鳴るように、スクリプトから制御してみましょう。PlayerController2Dをダブルクリックで開き、以下のように変更します。
|
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; using UnityEngine.Audio; public class PlayerController2D : MonoBehaviour { [Header("Sound Effects")] private AudioSource audioSource; public AudioClip moveSound; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { audioSource = GetComponent<AudioSource>(); } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.UpArrow)) { float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.DownArrow)) { float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y - 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.RightArrow)) { float nextPlayerX = transform.position.x + 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.LeftArrow)) { float nextPlayerX = transform.position.x - 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } } } |
今回追加したのは、プレイヤーが移動した瞬間に、moveSoundに設定した音を再生する処理です。
まずは音を鳴らす機能を使うための追加文です。
|
1 |
using UnityEngine.Audio; |
2行目に追加したこの命令は「UnityのAudio機能をこのプログラムで使うよー」という宣言です。UnityやC#で予め用意されている機能を呼び出して使う場合にこのusing文を追加します。よく見ると1行目にも既に書いてありますね。
|
1 |
using UnityEngine; |
↑これはUnityのゲームエンジンの基本機能を使うよーという宣言です。StartやUpdateなどはこの宣言文を用意することで使うことができます。
次に、こちらを見てみましょう。
|
1 2 3 |
[Header("Sound Effects")] private AudioSource audioSource; public AudioClip moveSound; |
AudioSourceは、音を再生するためのコンポーネントです。
先ほどPlayerに追加したAudio Sourceを、スクリプトから使えるようにするために用意しています。
AudioClipは、再生する音そのものです。
今回の場合、moveSoundに「ぺタっ.mp3」を設定して、移動したときにその音を鳴らします。後ほど設定します。
次に、Startの中を見てみましょう。
|
1 2 3 4 |
void Start() { audioSource = GetComponent<AudioSource>(); } |
ここでは、PlayerについているAudio Sourceコンポーネントを取得しています。GetComponent<AudioSource>() は、「このオブジェクトについているAudio Sourceを取得する」という意味です。今回このスクリプトはPlayerに付いて、Player自身に付いているAudio Sourceを取得しています。

↑この部分ですね。今書いたコードによってPlayerController2DがAudio Sourceを取得できるようになります。Unityではこのようにスクリプトでコンポーネント内容を取得してその機能を使っていきます。
次に、Update内に追加した処理を見ていきましょう。
上下左右の移動で追加した内容はすべて同じなので、今回は上方向の処理だけ確認します。
|
1 2 3 4 5 6 7 8 9 10 |
if (Input.GetKeyDown(KeyCode.UpArrow)) { float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } |
一番最後に追加した行で音を鳴らしています。これは、audioSourceを使って、moveSoundに設定されている音を1回だけ再生するという意味です。PlayOneShotは、効果音を鳴らしたいときによく使います。今回のように、歩いた瞬間に「ぺタっ」と1回だけ鳴らしたい場合に使いやすいです。
ここまでできたら、Ctrl + Sで保存してUnityに戻りましょう。Unityに戻ったら、Playerを選択します。

InspectorにあるPlayerController2Dの中に、Move Sound という項目が表示されていると思います。

ProjectウィンドウのSoundsフォルダに入れた「ぺタっ.mp3」を、Move Soundの欄にドラッグ&ドロップしてください。

これで、スクリプトのmoveSoundに音素材を設定できました。最後に再生ボタンを押して、矢印キーで移動してみましょう。プレイヤーが移動するたびに音が鳴れば成功です。
(↑注意:再生すると音が出ます)
ひよこが移動方向を向くようにしよう
音を追加したことで、少し操作している感じが出てきました。ですが、まだ違和感があると思います。
今の状態では、どの方向に移動してもひよこの見た目が同じままです。
そこで、移動方向に合わせてひよこの向きが変わるようにしてみましょう。
まずは、どのように向きを変えるのかを確認しておきます。
横の向き変更
まずは、HierarchyからPlayerを選択しましょう。

Inspectorを見てみると、Sprite Rendererというコンポーネントがあると思います。その中にあるFlipのXにチェックを入れてみましょう。

すると、ひよこが左右反転して左を向くと思います。

つまり、左右の向き変更は、このFlip Xをスクリプトからオン・オフすることで作れます。右を向かせたいときはFlip Xをオフ、左を向かせたいときはFlip Xをオンにします。
縦の向き変更
次に、上下の向き変更について確認していきましょう。
先ほど見たFlipには、XだけでなくYもあります。ですが、Flip Yにチェックを入れてみると、ひよこが上下反転して逆さまになるだけです。

これでは、上や下を向いているようには見えません。そこで、上下の向きについては、画像そのものを切り替えます。
Projectウィンドウから、倉庫番ゲーム画像素材フォルダを開きましょう。

その中に、ひよこの前向き画像と後ろ向き画像が用意されていると思います。

つまり、スクリプトから画像を切り替えて、上向き・下向きを表現していきます。
では、PlayerController2Dを開いて、以下のように変更しましょう。
|
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
using UnityEngine; using UnityEngine.Audio; public class PlayerController2D : MonoBehaviour { [Header("Sound Effects")] private AudioSource audioSource; public AudioClip moveSound; [Header("Player Sprites")] public Sprite upSprite; public Sprite downSprite; private SpriteRenderer spriteRenderer; private Sprite defaultSprite; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { audioSource = GetComponent<AudioSource>(); spriteRenderer = GetComponent<SpriteRenderer>(); defaultSprite = spriteRenderer.sprite; } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.UpArrow)) { spriteRenderer.sprite = upSprite; spriteRenderer.flipX = false; float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.DownArrow)) { spriteRenderer.sprite = downSprite; spriteRenderer.flipX = false; float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y - 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.RightArrow)) { spriteRenderer.sprite = defaultSprite; spriteRenderer.flipX = false; float nextPlayerX = transform.position.x + 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.LeftArrow)) { spriteRenderer.sprite = defaultSprite; spriteRenderer.flipX = true; float nextPlayerX = transform.position.x - 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } } } |
今回も作りはとてもシンプルです。まず、追加した変数を確認していきましょう。
|
1 2 3 4 5 |
[Header("Player Sprites")] public Sprite upSprite; public Sprite downSprite; private SpriteRenderer spriteRenderer; private Sprite defaultSprite; |
upSpriteは上向きの画像を入れるための変数です。
downSpriteは下向きの画像を入れるための変数です。
spriteRendererは、画像を表示しているコンポーネントです。
ひよこの画像を変更したり、左右反転させたりするときに使います。
defaultSprite は、最初のひよこの画像を保存しておくための変数です。
右向きや左向きに戻したいときに使います。
ちなみに、ここでupSpriteとdownSpriteにはpublicというものが付いていて、spriteRendererとdefaultSpriteには付いていません。publicが付いているものにはUnityエディタや他のスクリプトからも参照でき、privateが付いているものを参照することができません(詳しく解説し出すともっと色々ありますが、ひとまずの理解として)。
次に、Startの中を見てみましょう。
|
1 2 3 4 5 6 |
void Start() { audioSource = GetComponent<AudioSource>(); spriteRenderer = GetComponent<SpriteRenderer>(); defaultSprite = spriteRenderer.sprite; } |
まず、GetComponent<SpriteRenderer>() で、PlayerについているSprite Rendererを取得しています。これで、スクリプトからひよこの画像を変更できるようになります。
その下の、defaultSprite = spriteRenderer.sprite;では、最初に設定されているひよこの画像を defaultSprite に保存しています。これにより、上向きや下向きの画像に変更した後でも、右向きや左向きに戻すことができます。
実際に向きを変えている処理
最後に、実際に向きを変えている処理を確認していきましょう。
上に移動するとき
|
1 2 |
spriteRenderer.sprite = upSprite; spriteRenderer.flipX = false; |
こちらでは、画像を上向きの画像に変更しています。
左右反転は必要ないので、flipX は false にしています。
下に移動するとき
|
1 2 |
spriteRenderer.sprite = downSprite; spriteRenderer.flipX = false; |
こちらでは、画像を下向きの画像に変更しています。
こちらも左右反転は必要ないので、flipX は false にしています。
右に移動するとき
|
1 2 |
spriteRenderer.sprite = defaultSprite; spriteRenderer.flipX = false; |
こちらでは、画像を最初の画像に戻しています。
右向きは通常の向きなので、flipX は false にしています。
左に移動するとき
|
1 2 |
spriteRenderer.sprite = defaultSprite; spriteRenderer.flipX = true; |
こちらでは、画像を最初の画像に戻しています。
flipX を true にして左右反転させています。
これで、ひよこが左を向いているように見えます。
スクリプトの変更箇所は以上です。
やっていることは、移動する方向に合わせて、画像を変える&左右反転させているだけです。では、実際にUnityで確認してみましょう。
Ctrl + Sでスクリプトを保存して、Unityに戻ります。HierarchyからPlayerを選択してください。

InspectorのPlayerController2Dに、Up SpriteとDown Spriteという項目が表示されていると思います。

Projectウィンドウから、上向きのひよこ画像をUp Spriteに、下向きのひよこ画像をDown Spriteにドラッグ&ドロップして設定しましょう。

ここまでできたら、再生ボタンを押して確認します。

矢印キーで移動したときに、ひよこが移動方向を向くようになっていれば成功です。
プレイヤーが壁にぶつかったら止まるようにしよう
次に、プレイヤーが壁にぶつかったときに止まるようにしていきます。
これまでは、矢印キーを押すとプレイヤーがその方向に移動するだけで、壁があってもそのまま通り抜けてしまっていました。
今回は、移動する前に「次の移動先に壁があるか」を確認します。もし次の移動先が壁だった場合は、移動せずに処理を止めるようにします。
これまで移動するときは、例えば上方向の場合、以下のように次の移動先を作っていました。
|
1 2 3 4 |
float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); |
この nextPlayerPosition には、プレイヤーが次に移動しようとしている位置が入っています
今回はこの考え方を利用して、nextPlayerPosition の場所に壁があるかを調べます。もし壁があった場合は、ブロック音を鳴らして、return; で処理を止めます。
return; が実行されると、その下の処理には進みません。
つまり、以下の移動処理まで進まなくなります。
|
1 |
transform.position = nextPlayerPosition; |
そのため、壁がある場所には移動できなくなります。また、今回は壁にぶつかったときに「ビープ音」も鳴らしてみましょう。
まずは以下のリンクから「ビープ音4.mp3」をダウンロードします。
ダウンロードできたら、ProjectウィンドウのSoundsフォルダを開き、「ビープ音4.mp3」をドラッグ&ドロップしてインポートしておきましょう。

次に、実際にスクリプトを書いていきます。PlayerController2Dをダブルクリックで開き、以下のように変更しましょう。
|
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
using UnityEngine; using UnityEngine.Audio; public class PlayerController2D : MonoBehaviour { [Header("Sound Effects")] private AudioSource audioSource; public AudioClip moveSound; public AudioClip blockedSound; [Header("Player Sprites")] public Sprite upSprite; public Sprite downSprite; private SpriteRenderer spriteRenderer; private Sprite defaultSprite; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { audioSource = GetComponent<AudioSource>(); spriteRenderer = GetComponent<SpriteRenderer>(); defaultSprite = spriteRenderer.sprite; } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.UpArrow)) { spriteRenderer.sprite = upSprite; spriteRenderer.flipX = false; float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y + 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); if (IsWall(nextPlayerPosition)) { audioSource.PlayOneShot(blockedSound); return; } transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.DownArrow)) { spriteRenderer.sprite = downSprite; spriteRenderer.flipX = false; float nextPlayerX = transform.position.x; float nextPlayerY = transform.position.y - 1; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); if (IsWall(nextPlayerPosition)) { audioSource.PlayOneShot(blockedSound); return; } transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.RightArrow)) { spriteRenderer.sprite = defaultSprite; spriteRenderer.flipX = false; float nextPlayerX = transform.position.x + 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); if (IsWall(nextPlayerPosition)) { audioSource.PlayOneShot(blockedSound); return; } transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } if (Input.GetKeyDown(KeyCode.LeftArrow)) { spriteRenderer.sprite = defaultSprite; spriteRenderer.flipX = true; float nextPlayerX = transform.position.x - 1; float nextPlayerY = transform.position.y; Vector2 nextPlayerPosition = new Vector2(nextPlayerX, nextPlayerY); if (IsWall(nextPlayerPosition)) { audioSource.PlayOneShot(blockedSound); return; } transform.position = nextPlayerPosition; audioSource.PlayOneShot(moveSound); } } private bool IsWall(Vector2 checkPosition) { Collider2D[] hitColliders = Physics2D.OverlapPointAll(checkPosition); for (int i = 0; i < hitColliders.Length; i++) { if (hitColliders[i].CompareTag("Wall")) { return true; } } return false; } } |
今回追加した内容を確認していきましょう。まず、音の変数にこちらを追加しました。
|
1 |
public AudioClip blockedSound; |
blockedSound は、壁にぶつかったときに鳴らす音を入れるための変数です。
前回使った moveSound は移動できたときの音でした。今回追加した blockedSound は、移動できなかったときの音として使います。
次に、移動処理の中に以下の判定を追加しました。
|
1 2 3 4 5 |
if (IsWall(nextPlayerPosition)) { audioSource.PlayOneShot(blockedSound); return; } |
これは、次の移動先が壁かどうかを確認する処理です。nextPlayerPosition には、プレイヤーが次に移動しようとしている位置が入っています。つまり、
|
1 |
IsWall(nextPlayerPosition) |
は、「次に移動しようとしている場所は壁ですか?」と確認しているイメージです。
もし壁だった場合は、if文の中に入ります。
|
1 |
audioSource.PlayOneShot(blockedSound); |
ここで、壁にぶつかったときの音を鳴らします。その下にある、
|
1 |
return; |
こちらは、ここで処理を終わらせるという意味です。return; が実行されると、その下にある移動処理まで進みません。そのため、壁がある場合は、
|
1 |
transform.position = nextPlayerPosition; |
が実行されず、プレイヤーは移動しません。
IsWall関数を作りました そもそも関数とは?
今回、Unityで用意された関数ではなく、初めて自分で関数を作りました。それがこちらです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private bool IsWall(Vector2 checkPosition) { Collider2D[] hitColliders = Physics2D.OverlapPointAll(checkPosition); for (int i = 0; i < hitColliders.Length; i++) { if (hitColliders[i].CompareTag("Wall")) { return true; } } return false; } |
関数とは、簡単にいうと「処理をまとめて名前をつけたもの」です。
今回の場合は、「指定した場所に壁があるかを調べる処理」に IsWall という名前をつけています。
IsWall は英語で考えると、「壁ですか?」という意味です。そのため、
|
1 |
if (IsWall(nextPlayerPosition)) |
先ほどの移動処理に足したこちらは、「もし次の移動先が壁だったら」という意味になります。
trueとfalseについて
IsWall の前には bool と書かれています。
|
1 |
private bool IsWall(Vector2 checkPosition) |
bool は、true または false を返すときに使います。
true は「はい」、false は「いいえ」のようなものです。
今回の IsWall は、調べた場所に壁があれば true を返します。
|
1 |
return true; |
反対に、壁がなければ false を返します。
|
1 |
return false; |
つまり、IsWall は「壁があるかどうか」を調べて、結果を true または false で教えてくれる関数です。
引数とは?
IsWall の中には、Vector2 checkPosition というものがあります。
|
1 |
private bool IsWall(Vector2 checkPosition) |
この checkPosition は、「どの位置を調べるのか」を受け取るための変数です。
こういう、関数に渡す情報のことを「引数」といいます。
今回の場合、
|
1 |
IsWall(nextPlayerPosition) |
と書くことで、nextPlayerPosition を IsWall に渡しています。
つまり、「この位置に壁があるか調べてください」とお願いしているイメージです。
そして、IsWall の中では、受け取った位置を checkPosition という名前で使っています。
移動先が壁かどうかを調べる処理
次に、この部分を見てみましょう。
|
1 |
Collider2D[] hitColliders = Physics2D.OverlapPointAll(checkPosition); |
Unityに内蔵された機能を利用するのでちょっとわかりづらいですが少しずつ慣れていきましょう。
これは、checkPosition の場所にあるCollider2Dを全て調べる処理です。Collider2Dは、2Dゲームの当たり判定に使うUnityで用意されているコンポーネントです。
今回のゲームでは、壁にCollider2Dをつけておくことで、スクリプトから「この場所に壁がある」と判定できるようになります。
Physics2D.OverlapPointAll(checkPosition) は、指定した位置に重なっているCollider2Dをすべて取得します。取得した結果は、hitColliders という変数に入れています。
for文で1つずつ確認する
次に、この部分です。
|
1 2 3 4 5 6 7 |
for (int i = 0; i < hitColliders.Length; i++) { if (hitColliders[i].CompareTag("Wall")) { return true; } } |
ここでは、取得したCollider2Dを1つずつ確認しています。
for 文は、同じような処理を繰り返すときに使います。
今回の場合、指定した位置にあるCollider2Dを順番に見ていき、その中にWallタグが付いているものがあるかを調べています。
(タグとはUnityでゲームオブジェクトに付けられる名札のようなものです。後ほど設定します。)
|
1 |
hitColliders[i].CompareTag("Wall") |
これは、そのオブジェクトのTagが Wall かどうかを確認する処理です。もしWallタグが付いていたら、
|
1 |
return true; |
で、「壁がありました」と返します。
すべて確認してもWallタグが見つからなかった場合は、最後に
|
1 |
return false; |
が実行されます。これは、「壁はありませんでした」という意味です。
Unity側の設定をしよう
Ctrl + Sでスクリプトを保存して、Unityに戻ります。
このスクリプトを動かすには、Unity側で壁にタグや当たり判定のためのColliderの設定をしておく必要があります。
PrefabsフォルダにあるWallに設定をしていきましょう。
Projectウィンドウから Assets > Prefabs フォルダを開き、その中にある Wall をダブルクリックして選択します。

まず、InspectorのAdd Componentから Box Collider 2D を追加しましょう。

Box Collider 2Dは、2Dの当たり判定を作るためのコンポーネントです。今回のスクリプトでは、次の移動先にCollider2Dがあるかどうかを調べています。そのため、WallにBox Collider 2Dを追加しておく必要があります。
次に、WallのTagを Wall に設定します。Tagの項目から Add Tag… を選択します。

Wall というタグを作成してください。


タグを作成しただけでは、まだWallには設定されていません。もう一度Prefabsフォルダ内の Wall を選択し、InspectorのTagを Wall に変更しましょう。

このTagを設定しておかないと、スクリプト内のCompareTag(“Wall”)で壁として判定できません。PrefabのWallに設定しておけば、そこから複製して配置したWallにも同じ設定が反映されます。
また、Blocked Soundの設定も必要です。
Playerを選択

InspectorのPlayerController2DにあるBlocked Soundに、先ほどインポートした「ビーブ音4.mp3」をドラッグ&ドロップしましょう。

再生ボタンを押して、壁の方向に移動してみましょう。

壁にぶつかったときにプレイヤーが止まり、ブロック音が鳴れば成功です。
今回のプロジェクトファイル
ここまでの操作を行ったプロジェクトファイルを用意しました。
まとめ
お疲れ様でした!
第3回ではUnityとC#スクリプトを使ってプレイヤーを倉庫番らしく動かす仕組みを実装しました。
【今回学習した内容のおさらい】
-
1マスずつの移動: Update関数とInput.GetKeyDownを使い、方向キーを押した瞬間にX座標・Y座標を1ずつ変化させる処理を作りました。
-
演出の追加(音と向き): AudioSourceでの足音再生や、SpriteRendererをスクリプトから制御して移動方向にひよこを向かせる演出を加えました。
-
壁の当たり判定: 初めての「自作関数(IsWall)」やPhysics2D.OverlapPointAllを使い、次のマスに壁があるかを判定して移動を止める重要な仕組みを学びました。
プログラムの量が増えて難しく感じた部分もあったかもしれませんが、「キー入力を受け取る」、「コンポーネントを取得する」、「条件に合わせて処理を分ける」といった一連の流れはあらゆるゲーム開発の基本になります。
これでプレイヤーの基本操作はバッチリです!
しかし、まだ肝心の「箱」を動かすことができていまません。
次回(第4回)はいよいよ、倉庫番ゲームの核心である「箱を押し動かす処理」や「ゴールに乗ったときのクリア判定」を実装し、ゲームを完成させていきます。
流れとしてはプレイヤーの移動先に箱があるかを調べ、箱がある場合はその先のマスへ箱を動かすようにします。さらに、箱の先に壁や別の箱がある場合は押せないようにして倉庫番らしいルールを作っていきます。
倉庫番パズルゲームの仕組みを一緒に完成させていきましょう!
次の記事:

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







コメント