Open1

Unityでオブジェクトを大量に出すための実装。

MachiaWorksMachiaWorks

概要

オブジェクトを大量に描画したい

ネックはなにか

ネックは「GameObjectを利用している」こと。
GameObjectは最初から描画やモデル定義の実装がされており、各処理はマルチコア(マルチスレッド)で処理されるため速度面で有利。
ただ、毎フレームの処理数が多く、かつメモリ割り当ても負荷がかかるため、
オブジェクト管理を別に行い「描画だけを担当させる」実装が必要とする。

ちなみにプロファイラで確認してみると、メモリ割り当てやCPU使用率で割と負荷を食う。
特にメモリ割り当て。

よって、配列等でオブジェクトのメモリは確保しつつ、
描画だけ担当させる処理が必要とする。

実装

DrawMesh関数を利用する。

https://docs.unity3d.com/ScriptReference/Graphics.DrawMesh.html

ただ、これだと描画順やカリング等の設定をよく考えないと、
自分の想定する描画にならないため注意が必要。

確認事項

マテリアルのバッチが行われるかは要確認。

同じマテリアルであればバッチ処理によって一気に描画が発生するが、
違うマテリアル、もしくは同じマテリアルでもUV座標を変更した場合、
利用バッチ数が増加するため注意。(Batches/Saved by Batchingの数が増減)
アニメーションさせる必要のないテクスチャはそのままで描画するほうがバッチ処理が効率的に動く。

コード

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class DynamicMeshUsingDrawMesh : MonoBehaviour {
    [SerializeField]
    Material m_mat;
    [SerializeField]
    Material m_mat2;
    [SerializeField]
    Material m_mat3;
    [SerializeField]
    Material m_mat4;
    Mesh m_mesh;
    public float buf_x=1.0f;
    public float buf_y=1.0f;
    public float offset_x=0.0f;
    public float offset_y=0.0f;
    private int bullet_num=1024;
    private Vector2[] uv_base;
    private Vector2[] uv_result=new Vector2[4];
    private float delta = 0.01666667f;
    private float _time;
    private float _pre_time;
    public class status{
        public float x;
        public float y;
        public float z;
        public bool isAlive;
        public int count;
        public float dx;
        public float dy;
        public status(){
            x=0.0f;
            y=0.0f;
            isAlive=false;
            count=0;
            dx=0.0f;
            dy=0.0f;
        }
    };
    private Vector2 offset;
    public status[] bullet_status;
    private MaterialPropertyBlock _materialPropertyBlock;
    void Start () {
        // 動的なMesh生成
        m_mesh = new Mesh();
        m_mesh.vertices = new Vector3[] {
            new Vector3 (-0.125f, -0.25f),
            new Vector3 (0.125f, -0.25f),
            new Vector3 (0.125f, 0.25f),
            new Vector3 (-0.125f, 0.25f),
        };
        uv_base = new Vector2[]
        {
            new Vector2(0,0),
            new Vector2(0.25f,0),
            new Vector2(0.25f,1.0f),
            new Vector2(0,1.0f),
        };
        m_mesh.triangles = new int[] {
            0, 1, 2,
            0, 2, 3,
        };
        m_mesh.RecalculateNormals();
        m_mesh.RecalculateBounds();
        bullet_status = new status[bullet_num];
        for(int i=0;i<bullet_num; i++){
            bullet_status[i] = new status();
            bullet_status[i].dx = Random.Range(-0.05f, 0.05f);
            bullet_status[i].dy = Random.Range(-0.05f, 0.05f);
            bullet_status[i].isAlive = true;
            bullet_status[i].count=i;
            bullet_status[i].z = i*0.00001f;
        }
        //_materialPropertyBlock = new MaterialPropertyBlock();
        _time=Time.deltaTime;
        _pre_time=Time.deltaTime;
    }
    private void Update()
    {
        bool isMove = false;
        _time += Time.deltaTime;
        if( _time> delta){
            isMove =true;
            _time=0.0f;
            //Debug.Log(_time);
        }
        selfUpdate(isMove);
    }
    public void selfUpdate(bool isMove){
        //_materialPropertyBlock.Clear();
        Material tmp = m_mat;
 
        for(int i=0; i<bullet_num; i++ ){
            //描画命令
            if( bullet_status[i].isAlive==true){
 
                //UV座標の更新
                for( int j=0; j<4; j++){
                    {
                        uv_result[j].x = uv_base[j].x;// + (bullet_status[i].count%4*0.25f);
                    }
                    //Y座標は変化なし
                    uv_result[j].y= uv_base[j].y;
                }
                //UV座標をアニメさせる
                m_mesh.uv = uv_result;
                if( isMove== true){
                    bullet_status[i].x += bullet_status[i].dx;
                    bullet_status[i].y += bullet_status[i].dy;
                }
                if( bullet_status[i].count%4 == 0)
                    tmp = m_mat;
                else if( bullet_status[i].count%4 ==1)
                    tmp = m_mat2;
                else if( bullet_status[i].count%4 == 2)
                {
                    tmp = m_mat3;
                }
                else if( bullet_status[i].count%4 == 3){
                    tmp = m_mat4;
                }
                else{}
/*
                offset.x = bullet_status[i].count%4*0.25f;
                offset.y = 0.0f;
                */
                //_materialPropertyBlock.SetTextureOffset("_MainTex", offset);
                //_materialPropertyBlock.SetVector("_MainTex_ST", new Vector4(1.0f, 1.0f, bullet_status[i].count%4*0.25f, 0.0f));
                Graphics.DrawMesh(m_mesh, new Vector3(bullet_status[i].x,
                                                bullet_status[i].y,
                                                bullet_status[i].z),
                                                Quaternion.AngleAxis(20*(float)(bullet_status[i].count%18), new Vector3(0,0,1)), //Quaternion.identity,
                                                tmp, 0
                                                //,null, 0, _materialPropertyBlock, false, false
                                                );
            }
            //カウント
            if( isMove == true){
                bullet_status[i].count++;
                if( bullet_status[i].count>0)
                    bullet_status[i].isAlive=true;
                //画面外処理
                if( bullet_status[i].x <-5.0f || bullet_status[i].x > 5.0){
                    bullet_status[i].x=0.0f;
                    bullet_status[i].y=0.0f;
                    bullet_status[i].count=-30;
                    bullet_status[i].isAlive=false;
                }else if( bullet_status[i].y <-5.0f || bullet_status[i].y > 5.0){
                    bullet_status[i].x=0.0f;
                    bullet_status[i].y=0.0f;
                    bullet_status[i].count=-30;
                    bullet_status[i].isAlive=false;
                }
            }
        }
    }
}