[Unity]簡単にオブジェクトプールを作る

#はじめに
こんな記事見てるくらいだから初心者ではないって思って書いてるんで、初歩的なことは省いてます。

2021/11/20追記①｛
Unity標準のオブジェクトプール登場とのこと
[Unity 2021から利用できるUnity標準のオブジェクトプールについて](https://blog.yucchiy.com/2021/04/objectpool-in-unity-2021/)
でも多分この記事見てる人には難しい
｝

#環境
Unity 2019.2.11f

#オブジェクトプールとは
**大量に生成&破壊**する物（弾幕ゲームの弾とか）を予め生成しておく。（プール）
プーリングしたオブジェクトを使い回すことで、生成&破壊の処理をしなくなり動作が軽くなる。

って感じ

#Q.どう簡単なんですか
**A.リストとか使わないあたり**
プールの保存及び探索をGameObjectの子要素にしているので、特に難しいことしてない
あと重くない（どれくらい軽いかはわからない）

###内容はこれだけ

```Pool.cs
Transform pool; //オブジェクトを保存する空オブジェクトのtransform
void Start(){
 pool = new GameObject("名前").transform;
}

void GetObject(GameObject obj , Vector3 pos , Quaternion qua){
 foreach(Transform t in pool) {
    //オブジェが非アクティブなら使い回し
    if( ! t.gameObject.activeSelf){ 
      t.SetPositionAndRotation(pos,qua); 
      t.gameObject.SetActive(true);//位置と回転を設定後、アクティブにする
    } 
  } 
  //非アクティブなオブジェクトがないなら生成
  Instantiate(obj , pos , qua , pool);//生成と同時にpoolを親に設定
} 
```
#サンプルコード
とりあえずサンプルとして「Player」というプレイヤー用のスクリプトと「Bullet」という弾スクリプトがあるとして...

**弾用のスクリプト**

```Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    void Update()
    {
        //右方向に移動
        transform.position += transform.right * 10 * Time.deltaTime ;
    }

    private void OnBecameInvisible()
    {
        //画面外に行ったら非アクティブにする
        gameObject.SetActive(false);
    }
}

```
**プレイヤー用のスクリプト**

```Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    //生成する弾
    [SerializeField] GameObject bullet = null;

    //弾を保持（プーリング）する空のオブジェクト
    Transform bullets;
    void Start()
    {
        //弾を保持する空のオブジェクトを生成
        bullets = new GameObject("PlayerBullets").transform;
    }

    void Update()
    {
        //まわれまーわれメリーゴーランド
        transform.Rotate(0, 0, 0.5f);

        //弾生成関数を呼び出し
        InstBullet(transform.position, transform.rotation);
    }

    /// <summary>
    /// 弾生成関数
    /// </summary>
    /// <param name="pos">生成位置</param>
    /// <param name="rotation">生成時の回転</param>
    void InstBullet(Vector3 pos,Quaternion rotation)
    {
        //アクティブでないオブジェクトをbulletsの中から探索
        foreach(Transform t in bullets)
        {
            if (!t.gameObject.activeSelf)
            {
                //非アクティブなオブジェクトの位置と回転を設定
                t.SetPositionAndRotation(pos, rotation);
                //アクティブにする
                t.gameObject.SetActive(true);
                return;
            }
        }
        //非アクティブなオブジェクトがない場合新規生成

        //生成時にbulletsの子オブジェクトにする
        Instantiate(bullet, pos, rotation, bullets);
    }
}
```
こんなかんじ
![ObjectPoolBase.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542691/d8515c02-54e7-5c11-99e0-bac6c23ae03b.gif)

ヒエラルキーのBulletがアクティブと非アクティブを繰り返していることがわかります
#ちょっと応用（返り値を設定する）
返り値にクラス型を用いる
いつのバージョンからか、Instantiate()の引数にクラスが入れられるようになっていたのでそれを利用しています。
が、結局GetComponet<>()使うことになるので負荷軽減としてどうなんだって思いますがね...
関数の返り値がBullet型なのでそれを利用してpublicのspeedを変えています。
（Bulletのspeedをただの変数にしなかったのはインスペクタに表示されるのが嫌だっただけです）

**弾用のスクリプト**

```Bullets.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    //Playerから入力
    public float speed { set; get; }

    void Update()
    {
        //右方向に移動
        transform.position += transform.right * speed * Time.deltaTime ;
    }

    private void OnBecameInvisible()
    {
        //画面外に行ったら非アクティブにする
        gameObject.SetActive(false);
    }
}
```

```Player.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    //生成する弾
    [SerializeField] Bullet bullet = null;

    //弾を保持（プーリング）する空のオブジェクト
    Transform bullets;
    void Start()
    {
        //弾を保持する空のオブジェクトを生成
        bullets = new GameObject("PlayerBullets").transform;
    }

    void Update()
    {
        //まわれまーわれメリーゴーランド
        transform.Rotate(0, 0, 0.5f);

        //弾生成関数を呼び出し
        Bullet b = InstBullet(transform.position, transform.rotation);
        b.speed = Random.Range(5, 20f);
    }

    /// <summary>
    /// 弾生成関数
    /// </summary>
    /// <param name="pos">生成位置</param>
    /// <param name="rotation">生成時の回転</param>
    Bullet InstBullet(Vector3 pos,Quaternion rotation)
    {
        //アクティブでないオブジェクトをbulletsの中から探索
        foreach(Transform t in bullets)
        {
            if (!t.gameObject.activeSelf)
            {
                //非アクティブなオブジェクトの位置と回転を設定
                t.SetPositionAndRotation(pos, rotation);
                //アクティブにする
                t.gameObject.SetActive(true);
                return t.GetComponent<Bullet>();
            }
        }
        //非アクティブなオブジェクトがない場合新規生成

        //生成時にbulletsの子オブジェクトにする
        return Instantiate(bullet, pos, rotation, bullets);
    }
}
```


こんなかんじ
って言っても速度をPlayerから操作できるようにしただけですが
![ObjectPool.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542691/dfdd58c3-c02b-bba0-bda4-4c786966730c.gif)

#まとめ
例えば非アクティブのオブジェクトを別の親オブジェクトに移動させるなどで探索する負荷は減りますが、
親子関係の変更は負荷が高いと聞いたので大量にやるオブジェクトではあまり推奨できないなぁ　と

アクティブ用と非アクティブ用のリストを作って親子関係に変更を加えないのがシンプルで負荷が少なそうだなぁと思ったという追記2021/09/24
負荷軽減として使われるオブジェクトプールを簡単に作れるので是非活用してみてください。

（まとめってなんだっけ？）