この講座では、Unity初心者の方でも作れる「スイカゲーム風パズルゲーム」講座を作成します。
前回はUnityの下準備を行い、果物を落とすためのステージを作成しました。また、果物に Rigidbody2D や Collider を追加し、重力で落下させたり、床や壁にぶつかるようにしました。
前回の記事:

今回は前回準備した果物 Prefab を使ってゲーム中に果物をランダムに表示する仕組みを作成します。
さらに、表示された果物をマウスで左右に動かしマウスを離した瞬間に落とせるようにしていきます。
マージパズルの果物をランダムに表示させよう
まずは前回作成した果物をランダムに表示する仕組みを作成します。
今回作る仕組みは、Inspector画面で果物プレハブを設定し、その中からランダムに1つ選んで Scene上に表示するというものです。
スクリプトを書こう
まずはスクリプトをまとめるフォルダを作成します。Project ウィンドウで右クリックし、Create > Folder を選択します。作成したフォルダの名前を Scripts にしましょう。
次に、作成した Scripts フォルダを開きます。フォルダ内で右クリックし、Create > MonoBehaviour Script を選択します。スクリプトの名前は GameManager2D にしましょう。
今回はこのスクリプトから果物をランダムに選び、指定した位置に表示させます。作成した GameManager2D をダブルクリックして開きましょう。
GameManager2D.cs(新規作成時)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using UnityEngine; public class GameManager2D : 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 は、ゲームが始まったときに1回だけ実行される場所です。
たとえば、最初の果物を表示する、最初の数値を決める、といった準備の処理を書くときに使います。
Update は、ゲーム中に何度も繰り返し実行される場所です。
マウスの位置を確認する、キー入力を確認する、キャラクターを動かすといった毎回確認したい処理を書くときに使います。
今回はまずゲーム開始時に果物を1つ表示したいので、Start の中で果物を表示する処理を呼び出します。
では、GameManager2D に果物をランダムに表示するプログラムを書いていきましょう。以下のように追記・修正してください。
GameManager2D
|
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 |
using UnityEngine; public class GameManager2D : MonoBehaviour { [Header("Fruits")] public GameObject[] fruitPrefabs; [Header("SpawnSettings")] public float spawnY = 4f; private int currentLevel; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } // Update is called once per frame void Update() { } int RandomStartLevel() { return Random.Range(0, fruitPrefabs.Length); } void CreateFruit() { Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); } } |
スクリプトの解説
このスクリプトでは登録した果物の中からランダムに1つを選び、ゲーム開始時に Scene 上へ表示します。
内部では、次の順番で処理しています。
- 果物プレハブをUnityエディタのインスペクター画面で登録しておく
- ゲーム開始時に果物それぞれにランダムな番号を割り振る
- 決めた番号の果物プレハブを選ぶ
- 選んだ果物をScene上に表示する
つまり、このスクリプトでは「果物を選ぶ処理」と「選んだ果物を表示する処理」を作っています。
変数宣言
|
1 2 |
[Header("Fruits")] public GameObject[] fruitPrefabs; |
fruitPrefabs は、果物の Prefab を入れておくための変数です。前回作成したいちごやさくらんぼなどの果物 Prefab を、Unity の Inspector からここに登録します。
GameObject[] の [] は、複数の GameObject をまとめて入れられるという意味です。今回は、さくらんぼ、いちご、ぶどうなど、複数の果物 Prefab を登録するために使います。
[Header(“Fruits”)] は、Inspector 上で項目を見やすくするための表示用の設定です。処理そのものには影響しません。
|
1 2 |
[Header("SpawnSettings")] public float spawnY = 4f; |
spawnY は、果物を表示する高さを決める変数です。今回は4fにしているので、果物は Y 座標4 の位置に表示されます。こちらも public にしているのでUnity の Inspector から数値を変更できます。
[Header(“SpawnSettings”)] は、Inspector 上で項目を見やすくするための表示用の設定です。
|
1 |
private int currentLevel; |
currentLevel は、今回表示する果物の番号を保存するための変数です。fruitPrefabs に登録した果物は、0番、1番、2番のように番号で管理されます。そのため、ランダムに選ばれた番号を currentLevel に入れておきます。
private にしているので、Inspector には表示されません。スクリプト内部だけで使う変数です。
Start でランダムな果物を決める
このスクリプトでは、ゲーム開始時に最初の果物を表示します。そのため、Start() の中に処理を書いています。
|
1 2 3 4 5 |
void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } |
Start は、ゲームが始まったときに 1 回だけ呼ばれる場所です。
ここでは、まず RandomStartLevel() を使って、表示する果物の番号をランダムに決めています。
|
1 |
currentLevel = RandomStartLevel(); |
RandomStartLevel() の結果を currentLevel に入れています。つまり、この時点で何番目の果物を出すかが決まります。
そのあと、CreateFruit() を呼び出しています。
|
1 2 3 4 5 |
void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } |
CreateFruit() は、選ばれた番号の果物を Scene 上に表示するための処理です。
つまり Start の中では、
- ランダムに果物の番号を決める
- その番号の果物を表示する
という順番で処理しています。
RandomStartLevel で果物の番号をランダムに選ぶ
次に、ランダムな番号を作る処理を見ていきましょう。
|
1 2 3 4 |
int RandomStartLevel() { return Random.Range(0, fruitPrefabs.Length); } |
RandomStartLevel() は、表示する果物の番号をランダムに決める処理です。
|
1 |
return Random.Range(0, fruitPrefabs.Length); |
この部分で、0から fruitPrefabs.Length の手前までの数字をランダムに選んでいます。
たとえば fruitPrefabs に果物が 5 個登録されている場合、fruitPrefabs.Length は 5 になります。このとき、選ばれる番号は[0,1,2,3,4]のどれかです。
5は選ばれません。
配列の番号は 0 から始まるため、果物が5個ある場合、最後の番号は4になります。そのため、Random.Range(0, fruitPrefabs.Length) と書くことで、登録されている果物の中から正しく1つ選べます。
| fruitPrefabs.Length | 1個目 | 2個目 | 3個目 | 4個目 | 5個目 |
| 配列番号 | 0 | 1 | 2 | 3 | 4 |
CreateFruit で果物を表示する
最後に、選ばれた果物を実際に表示する処理です。
|
1 2 3 4 |
void CreateFruit() { Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); } |
CreateFruit() では、Instantiate を使って果物 Prefab を Scene 上に作成しています。
Instantiate は、Prefab から実物を作るための Unity の機能です。
|
1 |
fruitPrefabs[currentLevel] |
この部分では、ランダムに選ばれた番号の果物 Prefab を指定しています。たとえば currentLevel が0なら、fruitPrefabs の0番目の果物が表示されます。
currentLevel が2なら、fruitPrefabs の2番目の果物が表示されます。
|
1 |
new Vector3(0f, spawnY, 0f) |
これは、果物を表示する位置です。
Vector3 は、X、Y、Z の3つの数値で位置を指定します。今回の指定は、次の意味になります。
| X座標 | 0 |
| Y座標 | spawnY |
| Z座標 | 0 |
つまり、横方向は中央、高さは spawnY で指定した位置、奥行きは0の場所に果物を表示します。
Quaternion.identity は、回転させずにそのままの向きで表示するという意味です。
動作を確認しよう
ここまで書けたら、Ctrl + S で必ずスクリプトを保存して Unity に戻りましょう。
次に、作成した GameManager2D スクリプトをアタッチするためのオブジェクトを作成します。Hierarchy ウィンドウで右クリックし、Create Empty を選択します。作成されたオブジェクトの名前を GameManager に変更しましょう。
次に、Project ウィンドウにある GameManager2D スクリプトを、Hierarchy にある GameManager へドラッグ&ドロップします。
これで、GameManager に GameManager2D スクリプトをアタッチできました。
次に、Hierarchy の GameManager を選択します。
Inspector に GameManager2D の設定項目が表示されるので、fruitPrefabs に Project ウィンドウの Prefabs フォルダにある果物 Prefab を登録します。
このとき、果物は必ず画像と同じ順番で設定してください。
fruitPrefabs に登録した順番は、スクリプト内では0番、1番、2番のように番号で管理されます。今回は0番から小さいという基準で作成します。
ここまでできたら、再生して確認してみましょう。
ゲームを再生すると、Scene 上に果物が1つ表示されます。
一度再生を止めて、もう一度再生してみましょう。
再生するたびに別の果物が表示されることが確認できれば、ランダムに果物を表示する仕組みは完成です。
出てくる果物を制限しよう
ここまでで果物をランダムに表示する仕組みができました。
ただし、今のままだと登録した果物が全てランダムに選ばれてしまいます。本家のスイカゲームでも、最初からパイナップルやスイカのような大きな果物が出てくることはありません。
そこで今回は最初に出てくる果物を制限していきます。4つ目のオレンジまでが出るようにしておきましょう
Project ウィンドウから Assets > Scripts フォルダを開きます。GameManager2D をダブルクリックして開きましょう。
開けたら、以下のように追記・修正してください。
GameManager2D
|
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 |
using UnityEngine; public class GameManager2D : MonoBehaviour { [Header("Fruits")] public GameObject[] fruitPrefabs; public int maxStartLevel = 3; [Header("SpawnSettings")] public float spawnY = 4f; private int currentLevel; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } // Update is called once per frame void Update() { } int RandomStartLevel() { return Random.Range(0, maxStartLevel + 1); } void CreateFruit() { Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); } } |
スクリプトの解説
今回追加・修正したのは2か所です。
- 最初に出せる果物の最大番号を決める
- RandomStartLevel でその番号の範囲まででランダムに選ぶ
登録した果物すべてから選ぶのではなく、指定した番号までの果物からランダムに選ぶようにしています。
変数宣言
|
1 2 3 |
[Header("Fruits")] public GameObject[] fruitPrefabs; public int maxStartLevel = 3; |
maxStartLevel は、出てくる果物の最大番号を決める変数です。
fruitPrefabs に登録した果物は、0番、1番、2番、3番のように番号で管理されます。
たとえば、0番がさくらんぼ、3番がオレンジになります。
今回は maxStartLevel を3にしているので、0番から3番までの果物が出るようになります。
public にしているので、Unity の Inspector から数値を変更できます。あとから、出現する果物の範囲を調整したいときも、スクリプトを書き換えずに Inspector から変更できます。
RandomStartLevel で出てくる果物を制限する
次に、RandomStartLevel の中を確認しましょう。
|
1 2 3 4 |
int RandomStartLevel() { return Random.Range(0, maxStartLevel + 1); } |
ここでは、Random.Range を使って、表示する果物の番号をランダムに決めています。
前回は fruitPrefabs.Length を使って、登録した果物全体からランダムに選んでいました。
今回は maxStartLevel を使うことで、指定した番号までの果物だけを選ぶようにしています。
重要なのは、maxStartLevel + 1 と書いている部分です。Random.Range を整数で使う場合、後ろの数字は含まれません。
たとえば、次のように書いた場合です。
|
1 |
Random.Range(0, 3) |
この場合、実際に選ばれるのは0、1、2までです。
3は選ばれません。
今回は maxStartLevel が3なので、みかんまで出したい場合は3も選ばれる必要があります。
そのため、maxStartLevel + 1 と書いています。
|
1 |
return Random.Range(0, maxStartLevel + 1); |
これで、maxStartLevel が3のときに、0、1、2、3 の中からランダムに選ばれるようになります。
動作を確認しよう
ここまで書けたら、Ctrl + S で必ず保存して Unity に戻りましょう。Unity に戻ったら、GameManager を選択します。
Inspector に maxStartLevel が追加されていることを確認しましょう。
確認できたら、ゲームを再生してみましょう。何度か再生を止めて、もう一度再生することを繰り返します。
表示される果物が、オレンジ以下の数値の果物だけになっていれば成功です。
果物をマウスで動かして落とせるようにしよう
次に、表示された果物をマウスで左右に動かせるようにします。
仕組みとしては、マウスの左クリックが押されている間、果物がマウスの位置についてくるようにします。
ただし、マウスの位置にそのまま合わせてしまうと、果物が壁の外まで移動できてしまいます。
そのため、今回は左右に動ける範囲を決めてその中でのみ果物を動かせるようにします。
スクリプトを書こう
Project ウィンドウから Assets > Scripts フォルダを開きます。GameManager2D をダブルクリックして開きましょう。
開けたら、以下のように追記・修正してください。
GameManager2D
|
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 |
using UnityEngine; public class GameManager2D : MonoBehaviour { [Header("Fruits")] public GameObject[] fruitPrefabs; public int maxStartLevel = 3; [Header("SpawnSettings")] public float spawnY = 4f; public float minX = -2.5f; public float maxX = 2.5f; private int currentLevel; private GameObject currentFruit; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } // Update is called once per frame void Update() { if (Input.GetMouseButton(0)) { Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); float x = Mathf.Clamp(mousePosition.x, minX, maxX); currentFruit.transform.position = new Vector3(x, spawnY, 0f); } } int RandomStartLevel() { return Random.Range(0, maxStartLevel + 1); } void CreateFruit() { currentFruit = Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); } } |
スクリプトの解説
今回のスクリプトでは、表示した果物をマウスで左右に動かせるようにしています。
内部では、次の順番で処理しています。
- 左右に動ける範囲を決める
- 現在表示されている果物を currentFruit に保存する
- マウスの左クリックが押されているか確認する
- マウスの位置を Scene 上の座標に変換する
- 左右の移動範囲からはみ出さないようにする
- 決まった位置を現在の果物に反映する
変数宣言
|
1 2 |
public float minX = -2.5f; public float maxX = 2.5f; |
minX と maxX は、果物が左右に動ける範囲を決める変数です。
minX は左端の位置、maxX は右端の位置です。
今回は minX が-2.5f、maxX が2.5fなので、果物は X 座標-2.5から2.5の範囲内で動きます。この範囲を決めておくことで、果物が壁の外まで移動してしまうのを防げます。
|
1 |
private GameObject currentFruit; |
currentFruit は、今の果物を入れておくための変数です。
今回の処理では、現在表示されている果物の位置を変更します。そのためには、「今動かす果物」がどれなのかをスクリプト側で覚えておく必要があります。そこで、CreateFruit で作成した果物を currentFruit に入れておきます。
Update でマウス入力を確認する
今回のマウス操作は、Update の中に書いています。
|
1 2 3 4 5 6 7 8 9 |
void Update() { if (Input.GetMouseButton(0)) { Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); float x = Mathf.Clamp(mousePosition.x, minX, maxX); currentFruit.transform.position = new Vector3(x, spawnY, 0f); } } |
Update は、ゲーム中に何度も繰り返し呼ばれる処理です。マウスの入力はゲーム中に毎回確認する必要があるため、Update の中で確認します。
|
1 |
if (Input.GetMouseButton(0)) |
この部分では、マウスの左クリックが押されているかを確認しています。Input.GetMouseButton(0) は、マウスの左クリックが押されている間 true になります。つまり、左クリックを押している間だけ、if文の中の処理が実行されます。
左クリックを離している間は、この中の処理は実行されません。
|
1 |
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); |
この行では、マウスの位置を取得しています。
Input.mousePosition は、画面上でのマウスの位置です。
ただしこれはあくまでマウスなどを動かすスクリーン座標系の位置情報です。このままだとUnityのScene上の位置としては使えません。
そこで、Camera.main.ScreenToWorldPoint を使って、画面上のマウス位置を Scene 上の座標に変換しています。
これにより画面上のマウス位置をゲーム内の座標として扱えるようになります。
|
1 |
float x = Mathf.Clamp(mousePosition.x, minX, maxX); |
この行では、マウスの X 座標を、minX から maxX の範囲内に収めています。
mousePosition.x は、マウスの横方向の位置です。ただし、そのまま使うと壁の外まで移動できてしまいます。そこで Mathf.Clamp を使い、X 座標が minX より小さくならず、maxX より大きくならないようにしています。
Clamp とは?
Clamp は、数値を決められた範囲内に収めるための機能です。
たとえば、今回のように minX が-2.5、maxX が2.5の場合で考えてみましょう。
- マウスの X 座標が1のときは、範囲内なのでそのまま1になります。
- マウスの X 座標が5のときは、右に行きすぎているので2.5になります。
- マウスの X 座標が-5のときは、左に行きすぎているので-2.5になります。
つまり Clamp は、範囲内ならそのまま使う、小さすぎるなら最小値にする、大きすぎるなら最大値にするという処理です。
今回の場合は、果物が壁の外へ移動しないようにするために使っています。
|
1 |
currentFruit.transform.position = new Vector3(x, spawnY, 0f); |
この行では、現在の果物の位置を変更しています。currentFruit は、今表示されている果物です。transform.position は、その GameObject の位置です。
new Vector3(x, spawnY, 0f) では、果物の新しい位置を指定しています。
- X には、Mathf.Clamp で制限した x を使います。
- Y には spawnY を使います。
- Z は0fにしています。
これで果物は高さを保ったまま、左右だけマウスに合わせて動くようになります。
CreateFruit で現在の果物を保存する
|
1 2 3 4 |
void CreateFruit() { currentFruit = Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); } |
前回は、Instantiate で果物を作成するだけでした。今回は、先頭に currentFruit = を追加しています。これにより、作成した果物の GameObject が currentFruit に保存されます。
この保存がないと、Update の中でどの果物を動かすのかが分かりません。
現在表示されている果物を currentFruit に入れておくことで、その果物の transform.position を変更できるようになります。つまり、CreateFruit で果物を作るだけでなく、あとから動かすために currentFruit として覚えておくようにしています。
動作を確認しよう
ここまでできたら、Ctrl + S で必ず保存して Unity に戻りましょう。Unity に戻ったら、ゲームを再生して確認します。
左クリックを押している間、果物がマウスについてくれば成功です。
ただし、この状態で確認すると果物がそのまま落下してしまうことに気づくと思います。
落下しないようにしよう
果物が落下してしまう原因は、果物 Prefab についている Rigidbody2D です。Rigidbody2D が有効になっていると、果物は重力の影響を受けます。そのため、表示された瞬間から下に落ちてしまいます。
今回は、マウスで左右に動かしている間はまだ落としたくありません。そこで、果物を作成した直後にスクリプトから Rigidbody2D の物理シミュレーションと Collider をいったんオフにします。
Project ウィンドウから Assets > Scripts フォルダを開きます。GameManager2D をダブルクリックして開きましょう。
開けたら、以下のように追記してください。
GameManager2D
|
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 |
using UnityEngine; public class GameManager2D : MonoBehaviour { [Header("Fruits")] public GameObject[] fruitPrefabs; public int maxStartLevel = 3; [Header("SpawnSettings")] public float spawnY = 4f; public float minX = -2.5f; public float maxX = 2.5f; private int currentLevel; private GameObject currentFruit; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } // Update is called once per frame void Update() { if (Input.GetMouseButton(0)) { Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); float x = Mathf.Clamp(mousePosition.x, minX, maxX); currentFruit.transform.position = new Vector3(x, spawnY, 0f); } } int RandomStartLevel() { return Random.Range(0, maxStartLevel + 1); } void CreateFruit() { currentFruit = Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); currentFruit.GetComponent<Rigidbody2D>().simulated = false; currentFruit.GetComponent<Collider2D>().enabled = false; } } |
スクリプトの解説
今回追加したのは、CreateFruit() の中にある次の 2 行です。
|
1 2 3 4 5 6 |
void CreateFruit() { currentFruit = Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); currentFruit.GetComponent<Rigidbody2D>().simulated = false; currentFruit.GetComponent<Collider2D>().enabled = false; } |
この2行では、作成した果物の Rigidbody2D と Collider2D を取得して、一時的にオフにしています。
|
1 |
currentFruit.GetComponent<Rigidbody2D>().simulated = false; |
currentFruit は、今作成した果物です。GetComponent<Rigidbody2D>() は、その果物についている Rigidbody2D を取得する処理です。その後に simulated = false と書くことで、Rigidbody2D の物理シミュレーションを止めています。
Rigidbody2D の物理シミュレーションを止めると、重力の影響を受けなくなります。そのため、果物を表示しても勝手に落下しなくなります。
次に、こちらの行です。
|
1 |
currentFruit.GetComponent<Collider2D>().enabled = false; |
GetComponent<Collider2D>() は、その果物についている Collider2D を取得する処理です。enabled = false と書くことで、Collider2D を一時的にオフにしています。
Collider2D は、ほかの物体とぶつかるための当たり判定です。今回はまだ果物を落としていない状態なので、マウスで動かしている間は当たり判定もオフにしておきます。
これで、果物を表示した直後に落下せず、マウスで左右に動かせる状態になります。
動作を確認しよう
ここまで書けたら、Ctrl + S で必ず保存して Unity に戻りましょう。Unity に戻ったら、ゲームを再生して確認します。
今度は果物が落下せず、左クリックを押している間だけ左右に動かせれば成功です。
マウスを離すと果物が落下するようにしよう
次はマウスの左クリックを離した瞬間に果物が落下するようにします。
さっき果物が勝手に落ちないように作成した直後に Rigidbody2D と Collider2D をオフにしました。
今回はその逆、マウスを離したタイミングでRigidbody2D と Collider2D をオンに戻して、果物が重力で落ちるようにします。
ここで大事なのは、落としたあとの果物を操作できないようにすることです。落下した果物をそのまま currentFruit として持ち続けてしまうと、落としたあともマウス操作で動かせてしまいます。
そのため、果物を落とした後は currentFruit を空にして、操作対象から外します。
スクリプトを書こう
Project ウィンドウから Assets > Scripts フォルダを開きます。GameManager2D をダブルクリックして開きましょう。
開けたら、以下のように追記・修正してください。
GameManager2D
|
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 |
using UnityEngine; public class GameManager2D : MonoBehaviour { [Header("Fruits")] public GameObject[] fruitPrefabs; public int maxStartLevel = 3; [Header("SpawnSettings")] public float spawnY = 4f; public float minX = -2.5f; public float maxX = 2.5f; private int currentLevel; private GameObject currentFruit; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } // Update is called once per frame void Update() { if (currentFruit == null) { return; } if (Input.GetMouseButton(0)) { Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); float x = Mathf.Clamp(mousePosition.x, minX, maxX); currentFruit.transform.position = new Vector3(x, spawnY, 0f); } if (Input.GetMouseButtonUp(0)) { currentFruit.GetComponent<Rigidbody2D>().simulated = true; currentFruit.GetComponent<Collider2D>().enabled = true; currentFruit = null; } } int RandomStartLevel() { return Random.Range(0, maxStartLevel + 1); } void CreateFruit() { currentFruit = Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); currentFruit.GetComponent<Rigidbody2D>().simulated = false; currentFruit.GetComponent<Collider2D>().enabled = false; } } |
スクリプトの解説
今回はマウスを離した瞬間に果物を落下させる処理を追加しています。
まず、Update の先頭に次の処理を追加しています。
|
1 2 3 4 |
if (currentFruit == null) { return; } |
これは操作する果物がないときにこのif文以降の処理を実行しないための処理です。
currentFruit には、今操作している果物が入っています。しかし、果物を落とした後に currentFruit を null にするため、そのあとは操作対象の果物がなくなります。その状態で currentFruit の位置を変えようとすると、存在しないものを動かそうとしてエラーになります。
そこで、currentFruit が null のときは return で処理を止めています。
つまり、操作できる果物がないときは、マウス移動や落下の処理を行わないようにしています。
また、return ○○のようにreturnの続きが記述されている場合はその○○を処理の呼び出し先に返して処理を抜けるという意味になります。
次に左クリックを離した瞬間の処理です。
|
1 2 3 4 5 6 7 |
if (Input.GetMouseButtonUp(0)) { currentFruit.GetComponent<Rigidbody2D>().simulated = true; currentFruit.GetComponent<Collider2D>().enabled = true; currentFruit = null; } |
Input.GetMouseButtonUp(0) は、マウスの左クリックを離した瞬間に true になります。
押している間ではなく、離した瞬間だけ反応するのがポイントです。
まず Rigidbody2D の物理シミュレーションをオンに戻しています。
|
1 |
currentFruit.GetComponent<Rigidbody2D>().simulated = true; |
前の手順では果物が勝手に落ちないように simulated を false にしていました。ここで true に戻すことで、果物が重力の影響を受けるようになります。
次に、Collider2D もオンに戻しています。
|
1 |
currentFruit.GetComponent<Collider2D>().enabled = true; |
Collider2D は当たり判定です。これをオンに戻すことで、果物が床や壁にぶつかるようになります。
最後に、currentFruit を null にしています。
|
1 |
currentFruit = null; |
これにより、落とした果物は操作対象ではなくなります。つまり、マウスを離した後はその果物をもう左右に動かせません。
この処理がないと、落下した果物を currentFruit として持ち続けてしまい、落下後にマウス操作で動かせてしまいます。そのため果物を落としたタイミングで currentFruit を空にして、操作対象から外しています。
動作を確認しよう
ここまで書けたら、Ctrl + S で必ず保存して Unity に戻りましょう。Unity に戻ったら、ゲームを再生して確認します。
左クリックを押している間は果物を左右に動かせます。左クリックを離した瞬間に果物が下へ落下すれば成功です。
また、落下した果物をそのあとマウスで動かせないことも確認しておきましょう。
次の果物が出てくるようにしよう
果物を落とした後は次の果物が出てくるようにします。
仕組みはシンプルです。ゲーム開始時の Start() で行っていた、
|
1 2 3 4 5 |
void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } |
- ランダムに果物を選ぶ
- 選んだ果物を表示する
という処理を果物を落とした後にも実行します。これで1つ目の果物を落としたあとに、次の果物が表示されるようになります。
Project ウィンドウから Assets > Scripts フォルダを開きます。GameManager2D をダブルクリックして開きましょう。
開けたら、以下のように追記・修正してください。
GameManager2D
|
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 |
using UnityEngine; public class GameManager2D : MonoBehaviour { [Header("Fruits")] public GameObject[] fruitPrefabs; public int maxStartLevel = 3; [Header("SpawnSettings")] public float spawnY = 4f; public float minX = -2.5f; public float maxX = 2.5f; private int currentLevel; private GameObject currentFruit; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } // Update is called once per frame void Update() { if (currentFruit == null) { return; } if (Input.GetMouseButton(0)) { Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); float x = Mathf.Clamp(mousePosition.x, minX, maxX); currentFruit.transform.position = new Vector3(x, spawnY, 0f); } if (Input.GetMouseButtonUp(0)) { currentFruit.GetComponent<Rigidbody2D>().simulated = true; currentFruit.GetComponent<Collider2D>().enabled = true; currentFruit = null; currentLevel = RandomStartLevel(); CreateFruit(); } } int RandomStartLevel() { return Random.Range(0, maxStartLevel + 1); } void CreateFruit() { currentFruit = Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); currentFruit.GetComponent<Rigidbody2D>().simulated = false; currentFruit.GetComponent<Collider2D>().enabled = false; } } |
スクリプトの解説
今回は果物を落とした後に次の果物を表示する処理を追加しています。
|
1 2 3 4 5 6 7 8 9 10 |
if (Input.GetMouseButtonUp(0)) { currentFruit.GetComponent<Rigidbody2D>().simulated = true; currentFruit.GetComponent<Collider2D>().enabled = true; currentFruit = null; currentLevel = RandomStartLevel(); CreateFruit(); } |
この2行は、Start() の中で行っている処理と同じです。
|
1 |
currentLevel = RandomStartLevel(); |
ここでは、次に表示する果物の番号をランダムに決めています。
RandomStartLevel() で選ばれた番号を currentLevel に入れることで、次にどの果物を出すかを決めています。
|
1 |
CreateFruit(); |
ここでは、currentLevel に入っている番号の果物を Scene 上に表示しています。
CreateFruit() の中では、果物を作成したあと、その果物を currentFruit に保存しています。
そのため、新しく表示された果物をまたマウスで左右に動かせるようになります。
動作を確認しよう
ここまで書けたら、Ctrl + S で必ず保存して Unity に戻りましょう。Unity に戻ったら、ゲームを再生して確認します。
左クリックを押している間は果物を左右に動かせます。左クリックを離すと果物が落下し、その直後に次の果物が表示されれば成功です。
連投を阻止しよう
現在の状態では、クリックを連打すると果物がどんどん出てきてしまいます。このままだと果物を落とす間隔が短すぎてゲームになりません。
そこで、果物を落とした後に少し待ち時間を作りすぐに次の果物が出ないようにします。
Project ウィンドウから Assets > Scripts フォルダを開きます。GameManager2D をダブルクリックして開きましょう。
開けたら、以下のように追記・修正してください。
GameManager2D
|
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 |
using UnityEngine; public class GameManager2D : MonoBehaviour { [Header("Fruits")] public GameObject[] fruitPrefabs; public int maxStartLevel = 3; [Header("SpawnSettings")] public float spawnY = 4f; public float minX = -2.5f; public float maxX = 2.5f; public float nextFruitDelay = 0.8f; private int currentLevel; private GameObject currentFruit; private float Timer; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { currentLevel = RandomStartLevel(); CreateFruit(); } // Update is called once per frame void Update() { if (currentFruit == null) { Timer += Time.deltaTime; if (Timer > nextFruitDelay) { CreateFruit(); Timer = 0; } return; } if (Input.GetMouseButton(0)) { Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); float x = Mathf.Clamp(mousePosition.x, minX, maxX); currentFruit.transform.position = new Vector3(x, spawnY, 0f); } if (Input.GetMouseButtonUp(0)) { currentFruit.GetComponent<Rigidbody2D>().simulated = true; currentFruit.GetComponent<Collider2D>().enabled = true; currentFruit = null; currentLevel = RandomStartLevel(); //CreateFruit(); (この行は忘れずに削除) } } int RandomStartLevel() { return Random.Range(0, maxStartLevel + 1); } void CreateFruit() { currentFruit = Instantiate(fruitPrefabs[currentLevel], new Vector3(0f, spawnY, 0f), Quaternion.identity); currentFruit.GetComponent<Rigidbody2D>().simulated = false; currentFruit.GetComponent<Collider2D>().enabled = false; } } |
スクリプトの解説
今回は果物を落とした後に少し時間を空けてから次の果物を表示する処理を追加しました。
変数宣言
|
1 |
public float nextFruitDelay = 0.8f; |
nextFruitDelayは次の果物が出てくるまでの待ち時間を決める変数です。
今回は0.8fにしているので、果物を落としてから約0.8秒後に次の果物が表示されます。
publicにしているのでUnityエディタのInspector画面から待ち時間を調整できます。
|
1 |
private float Timer; |
Timer は、果物を落としてからどれくらい時間が経ったかを保存するための変数です。
この変数を使って、次の果物を出すタイミングを管理します。privateにしているのでInspector画面には表示されません。スクリプトの中だけで使います。
Update で待ち時間を作る
今回のポイントは、Update関数の先頭に追加したこの部分です。
|
1 2 3 4 5 6 7 8 9 10 11 |
if (currentFruit == null) { Timer += Time.deltaTime; if (Timer > nextFruitDelay) { CreateFruit(); Timer = 0; } return; } |
currentFruit が null ということは、現在操作できる果物がない状態です。
今回の処理では、果物を落とした後に currentFruit を null にしています。
つまり、currentFruit が null の間は次の果物を出すまでの待ち時間として扱えます。
|
1 |
Timer += Time.deltaTime; |
この行では、Timer に時間を加算しています。
Time.deltaTime は、前のフレームから今のフレームまでにかかった時間です。Update は何度も繰り返し呼ばれるので、そのたびに Time.deltaTime を足していくことで、経過時間を測ることができます。
次に、この部分で待ち時間が終わったかを確認しています。
|
1 2 3 4 5 |
if (Timer > nextFruitDelay) { CreateFruit(); Timer = 0; } |
Timer が nextFruitDelay より大きくなったら、指定した待ち時間が経ったということです。
そのタイミングで CreateFruit() を呼び出し、次の果物を表示します。CreateFruit() を実行すると、新しい果物が currentFruit に入ります。そのため、またその果物をマウスで操作できるようになります。
最後に、Timer = 0; で時間をリセットしています。これを入れておくことで、次に果物を落としたときも、また0秒から待ち時間を測れるようになります。
果物を落としたときの処理
左クリックを離したときの処理も少し変わっています。
|
1 2 3 4 5 6 7 8 9 10 |
if (Input.GetMouseButtonUp(0)) { currentFruit.GetComponent<Rigidbody2D>().simulated = true; currentFruit.GetComponent<Collider2D>().enabled = true; currentFruit = null; currentLevel = RandomStartLevel(); //CreateFruit(); (この行は忘れずに削除) } |
前回は CreateFruit() を呼び出していましたが、今回は呼び出しません。忘れずに削除してください。
CreateFruit() は、Timer が nextFruitDelay を超えた後に呼び出します。
動作を確認しよう
ここまで書けたらCtrl + S で必ず保存してUnityエディタに戻りましょう。Unityに戻ったらゲームを再生して確認します。
左クリックを離すと果物が落下し、少し待ってから次の果物が表示されれば成功です。
クリックを連打しても果物がすぐに連続で出てこなければ、連投防止の仕組みができています。
プロジェクトファイルのダウンロード
今回の講座で作ったプロジェクトファイルのダウンロードリンクを用意しました。
まとめ
今回は、前回作成した果物プレハブを使って、果物をランダムに表示する仕組みを作成しました
また、最初から大きな果物が出ないように maxStartLevel で出現する果物の範囲を制限しました。
さらに、マウスの左クリックを押している間は果物を左右に動かし、左クリックを離した瞬間に落下するようにしました。
最後に、nextFruitDelay と Timer を使って、果物を落としたあとに少し待ってから次の果物が出るようにし、連投を防止しました。
ここまでで果物を表示して、動かして、落とすという基本的な操作ができるようになりました。
次回は同じ果物同士がぶつかったときに合体する仕組みを作成します。
併せて次に出る果物の表示やスコア加算の仕組みも作成していきます。
次の記事:

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


























コメント