Unity3D心得分享

本篇文章的內容以各類tips爲主,不間斷更新html

 

2019/10/30 最近更新:使用質數做爲延遲量可避免最小公倍數出現的狀況
數組

 

 

Unity DEMO學習

===========================緩存

 

Unity3D Adam Demo的學習與研究app

 

Unity3D The Blacksmith Demo部份內容學習dom

 

Viking Village維京村落demo中的地面積水效果編輯器

 

Viking Village維京村落demo中的粒子距離消隱ide

 

The Courtyard demo學習函數

 

ShadowGun Demo學習(非技術向)工具

 

Unity AngryBots憤怒的機器人demo研究post

 

Unity Generic Tips

===========================

 

Unity3D 材質球設置參數無效果的解決方法

 

Unity3D歐拉和四元數兩種旋轉的用法

 

使用四元數點乘比較插值是否即將完成

 

解決攝像機旋轉約束問題(RotateDelta,RotateClamp)

 

Unity中經過類名字符串取組件的方法

 

Unity在編輯器狀態下清空控制檯信息

 

雙面MeshCollider腳本

 

運行時修改代碼不自動刷新

而後按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()

 

 

 

Unity Optimize Tips

===========================

 

遍歷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裏

以提升性能。

 

 

 

Unity Editor Tips

===========================

 

Animator在Editor狀態下預覽工具

 

Unity3D中Console控制檯的擴展

 

在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;
}
相關文章
相關標籤/搜索