選自過去1~2周 自己所看到外文內容:https://twitter.com/unity3d 和各種其他博客來源吧
1、
1)Unity x Android Studio混用經驗分享---Laird / 果思設計 資深工程師 【臺灣】
2)關於Unity快速搭建3D場景的最最最入門小技巧---Wei J / CyberTail Games Co-founder
3)如何處理出好看的卡通渲染---Kuro / 火炬 共同創辦人
https://www.youtube.com/watch?v=ZazQ9MiGEXk
常見兩種方式, Android 導出 jar, .aar 爲文件到Unity中作爲Plugins用, 或者Unity Export Project 工程然後 安卓開發。 作者分享的是第二種。
2、SRP ScriptableRenderPipeline
https://github.com/Unity-Technologies/ScriptableRenderPipeline
利用Unity的功能自己寫一個 :
https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/custom-pipeline/
https://github.com/keijiro/Retro3DPipeline
3、 在Unity中陰影的實現方式:
https://luumoon.github.io/2018/02/28/Planar-Shadow-%E5%AE%9E%E7%8E%B0%E5%8F%8A%E5%BA%94%E7%94%A8/
1) Unity自帶陰影
2)Projector Shadow方式
其中有插件,如Dynamic Shadow Projector (免費)。
https://zhuanlan.zhihu.com/p/42433900 看評論好像這種方案的性能並沒好出太多~~
3)Planar Shadow
聽說是鎮魔曲遊戲使用這個方案
- https://forum.unity.com/threads/shader-sharing-the-most-simple-shadow-shader-planar-projection-shadowing.319830/
- https://github.com/ozlael/PlannarShadowForUnity
- http://qiankanglai.me/2016/12/23/planar-shadow/index.html
4)Unity 2017 Tutorial - Blob Shadow Projector - Fake Shadow - YouTube
就是使用Unity提供的 Projector 組件, 做假的陰影, 正常如果遊戲地面平臺 可以使用一個假的陰影模型, 但是效果很差。 但是都說Projector有些性能問題。 比如Unity Standard Assets 中有 Effects.unitypackage -》 Projectors -》 BlobShadowProjector.prefab
5) 直接使用一個假的模型片 (不動的npc, 小範圍移動的怪(地要平),這個模型片也要高於地面一些, 簡單粗暴,可以考慮使用, Unity動態合批,這種陰影都可以合併爲一個DC) Unlit/Transparent shader就行
4、 Unity Chan的卡通渲染Shader, UTS已經到2.0.6版了,作者小林信行也把它和Aura 2, 一套製作環境光源和霧氣的套件整合,重新賦予2014年所發佈的演唱會專案新的感覺。
想做VTuber類型專案,一定要研究這個Shader。
https://www.youtube.com/watch?v=UqHwICpl5ps&feature=share
UTS2 v.2.0.6 : https://github.com/unity3d-jp/UnityChanToonShaderVer2_Project
Aura 2 - Volumetric Lighting & Fog :https://assetstore.unity.com/packages/tools/particles-effects/aura-2-volumetric-lighting-fog-137148
Unite Beijing 2018的一段Unity烘焙光照演說,仔細說明瞭參數設定與光照烘焙注意事項,非常值得一聽。
衆多技術大牛在Unite Beijing 2018中爲開發者帶來了精彩的技術演講,內容涵蓋遊戲開發、技術美術、工業等多個領域。錯過本次大會的開發者也無需擔心,Connect爲大家整理了文字版演講實錄,讓我們看看都有哪些內容吧!
- 淺談伽瑪和線性顏色空間
- Unity Shader着色器優化
- Lightmap烘焙最佳實踐
- 解析AssetBundle https://www.youtube.com/watch?v=mMjcDjM8Fm8
- 玩轉移動端大型世界開發 劉偉賢 ─【Unity 議程】Unity MMORPG 遊戲優化https://www.youtube.com/watch?v=Z1zE-AWgYZ4
- 基於照片建模的遊戲製作流程
- Playable API:定製你的動畫系統 成亮 ─ 【Unity 議程】利用 Cinemachine 實作遊戲運鏡系統 https://www.youtube.com/watch?v=3hDvBcQZxKQ
- Unity工業之路
- 使用自定義渲染紋理實現炫酷特效
- 未來影像,影向未來
- 《崩壞3》:在Unity中實現高品質的卡通渲染(上)
- 《崩壞3》:在Unity中實現高品質的卡通渲染(下)
- 育碧-《Eagle Flight》背後的研發
- 《旅行青蛙》小規模開發的匠心獨運
- 江毅冰-《從AAA遊戲到實時渲染的動畫電影》
- ProBuilder快速關卡建模實踐
- 利用Cinemachine快速創建遊戲中的相機系統
5、
https://connect.unity.com/p/fake-nulle-a-operator-i
運算符 "??" 和 "?." 是假的空
對象存儲在內存中的哪個位置?
在我更多地談論假空值之前,我需要解釋一下內存管理的方式以及特定對象存儲在內存中的位置。
類的實例instancje (zwykłych)存儲在哪裏?
下面是我們可以在C#的幫助下創建的典型類的示例。
class ExampleClass
{
}
只要我們引用它們,這些類的實例將保留在內存中。
ExampleClass instancja = new ExampleClass();
如果我們沒有引用它們,它們將被垃圾收集器刪除。這些類的實例將存在於託管內存中
ExampleClass instancja = new ExampleClass();
// 我賦值null因此我失去了對ExampleClass實例的引用
instancja = null;
我賦值null因此我 對ExampleClass類實例的引用
繼承自UnityEngine.Object類的類的實例存儲在何處?
從UnityEngine.Object 類開始,它們繼承:
- gameObject
- 所有組件(例如Transform,Rigidbody等)
- 所有Asset(例如,Texture,ScriptableObject,AudioClip等)
這些對象同時在本機和託管內存中。
它是由什麼產生的?
Unity編輯器是用C ++編寫的。在這種語言中,已經實現了各個類和編輯器功能的操作。我們無法直接獲得各個類的實現。
我們在C#中編寫與我們的遊戲相關的代碼,因爲它的簡單性(相對於C ++語言)。
我們在腳本中使用的與資產,組件和遊戲對象相關的類是包裝器,它允許您處理實際位於本機端的實例實例。
下面是GameObject 類的示例。該類不實現gameObjects本身。它允許您管理本機端的gameObject實例並與之通信。
概括
從UnityEngine.Object 類繼承的對象實例同時存在於本機和託管內存中,其他對象的實例僅存在於託管內存中。
檢查對象是否存在的方法
首先讓我討論UnityEngine.Object 類中的方法。
UnityEngine.Object 上的轉換 bool運算符
UnityEngine.Object 類有一個特殊的運算符,允許您檢查對象是否存在。
https://docs.unity3d.com/ScriptReference/Object-operator_Object.html
using UnityEngine;
public class FakeNullTest : MonoBehaviour
{
void Start()
{
GameObject gameObject = new GameObject();
//我們可以直接將對象分配給bool值以檢查它是否存在
bool obiektIstnieje = gameObject;
// 我們也可以直接否認對象的存在
bool obiektNieIstnieje = !gameObject;
// 或者將對象 放在if 中
if (gameObject)
{
Debug.Log(gameObject);
}
//而不用直接判空
//if (gameObject!=null)
//{
//}
}
}
操作符 == 和 !=
要檢查對象是否存在,我們還可以使用operator ==或!=
using UnityEngine;
public class FakeNullTest : MonoBehaviour
{
void Start()
{
GameObject gameObject = new GameObject();
// 我們檢查對象是否爲null
if (gameObject!=null)
{
Debug.Log(gameObject);
}
}
}
方法函數 System.Object.ReferenceEquals() 和 System.Object.Equals()
這是另外兩種檢查對象是否存在的方法。
但是,正如您將立即看到的那樣,對於從UnityEngine.Object 繼承的對象,它們並不總是在Unity編輯器中正常工作。以下示例檢查gameObject在尚未初始化時是否爲null。
我們在編輯器中執行測試。
using UnityEngine;
public class FakeNullTest : MonoBehaviour
{
public GameObject myGameObject;
void Update()
{
Debug.Log("對象是null嗎?"); // Czy obiekt jest nullem?
bool referenceEquals = System.Object.ReferenceEquals(myGameObject, null);
Debug.Log("referenceEquals: " + referenceEquals);
bool equals = System.Object.Equals(myGameObject, null);
Debug.Log("equals: " + equals);
// 爲了比較,我們還使用operator ==
bool operatorPorównania = myGameObject == null;
Debug.Log("operatorPorównania: " + operatorPorównania); // 比較運算符
}
}
請注意前兩種方法的值不正確。這些方法返回對象仍然存在的信息。爲什麼會這樣?
上面的運算符如何檢查UnityEngine.Object 是否爲空?
在最開始,我提到繼承自UnityEngine.Object 類的對象實例存在於本機內存中,並且託管內存中有一個包裝器,用於在本機內存中進行通信和實例管理。
見下圖。
方法System.Object.ReferenceEquals()和System.Object.Equals()比較存在於對象實例管理的內存。
但是,運算符==,!=和對象轉換爲bool 引用本機內存中的對象實例,忽略託管部分中實例的存在。因此,我們有時可能會遇到這樣的情況:即使對象仍然存在於託管頁面上,這些操作符將“假裝爲空對象”。編輯器的這種機制稱爲"fake null"。
什麼是"fake null"?
Unity編輯器有一個特殊的機制,用於處理從UnityEngine.Object 類繼承的對象的null 。
當我們在Destroy()方法的幫助下加載新場景或銷燬對象時,我們會銷燬本機端的對象。然後,垃圾收集器將刪除託管頁面上對象的包裝。
當一個對象在本機和託管端不存在時,它是一個“ 真正的空”。
如果對象在本機端不存在並且仍然存在於管理端,則該對象將是"fake null"。
在什麼情況下設施將是“假nullem”?
第一種情況 - “ 假nulle ”僅存在於編輯器中。編譯的應用程序中沒有“ 假nulle ” - 對象將存在與否。
該物體可能是“ 假空字符串”,例如,在對象從方法去除的情況下銷燬(方法破壞本機端對象),並在旁邊還有個管理垃圾收集器,因爲它尚未刪除。(下面是一個示例代碼)
using System.Collections;
using UnityEngine;
public class FakeNullTest : MonoBehaviour
{
public GameObject myGameObject;
private void Start()
{
myGameObject = new GameObject();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
StartCoroutine(DestroyAndLogInfo());
}
IEnumerator DestroyAndLogInfo()
{
// 我們摧毀了這個物體
Destroy(myGameObject);
// 只有在調用Update()完成後纔會銷燬對象
// 這就是我們等待一幀寫出日誌的原因
yield return null;
Debug.Log("Czy obiekt jest nullem?"); // 對象是null嗎?
bool referenceEquals = System.Object.ReferenceEquals(myGameObject, null);
Debug.Log("referenceEquals: " + referenceEquals);
bool equals = System.Object.Equals(myGameObject, null);
Debug.Log("equals: " + equals);
bool operatorPorównania = myGameObject == null;
Debug.Log("operatorPorównania: " + operatorPorównania);
}
}
爲什麼System.Object.ReferenceEquals()和System.Object.Equals()方法不能正確地用於UnityEngine.Object?
這兩種方法對於"fake null"不起作用,因爲這些方法比較了管理方的引用。在"fake null" 的情況下,該對象仍將存在於託管內存中,即使本機不再存在。
System.Object.ReferenceEquals()和System.Object.Equals()方法何時適用於UnityEngine.Object?
這些方法在編譯應用程序後將正常工作,因爲"fake null"僅存在於Unity編輯器中。
我們還可以在代碼中指定空值。這樣,"fake null"將變爲“ 真正的空”。
using UnityEngine;
public class FakeNullTest : MonoBehaviour
{
public GameObject myGameObject;
void Update()
{
//我們分配一個空值來獲得“真正的空”
myGameObject = null;
Debug.Log("Czy obiekt jest nullem?");
bool referenceEquals = System.Object.ReferenceEquals(myGameObject, null);
Debug.Log("referenceEquals: " + referenceEquals);
bool equals = System.Object.Equals(myGameObject, null);
Debug.Log("equals: " + equals);
bool operatorPorównania = myGameObject == null;
Debug.Log("operatorPorównania: " + operatorPorównania);
}
}
現在這些方法返回與== 運算符相同的值。
使用"fake null"有什麼好處?
由於"fake null"機制,Unity編輯器可以獲得有關不存在的對象的更多詳細信息。
下面您可以看到每個空值顯示的錯誤比較。
在"fake null" 的情況下錯誤更詳細包含我沒有引用的信息。“ 真的空”的錯誤顯示有關缺失引用的更差的信息。
using UnityEngine;
public class FakeNullTest : MonoBehaviour
{
// 未初始化的對象
public GameObject myGameObject;
void Update ()
{
//如果我們分配一個空值,那麼我們得到一個“真正的空”
// 如果我們評論這一行,我們將得到“fake null”
myGameObject = null;
// 我指的是activeSelf大小來觸發錯誤
bool a = myGameObject.activeSelf;
}
}
有關"fake null"的更多詳細信息,請參閱此帖子:
https://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/
Fake null a operator "??" 和 "?:".
首先,幾句話會提醒您這些操作符是如何運作的。
操作員“??”如何工作?
Null-coalescing運算符或“ ?? ”以這樣的方式工作:當對象存在時,它返回此對象,如果對象爲null,則返回運算符“ ?? ” 右側的值。
https://docs.microsoft.com/pl-pl/dotnet/csharp/language-reference/operators/null-coalescing-operator
下面是一個示例代碼:
using UnityEngine;
public class NullCoalescingOperator : MonoBehaviour
{
void Start()
{
// 對象爲null時的情況
string obiektNieIstnieje = null;
string wynik = obiektNieIstnieje ?? "Wartość z prawej strony operatora ??"; // 操作員右側的價值?
Debug.Log("Gdy mamy referencje do nulla: "+ wynik); // 當我們引用null時:
// 對象存在時的情況
string obiektIstnieje = "Obiekt!"; // 對象!
string wynik2 = obiektIstnieje ?? "To zostanie zwrócone jeśli obiekt przed operatorem ?? jest nullem";
Debug.Log("Gdy mamy referencje do obiektu: " + wynik2);
}
}
控制檯窗口中將顯示以下日誌:
“?.” 如何工作?
空條件運算符 "?." 或 "?[]".
?. 是與null比較的縮短版本。如果對象爲null,則不會調用 ?. 後面的代碼。
下面是一個示例代碼,可幫助您瞭解此運算符:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NullConditionalOperator : MonoBehaviour {
void Start()
{
string wynik = "";
string obiektNieIstnieje = null;
wynik = obiektNieIstnieje?.Replace(oldChar: 'A', newChar: 'Z');
Debug.Log("Gdy mamy referencje do nulla: "+ wynik);
// 使用運算符相當於上面的代碼?.
//if (obiektNieIstnieje != null)
//{
// wynik = obiektNieIstnieje.Replace(oldChar: 'A', newChar: 'Z');
//}
//Debug.Log("Gdy mamy referencje do nulla: " + wynik);
string obiektIstnieje = "AAAAAAA!";
wynik = obiektIstnieje.Replace(oldChar: 'A', newChar: 'Z');
Debug.Log("Gdy mamy referencje do obiektu: " + wynik);
}
}
控制檯窗口中將顯示以下日誌:
使用運算符“??”和“?.” 時爲什麼要小心
上述運算符與檢查對象是否爲空密切相關。他們將對象與管理方面的null進行比較,因此它們無法正常用於"fake null"。
下面是在“ 真的空” 的情況下這些運算符的操作差異的示例。
所有資產,組件和遊戲對象都繼承自UnityEngine.Object 類。
using UnityEngine;
public class FakeNullTest : MonoBehaviour
{
// 未初始化的對象
public GameObject myGameObject;
void Update ()
{
// 如果我們分配一個空值,那麼我們得到一個“真正的空”
// 如果我們註釋這一行,我們將得到“fake null”
myGameObject = null;
string name = "AAAA";
// 如果myGameObject存在,則返回其名稱
// 否則沒有任何返回
name = myGameObject?.name;
Debug.Log("?. "+ name);
// 如果myGameObject存在,則返回它
// 如果你還沒有 就創建一個新的gameObject
GameObject temp = myGameObject ?? new GameObject("Nowo utworzony GameObject");
Debug.Log("?? " + temp.name);
}
}
因此,對於從UnityEngine.Object 類繼承的對象,請不要使用這些運算符。用作運算符!= ,== 和轉換爲bool 的替代品,因爲它們支持“ Fake null ”。
在這裏,您可以找到有關如何替換這兩個運算符的信息:
關於“fake null ”主題的參考書目和其他材料。
https://blogs.unity3d.com/2014/05/16/custom-operator-should-we-keep-it/
https://answers.unity.com/questions/1398006/2d-array-initialized-but-send-null.html
https://answers.unity.com/questions/1236014/-operator-not-working.html
https://www.youtube.com/watch?v=feRt_q9wRz0&list=PLfurnpjsyGQItrxGMNLzphKIemfXWhVmg
4、達哥 講 Unity優化: https://www.youtube.com/watch?v=Cjh5naX8UU8
Unity臺北場優化分享 II PPT : https://www.slideshare.net/KelvinLo5/unity-ii?fbclid=IwAR1CRnFkMxj_oblBnVXfDGaBkZ9eBpTRtRykXUy8DezbAk-DgpqyLZEBL9g
https://blog.csdn.net/u010019717/article/details/90729425
5、 關於: Draw Call 和 SetPass Call 兩個指標哪個更重要?
SetPass Calls: 56 Draw Calls: 90 Total Batches: 73 Tris: 6.1k Verts: 10.2k
(Dynamic Batching) Batched Draw Calls: 41 Batches: 24 Tris: 0 Verts: 1.0k
(Static Batching) Batched Draw Calls: 0 Batches: 0 Tris: 0 Verts: 0
(Instancing) Batched Draw Calls: 0 Batches: 0 Tris: 0 Verts: 0
Profiler 中: Draw Calls【未合批之前的數量】 = Total Batches【Stats 窗口中的Batches】 + Saved by batching 【Stats 窗口中的】
上面的數據只有 動態批處理, 所以 : Batched Draw Calls = Batches + Saved by batching
還是要弄懂這幾個值:
FrameDebug 窗口中顯示的是實際的DrawCall 數, 也就是Total Batches【Stats 窗口中的Batches】。
Save by batching是 Unity靠Batching爲我們節省了多少個Draw Call
https://unity3d.com/cn/learn/tutorials/temas/performance-optimization/optimizing-graphics-rendering-unity-games
UWA 上說 SetPass Call 更重要 !
https://blog.uwa4d.com/archives/QA_Rendering.html
https://answer.uwa4d.com/question/58d29b8b5a5050b366a6b6ae
https://answer.uwa4d.com/question/59ddc3fa43cf099e2d2295be
簡單來說,Unity引擎中Total Batches是我們建議最需要關注的指標。
1個Setpass Call或者Batch,相當於是一次Render State的切換,而1個Draw Call則是CPU讓GPU去進行渲染某一個Object的1次操作。在當前的移動設備中,1次Render State的切換要1個Draw Call本身要耗時。所以,Total Batches是我們較爲建議的關注指標,也是UWA性能報告中所提供的Draw Call查看指標。而Frame Debugger中,其數量是與Total Batches相一致的,即查看的是每一個Batch的渲染物體。
更爲詳細一些的說明,可以看 這個帖子 。
而Unity Profiler中的Draw Call,其理論上對應的則是glDrawElements的調用次數,其與高通或其他第三方工具所返回的Gl Trace信息操作數量不太一致,但應該與其中的glDrawElements API的調用次數基本一致,題主可以自行檢測看看。
SetPass calls:在Unity 4.x和3.x原來的Stats面板的第一項是“Draw calls”,然而到了Unity5.X版本,Stats上沒有了“Draw calls”,卻多出來一項”SetPass calls“。
比如說場景中有100個gameobject,它們擁有完全一樣的Material,那麼這100個物體很可能會被Unity裏的Batching機制結合成一個Batch。所以用“Batches”來描述Unity的渲染性能是不太合適的,它只能反映出場景中需要批處理物體的數量。那麼可否用“Draw calls”來描述呢?答案同樣是不適合。每一個“Draw calls”是CPU發送個GPU的一個渲染請求,請求中包括渲染對象所有的頂點參數、三角面、索引值、圖元個數等,這個請求並不會佔用過多的消耗,真正消耗渲染資源的是在GPU得到請求指令後,把指令發送給對應物體的Shader,讓Shader讀取指令並通知相應的渲染通道(Pass)進行渲染操作。
假設場景中有1個gameobject,希望能顯示很酷炫的效果,它的Material上帶有許多特定的Shader。爲了實現相應的效果,Shader裏或許會包含很多的Pass,每當GPU即將去運行一個Pass之前,就會產生一個“SetPass call”,因此在描述渲染性能開銷上,“SetPass calls”更加有說服力。
向GPU發送命令時發生的最昂貴的操作是SetPass調用。如果我們的遊戲由於向GPU發送命令而受CPU限制,減少SetPass調用的數量可能是提高性能的最佳方法。
https://forum.unity.com/threads/setpass-calls-vs-drawcalls.508979/
SetPass refers to the material setup, and DrawCalls refer to each item submitted for rendering.
When batching is enabled, Unity can perform a single SetPass call, and then submit multiple draw calls, re-using the same material state.
SetPass指的是材質設置,DrawCalls指的是提交渲染的每個項目。
啓用批處理後,Unity可以執行單個SetPass調用,然後提交多個繪製調用,重新使用相同的材質狀態。
渲染簡介
簡單地看一下Unity渲染幀時會發生什麼。
注意:在本文中,我們將使用術語“object對象”來表示可以在我們的遊戲中呈現的對象。任何帶有Renderer組件的GameObject都將被稱爲對象。
在最基本的層面上,渲染可以描述如下:
- 中央處理單元,被稱爲CPU,找出必須繪製的內容以及如何繪製。。
- CPU將指令發送到圖形處理單元,稱爲GPU。
- GPU根據CPU的指令繪製內容。
對於每個渲染幀,CPU執行以下工作:
- CPU檢查場景中的每個對象以確定是否應該渲染它。只有滿足某些條件纔會呈現對象; 例如,其邊界框的某些部分必須位於相機的視錐體內。據說將無法渲染的對象被剔除。
- CPU收集有關將要呈現的每個對象的信息,並將此數據排序爲稱爲draw calls繪圖調用命令。繪圖調用包含有關單個網格的數據以及應如何呈現該網格; 例如,應該使用哪些紋理。在某些情況下,共享設置的對象可以組合到同一個繪圖調用中。將不同對象的數據組合到同一個繪製調用中稱爲批處理batching。
- CPU 爲每個繪製調用創建一個稱爲batch的數據包。批量有時可能包含繪製調用以外的數據,但這些情況不太可能導致常見的性能問題,因此我們不會在本文中考慮這些。
對於包含繪製調用的每個批處理batch,CPU現在必須執行以下操作:
- CPU可以向GPU發送命令以將多個已知的變量統一地改變爲 render state。此命令稱爲SetPass call。SetPass調用告訴GPU用於渲染下一個網格的設置。僅當要渲染的下一個網格需要從前一個網格更改渲染狀態時,纔會發送SetPass call。
- CPU將繪圖調用發送到GPU。繪圖調用指示GPU使用最近的SetPass調用中定義的設置呈現指定的網格。
- 在某些情況下,批次batch可能需要多次pass。pass是着色器代碼的一部分,新pass需要更改渲染狀態。對於批處理中的每個pass,CPU必鬚髮送新的SetPass call,然後必須再次發送draw call。
同時,GPU執行以下工作:
- GPU按照發送順序處理來自CPU的任務。
- 如果當前任務是SetPass call,則GPU更新渲染狀態。
- 如果當前任務是draw call,則GPU渲染網格。這是分階段發生的,由着色器代碼的不同部分定義。渲染的這一部分很複雜,我們不會詳細介紹它,但是我們理解一段稱爲頂點着色器的代碼告訴GPU如何處理網格的頂點,然後是一段代碼稱爲片段着色器告訴GPU如何繪製單個像素。
- 重複此過程,直到GPU處理完所有從CPU發送的任務爲止。
從廣義上講,CPU爲了渲染幀而必須執行的工作分爲三類:
- 確定必須繪製的內容
- 準備GPU的命令
- 將命令發送到GPU
將命令發送到GPU
將命令發送到GPU所花費的時間是遊戲受CPU限制的最常見原因。
向GPU發送命令時發生的最昂貴的操作是SetPass調用。如果我們的遊戲由於向GPU發送命令而受CPU限制,減少SetPass調用的數量可能是提高性能的最佳方法。
我們可以看到在Unity的Profiler窗口的渲染分析器中發送了多少個 SetPass calls和batches。在性能受損之前可以發送的SetPass調用的數量在很大程度上取決於目標硬件。
SetPass calls的數量及其與batches數量的關係取決於幾個因素,我們將在本文後面更詳細地介紹這些主題。但是,通常情況是:
- 在大多數情況下,減少批次數和/或使更多對象共享相同的渲染狀態將減少SetPass調用的數量。
- 在大多數情況下,減少SetPass調用的數量將提高CPU性能。
如果減少批量數量並不會減少SetPass調用的數量,那麼它仍然可以帶來性能提升。這是因 大致有三種減少批次和SetPass調用的方法。具體看文檔!:
- 減少要渲染的對象數量可能會減少批量和SetPass調用。 (減少角色數量,相機視錐體剔除距離,用距離隱藏對象Layer Cull Distances, 遮擋剔除 )
- 減少每個對象必須呈現的次數通常會減少SetPass調用的次數。 (實時照明,陰影和反射,確切的說取決於我們爲遊戲選擇的渲染路徑)
- 將必須渲染的對象數據組合成較少的批次將減少批次數。 (靜態批處理,動態批處理,GPU Instancing , 圖集, 網格合併, 小心Renderer.material產生副本 )
優化不能盲目的進行優化。 https://unity3d.com/learn/tutorials/temas/performance-optimization/diagnosing-performance-problems-using-profiler-window?playlist=44069
首先要確定是CPU 還是GPU 導致的性能問題更爲嚴重:
Profiler , GPU Usage 中, 在中間有一個 CPU:xx ms GPU: xx ms 的顯示, 通過比較這兩個值就可以確認。
- 確定我們的遊戲是否受CPU限制 及其解決辦法
- GC分析及其 解決辦法
- 物理分析 及其解決辦法
- 慢腳本分析 及其解決辦法
關注時間比關注幀率可能更重要:
雖然幀速率是談論遊戲性能的常用方式,但當我們嘗試提高遊戲性能時,我們更有用的是考慮以毫秒爲單位渲染幀所需的時間。這有兩個原因。首先,這是一個更精確的措施。當我們努力提高遊戲性能時,每毫秒都可以計入我們的目標。其次,幀率的相對變化意味着在不同尺度上的不同事物。從60到50 FPS的變化表示額外的3.3 ms的處理時間,但是從30到20 FPS的變化表示額外的16.6 ms的處理時間。這兩個示例都是10 FPS下降,但渲染幀所花費的時間差異很大。
我們有必要了解幀必須渲染多少毫秒才能滿足常見的幀速率。要找到這個數字,我們應該遵循公式1000 / [所需的幀速率]。使用這個公式,我們可以看到,對於每秒渲染30幀的遊戲,它必須在33.3毫秒內渲染每個幀。對於以60 FPS運行的遊戲,它必須在16.6毫秒內渲染每個幀。
對於渲染的每個幀,Unity必須執行許多不同的任務。簡單來說,Unity必須更新遊戲狀態,拍攝遊戲快照,然後將快照繪製到屏幕上。每幀期間必須執行的任務包括讀取用戶輸入,執行腳本和執行照明計算等操作。除此之外,還有一些操作可以在單幀期間多次發生,例如物理計算。當所有這些任務都足夠快地執行時,我們的遊戲將具有一致且可接受的幀速率。當所有這些任務都無法足夠快地執行時,幀渲染時間太長,幀速率將下降。
瞭解哪些任務執行時間過長對於瞭解如何解決我們的性能問題至關重要。一旦我們知道哪些任務正在降低我們的幀速率,我們就可以嘗試優化遊戲的那一部分。這就是分析如此重要的原因:分析工具向我們展示了每個任務在任何給定幀中所花費的時間。
6、 介紹“Unity Delayed Asset”,它可以從Inspector上的Reference而不是文件路徑讀取Resources文件夾的資源
https://github.com/Trisibo/Unity-delayed-asset
using UnityEngine;
public class Example : MonoBehaviour
{
private void Update()
{
if ( Input.GetKeyDown( KeyCode.Space ) )
{
var texture = Resources.Load<Texture>( "hoge" );
}
}
}
通常,在使用Resources.Load時,會編寫如上所示的代碼。
using Trisibo;
using UnityEngine;
public class Example : MonoBehaviour
{
[SerializeField, DelayedAssetType( typeof( Texture ) )]
private DelayedAsset m_textureReference;
private void Update()
{
if ( Input.GetKeyDown( KeyCode.Space ) )
{
var texture = m_textureReference.Load() as Texture;
}
}
}
如果您使用“Unity Delayed Asset”,您將能夠編寫這樣的代碼。
與通常的書寫風格相比,源代碼將是多餘的。
您可以在Inspector中的Resources.Load中設置要加載的資產
加載場景時,不會加載Inspector中設置的資源,但會在調用Load函數時加載
注: 思想挺好的,內部封裝只是引用Resources的路徑,爲Editor提供擴展預覽。 給人感覺好像直接引用資源一樣方便使用。
7、 如果使用第三方的音頻庫播放音頻, 就要禁用Unity自帶的Audio模塊。
腳本檢查是否處理了。
using NUnit.Framework;
using System.Linq;
using UnityEditor;
namespace UniCommonTestRunner
{
/// <summary>
/// 測試音頻是否無效
/// </summary>
public partial class UniCommonTestRunner
{
[Test]
public void CheckAudioDisable()
{
var path = "ProjectSettings/AudioManager.asset";
var manager = AssetDatabase.LoadAllAssetsAtPath( path ).FirstOrDefault();
var obj = new SerializedObject( manager );
var prop = obj.FindProperty( "m_DisableAudio" );
Assert.IsTrue( prop.boolValue );
}
}
}
8、 正常添加這種菜單都是在Mono 腳本內部添加 :
public class Test : MonoBehaviour
{
[ContextMenu("Test")]
public void TestTest()
{
}
但是圖片中還有一個我添加的 菜單命令 “Set Random Rotation”。 這種是怎麼添加的呢?
[MenuItem("CONTEXT/Transform/Set Random Rotation")]
private static void RandomRotation(MenuCommand command)
{
var transform = command.context as Transform;
Undo.RecordObject(transform, "Set Random Rotation");
transform.rotation = Random.rotation;
}
如果需要所有組件都有的話就:
[MenuItem("CONTEXT/Component/Set Random Rotation")]
[MenuItem("CONTEXT/MonoBehaviour/Set Random Rotation")]
9、 在不改變比率的情況下將Unity圖片適合屏幕尺寸
- 相機:位置(0, 0,-5)
- board: 位置(0, 0, 0) scale(1, 1, 1)
// //指定的尺寸比
float targetRatio= targetWidth / targetHeight;
// 屏幕比
float screenRatio = (float) Screen.width / (float) Screen.height;
// Camera.orthographicSize是高度的一半,計算高度 要乘2
var height = _camera.orthographicSize * 2f;
float width = 0;
if (screenRatio > targetRatio)
{
// 因爲屏幕長度超過指定大小,所以將指定的大小寬度適合屏幕寬度
width = screenRatio * height;
height = width / targetRatio;
}
else
{
//由於屏幕是垂直的,因此垂直於屏幕高度
width = height * targetRatio;
}
board.localScale = new Vector3(width, height, 1f);
https://www.shibuya24.info/entry/screen_fit
10、 [Unity]介紹可以測量編譯時間的“CompileTime.cs”
# // Originally found here: https://answers.unity.com/questions/1131497/how-to-measure-the-amount-of-time-it-takes-for-uni.html
using UnityEngine;
using UnityEditor;
[InitializeOnLoad]
class CompileTime : EditorWindow
{
static bool isTrackingTime;
static double startTime;
static CompileTime()
{
EditorApplication.update += Update;
startTime = PlayerPrefs.GetFloat("CompileStartTime", 0);
if (startTime > 0)
{
isTrackingTime = true;
}
}
static void Update()
{
if (EditorApplication.isCompiling && !isTrackingTime)
{
startTime = EditorApplication.timeSinceStartup;
PlayerPrefs.SetFloat("CompileStartTime", (float)startTime);
isTrackingTime = true;
}
else if (!EditorApplication.isCompiling && isTrackingTime)
{
var finishTime = EditorApplication.timeSinceStartup;
isTrackingTime = false;
var compileTime = finishTime - startTime;
PlayerPrefs.DeleteKey("CompileStartTime");
Debug.Log("Script compilation time: \n" + compileTime.ToString("0.000") + "s");
}
}
}
將上述腳本添加到Unity項目的“Editor”文件夾中 , 譯時間將在控制檯窗口中顯示
11、 可視化Unity遊戲中每個資產佔用的空間,並快速優化遊戲的文件大小
通常,您可以在構建之後查看Unity Editor的日誌,以查看遊戲文件大小的某些統計信息。這就是它的樣子:
Textures 33.1 mb 54.1%
Meshes 0.0 kb 0.0%
Animations 0.0 kb 0.0%
Sounds 8.3 mb 13.6%
Shaders 172.8 kb 0.3%
Other Assets 8.2 mb 13.4%
Levels 82.1 kb 0.1%
Scripts 4.7 mb 7.7%
Included DLLs 6.4 mb 10.5%
File headers 201.5 kb 0.3%
Complete size 61.3 mb 100.0%
資源文件夾中使用的資產和文件,按未壓縮大小排序。
如何運行
雙擊UnitySizeExplorer.exe
轉到File>Open
導航到Unity Editor的日誌文件。 (通常在$ HOME \ AppData \ Local \ Unity \ Editor下)
選中或取消選中樹視圖中的項目,以從餅圖和估計的文件大小添加/刪除它們。 展開文件夾以直接使用其子項。
如果您首先過濾掉非常小的文件,因爲它們會混淆UI並使工具變得遲緩,這會有所幫助。 轉到過濾器>小。 這些項目仍將以最終大小計算,但將從樹視圖和餅圖中隱藏。
請注意,在Unity中構建之前必須清除日誌文件的內容,以確保只有一個大小條目。
https://github.com/aschearer/unitysizeexplorer
12、[Unity]禁用iOS上的加速度計以提高性能
如果您不在iOS中使用加速計,請在Unity菜單中的“文件>構建設置...”中
打開“播放器設置...” ,
在“其他設置”中“加速計頻率”“禁用”通過使
您可以提高性能只有一點點
PlayerSettings.accelerometerFrequency
如果要從腳本更改,請參閱上面的屬性
https://docs.unity3d.com/2018.3/Documentation/Manual/iphone-iOS-Optimization.html
爲什麼Android沒有這個選項?
13、 The relationship between AddForce () and physics. #unitytips
https://twitter.com/UnityBerserkers/status/1128421790685048832
我覺得評論中的 “爲什麼不是4個不同的功能; AddForce,Accelerate,SetMomentum和AddVelocity?” 也值得思考 ~~
類似講到Unity物理方法的 : https://connect.unity.com/p/why-we-add-forces-in-fixedupdate-not-in-update-eng Update和FixedUpdate 調用的區別分析
方法如AddForce()/ AddTorque()應在FixedUpdate()中被調用 (或與之相關的物理循環的其它方法)。
如果任何方法會導致主體以恆定加速度移動較長時間,則應在FixedUpdate()中調用它。
在我們僅向主體添加一次力的情況下,我們也可以在Update()中使用AddForce()/ AddTorque()方法。這種情況的一個例子可以是發射射彈,其中僅在運動開始時我們在射彈上增加力。
其實還有些位置同步是需要放到LateUpdate 中的, 否則顯示上會出現抖動。
14、
一個網站 http://alexanderameye.github.io ,其中有幾個Unity教程。 主題範圍從ocean waves到toon shading。 一定要看看!
“收集一些整潔的數學和物理技巧”
https://twitter.com/unitycoder_com/status/1128044764912390144
https://github.com/zalo/MathUtilities
15、 哦,看看我在暫存UPM存儲庫中找到了什麼,這是UIElements可視化編輯器的早期預覽版! “UI Builder允許您使用UIElements,UXML和USS直觀地創建和編輯UI” - 這對於使用UIElements製作編輯器工具非常有用?
此外,在2019.2β中他們添加了一個超級有用的示例工具,讓您可以看到默認樣式和代碼示例,瞭解如何使用所有控件和內容(Window➡UI➡UIElementsSamples)在我試圖弄清楚如何幫助時幫了很多忙 將類型分配給枚舉下拉列表
https://twitter.com/LotteMakesStuff/status/1127389073369391109
很多人都在期待 他的運行時支持。
16、方便#unity3d着色器提示:
如果您想要那麼酷的“立即修復”"Fix now"按鈕來更改屬性上法線貼圖的紋理類型,請在紋理採樣器之前添加“[Normal]”標籤!
並且不要忘記將“白色” "white" 改爲“凹凸”"bump" 作爲默認值!
https://twitter.com/HarryAlisavakis/status/1128211169741869056
17、 Project 選中資源有一個命令, “Find References in scene” 可以查看一個資源的引用。
ref:ty_public_resource/vegetation/Textures/ty_xiaocao04.tga
今天我試了一下, 把這個命令拷貝到 Project中進行搜搜, 果然可以使用。
t:prefab ref:ty_public_resource/vegetation/Textures/ty_xiaocao04.tga
兩個一起使用,讓它在 Prefab中搜索引用這個圖片的Prefab。
18、 關於New Memory Profiler https://forum.unity.com/threads/new-memory-profiler-preview-package-available-for-unity-2018-3.597271/
內存分析很強大, 但是2018.3 以下版本只能 只能使用舊的了~~~~
19、 [Unity]關於製作編輯器擴展插件時的平臺判斷
依賴於平臺的編譯:
using UnityEngine;
using System.Collections;
public class PlatformDefines : MonoBehaviour {
void Start () {
#if UNITY_EDITOR
Debug.Log("Unity Editor");
#endif
#if UNITY_IOS
Debug.Log("Iphone");
#endif
#if UNITY_STANDALONE_OSX
Debug.Log("Stand Alone OSX");
#endif
#if UNITY_STANDALONE_WIN
Debug.Log("Stand Alone Windows");
#endif
}
}
使用Application.platform:
if (Application.platform == RuntimePlatform.OSXEditor) {
// MacOS上的編輯器
}
else if (Application.platform == RuntimePlatform.WindowsEditor) {
// Windows上的編輯器
}
else if (Application.platform == RuntimePlatform.LinuxEditor) {
// Linux上的編輯器
}
else {
//其他
}
20、 [Unity] 當粒子回收時設置處理(回調)
OnParticleSystemStopped
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnParticleSystemStopped.html
因爲如果啓用了循環,則循環不會停止。不會執行OnParticleSystemStopped,
但是,如果您使用Stop停止ParticleSystem,將執行OnParticleSystemStopped。
21、 今天看到一個 靠拆包賺錢的工具 : 一直在更新~
https://devxdevelopment.com/UnityUnpacker
https://devxdevelopment.com/DevXUnityUnpackerChangeLog
可以直接打開包:
這個工具也是一樣, il2cpp 沒辦法完成看到實現部分, 只能看到類和函數簽名。
它這個AssetBundle 查看也不錯。 依賴關係全都出來了。
免費開源的工具 Il2CppDumper 反編譯的 il2cpp 只能看到類和函數的簽名,具體實現細節看不到。
針對於一幅大小爲 4M,像素分辨率爲 1024x1024,格式爲 RGBA(每通道8 位)的紋理而言,
Unity 中所有可用的 ASTC 塊大小所對應的壓縮比率。
合併網格以減少繪製調用數量
爲減少渲染所需的繪製調用數量,您可以使用Mesh.CombineMeshes() 方法將多個網格合併爲一 個網格。如果所有網格的材質相同,請將 mergeSubMeshes 參數設置爲 true,以便它可以根據合 並組中的每個網格生成單一子網格。
把多個網格合併爲單個較大的網格將幫助您:
創建更高效的遮擋器。
將多個基於圖塊的資源轉變爲大型、無縫、實心的單一資源。
網格合併腳本對性能優化很有幫助,但是這取決於您的場景構成。較大網格在視圖中的停留時間長於 較小網格,請進行試驗,以獲取正確的網格大小。 應用此技術的一種方法是在層級中創建空的遊戲對象,使其成爲您想要合併的所有網格的父網格,然 後爲其附加一個腳本。
避免讀取/寫入網格
如果運行時修改了模型,則 Unity 在保留原始數據的同時會在內存中另外保存一份網格數據副本。 如果運行時未修改模型(即使準備縮放),也請在導入設置的模型選項卡中禁用讀取/寫入已啓用選 項。這樣便無需另外保存一份副本,因而可節省內存。
使用紋理貼圖集
您可以使用紋理貼圖集減少一組對象所需的繪製調用數量。 紋理貼圖集是指合併成一個大型紋理的紋理組。多個對象可通過一組合適的座標重複使用此紋理。這 有助於 Unity 對共享相同材質的對象採用自動批處理。
設置對象的 UV 紋理座標時,請避免更改其材質的 mainTextureScale 和 mainTextureOffset 屬性。這會創建新的獨特材質,該材質無法與批處理一同運行。請通過 MeshFilter 組件獲取網格 數據並使用 Mesh.uv 屬性更改每個像素元的座標。 下圖顯示了紋理貼圖集:
使用 Early-z Mali
GPU 包含了執行 Early-Z 算法的功能。Early-Z 可通過去除過度繪製的片段來提升性能。 Mali GPU 通常對大部分內容執行 Eaxly-Z 算法,但在一些情形中並不執行,以達到確保正確度的目的。這可 能比較難以從 Unity 內部加以控制,因爲它依賴於 Unity 引擎以及編譯器生成的代碼。不過,您可以查看一些 跡象。
針對移動平臺編譯您的着色器,再查看其代碼。確保您的着色器不會落入下列類別之一:
着色器有副作用
這意味着着色器線程在執行期間修改了全局狀態,因此二次執行該着色器可能會產生不同的結果。通 常,這表示您的着色器寫入到共享的讀取/寫入內存緩衝區,如着色器存儲緩衝區對象或圖像。例如, 如果您創建通過遞增計數器來測量性能的着色器,它就有副作用。 如下所列不歸類爲副作用:
只讀內存訪問。
寫入到僅寫入緩衝區。
純粹的局部內存訪問。
着色器調用 discard()
如果片段着色器在執行期間可以調用 discard(),那麼 Mali GPU 無法啓用Early-Z。這是因爲,片 段着色器可以丟棄當前的片段,但深度值之前被 Early-Z 測試修改,而這是無法逆轉的。 啓用了 Alpha-to-coverage 如果啓用了 Alpha-to-coverage,則片段着色器會計算稍後爲定義 alpha 而要訪問的數據。 例如,在渲染一棵樹的樹葉時,它們通常會表示爲平面,其紋理定義樹葉的哪個區域是透明還是不 透明。如果啓用了Early-Z,您會獲得不正確的結果,因爲場景的一部分可能會被該平面的透明部分 遮擋。
深度源不固定的函數
用於深度測試的深度值不來自頂點着色器。如果您的片段着色器寫入到 gl_FragDepth,Mali GPU 無法執行 Early-Z 測試。
個人主頁挺有創意: 可以跟3d場景物體交互 。
https://github.com/lettier/3d-game-shaders-for-beginners 3d-game-shaders-for-beginners
Table Of Contents
- Setup
- Building The Example Code
- Running The Demo
- Reference Frames
- GLSL
- Render To Texture
- Texturing
- Lighting
- Normal Mapping
- Outlining
- Fog
- Bloom
- SSAO
- Depth Of Field
- Posterization
- Cel Shading
- Pixelization
- Sharpen
- Film Grain
一個遊戲: https://logicworld.net/
https://habr.com/ru/post/453564/#Content
https://play.google.com/store/apps/details?id=com.ViacheslavRud.Circuit
Unity中實現類似於 數字電路 使用標準邏輯門(AND,NAND,OR,NOR,XOR,XNOR,NOR,NOT)
類似RPGMaker的國產遊戲開發工具,但是能開發的遊戲類型不止RPG https://www.evkworld.com/