2017-07-13 15:36:27 jxw167 阅读数 682

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

Unity引擎优化技术,无非涉及到三点:CPU优化,GPU优化,内存优化。

先谈谈内存优化:大概可以分成三大部分:

   1、 Unity3D内部的内存

   2、 Mono的托管内存

   3、 引入的DLL或者第三方DLL所需要的内存。

其中Unity3D内部的内存包括如下:

  • 资源:纹理、网格、音频等等
  • GameObject和各种组件。
  • 引擎内部逻辑需要的内存:渲染器,物理系统,粒子系统等等
   我们接下来通过代码执行的时间方式给读者介绍一下关于组件的使用效率问题,代码如下所示:
using UnityEngine;

public class CachedMB : MonoBehaviour
{


	Transform _transform;
	public Transform transform
	{
		get { return _transform ?? (_transform = base.transform); }
	}


	//for testing
	public Transform uncachedTransform
	{
		get { return base.transform; }
	}

	Rigidbody _rigidbody;
	public Rigidbody rigidbody
	{
		get { return _rigidbody ?? (_rigidbody = base.GetComponent<Rigidbody>()); }
	}

	Camera _camera;
	public Camera camera
	{
		get { return _camera ?? (_camera = base.GetComponent<Camera>()); }
	}

	Light _light;
	public Light light
	{
		get { return _light ?? (_light = base.GetComponent<Light>()); }
	}

	private Animation _animation;
	public Animation animation
	{
		get { return _animation ?? (_animation = base.GetComponent<Animation>()); }
	}


	private ConstantForce _constantForce;
	public ConstantForce constantForce
	{
		get { return _constantForce ?? (_constantForce = base.GetComponent<ConstantForce>()); }
	}

	private Renderer _renderer;
	public Renderer renderer
	{
		get { return _renderer ?? (_renderer = base.GetComponent<Renderer>()); }
	}

	private AudioSource _audio;
	public AudioSource audio
	{
		get { return _audio ?? (_audio = base.GetComponent<AudioSource>()); }
	}


	private GUIText _guiText;
	public GUIText guiText
	{
		get { return _guiText ?? (_guiText = base.GetComponent<GUIText>()); }
	}


	private GUITexture _guiTexture;
	public GUITexture guiTexture
	{
		get { return _guiTexture ?? (_guiTexture = base.GetComponent<GUITexture>()); }
	}


	private NetworkView _networkView;
	public NetworkView networkView
	{
		get { return _networkView ?? (_networkView = base.GetComponent<NetworkView>()); }
	}


	private Collider _collider;
	public Collider collider
	{
		get { return _collider ?? (_collider = base.GetComponent<Collider>()); }
	}


	private HingeJoint _hingeJoint;
	public HingeJoint hingeJoint
	{
		get { return _hingeJoint ?? (_hingeJoint = base.GetComponent<HingeJoint>()); }
	}


	private ParticleEmitter _particleEmitter;
	public ParticleEmitter particleEmitter
	{
		get { return _particleEmitter ?? (_particleEmitter = base.GetComponent<ParticleEmitter>()); }
	}


	private ParticleSystem _particleSystem;
	public ParticleSystem particleSystem
	{
		get { return _particleSystem ?? (_particleSystem = base.GetComponent<ParticleSystem>()); }
	}


	private GameObject _gameObject;
	public GameObject gameObject
	{
		get { return _gameObject ?? (_gameObject = base.gameObject); }
	}


	private string _tag;
	public string tag
	{
		get { return _tag ?? (_tag = base.tag); }
		set { _tag = value; base.tag = value; }
	}


	private string _name;
	public string name
	{
		get { return _name ?? (_name = base.name); }
		set { _tag = value; base.tag = value; }
	}

}
接下来开始写测试代码案例:
using UnityEngine;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;

public class CacheTest : CachedMB
{
	const int ITERATIONS = 1000000;
	// Use this for initialization

	Transform cached;

	IEnumerator Start()
	{
		cached = uncachedTransform;

		for (; ; )
		{
			yield return null;
			if (!Input.GetKeyDown(KeyCode.T)) continue;

			var sw1 = Stopwatch.StartNew();
			{
				Transform trans1;
				for (int i = 0; i < ITERATIONS; i++)
					trans1 = GetComponent<Transform>();
			} sw1.Stop();


			var sw2 = Stopwatch.StartNew();
			{
				Transform trans2;
				for (int i = 0; i < ITERATIONS; i++)
					trans2 = transform;
			} sw2.Stop();


			var sw3 = Stopwatch.StartNew();
			{
				Transform trans3;
				for (int i = 0; i < ITERATIONS; i++)
					trans3 = cached;
			} sw3.Stop();

			var sw4 = Stopwatch.StartNew();
			{
				Transform trans4;
				for (int i = 0; i < ITERATIONS; i++)
					trans4 = uncachedTransform;
			} sw4.Stop();

			UnityEngine.Debug.LogWarning(ITERATIONS + " iterations");
			UnityEngine.Debug.LogWarning("GetComponent " + sw1.ElapsedMilliseconds + "ms");
			UnityEngine.Debug.LogWarning("MonoBehaviour " + sw4.ElapsedMilliseconds + "ms");
			UnityEngine.Debug.LogWarning("CachedMB " + sw2.ElapsedMilliseconds + "ms");
			UnityEngine.Debug.LogWarning("Manual Cache " + sw3.ElapsedMilliseconds + "ms");
		}
	}
}

执行结果如下所示:


通过显示的结果,我们知道,GetComponent是比较耗费时间的,在游戏开发中尤其是在每帧调用中少用。Manual Cache是耗费时间最少的,应该在游戏开发中尽量多用。
  


2018-04-06 22:46:06 java3182 阅读数 1310

验证结果

结果是在Unity编辑环境下测试10次的平均值,单位是秒,Unity版本是5.6.5

对象数量 搜索方法 执行1000次 运行10,000次 100,000次执行
100 Find 0.002197409秒 0.0190155秒 0.1939484秒
100 FindGameObjectWithTag 0.0002516746秒 0.001338196秒 0.01151428秒
100 FindObjectOfType 0.2374741秒 2.544969秒 5.801984秒
1000 Find 0.037534秒 0.357311秒 3.543825秒
1000 FindGameObjectWithTag 0.000655秒 0.002315秒 0.018519秒
1000 FindObjectOfType 0.157907秒 1.552248秒 16.20919秒
10000 Find 0.429361秒 4.274960秒 42.64132秒
10000 FindGameObjectWithTag 0.000346秒 0.001686秒 0.016235秒
10000 FindObjectOfType 1.375987 秒 14.24521秒 太长省略

验证脚本如下

using System;
using UnityEngine;

/// <summary>
/// Find 系列方法执行速度测试脚本
/// </summary>
public sealed class FindTest : MonoBehaviour{

    public int LoopCount        = 1000;
    public int TestCount        = 10;
    public int NumGameObjects   = 100;

    private void Awake(){

        var types = new PrimitiveType[]{
            PrimitiveType.Capsule, 
            PrimitiveType.Cube,             
            PrimitiveType.Cylinder, 
            PrimitiveType.Plane, 
            PrimitiveType.Quad,             
            PrimitiveType.Sphere, 
        };

        for (int i = 0; i < NumGameObjects; i++){
            GameObject.CreatePrimitive(types[UnityEngine.Random.Range(0, types.Length)]);
        }
    }

    private void OnGUI()
    {
        DrawButton(0,   "Find",                     () => GameObject.Find("Player")                     );
        DrawButton(100, "FindGameObjectWithTag",    () => GameObject.FindGameObjectWithTag("Player")    );
        DrawButton(200, "FindObjectOfType",         () => GameObject.FindObjectOfType<BoxCollider>()    );
    }

    private void DrawButton(float buttonY, string buttonText, Action findAct)
    {
        if (GUI.Button(new Rect(0, buttonY, Screen.width, 100), buttonText))
        {
            var sum = 0f;
            for (int i = 0; i < TestCount; i++)
            {
                var time = Time.realtimeSinceStartup;
                for (int j = 0; j < LoopCount; j++)
                {
                    findAct();
                }
                time = Time.realtimeSinceStartup - time;
                sum += time;
            }
            var avg = sum / TestCount;
            Debug.Log(avg);
        }
    }
}

结论

通过上面测试的结果表明:Find方法和FindObjectOfType方法与查找的对象数量和查找次数成正比,即对象数量越多,查找次数越多,方法执行的越慢。特别是FindObjectOfType方法是搜索整个游戏的对象中的所有组件,所以很慢。
而FindGameObjectWithTag方法,对象数量和查找次数增加,执行的效率没有明显的变慢。因此,如果游戏对象的查找速度是瓶颈的话,应该改用FindGameObjectWithTag方法提高执行效率。

2011-11-15 09:21:03 tank2308635 阅读数 23

 

优化心得   

链接:http://www.cocoachina.com/bbs/read.php?tid-56333.html

 

第一个3D游戏开始优化. 写一点心得, 留作备份.  为什么写在论坛里. 因为我做优化的时候没搜到, 所以分享一下.

2011.5.1
1. 测试时候注意看Stats很重要, drawCalls 上限35(具体怎么降低DrawCalls,请自己搜搜)

1.1. V大说可以用GUI插件把所有gui的drawCall整合成1次, 还没问具体怎么办.  

2. Batched越高越好, 整合渲染, 

2.1. 具体还不知道怎么搞

3. 灯光能不用就不用, 阴影用个面代替, 投射阴影和实时灯光,简直就是frameKller.

4. 模型尽量减少面. 这个和drawCall相辅相成. 我上个游戏满屏只有400面, drawCalls都干到快90了,还一样能跑到30帧在真机上. 当然没有复杂的运动是另个方面.

5. 模型尽量不要分开, 比如我现在坦克游戏, 车身和履带是分开的, 分别都是一次drawCalls,(打英文太烦了,以后drawCall 翻译为"绘图调用") 做到一起,就是一次

5.1. 用CombineInstance似乎也能实现, 还没细研究.

6. http://www.unifycommunity.com/wiki/index.php?title=IPhone_Optimization_Tips     这个地址说了很多实用技巧, 总结如下:

6.1. 别用ragdoll, 我没用过,似乎是纸娃娃系统

6.2. 骨骼很重要, 暂时没用过

6.3. 最终输出的时候,在unity - edit - project setting - player 里, 把 other settings - script call optimization 改成 快速, 但没有例外. fast but no exceptions

6.4. 注意音乐设置,暂时没用过

6.5. unity - edit - project setting - time 里, 把 maximum allowed timestep 改成0.1

6.6 绘图调用次数保持20. 估计帖子比较老

6.7 一些脚本的优化手段,自己去看把, 写的了代码,看不懂英文,可以自 杀了.

7. http://wg2009perfect.blog.163.com/blog/static/127997663201071534837653/   整合技巧, 看看不错

8. 整合模型, 在max里和程序里都可以,我喜欢max,谁叫我是美工呢.
2015-11-18 19:20:18 heyuchang666 阅读数 3857

Unity3d 支持的图形格式和大小优化

主要参考了官方文档,Unity3D支持的图形文件格式有 PSD, TIFF, JPG, TGA, PNG, GIF,

     BMP, IFF, PICT(但根据本人的亲手测试,Unity3D对高压缩游戏贴图DDS是具有良好的解析功能的,

    但DDS具体也分很多格式类型,我仅对游戏中提取出的DDS进行了测试)。

        PSD中层在导入Unity之后将会自动合并,但原PSD文件的层则作为保留,不必介意在PSD里面存放有多个图层。

        纹理尺寸是一个重点注意事项,因为非2的幂次尺寸纹理用于GUI纹理时使用会非常好,然而,在其他方面,该纹理将被转化为一个非压缩的RGBA32位格式,这会占用更多的显存,并且大大降低加载速度。但非2的幂次尺寸纹理可以在导入设置中使用Scale NonPower2 Sizes Up将非2的幂次纹理资源放大到2的幂次。

       这样可以让该图形被当作其他2的幂次尺寸纹理那样使用,被压缩之后占用较小的显存空间,并有较快的加载速度!

        还有作为性能的考虑,模型贴图要尽量使用Mip Maps。使用该Mip maps将多使用33%的内存,

    但能带来很大的性能上的优势。距离相机较远的物体将使用较小的纹理。

MIP map(有时候拼写成mipmap):是一种电脑图形图像技术,用于在三维图像的二维代替物中达到立体感效应。MIP map技术与材质帖图技术结合,根据距观看者远近距离的不同,以不同的分辨率将单一的材质帖图以多重图像的形式表现出来并代表平面纹理:尺寸最大的图像放在前面显著的位置,而相对较小的图像则后退到背景区域。每一个不同的尺寸等级定义成一个MIP map水平。MIP map技术帮助避免了不想要的锯齿边缘(称为锯齿状图形)在图像中出现,这种锯齿状图形可能是由于在不同分辨率下使用bit map图像产生的。

        最后在非地面和地板模型对象上,尽量不要使用Anisotropic filtering


2015-10-17 13:57:24 github_32062421 阅读数 6798

关于Unity3d 5.1.1f1 DrawCall优化。

在做动作游戏中总会有很多重复的特效出现。比如:受击特效,治疗特效等公用特效。我们的程序跟我们说为了减少DC(DrawCall),要求说受击后播放了受击特效后,再次受击是不会再次播放一个受击特效的。要等第一个受击特效播放完后如果还受击了那再播放受击。

一开始我是拒绝的,但是因为我们项目DC的确蛮高了。后面我还是答应了。

最后测试后打击感差了2的1024次方。最后为了解决这个问题,我研究了Unity3d粒子系统。并做了测试。


这是我们的受击特效。DC是5个

如果创建N个的话 DC会怎么样呢?


可以看到Batches 1257....难怪卡死了....


这个特效是由5个例子特效组成的。我把每个粒子特效的OrderInLayer重新设置。2000,2001,2002,2003,2004

确保每个粒子的OrderInLayer不同.不要重复.


接下来就是见证奇迹的时候了......0 0、


可以看到重复创建的特效都跑到Saved by batching了。

大家理解了吗?特效又有新的事情要做了。


PS:关于单个特效也是能优化的。原理是一样的


这是一个散射子弹的特效。看这个特效的结构是相同的特效复制了5份换成了不同的角度。

我还是一样的把OrderInLayer重新设置了。不过每个sq_skill_3_ball,sq_skill_3_fire,sq_skill_3_light都是设置成一样的。

再一次见证奇迹的时刻- -、

没修改前DC的顶峰会随时间在12-16徘徊。

修改后DC的顶峰稳定在8.

你懂了吗?快叫特效去改吧偷笑

Unity3D 优化相关

阅读数 10

Unity全面优化

阅读数 29

没有更多推荐了,返回首页