本篇文章的內容以各類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; }