打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Untiy3d

本文固定连接:http://blog.csdn.net/u013108312/article/details/52723347
我们玩过的游戏中,神优化和渣优化的案例都不胜枚举,它们直接影响着玩家们的游戏体验。这篇文章将从最基础的概念开始,配合实例讲解各种优化的技巧,最终建立起一套属于自己的优化方法,完成对游戏项目的评估和优化工作。

1. 简介

1.什么是性能优化

1.常见的优化类型

1.性能优化

2.流出优化

3.体验优化

2.性能优化目标

1.游戏流畅运行

1.多种帧数标准 30-60帧
2.避免卡顿

2.游戏符合市场需要

1.硬件兼容性
2.安装包/数据包大小

2.优化常见的误区

误区1:我的游戏很简单不需要优化 (性能黑点)
误区2:优化工作尽早进行
误区3:性能优化=Debug

3.优化的两大原则

1.不过早做优化
2.用户不察觉
玩家不一定能发现欠优化的地方
玩家不一定能发现优化欠佳的地方

4.优化的组成部分

  • 脚本
    – 常见的性能黑点(正确的代码放到了错误的地方)底层!!!
    – 如何找到需要优化的代码

    1.常规循环
    2.变量的隐性调用
    3.Gmaeobject.Find 储存到变量中 gameobject Go; 查找一次就可以了。
    4.多线程

    IEnumerator Work()    {        //线程不安全        //StartCoroutine(MyIoWork1());        //StartCoroutine(MyIoWork2());        yield return StartCoroutine(MyIoWork1());        yield return StartCoroutine(MyIoWork2());    }    IEnumerator MyIoWork1()    {        for (int i = 0; i < 1000; i++)        {            System.IO.File.Delete("c:\a.zip");            yield return null;        }    }    IEnumerator MyIoWork2()    {        for (int i = 0; i < 1000; i++)        {            System.IO.File.Delete("c:\a.zip");            yield return null;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

5.数学
计算距离:Mathf.Sqrt
计算方向:Vector3.Angle
Minimize use of complex mathematical operations such as pow, sin and cos in pixel shaders.

using UnityEngine;using System.Collections;public class TestMagnitude : MonoBehaviour {    public Transform player1;    public Transform player2;    void Start()    {        Calc();    }    void Calc()    {        float distance1 = (player1.position - transform.position).magnitude;        float distance2 = (player2.position - transform.position).magnitude;        Debug.Log("Player1 is closer than Player2:" + (distance1 < distance2).ToString());        float distance11 = (player1.position - transform.position).sqrMagnitude;        float distance22 = (player2.position - transform.position).sqrMagnitude;        Debug.Log("Player1 is closer than Player2:" + (distance1 < distance2).ToString());    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
using UnityEngine;using System.Collections;public class Compare : MonoBehaviour {    public Transform player1;    public Transform player2;    // Use this for initialization    void Start () {        float angle = Vector3.Angle(player2.forward, player1.forward);        float dot = Vector3.Dot(player2.forward,player1.forward);        Debug.Log("Dot=" + dot +" Angle=" + angle);    }    // Update is called once per frame    void Update () {    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

6.Object Pool 适用于频繁操作的对象,需要缓存

using UnityEngine;using System.Collections;using System.Collections.Generic;public class TestPool : MonoBehaviour{    public Transform root;    public GameObject prefab;    public float loopCount;    public float prefabCount;    List<GameObject> objects;    // Use this for initialization    void Start()    {        objects = new List<GameObject>();        System.DateTime startTime = System.DateTime.Now;        TestCaseWaitOutPool();        System.DateTime endTime = System.DateTime.Now;        string totalMs = (endTime - startTime).TotalMilliseconds.ToString();        Debug.Log("Test case Without Pool take " + totalMs + "ms.");    }    // Update is called once per frame    void Update()    {    }    void TestCaseWaitOutPool()    {        for (int j = 0; j < loopCount; j++)        {            for (int i = 0; i < prefabCount; i++)            {                //create prefab                GameObject go = Instantiate(prefab);                //set parent                go.transform.parent = root;                //add to list                objects.Add(go);            }            for (int i = 0; i < prefabCount; i++)            {                GameObject.Destroy(objects[i]);            }            //destory prefab            objects.Clear();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

使用 Pool 之后。

using UnityEngine;using System.Collections;using System.Collections.Generic;public class TestPool : MonoBehaviour{    public SimplePool pool;    public Transform root;    public GameObject prefab;    public float loopCount;    public float prefabCount;    List<GameObject> objects;    // Use this for initialization    void Start()    {        objects = new List<GameObject>();        // with out pool        System.DateTime startTime = System.DateTime.Now;        TestCaseWaitOutPool();        System.DateTime endTime = System.DateTime.Now;        string totalMs = (endTime - startTime).TotalMilliseconds.ToString();        Debug.Log("Test case Without Pool take " + totalMs + "ms.");        // with pool        System.DateTime poolstartTime = System.DateTime.Now;        TestCaseWithPool();        System.DateTime poolendTime = System.DateTime.Now;        string pooltotalMs = (poolendTime - poolstartTime).TotalMilliseconds.ToString();        Debug.Log("Test case With Pool take " + pooltotalMs + "ms.");    }    void TestCaseWaitOutPool()    {        for (int j = 0; j < loopCount; j++)        {            for (int i = 0; i < prefabCount; i++)            {                //create prefab                GameObject go = Instantiate(prefab);                //set parent                go.transform.parent = root;                //add to list                objects.Add(go);            }            for (int i = 0; i < prefabCount; i++)            {                GameObject.Destroy(objects[i]);            }            //destory prefab            objects.Clear();        }    }    void TestCaseWithPool()    {        for (int i = 0; i < loopCount; i++)        {            List<GameObject> objectsList = pool.GetObjects((int)prefabCount);            pool.DestroyObjects(objectsList);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
using UnityEngine;using System.Collections;using System.Collections.Generic;public class SimplePool : MonoBehaviour{    public Transform root;    public GameObject prefab;    public int size;    List<GameObject> pooled;    void Start()    {        pooled = new List<GameObject>();        Prewarm();    }    void Prewarm()    {        PoolObjects(size);    }    List<GameObject> PoolObjects(int _amount)    {        List<GameObject> newPooled = new List<GameObject>();        for (int i = 0; i < _amount; i++)        {            GameObject go = Instantiate(prefab);            go.transform.parent = root;            go.SetActive(false);            newPooled.Add(go);        }        pooled.AddRange(newPooled);        return newPooled;    }    public List<GameObject> GetObjects(int _amount)    {        List<GameObject> pooledObjects = pooled.FindAll(_go => !_go.activeSelf);        if (pooledObjects.Count < _amount)        {            List<GameObject> newObjects = PoolObjects(_amount - pooledObjects.Count);            pooledObjects.AddRange(newObjects);            foreach (var go in pooledObjects)            {                go.SetActive(true);            }            return pooledObjects;        }        else        {            foreach (var go in pooledObjects)            {                go.SetActive(true);            }            return pooledObjects;        }    }    public void DestroyObjects(List<GameObject> objects)    {        for (int i = 0; i < objects.Count; i++)        {            objects[i].SetActive(false);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
Test case Without Pool take 226.1487ms.UnityEngine.Debug:Log(Object)TestPool:Start() (at Assets/TestPool.cs:26)Test case With Pool take 58.0407ms.UnityEngine.Debug:Log(Object)TestPool:Start() (at Assets/TestPool.cs:34)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

时间从 226.1487ms 缩短到58.0407ms。看看吧,这就是效率

7.Total 与 Self
在 Unity-Window-Profiler
Overview 里面的 Total,Self 观察 百分百

using UnityEngine;using System.Collections;public class TestTime : MonoBehaviour {    public GameObject prefab;    void Start()    {        //self        System.Threading.Thread.Sleep(2000);        Create(); // others    }    void Create()    {        for (int i = 0; i < 10000; i++)        {            GameObject go = GameObject.Instantiate(prefab);            GameObject.Destroy(go);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 图形与物理
    –美术资源
    对Mesh的优化

打开Game场景Stats 观察 Tris(三角形),Verts(顶点数)
模型面数
在这里可以使用插件 Simple LOD可以生成更低的模型tris减少,但是模型会变得不清晰

using UnityEngine;using System.Collections;public class TestLod : MonoBehaviour {    public float[] camDistance;    public Mesh[] lod;    SkinnedMeshRenderer skin;    // Use this for initialization    void Start () {        skin = GetComponent<SkinnedMeshRenderer>();    }    // Update is called once per frame    void Update () {        int level = GetLevel();        skin.sharedMesh = lod[level];    }    int GetLevel()    {        float distance = Vector3.Distance(transform.position,Camera.main.transform.position);        for (int i = 0; i < camDistance.Length; i++)        {            if (distance < camDistance[i])            {                return i;            }        }        return camDistance.Length;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

Material
SetPass Calls 在移动平台上最好不要超过60或者80,PC 300
2D UI 打包成图集 减少setpass calls
3D 合并材质
本质 共享材质,压缩材质

Shader
Main Maps 越少越好
这里在推荐一个插件: shader Fore
使用 Mobile 使用 图形化工具

粒子
数量 数量要限制
透明
材质 /shader 移动端:SM2.0

–物理效果
1.镜头
Clipping Planes
Occlusion Culling 默认勾上了。但是没有任何效果
打开Window-Occlusion Culling 需要bake 一下
需要bake的东西,必须是Static
Smallers Occluder 挡住后面的东西,优化做不好,就可能是负优化
在Scene场景,选中摄像机,可以设置Occlusion Culling是Edit或者Visualize。这个时候随着镜头的移动,镜头中的物体就会动态的显示了。
从屏幕上看到的点,都不会剔除掉的。
Unity 3专业版内置了一个强大的 Occlusion Culling 插件 Umbra免费的
2.光照
Bake and Probes
我们的目标就是降低:SetPass Calls
Light 选则Bake光,使用静态光。出现色差
设置:Scenes In Build Player Setting
找到Color Space 有 Linear(不支持移动端) Gamma
缺点:移动的物体不会受到光照的影响。这个时候就需要创建光照探针了。Light-Light Probe Group.记录静态光照的效果。

3.碰撞
Collider尽可能简单
控制rigidbody数量
Rigidbody检查方式,检测间隔,Collision Detection 持续的。离散型的。

4.CheckList
Simple checklist to make your game faster
对于PC建筑(取决于目标GPU)时,请记住下面的200K和3M顶点数每帧。
如果你使用内置着色器,从挑选的那些移动或熄灭类别。他们在非移动平台以及工作,但更复杂的着色器的简化和近似版本。
保持每个场景低的不同材料的数量,并共享不同的对象尽可能之间尽可能多的材料。
将Static非运动物体的属性,以允许像内部优化静态批次。
只有一个(最好是定向)pixel light影响几何体,而不是整数倍。
烘烤照明,而不是使用动态照明。
尽可能使用压缩纹理格式,以及超过32位纹理使用16位纹理。
避免使用雾在可能的情况。
使用遮挡剔除,以减少可见的几何图形的量和抽取呼叫中的有很多闭塞复杂静态场景的情况。闭塞记扑杀设计你的水平。
使用包厢到“假”遥远的几何体。
使用像素着色器或纹理组合搭配,而不是多遍方法有几个纹理。
使用half精度变量在可能的情况。
尽量减少使用复杂的数学运算,如的pow,sin并cos在像素着色器。
使用每个片段较少纹理。

  • 文件
    1.AssetBundle 创建 读取
    设置Prefab。
    AssetBundle New:env/go
using UnityEngine;using System.Collections;using System;public class TestAssetBundle : MonoBehaviour {    public string path;    public string file;    // Use this for initialization    void Start () {        StartCoroutine( Load());    }    IEnumerator Load()    {        string _path = "file:///" + Application.dataPath + path;        WWW www = WWW.LoadFromCacheOrDownload(_path,1);        yield return www;        AssetBundle bundle = www.assetBundle;        AssetBundleRequest request = bundle.LoadAssetAsync(file);        yield return request;        GameObject prefab = request.asset as GameObject;        Instantiate(prefab);        //Clean        bundle.Unload(false);        www.Dispose();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

2.移动端打包优化
缩减包体积
设置 Andriod player setting Optimlzation .NET2.0(完整版) .NET2.0 Subset(简化版)
Stripping Leve Disabled .Strip Assembies. Strip Byte Code(一般用这个就可以了) .Use Micro mscorlib

1.momo version
full
subset
2.stripping level
disabled
strip bute code
3.媒体文件
图片 psd/png/jpg
音频 ogg/mp3/wav
fbx 公用animationclip

3.跨平台开发效率优化
节省时间 utomate插件
Debug SRDebugger插件

–安装包的优化
–资源包的优化
–工作流程的优化

5.所有游戏都需要优化吗?

性能完美是我们追求的目标
不同类型的游戏对优化的侧重点不一样
优化工作会占生命周期非常大的一部分

2. Profiler

这里要多查看 官网的API 在Manual 里面搜索 Profiler
Unity3D-Window-Profiler
Profiler window
CPU Usage Area
Rendering Area
Memory Area
Audio Area
Physics Profiler
GPU Area
观察Profiler 窗口 测试代码:

using UnityEngine;using System.Collections;public class BadExampleProfiler : MonoBehaviour {    public GameObject cube;    // Update is called once per frame    void Update () {        Loop ();    }    void Loop()    {        for (int i = 0; i < 100; i++) {            float x = Random.Range (0,360f);            float y = Random.Range (0,360f);            float z = Random.Range (0,360f);            Vector3 randomVector = new Vector3 (x,y,z);            Object.Instantiate (cube, randomVector, Quaternion.Euler(randomVector));        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

3. 图形优化

4. 文件优化

5. 结束

建立一套属于自己的优化方法
1.确定优化目标 (帧数)(卡顿)
2.选择合适的工具(Profiler)(SRDebugger)
3.找到性能瓶颈(脚本)(图形)(绕开)
4.无法解决(找手册)(问google)(绕开)
5.经常与团队沟通

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
使用Multiplayer Networking做一个简单的多人游戏例子
Unity3D脚印4——GameObject
unity3d 尝试 基于地理定位的 增强现实
[Unity UGUI]各种优化效果
unity编辑器常用功能
Roll a ball
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服