Unityでの無限生成を実装してみた

こんにちは！プログラミングコミュニティGeekSalon所属メンターのガッキーと申します！今回は、何かとGameコースで質問の多いステージ無限生成の実装の仕方を書いていきます。今回初めての記事執筆になるので分かりづらい点などありましたら、言ってくださるとありがたいです🙇‍♂️
また、この無限生成の方法は複数のパターンのステージを生成したい方にオススメになっています。シンプルな無限生成は他の記事を参考にすると、やりやすいかなと感じます！


完成イメージこんな感じ！！

![mugenn.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2979202/80d92d3e-8fd5-3b05-b781-529f188c1364.gif)


# １. ステージ無限生成実装の手順

1. プレイヤーの準備
2. ステージの準備
3. ステージ無限生成のスクリプトを書く
4. Unityで少しいじる
5. ステージ上に敵などのオブジェクトを設置する場合


## 1.1プレイヤーの準備
1. Hierarchyにて3D objectの`Player`を生成する
2. Projectにて`PlyaerScript`を生成する
```c#:PlayerScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerScript : MonoBehaviour
{
    // Start is called before the first frame update
    float speed =5.0f; //speedの変数宣言
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.position += new Vector3(0,0,speed*Time.deltaTime);//移動するコード
    }
}

```
## 1.2ステージの準備
1. Hierarchyにて3D objectの`Plane`を3つ生成する
2. 3つのPlaneを見分けやすくするためにMaterialで色をつける
3. 以下の写真の形で３つ並べる![スクリーンショット 2022-12-05 14.13.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2979202/52e76b76-e301-5001-e3c7-c71081bc451c.png)
1個目の赤い`Plane`の座標は `(0,0,5)`、2個目の緑のPlaneの座標は`(0,0,15)`、3個目の青のPlaneの座標は`(0,0,25)`とする。Planeの大きさはdefaltで縦の長さ10,横の長さ10である。

## 1.3ステージ無限生成のスクリプトを書く
1. Hierarchyにて`Create Empty`を選択し`GameManager`という名前の空objectを生成する。
2. Projectにて`GroundGenerator`というスクリプトを生成する。
```c#:GroundGenerator.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GroundGenerator : MonoBehaviour
{
    // Start is called before the first frame update
public GameObject player;//UnityからPlayerを格納
	public GameObject[] grounds = new GameObject[3];//Unityから生成するGroundのPrefabをアタッチ

	float border = 15;
	float playerStartPosZ;//Playerの初期x座標
	float playerNowPosZ;//Playerの現在x座標
	


	void Start () {
		player = GameObject.Find("Player");//Hierarcyの中から名前が"Player"のものを探して来て取得→変数playerに格納

		playerStartPosZ = player.transform.position.z;//最初の基準値となるPlayerの
	}

	void Update () {
		GenerateGround();//Groundを一定の間隔で生成
	}

    //Groundを一定の間隔で生成
	void GenerateGround()
	{
		playerNowPosZ = player.transform.position.z;//Playerの現在x座標を変数playerNowPosXに格納
        float playerDistance = playerNowPosZ - playerStartPosZ;//Playerの移動距離(playerNowPosXとplayerStartPosXの差分)を変数playerDistanceに格納
        if (playerDistance > border)
        {
            //ステージ生成
            Debug.Log("ステージ生成");
			Instantiate(grounds[Random.Range(0, 3)], new Vector3(0, 0, player.transform.position.z + 20), Quaternion.identity);//Playerの一定距離だけ先にステージ生成(-5.5fはステージ生成の位置補正の為)
            playerDistance = 0;//playerDistanceのリセット
            border = 10;//borderの再設定
            playerStartPosZ = playerNowPosZ;//playerStartPosの再設定
        }
	}
} 
```
3.1個ずつ解説
```c#:GroundGenerator.cs

public class GroundGenerator : MonoBehaviour
{
    // Start is called before the first frame update
    public GameObject player;//UnityからPlayerを格納
	public GameObject[] grounds = new GameObject[3];//Unityから生成するGroundのPrefabをアタッチ

	float border = 15;
	float playerStartPosZ;//Playerの初期x座標
	float playerNowPosZ;//Playerの現在x座標
}

```
まずは変数宣言をする。
- 　playerはUnityにては紐付け可能にするためにpublicを付ける。
- 　　ステージは何パターン用意するかによって数字を変える必要がある。今回はステージを３パターン用意するので、3と書く。
- 　　今回使う`border`という変数は、「ここを超えたら次のステージが先に生成される」と覚えてください。
- 　`　playerStartPosZ`は最初のplayerのz座標
- 　`playerNowPosZ`は更新し続けられるplayerのz座標
```c#:GroundGenerator.cs

public class GroundGenerator : MonoBehaviour
{
void Start () {
		player = GameObject.Find("Player");//Hierarcyの中から名前が"Player"のものを探して来て取得→変数playerに格納

		playerStartPosZ = player.transform.position.z;//最初の基準値となるPlayerの
	}}

```
- Hierarcyの中から名前が`Player`のものを探して来て取得→変数playerに格納する。
- 　最初の基準値をとなる最初のplayerのz座標を設定
```c#:GroundGenerator.cs

public class GroundGenerator : MonoBehaviour
{
	void Update () {
		GenerateGround();//Groundを一定の間隔で生成
}
    //Groundを一定の間隔で生成
	void GenerateGround()
	{
		playerNowPosZ = player.transform.position.z;//Playerの現在x座標を変数playerNowPosXに格納
        float playerDistance = playerNowPosZ - playerStartPosZ;//Playerの移動距離(playerNowPosXとplayerStartPosXの差分)を変数playerDistanceに格納
        if (playerDistance > border)
        {
            //ステージ生成
            Debug.Log("ステージ生成");
			Instantiate(grounds[Random.Range(0, 3)], new Vector3(0, 0, player.transform.position.z + 20), Quaternion.identity);//Playerの一定距離だけ先にステージ生成(-5.5fはステージ生成の位置補正の為)
            playerDistance = 0;//playerDistanceのリセット
            border = 10;//borderの再設定
            playerStartPosZ = playerNowPosZ;//playerStartPosの再設定
        }
	}

```
- `void Update`にて`GenerateGround`メソッドを定義
- `playerNowPosZ`はplayerが進んだ先でのz座標
- `playerDistance`はplayerの移動距離(playerNowPosXとplayerStartPosXの差分)
- if文でボーダーを超えた時に新しいステージを１つ生成するコードを書く
    - ボーダーを超えた時点で〇〇z座標先に生成(今回の場合はz座標の20先に生成)
- 一度playerDistanceをリセット
- borderの再設定をする(※理由は後ほど下で書きます)
- 最初のplayerStartPosZも座標が0のままではダメなので、更新しなければならない。
- イメージこんな感じ！！
![無限生成 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2979202/0d38b43e-9afd-34d0-3e3c-f9e3fb717e98.png)

## 1.4 Unityで少しいじる 
1. 最終的にこのように紐付け出来ていればOK
 ![スクリーンショット 2022-12-05 16.48.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2979202/518d1079-6dda-b65d-afb2-c1f82016079d.png)
2. 注意点で、Groundsに紐付けるステージはprojectの中にある物を使い、必ずpositionを(0,0,0)に設定する。これをやらないと生成される場所がズレる。
3. 初期設定の3つのPlaneは、Hierarchy内にprefabの状態で置いといてはならないので、必ずproject内に移動した後にUnpackしPrefab化を解除する。

## 1.5 ステージ上に敵などのオブジェクトを設置する場合
ランゲームなどでステージを生成する際に、敵を同時に生成するパターンが多い。
その時は、planeと敵オブジェクトなどを同時にprefab化する必要がある。
しかし、ステージ(スケールが(1,1,2)のもの)の中に敵を入れて親子関係にしたとしたら、敵の大きさが親のスケールに依存するので形が変形してしまいます💦

手順としては、こう
1. CreateEmptyで空オブジェクト「staga]という名前を生成する。『スケール(1,1,1)、座標(0,0,0)』
2. この空オブジェクトの中に、stage,敵オブジェクトを入れ親子関係にする、
イメージこんな感じ！！
![スクリーンショット 2022-12-06 15.53.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2979202/9c34390d-a772-8999-1a5f-6676fd2fbd0a.png)

3. これをprefab化してGamaManagerのGroundsに紐付ける。
4. 子オブジェクトのz座標は全部修正しなければならない
 planeの場合は全部z座標は0,enemyはそのplaneに合わせて位置を調整する。




以上がステージ無限生成の手順でした！

## 終わりに
今回の記事はここまでになります。
最後まで読んでくださりありがとうございました。

参考にした記事はこちらになります！
https://github.com/Ryo-Nakano/YokoScrollMugen/blob/master/Assets/Scripts/GroundGenerator.cs