本篇文章的内容以各类tips为主,不间断更新html
2019/10/30 最近更新:使用质数做为延迟量可避免最小公倍数出现的状况
数组
===========================缓存
Unity3D The Blacksmith Demo部份内容学习dom
Viking Village维京村落demo中的地面积水效果编辑器
Viking Village维京村落demo中的粒子距离消隐ide
Unity AngryBots愤怒的机器人demo研究post
===========================
解决摄像机旋转约束问题(RotateDelta,RotateClamp)
运行时修改代码不自动刷新
而后按Ctrl+R手动刷新,或者在Project面板下右键刷新
在代码中指定,暂停当前帧
Debug.Break或者Debug.LogError而后在控制台勾选报错暂停。
快速查看非闭合的MeshCollider
关闭MeshRenderer组件,便可显示出Collider的网格。
去除当前脚本警告
#pragma warning disable 0168
此示例能够清除变量未使用警告,具体对应警告ID。而且该预编译指令只对本文件有效。
也能够包裹使用
#pragma warning disable 0168 .... #pragma warning restore 0168
一种更为简短的惰性字段初始化写法
object mObj; object Obj { get { return mObj ?? (mObj = new object()); } }
之前一直这么写:
object Obj { get { mObj = mObj ?? new object(); return mObj; } }
无重复随机数建立
public int EliminateRepeatRandom(int last, int min, int max) { var current = Random.Range(min, max); if (last == current) return (current + (int)Mathf.Sign(Random.value) * Random.Range(min + 1, max - 1)) % max; else return current; }
编辑器窗口滑动缩放(相较直接滑动滚轮步幅更小)
Alt+鼠标右键滑动
编辑器Scene窗口,便捷操控3D场景的方法
按住鼠标右键,wasdqe按键移动,分别对应3个轴向
将选中物品马上移动到编辑器相机位置
ctrl+shift+f
显示当前选中物体的网格
在gizmos中勾选Selection Wire
去除恼人的standard shader高光
咱们在用standard shader调制反射质感时,会有一块高光区域很是碍眼。
能够在Specular Hightlights处将其关闭
打印Vector3类型,但不保留2位小数
vector3有一个重载,能够指定format,其中f10就是保留到小数10位
不过因为是本身实现的,不能在string.format里用
xx.position.ToString("f10")
比较浮点数一致
Mathf.Approximately
删除Project面板里文件夹的展开状态
在Library/expandedItems,删除这个文件会重置展开状态
N卡显存占用查看
C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe
控制台内执行
Unity GI缓存目录
C:\Users\...\AppData\LocalLow\Unity\Caches\GiCache
unity资源商店缓存目录
C:\Users\...\AppData\Roaming\Unity
unity资源商店下载的资源包目录
...\AppData\Roaming\Unity\Asset Store-(具体unity版本).x\
Unity打包后Log日志文件存放路径
C:\Users\XXXX\AppData\LocalLow\DefaultCompany
在Unity中使用快捷键重命名
按F2
从中间删除数组元素,或者复制数组中间元素
复制中间元素:ctrl+d
从中间删除数组元素:shift+delete
或者右键数组中某个元素也会出现操做选项
快速打开全部可展开内容
Shift+Ctrl+Alt 点击可展开内容
判断目标是否在相机的平截头体内
var planes = GeometryUtility.CalculateFrustumPlanes(Camera.main); var isContain = GeometryUtility.TestPlanesAABB(planes, bounds); if(isContain) { //in frustum.. }
即便作了池来缓存,依旧第一次激活GameObject时游戏会卡一下(即播技能特效顿卡问题)
我的猜想是贴图和mesh没传到GPU,mesh倒还好,主要是贴图。
目前我建议的方法是用Graphics把全部常常用的对象所有实时DrawMesh去画,且包含贴图材质球。
能够新建一个16X16的RT画上去,骗过unity。缺点是会多几百个批次。
方法很极端,但有用。
自定义ScriptableObject所生成对象的图标
只须要为原始ScriptableObject脚本对象赋上图标便可
清空StringBuilder
StringBuilder sb = new StringBuilder(); sb.Length = 0;
有多种方法,但修改Length属性效率最高,参考这位园友的测试:http://www.javashuo.com/article/p-vtzbsfqm-gp.html
Process执行的程序当前目录不正确问题(例.bat文件在d盘,但取到的当前目录是unity文件的目录)
var cacheDirectory = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory("your directory"); Process.Start("..."); Directory.SetCurrentDirectory(cacheDirectory);
执行以前设置一次当前目录便可。
Unity中快速将当前窗口放大至全屏
shift+space
获取当前项目中的全部程序集
AppDomain.CurrentDomain.GetAssemblies()
===========================
遍历list或者数组时,缓存count,能够减小调用次数(10万次循环测试)
for (int i = 0, iMax = temp.Count; i < iMax; i++) temp[i].GetHashCode();
在极端状况下,直接用枚举器遍历字典会更快,并且不会产生GC
UnityEngine.Profiling.Profiler.BeginSample("-----1"); foreach (var item in temp) item.Value.GetHashCode(); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("-----2"); foreach (var item in temp.Values) item.GetHashCode(); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("-----3"); using (var handle = temp.GetEnumerator()) { while (handle.MoveNext()) handle.Current.Value.GetHashCode(); } UnityEngine.Profiling.Profiler.EndSample();
使用Instantiate初始化参数去实例对象
public class Foo : MonoBehaviour { void Start() { var sw = new Stopwatch(); sw.Start(); var go = new GameObject(); go.transform.position = Vector3.one; for (int i = 0; i < 10000; i++) { var instancedGO = Instantiate(go); instancedGO.transform.SetParent(transform); instancedGO.transform.localPosition = Vector3.zero; instancedGO.transform.localRotation = Quaternion.identity; instancedGO.transform.localScale = Vector3.one; //102ms //Instantiate(go, transform.position, transform.rotation, transform);//74ms } sw.Stop(); UnityEngine.Debug.Log("sw: " + sw.ElapsedMilliseconds); } }
能够实例化出来再赋值,也可使用参数直接复制
可是1万次循环测试下两种方法差了20多ms
关于Unity事件函数空调用
尽可能少使用封装Unity事件的通用基类,这样会形成函数空调
unity会在C++层面检测是否实现某个事件函数(Start,Update,LateUpdate...etc)
若是没有这个函数则不会加入调用列表中
使用localPosition代替position
调用Position时背后会有许多操做执行,在代码容许的状况下可以使用localPositiond代替position
使用质数做为延迟量可避免最小公倍数出现的状况
例如设计某BOSS的AI时,特殊弹幕A的发射延迟为7,特殊弹幕B的发射延迟为11。
那么将低几率出现这两种特殊弹幕被同时发射的状况。
使用Matrix MultiplyPoint3x4而不是MultiplyPoint
对于非投影矩阵,使用MultiplyPoint3x4进行变换更快。
详见官方文档:https://docs.unity3d.com/ScriptReference/Matrix4x4.MultiplyPoint3x4.html
使用CopyTo将List无GC的转为数组,而不是ToArray
var tempArray = new int[3]; void Test() { list.CopyTo(tempArray);//0 GC }
避免直接调用Camera.main
Camera.main内部会去调用FindGameObjectWithTag,1万次循环的测试下形成了1ms左右的开销
而缓存后大约在0.2ms
mCacheCamera = mCacheCamera ?? Camera.main; Profiler.BeginSample("Foo"); for (int i = 0; i < 10000; i++) mCacheCamera.GetHashCode(); Profiler.EndSample();
高效向量投影
有时候咱们只须要投影轴上的值,而不是坐标。因此能够一行代码搞定:
var value = Vector3.Dot(point, onNormal);
而且少了normal向量的检查。比起Vector3的向量投影省了2步操做。
设置合理的各向异性过滤级别Aniso Level
贴图Aniso Level(Anisotropic filtering level)设置,能够改善mipmap贴图形成的远处模糊
但会增长图形硬件的性能开销,对该值要求不高的贴图,咱们能够下降它的值进行优化
实时反射探针优化
若是需求必须使用实时反射探针,能够将其更新模式设为脚本驱动,在渲染前对反射内容进行模型lod替换或shader、材质上的替换
可达到必定优化做用。
阴影优化
在场景中咱们可使用lod模型来投射阴影,场景中对于一些阴影对画面美观影响不大的模型,咱们能够关闭这些模型的阴影投射
来进行优化。
NGUI Panel优化
Ngui中panel内的内容是会静态合批的,因此当内容较多时把静态的物件和会变化的动态物件放在不一样的panel里
以提升性能。
===========================
在Editor下得到时间
EditorApplication.timeSinceStartup
得到编译器自打开到当前的时间。
Editor下得到滚轮滑动值
Event.current.delta
https://docs.unity3d.com/ScriptReference/Event-delta.html
注意要先判断当前鼠标按钮id,滚轮的id是1
获取Project面板中当前选中物体的路径
AssetDatabase.GetAssetPath(Selection.activeObject);
取Unity当前目录的路径(是Unity自身安装目录,取当前项目目录请用Directory.GetCurrentDirectory())
EditorApplication.applicationPath
EditorApplication.applicationContentsPath
显示对话框
EditorUtility.DisplayDialog
UnityDatabase拷贝文件
AssetDatabase.CopyAsset(须要拷贝文件,目标目录);
1.只能拷贝单个文件,不能拷贝目录
2.目标必须是目录路径,不能是文件路径
3.不用System.io而用它,由于插件可以跨平台
Editor状态下设置Scene窗口的相机位置
SceneView.lastActiveSceneView.pivot = ...;
SceneView.lastActiveSceneView.Repaint();
Editor下使用右键菜单
使用GenericMenu能够直接调出Unity的右键菜单
Editor下检查prefab是否都打了标签/读写标签Labels
AssetDatabase.GetLabels
AssetDatabase.SetLabels
Editor下拿到当前拖拽对象(跨窗口)
DragAndDrop.objectReferences
在DragExited时处理拖拽内容,就能够在松手时触发了
if (Event.current.type == EventType.DragExited && DragAndDrop.objectReferences.Length > 0) { var dragItem = DragAndDrop.objectReferences[0]; ...
若是是多选拖拽,数组里的就是多选的全部对象,不然就是一个。
Editor下控制控件的焦点
一般只要把focus设为空,就能够取消焦点
GUI.FocusControl("");
一些特殊的状况,好比弹出性组件,须要知道控件是否被改变过,而非值是否改变过
能够这么作,经过GetNameOfFocusedControl拿到Focus的name进行比较
var oldFocus = GUI.GetNameOfFocusedControl(); var changedIndex = EditorGUI.Popup(rect, index, array); var newFocus = GUI.GetNameOfFocusedControl(); if (newFocus != oldFocus) { //Do something... GUI.FocusControl(""); }
Editor下得到'剪切','拷贝','撤销'等命令
可使用commandName得到
Event.current.commandName == "Copy"
EditorWindow窗口大小锁死后没有边框的解决方法
var window = GetWindow(typeof(MyWindow), true); window.minSize = new Vector2(960, 540); window.maxSize = window.minSize;
用GetWindow建立窗口时,第二个参数填true。建立为独立的工具窗口,便可恢复边框
Editor下Dirty掉当前修改过的场景对象内容
通常非场景对象用
EditorUtility.SetDirty(target);
而场景对象则用
EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
Editor下BeginScrollView报错InvalidCastException
和调用顺序有关,也是Layout/Repaint和输入之间的问题。
改变调用前后顺序有可能解决。
在Editor下绘制自定义光标
好比缩放须要绘制缩放的自定义光标,滑动又须要滑动的
使用下方法便可绘制,Rect自定义Cursor区域的Rect。
须要每次OnGUI更新都调,而非只Add一次
EditorGUIUtility.AddCursorRect(Rect, MouseCursor.Pan);
Editor下的LayerMaskField
Unity并无提供LayerMask控件,其Layer控件返回的只是层编号
下面是一个扩展的LayerMaskField:
public static LayerMask LayerMaskField(string label, LayerMask layerMask) { List<string> layers = new List<string>(); List<int> layerNumbers = new List<int>(); for (int i = 0; i < 32; i++) { string layerName = LayerMask.LayerToName(i); if (layerName != "") { layers.Add(layerName); layerNumbers.Add(i); } } int maskWithoutEmpty = 0; for (int i = 0; i < layerNumbers.Count; i++) { if (((1 << layerNumbers[i]) & layerMask.value) > 0) maskWithoutEmpty |= (1 << i); } maskWithoutEmpty = EditorGUILayout.MaskField(label, maskWithoutEmpty, layers.ToArray()); int mask = 0; for (int i = 0; i < layerNumbers.Count; i++) { if ((maskWithoutEmpty & (1 << i)) > 0) mask |= (1 << layerNumbers[i]); } layerMask.value = mask; return layerMask; }