LoginSignup
20
20

More than 1 year has passed since last update.

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

Last updated at Posted at 2019-12-20

はじめに

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

2021/11/20追記①{
Unity標準のオブジェクトプール登場とのこと
Unity 2021から利用できるUnity標準のオブジェクトプールについて
でも多分この記事見てる人には難しい

環境

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

ヒエラルキーの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

まとめ

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

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

(まとめってなんだっけ?)

20
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
20