Help us understand the problem. What is going on with this article?

[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

まとめ

負荷軽減として使われるオブジェクトプールを簡単に作れるので是非活用してみてください。

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

NekoCan
UnityとかProcessingらへんが生息域の学生です。 リンク「ゲーム投稿サイトUnityRoom」 ねこですよろしくお願いします
https://unityroom.com/users/uray7t6i1xoh823cqwj9
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした