Unity3D 性能优化总结

优化相关前提

  1. Unity游戏安装包大/运行卡的原因: mono虚拟机(跨平台基础、简洁开发提高效率)

  2. Drawcall

    • https://zhuanlan.zhihu.com/p/26386905
    • https://www.zhihu.com/question/36357893
    • SetPasscall DrawCall和Batches
      http://www.manew.com/4702.html

资源优化

  1. 资源优化参考标准

    • Mesh
      动态模型:面片数<3000 、材质数<3 、骨骼数<50
      静态模型:顶点数<500
    • Audio
      长时间音乐(背景音乐)压缩格式 mp3
      短时间音乐(音效)非压缩格式 wav
      http://blog.youkuaiyun.com/u012565990/article/details/51794486
    • Texture
      贴图长宽<1024
    • Shader
      尽量减少复杂数学运算
      减少discard操作
  2. 模型优化
    减少面数和顶点数

  3. 贴图优化
    贴图合并,减少DrawCall的调用次数

  4. 减少冗余资源和重复资源

    • Resources 目录下的资源不管是否被引用,都会打包进安装包
      不使用的资源不要放在Resources目录下
    • 不同目录下的相同资源文件,如果都被引用,那么都会打包进资源包,造成冗余
      保证同一个资源文件在项目中只存放在一个目录位置
  5. 资源监测与分析
    https://www.uwa4d.com/#assetbundle
    腾讯、UWA 进行性能检测和优化

渲染优化(GPU)

  1. CPU GPU分工
    https://www.zhihu.com/question/21475727
  2. LOD - 层级细节
    在LOD2和LOD1可以修改贴图的品质,Cast Shadow和 ReceiveShadow可以移除
  3. Occlusion Culling - 遮挡剔除
  4. Lightmapping - 光照贴图
  5. 合并Mesh
using UnityEngine;
using UnityEditor;

public class MeshCombiner : MonoBehaviour
{
    //验证函数
    [MenuItem("Tool/MeshCombine", true, 10)]
    static bool MeshCombineValidate()
    {
        if (Selection.gameObjects.Length > 0)
            return true;
        else
            return false;
    }

    /// <summary>
    /// 给hierachy窗口中的物体添加一个右键选项
    /// 合并物体Mesh的方法MeshCombine()
    /// 
    /// 使用须知:需要将要合并mesh的物体全都放在一个空物体下
    /// </summary>
    [MenuItem("Tool/MeshCombine", false, 10)]
    public static void MeshCombine()
    {
        //获取当前选中物体
        foreach (GameObject go  in Selection.gameObjects)
        {
            //记录当前位置iveGameObje
            Vector3 goPosition = go.transform.position;

            //避免生成新物体时产生位置偏移
            go.transform.position = Vector3.zero;


            //获取子物体的所有mesh
            MeshFilter[] meshFilters = go.GetComponentsInChildren<MeshFilter>();
            //创建mesh合并的实例
            CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];
            //将meshFilters的mesh设置给combineInstances
            for (int j = 0; j < meshFilters.Length; j++)
            {
                //sharedMesh是公用的,是引用传递。而mesh是值传递,是各自拥有的实例。sharedMesh改变,则所有的使用到此mesh的都改变。
                combineInstances[j].mesh = meshFilters[j].sharedMesh;
                //将meshFilters[i]的局部转世界坐标的矩阵给combineInstances[i].transform,从而确定mesh在世界空间的坐标
                combineInstances[j].transform = meshFilters[j].transform.localToWorldMatrix;
            }

            //实例化一个新的mesh来获得合并出来的mesh
            Mesh mesh = new Mesh();
            mesh.CombineMeshes(combineInstances);             //将combineInstances合并成一张mesh
            mesh.name = go.name;                                            //设置mesh显示的name


            //生成一个名字相同的物体,添加MeshRenderer和MeshFilter组件
            GameObject gameObject = new GameObject(mesh.name, new System.Type[] { typeof(MeshRenderer), typeof(MeshFilter) });
            //将物体的生成注册到操作记录中
            Undo.RegisterCreatedObjectUndo(gameObject, "Create Gameobject");
            gameObject.transform.position = goPosition;
            MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
            //将合并出来的mesh给新物体的MeshFilter
            meshFilter.sharedMesh = mesh;

            //实例化一个黑色材质给这个物体
            Material material = new Material(Shader.Find("Standard"))
            {
                name = gameObject.name + " " + "Material",
                color = Color.black
            };
            gameObject.GetComponent<Renderer>().material = material;

            //回到原来位置
            go.transform.position = goPosition;
            Undo.DestroyObjectImmediate(go.gameObject);
        }

    }
}

代码优化(CPU)

  1. https://drive.google.com/file/d/0B__1zp7jwQOKcHJIM3N2TklqdEU/view?pageId=107075542061258892781
  2. 对象资源池 - Object Pooling

Shoot.cs

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

public class Shoot : MonoBehaviour
{
    public GameObject bulletPrefab;

    private BulletPool bulletPool;
    // Start is called before the first frame update
    void Start()
    {
        bulletPool = GetComponent<BulletPool>();
    }

    
    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // GameObject go = GameObject.Instantiate(bulletPrefab, transform.position, transform.rotation );
            GameObject go = bulletPool.GetBullet();
            if (go != null)
            {
                go.transform.position = transform.position;
                go.GetComponent<Rigidbody>().velocity = transform.forward * 10;
                //Destroy(go, 3);
                StartCoroutine(DestroyBullet(go));
            }
        }
    }
    
    IEnumerator DestroyBullet(GameObject go)
    {
        yield return new WaitForSeconds(3);
        go.SetActive(false);
    }
}

BulletPool.cs

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

public class BulletPool : MonoBehaviour
{
    public int poolCount = 10;

    public GameObject bulletPrefab;

    private List<GameObject> bulletList = new List<GameObject>();

    void InitPool()
    {
        for (int i = 0; i < poolCount; i++)
        {
            GameObject go = GameObject.Instantiate(bulletPrefab);
            bulletList.Add(go);
            go.SetActive(false);
            go.transform.parent = this.transform;
        }
    }

    public GameObject GetBullet()
    {
        foreach (GameObject go in bulletList)
        {
            if (go.activeInHierarchy == false)
            {
                go.SetActive(true);
                return go;
            }
        }
        return null;
    }

    // Start is called before the first frame update
    void Start()
    {
        InitPool();
    }

}

此对象池过于简单,实用性更强可参照对象池模式

带宽优化

  1. 减少纹理大小
  2. 利用缩放

其他优化

  1. 优化工具
    UWA Game Optimization Toolkit
  2. 文章
    使用Unity开发安卓游戏 应该如何进行性能优化
    http://gad.qq.com/article/detail/17252
  3. 编译性能优化
    http://gad.qq.com/article/detail/27927
    http://forum.china.unity3d.com/thread-13028-1-1.html

问题

  1. Stats中的render thread(渲染时间)和FPS(帧率)之间的关系
    假设渲染时间 render thread 为 0.4ms ,是否在渲染结束每帧之后会有空余时间,那么不考虑硬件支持,极致帧率应为 1000/0.4=2500FPS 么?

  2. 创建一个 cube , draw calls 和 batches 增加5 , 删除掉,在创建一个 sphere , draw calls 和 batches 也是增加5。为什么创建一个 cube 不删除,在创建一个 sphere , draw calls 和 batches 只增加了9 ? 同时增加两个物体, draw calls 和 batches 只增加了8?

  3. CPU和GPU都负责计算,为什么要用GPU负责渲染而不用CPU?
    CPU(中央处理器,Central Processing Unit)运行的是复杂指令,可以进行各种运算,所谓样样精样样松。
    而GPU(图形处理器,Graphic Processing Unit)指令集简单,工程师就可以将大部分晶体管投入数据运算,所以GPU在图形处理方面要比CPU快很多,一个专门的图形的核心处理器。
    显卡为了加强显示图像的能力,省掉了很多与显示功能无关的晶体管,所以显卡只能显示画面,但是在显示画面这个任务上,比cpu快很多。

  4. 合并mesh之后,子物体的材质是不能应用于合并出来的物体的,怎么办?合并材质贴图?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值