前回、コインのゲームオブジェクトを作りました。次は障害物を生成するのと同じ要領でコインを生成するゲームオブジェクトを作ってみましょう。
コイン生成用ゲームオブジェクトの作成
では、まずはコイン生成オブジェクト本体を用意します。
シーンに空のゲームオブジェクトを作り、
- 名前:CoinSpawner
- タグ:CoinSpawner(※新しく作ってください)
に変更して、座標を(9.5, 4, 0)くらいに設定しましょう。
コイン生成オブジェクトのC#スクリプト
そうしたら、次はコインを生成するためのC#スクリプトを書いていきます。新しいC#スクリプトを作成し、名前を「CoinSpawner」に変更したら次のスクリプトを書いてください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoinSpawner : MonoBehaviour
{
public bool isActive = false;
[SerializeField]
GameObject coinPrefab = null;
[SerializeField, Min(0.1f)]
float defaultMinWaitTime = 1;
[SerializeField, Min(0.1f)]
float defaultMaxWaitTime = 1;
[SerializeField, Min(0)]
float minPositionOffsetY = 0;
[SerializeField, Min(0)]
float maxPositionOffsetY = 0;
bool isSpawning = false;
float minWaitTime;
float maxWaitTime;
Coroutine timer;
//外部から値を代入するためのプロパティ
public float MinWaitTime
{
set
{
//あまりにも小さい値になるとものすごい数のコインが生成されてしまうので、0.1未満にならないようにする
minWaitTime = Mathf.Max(value, 0.1f);
}
get
{
return minWaitTime;
}
}
public float MaxWaitTime
{
set
{
maxWaitTime = Mathf.Max(value, 0.1f);
}
get
{
return maxWaitTime;
}
}
void Start()
{
InitSpawner();
}
//LateUpdateでコインの生成を実行することで、確実に壁が生成された後でコインを生成することができる
void LateUpdate()
{
if (!isActive)
{
//生成中なら中断する
if (timer != null)
{
StopCoroutine(timer);
isSpawning = false;
}
return;
}
//生成中じゃないなら生成開始
if (!isSpawning)
{
timer = StartCoroutine("SpawnTimer");
}
}
public void InitSpawner()
{
minWaitTime = defaultMinWaitTime;
maxWaitTime = defaultMaxWaitTime;
}
IEnumerator SpawnTimer()
{
isSpawning = true;
RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, Mathf.Infinity); //下向きにレイを飛ばす
float spawnPositionY = hit.point.y; //レイが当たったY座標を取得
spawnPositionY += Random.Range(minPositionOffsetY, maxPositionOffsetY); //高さをランダムにずらす
Instantiate(coinPrefab, new Vector2(transform.position.x, spawnPositionY + 0.5f), Quaternion.identity);
float waitTime = Random.Range(minWaitTime, maxWaitTime);
yield return new WaitForSeconds(waitTime);
isSpawning = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoinSpawner : MonoBehaviour
{
public bool isActive = false;
[SerializeField]
GameObject coinPrefab = null;
[SerializeField, Min(0.1f)]
float defaultMinWaitTime = 1;
[SerializeField, Min(0.1f)]
float defaultMaxWaitTime = 1;
[SerializeField, Min(0)]
float minPositionOffsetY = 0;
[SerializeField, Min(0)]
float maxPositionOffsetY = 0;
bool isSpawning = false;
float minWaitTime;
float maxWaitTime;
Coroutine timer;
//外部から値を代入するためのプロパティ
public float MinWaitTime
{
set
{
//あまりにも小さい値になるとものすごい数のコインが生成されてしまうので、0.1未満にならないようにする
minWaitTime = Mathf.Max(value, 0.1f);
}
get
{
return minWaitTime;
}
}
public float MaxWaitTime
{
set
{
maxWaitTime = Mathf.Max(value, 0.1f);
}
get
{
return maxWaitTime;
}
}
void Start()
{
InitSpawner();
}
//LateUpdateでコインの生成を実行することで、確実に壁が生成された後でコインを生成することができる
void LateUpdate()
{
if (!isActive)
{
//生成中なら中断する
if (timer != null)
{
StopCoroutine(timer);
isSpawning = false;
}
return;
}
//生成中じゃないなら生成開始
if (!isSpawning)
{
timer = StartCoroutine("SpawnTimer");
}
}
public void InitSpawner()
{
minWaitTime = defaultMinWaitTime;
maxWaitTime = defaultMaxWaitTime;
}
IEnumerator SpawnTimer()
{
isSpawning = true;
RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, Mathf.Infinity); //下向きにレイを飛ばす
float spawnPositionY = hit.point.y; //レイが当たったY座標を取得
spawnPositionY += Random.Range(minPositionOffsetY, maxPositionOffsetY); //高さをランダムにずらす
Instantiate(coinPrefab, new Vector2(transform.position.x, spawnPositionY + 0.5f), Quaternion.identity);
float waitTime = Random.Range(minWaitTime, maxWaitTime);
yield return new WaitForSeconds(waitTime);
isSpawning = false;
}
}
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CoinSpawner : MonoBehaviour { public bool isActive = false; [SerializeField] GameObject coinPrefab = null; [SerializeField, Min(0.1f)] float defaultMinWaitTime = 1; [SerializeField, Min(0.1f)] float defaultMaxWaitTime = 1; [SerializeField, Min(0)] float minPositionOffsetY = 0; [SerializeField, Min(0)] float maxPositionOffsetY = 0; bool isSpawning = false; float minWaitTime; float maxWaitTime; Coroutine timer; //外部から値を代入するためのプロパティ public float MinWaitTime { set { //あまりにも小さい値になるとものすごい数のコインが生成されてしまうので、0.1未満にならないようにする minWaitTime = Mathf.Max(value, 0.1f); } get { return minWaitTime; } } public float MaxWaitTime { set { maxWaitTime = Mathf.Max(value, 0.1f); } get { return maxWaitTime; } } void Start() { InitSpawner(); } //LateUpdateでコインの生成を実行することで、確実に壁が生成された後でコインを生成することができる void LateUpdate() { if (!isActive) { //生成中なら中断する if (timer != null) { StopCoroutine(timer); isSpawning = false; } return; } //生成中じゃないなら生成開始 if (!isSpawning) { timer = StartCoroutine("SpawnTimer"); } } public void InitSpawner() { minWaitTime = defaultMinWaitTime; maxWaitTime = defaultMaxWaitTime; } IEnumerator SpawnTimer() { isSpawning = true; RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, Mathf.Infinity); //下向きにレイを飛ばす float spawnPositionY = hit.point.y; //レイが当たったY座標を取得 spawnPositionY += Random.Range(minPositionOffsetY, maxPositionOffsetY); //高さをランダムにずらす Instantiate(coinPrefab, new Vector2(transform.position.x, spawnPositionY + 0.5f), Quaternion.identity); float waitTime = Random.Range(minWaitTime, maxWaitTime); yield return new WaitForSeconds(waitTime); isSpawning = false; } }
処理の流れは障害物生成オブジェクトのときと大体同じですが、コインを生成するときの処理は少し違います。
というのも、何の工夫もなくコインを生成するとコインが障害物に埋もれてしまうことがあるからです。そこでUnityの物理演算機能の一つである「Ray(レイ)」をつかって、コインを生成するY座標を調整するようにしてあります。
ここでレイってなんだ?と思う方がいると思うので少し説明しておきますが、レイというのは見えないレーザー光線のようなものだと思って頂ければ良いでしょう。Physics2D.Raycastメソッドで飛ばしたレイは2Dコライダーに当たると、そのコライダーの情報を「RaycastHit2D」という型で返します。ここではこの情報を使って、地面や障害物に当たった地点のY座標を取得しているというわけですね。
GameManagerの変更
最後に、またまたGameManagerを変更します。スクリプトを開いて次のように書き換えてください(※適宜省略してあります)。
//省略
Canvas gameOverCanvas; //追加その2
Button retryButton; //追加その2
GameObject playerObj; //追加その2
CoinSpawner coinSpawner; //追加その4
//省略
//--ここから追加--
public void InitGame()
{
gameStarted = false; //追加その3
level = 1;
mileage = 0;
maxScore = (int)Mathf.Pow(10, scoreDigits) - 1; //スコアの最大値を作成。例えば、8桁なら99999999
score = 0;
timerIsActive = false; //追加その3
wallSpawner.isActive = false; //追加その3
wallSpawner.InitSpawner(); //追加その3
coinSpawner.isActive = false; //追加その4
coinSpawner.InitSpawner(); //追加その4
//追加その3。UI表示の初期化
UpdateMileageUi();
UpdateLevelUi();
UpdateScoreUi();
}
//省略
public void GameOver()
{
if(playerObj != null)
{
Destroy(playerObj);
}
gameStarted = false; //追加その3
timerIsActive = false;
wallSpawner.isActive = false;
gameOverCanvas.enabled = true;
retryButton.enabled = true; //追加その3
coinSpawner.isActive = false; //追加その4
}
public void Retry()
{
//シーンにある障害物を全部消去する
GameObject[] walls = GameObject.FindGameObjectsWithTag("Wall");
foreach(GameObject wall in walls)
{
Destroy(wall);
}
//追加4.コインを消去
GameObject[] coins = GameObject.FindGameObjectsWithTag("Item");
foreach (GameObject coin in coins)
{
Destroy(coin);
}
gameOverCanvas.enabled = false;
retryButton.enabled = false; //追加その3
GameStart();
}
IEnumerator Countdown()
{
countdownText.enabled = false;
yield return new WaitForSeconds(1);
countdownText.enabled = true;
for(int i = 2; i >= 0; i--)
{
countdownText.text = (i + 1).ToString();
yield return new WaitForSeconds(1);
}
countdownText.enabled = false;
playerObj = Instantiate(playerPrefab, playerSpawnPosition, Quaternion.identity);
gameStarted = true;
wallSpawner.isActive = true;
coinSpawner.isActive = true; //追加その4
}
//シーン読み込み時に各種コンポーネントを取得するメソッド
public void LoadComponents()
{
if (moveSceneManager.SceneName == "Title")
{
return;
}
wallSpawner = GameObject.FindGameObjectWithTag("WallSpawner").GetComponent<WallSpawner>();
mileageText = GameObject.Find(mileageTextName).GetComponent<Text>();
scoreText = GameObject.Find(scoreTextName).GetComponent<Text>();
highScoreText = GameObject.Find(highScoreTextName).GetComponent<Text>();
levelText = GameObject.Find(levelTextName).GetComponent<Text>();
//追加その2
gameOverCanvas = GameObject.Find(gameOverCanvasName).GetComponent<Canvas>();
retryButton = GameObject.Find(retryButtonName).GetComponent<Button>();
//追加その3
countdownText = GameObject.Find(countDownTextName).GetComponent<Text>();
//ボタンにクリック時の処理を登録
retryButton.onClick.AddListener(() => Retry());
//追加その4
coinSpawner = GameObject.FindGameObjectWithTag("CoinSpawner").GetComponent<CoinSpawner>();
}
//省略
void LevelUp()
{
if(level < maxLevel && mileage % meterPerLevel == 0)
{
level++;
UpdateLevelUi();
//障害物の生成間隔を小さくする
wallSpawner.MinWaitTime /= divisor;
wallSpawner.MaxWaitTime /= divisor;
coinSpawner.MinWaitTime /= divisor; //追加その4
coinSpawner.MaxWaitTime /= divisor; //追加その4
}
}
//省略
//省略
Canvas gameOverCanvas; //追加その2
Button retryButton; //追加その2
GameObject playerObj; //追加その2
CoinSpawner coinSpawner; //追加その4
//省略
//--ここから追加--
public void InitGame()
{
gameStarted = false; //追加その3
level = 1;
mileage = 0;
maxScore = (int)Mathf.Pow(10, scoreDigits) - 1; //スコアの最大値を作成。例えば、8桁なら99999999
score = 0;
timerIsActive = false; //追加その3
wallSpawner.isActive = false; //追加その3
wallSpawner.InitSpawner(); //追加その3
coinSpawner.isActive = false; //追加その4
coinSpawner.InitSpawner(); //追加その4
//追加その3。UI表示の初期化
UpdateMileageUi();
UpdateLevelUi();
UpdateScoreUi();
}
//省略
public void GameOver()
{
if(playerObj != null)
{
Destroy(playerObj);
}
gameStarted = false; //追加その3
timerIsActive = false;
wallSpawner.isActive = false;
gameOverCanvas.enabled = true;
retryButton.enabled = true; //追加その3
coinSpawner.isActive = false; //追加その4
}
public void Retry()
{
//シーンにある障害物を全部消去する
GameObject[] walls = GameObject.FindGameObjectsWithTag("Wall");
foreach(GameObject wall in walls)
{
Destroy(wall);
}
//追加4.コインを消去
GameObject[] coins = GameObject.FindGameObjectsWithTag("Item");
foreach (GameObject coin in coins)
{
Destroy(coin);
}
gameOverCanvas.enabled = false;
retryButton.enabled = false; //追加その3
GameStart();
}
IEnumerator Countdown()
{
countdownText.enabled = false;
yield return new WaitForSeconds(1);
countdownText.enabled = true;
for(int i = 2; i >= 0; i--)
{
countdownText.text = (i + 1).ToString();
yield return new WaitForSeconds(1);
}
countdownText.enabled = false;
playerObj = Instantiate(playerPrefab, playerSpawnPosition, Quaternion.identity);
gameStarted = true;
wallSpawner.isActive = true;
coinSpawner.isActive = true; //追加その4
}
//シーン読み込み時に各種コンポーネントを取得するメソッド
public void LoadComponents()
{
if (moveSceneManager.SceneName == "Title")
{
return;
}
wallSpawner = GameObject.FindGameObjectWithTag("WallSpawner").GetComponent<WallSpawner>();
mileageText = GameObject.Find(mileageTextName).GetComponent<Text>();
scoreText = GameObject.Find(scoreTextName).GetComponent<Text>();
highScoreText = GameObject.Find(highScoreTextName).GetComponent<Text>();
levelText = GameObject.Find(levelTextName).GetComponent<Text>();
//追加その2
gameOverCanvas = GameObject.Find(gameOverCanvasName).GetComponent<Canvas>();
retryButton = GameObject.Find(retryButtonName).GetComponent<Button>();
//追加その3
countdownText = GameObject.Find(countDownTextName).GetComponent<Text>();
//ボタンにクリック時の処理を登録
retryButton.onClick.AddListener(() => Retry());
//追加その4
coinSpawner = GameObject.FindGameObjectWithTag("CoinSpawner").GetComponent<CoinSpawner>();
}
//省略
void LevelUp()
{
if(level < maxLevel && mileage % meterPerLevel == 0)
{
level++;
UpdateLevelUi();
//障害物の生成間隔を小さくする
wallSpawner.MinWaitTime /= divisor;
wallSpawner.MaxWaitTime /= divisor;
coinSpawner.MinWaitTime /= divisor; //追加その4
coinSpawner.MaxWaitTime /= divisor; //追加その4
}
}
//省略
//省略 Canvas gameOverCanvas; //追加その2 Button retryButton; //追加その2 GameObject playerObj; //追加その2 CoinSpawner coinSpawner; //追加その4 //省略 //--ここから追加-- public void InitGame() { gameStarted = false; //追加その3 level = 1; mileage = 0; maxScore = (int)Mathf.Pow(10, scoreDigits) - 1; //スコアの最大値を作成。例えば、8桁なら99999999 score = 0; timerIsActive = false; //追加その3 wallSpawner.isActive = false; //追加その3 wallSpawner.InitSpawner(); //追加その3 coinSpawner.isActive = false; //追加その4 coinSpawner.InitSpawner(); //追加その4 //追加その3。UI表示の初期化 UpdateMileageUi(); UpdateLevelUi(); UpdateScoreUi(); } //省略 public void GameOver() { if(playerObj != null) { Destroy(playerObj); } gameStarted = false; //追加その3 timerIsActive = false; wallSpawner.isActive = false; gameOverCanvas.enabled = true; retryButton.enabled = true; //追加その3 coinSpawner.isActive = false; //追加その4 } public void Retry() { //シーンにある障害物を全部消去する GameObject[] walls = GameObject.FindGameObjectsWithTag("Wall"); foreach(GameObject wall in walls) { Destroy(wall); } //追加4.コインを消去 GameObject[] coins = GameObject.FindGameObjectsWithTag("Item"); foreach (GameObject coin in coins) { Destroy(coin); } gameOverCanvas.enabled = false; retryButton.enabled = false; //追加その3 GameStart(); } IEnumerator Countdown() { countdownText.enabled = false; yield return new WaitForSeconds(1); countdownText.enabled = true; for(int i = 2; i >= 0; i--) { countdownText.text = (i + 1).ToString(); yield return new WaitForSeconds(1); } countdownText.enabled = false; playerObj = Instantiate(playerPrefab, playerSpawnPosition, Quaternion.identity); gameStarted = true; wallSpawner.isActive = true; coinSpawner.isActive = true; //追加その4 } //シーン読み込み時に各種コンポーネントを取得するメソッド public void LoadComponents() { if (moveSceneManager.SceneName == "Title") { return; } wallSpawner = GameObject.FindGameObjectWithTag("WallSpawner").GetComponent<WallSpawner>(); mileageText = GameObject.Find(mileageTextName).GetComponent<Text>(); scoreText = GameObject.Find(scoreTextName).GetComponent<Text>(); highScoreText = GameObject.Find(highScoreTextName).GetComponent<Text>(); levelText = GameObject.Find(levelTextName).GetComponent<Text>(); //追加その2 gameOverCanvas = GameObject.Find(gameOverCanvasName).GetComponent<Canvas>(); retryButton = GameObject.Find(retryButtonName).GetComponent<Button>(); //追加その3 countdownText = GameObject.Find(countDownTextName).GetComponent<Text>(); //ボタンにクリック時の処理を登録 retryButton.onClick.AddListener(() => Retry()); //追加その4 coinSpawner = GameObject.FindGameObjectWithTag("CoinSpawner").GetComponent<CoinSpawner>(); } //省略 void LevelUp() { if(level < maxLevel && mileage % meterPerLevel == 0) { level++; UpdateLevelUi(); //障害物の生成間隔を小さくする wallSpawner.MinWaitTime /= divisor; wallSpawner.MaxWaitTime /= divisor; coinSpawner.MinWaitTime /= divisor; //追加その4 coinSpawner.MaxWaitTime /= divisor; //追加その4 } } //省略
コインの生成を始めたり止めたりするような処理を追加しました。ゲームを実行して、次のGIFのようにコインが生成されるようになれば成功です。
次のページ→ハイスコアのセーブ処理の作り方