不是什么技術文章,純粹是我個人學習是遇到一些覺得需要注意的要點,當成筆記。
1.關於調試,在Android下無法斷點,Debug也無法查看,已修正參考第37條
查看日志方法可以啟動adb的log功能,或者自己寫個GUI控件直接在屏幕上顯示Info 參考30
2.所有自定義的編輯器擴展插件腳本必須放在Editor文件夾里,不然會導致編譯程序時出錯,
放到Editor文件下,編譯成游戲時才會忽略這些腳本
3.打包資源時,假設是在移動設備上使用,打包方式務必選擇成:BuildTarget.Android
BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.Android)
否則到安卓上會讀不出資源。PC則無此問題。
4.重新打包資源后,加入在LoadAssetBunldes時沒給指定讀取新版本,例如原來為:LoadFromCacheOrDownload(path, 1);
現在仍然為LoadFromCacheOrDownload(path, 1);那么是無法讀取到新包的。因為系統會先從緩存檢查有沒有版本為1的同名bundle,如果有,則直接使用緩存的,
如果沒有,則讀取這個新的包。因此,重新打包文件后,應該給文件包升1級,讀取的同時也多讀一級。這樣才會讀出來。
5.如果在測試的時候為了避免頻繁打包頻繁換級造成麻煩,則可以在每次加載時或者打包時選擇手動清空緩存:Caching.CleanCache();這樣就會強制讀取最新的包。
但千萬不要在正式版使用,因為這樣是清空全部數據,頻繁的讀取會造成性能消耗。PC就無所謂了。
6.想要控制磁盤緩存不超標. 只要設置Caching.maximumAvailableDiskSpace 的值為你預期的容量大小就可以(byte為單位)例如, 想要限定200M的緩存空間可以這樣:Caching.maximumAvailableDiskSpace = 200*1024*1024;
7.畫線可以用LineRenderer,或者直接GL畫,或者更方便的可以Debug.DrawLine,甚至可以將物理射線畫出Debug.DrawRay,但是Debug畫出的線只能在調試模
式看得到,編譯成游戲后將不再出現。參見:http://www.cnblogs.com/jeason1997/p/4805825.html
8.判斷游戲是否聯網
Application.internetReachability == NetworkReachability.NotReachable
NotReachable 網絡不可達
ReachableViaCarrierDataNetwork 網絡通過運營商數據網絡是可達的。
ReachableViaLocalAreaNetwork 網絡通過WiFi或有線網絡是可達的。
9.UGUI做在人物頭上血條的HUD的制作方法
public Transform follow;
Vector2 position = Camera.main.WorldToScreenPoint(follow.position);
img.rectTransform.position = position;//位置
img.rectTransform.localScale = new Vector2(2,2);//大小
10.動態改變相機的Culling Mask
http://answers.unity3d.com/questions/348974/edit-camera-culling-mask.html
11.安卓調式時,連接上手機,然后打開epclise就可以看到locat輸出Debug內容了。
12.AssetBundle依賴關系
如果一個公共對象被多個對象依賴,我們打包的時候,可以有兩種選取。一種是比較省事的,就是將這個公共對象打包到每個對象中。這樣會有很多弊端:內存被浪費了;加入公共對象改變了,每個依賴對象都得重新打包。AssetBundle提供了依賴關系打包。
//啟用交叉引用,用於所有跟隨的資源包文件,直到我們調用PopAssetDependencies BuildPipeline.PushAssetDependencies(); var options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets; //所有后續資源將共享這一資源包中的內容,由你來確保共享的資源包是否在其他資源載入之前載入 BuildPipeline.BuildAssetBundle( AssetDatabase.LoadMainAssetAtPath("assets/artwork/lerpzuv.tif"), null, "Shared.unity3d", options); //這個文件將共享這些資源,但是后續的資源包將無法繼續共享它 BuildPipeline.PushAssetDependencies(); BuildPipeline.BuildAssetBundle( AssetDatabase.LoadMainAssetAtPath("Assets/Artwork/Lerpz.fbx"), null, "Lerpz.unity3d", options); BuildPipeline.PopAssetDependencies(); 這個文件將共享這些資源,但是后續的資源包將無法繼續共享它 BuildPipeline.PushAssetDependencies(); BuildPipeline.BuildAssetBundle( AssetDatabase.LoadMainAssetAtPath("Assets/Artwork/explosive guitex.prefab"), null, "explosive.unity3d", options); BuildPipeline.PopAssetDependencies(); BuildPipeline.PopAssetDependencies();
13.非MonooBehavior腳本實現協程:
一般要實現多線程功能(即協程)時,一般都是在MonoBehavior腳本里StartCoroutine一個返回值為IEnumerator的函數,
但有些時候,需要在非繼承自MonoBehaviro的腳本(例如單例等)里也實現多線程效果,就無法通過自身實現了,我采取這樣的方法:
public class CoroutineProvider : MonoBehaviour { private static CoroutineProvider instance = null; public static CoroutineProvider GetInstance() { if (instance == null) { GameObject go = new GameObject(); go.name = "CoroutineProvider"; instance = go.AddComponent<CoroutineProvider>(); } return instance; } }
創建一個繼承自MonoBehavior的協程提供者,該提供者初始時並不存在,但有地方需要使用到協程時,就通過:
CoroutineProvider.GetInstance().StartCoroutine(Fuction());
這個時候就會在Scene里實例化一個GameObject,以它作為協程提供者的身份出現。
14.創建HTTP服務器並添加自定義格式文件下載支持:
為了讓游戲能夠自動更新,需每次登陸游戲時都向服務器下載版本文件驗證游戲版本,若發現版本不同,
則下載文件更新列表,根據列表下載最新的資源,但默認情況下HTTP只能下載一些默認資源,例如:rar,txt,xml等,
像“.assetbundle”這類的的自定義格式默認情況下HTTP服務器是無法下載的,如果提交請求,例如:http://127.0.0.1/Aseet/Resource.assetbundle,
則結果是404。為了能夠下載自定義格式文件,需要配置服務器,比如IIS的做法就是:
打開管理器->點擊根節點->在右邊找到IIS列表->找到MIME類型->打開並添加自定義格式擴展名
15.尋找游戲中的某個對象
為了降低耦合,盡量避免對象腳本間通過“public value”拖動對象進行互相引用。
但有時候有必須某個對象引用另一個對象,可以通過在腳本中查找對象。
常用的查找方法有:GameObject.Find / FindWithTag 等一系列,這種方法尋找對象雖然最簡單,
但是效率卻比較低,盡量避免在Update中使用,最好就是在Start和Awake中使用。
還有一種高效的做法,就是將特定對象歸類,並集中放到一個List中,使用時查找就快速多了。
16.獲取擁有某種類型組件的子gameobject,后面的參數的意思是運行獲取不活躍的對象,即active = false
gameObject.GetComponentsInChildren<Component> (true);
17.只刪除gameobject 腳本組件會殘留
徹底刪除方法如下:
GameObject go = new GameObject();
Component c = go.GetComponent<Component >();
MonoBehaviour.DestroyImmediate(c);
MonoBehaviour.DestroyImmediate(go);
18.單例模式慎用,如果一個GameObject對象為單例對象,請小心在其他的GameObject的OnDestory里引用該單例
因為在程序關閉前會執行所有GameObject的OnDestory,但是執行的先后順序不定。
如果GameObject_1在OnDestory時引用GameObject_2(單例對象),假設GO2的OnDestory先運行,這時候它的
Instance為null,被GO1引用時它會再創建一次。
而如果在OnDestory里創建任何對象,該對象都將不會被UnityEngine釋放,一直保留下來。
19.Unity3D 優化技術(3D圖形方面)
① http://blog.csdn.net/candycat1992/article/details/42127811
② http://blog.csdn.net/leonwei/article/details/18042603
③ 盡量不要動態的instantiate和destroy object,使用object pool代替,前者的開銷很大。
20.Unity中的.meta文件
每個資源文件(外部導入和內部創建的)對應一個.meta文件。這個.meta文件中的guid就唯一標識這個資源。
內部創建的資源會使用外部導入的資源,比如 內部資源材質Material使用貼圖Textures(預制體、場景中使用了更多的資源)。
材質怎么知道自己使用了那些資源呢? 就在自己的文件中記錄着其它資源的GUID。
而且每個meta里的標識都是隨機產生的,而且同一文件多次生成的meta文件里的標識一不同。
在多人合作項目中,如果你上傳資源時沒有同步上傳.meta文件,那么別人的機器就會為這個文件生成一個,但標識可能和你不同,
導致的結果就是Prefab引用的各種組件丟失。
21.Editor運行時從Scene視圖觀察對象
在Hierarchy選擇要觀察的GameObject,鼠標聯系點擊4下,
這樣的話,Scene視圖就會將焦點集中在該GameObject身上,
就跟綁定一個攝影機一樣,調式的時候非常方便。
22.Profiler分析器
分析器平時只檢測關鍵代碼,如果要檢測所有代碼,
就必須深度檢測,但深度檢測又非常消耗性能。所以有時候只想檢測某一小段代碼的時候,
可以設置檢測段:
Profiler.BeginSample ("標簽"); // 要檢測性能的代碼 Profiler.EndSample ();
然后就可以在分析器中找到相應的標簽了:
23.Unity 5.3 C# 部分源碼
https://bitbucket.org/xuanyusong/unity-decompiled/src/779abf10e556?at=master
24.調節腳本執行順序
Unity的不同腳本間的執行順序一般是沒有規矩的,不同腳本的同一函數,例如Start,啟動的順序也不同。
因此很少在U3D的腳本里的同一函數做先后順序依賴。例如在腳本A的Start里調用腳本B的某個屬性,而該
屬性又在腳本B的Start函數里初始化,由於不知道兩者的Start的先后順序,因此一般改為,在腳本B的Awake里
初始化該屬性,而不是Start。
但要強制改變腳本的執行順序也可以:
在這里調節腳本執行順序,數值越小,越先執行
25.通過命令行在控制台編譯U3D項目
示例:
(Unity.exe Path) -batchmode -projectPath (Project Path) -executeMethod MyEditorScript.MyBuildMethod -quit -logFile (Log Path)
UnityPath : Windows: C:\program files\Unity\Editor\Unity.exe
UnityPath : Mac OS: /Applications/Unity/Unity.app/Contents/MacOS/Unity
26.C# 以及 Uniyt3D 添加全局預編譯指令
#define DEBUG #if DEBUG dosome; #endif
吶,這就是預編譯指令拉,要注意,這不叫宏,跟C/C++的宏還是有一定差距的。
至於如何定義,在代碼里定義的話,當疼的C#只能在每個文件的文件頭定義才生效,也就是說,你在test1.cs里定義#define DEBUG的話,
那么你也必須在test2.cs里的頭部定義一個才能在test2里生效。如果有一百個文件要使用這個預編譯調節,那么你就要手動定義100次。
還好微軟留了下后路,其實全局預編譯指令可以在項目里的*.CSharp.csproj里設置,
右鍵項目,在屬性里設置即可,或者干脆直接用文本文件打開項目路徑下的該文件,直接編輯。
Unity也給我們留了解決方案,不過藏得比較深:
Editor->ProjectSetting->Player->Other->如圖紅圈處
需要注意的是,Unity里設置的全局預編譯,是按平台分開的,
也就是說,你在安卓平台下設置的指令,在PC平台是沒法使用的,除非復制過去。
這里有個插件可以方便地設置預編譯指令:
源代碼:
下載后把中文部分去掉,扔到“Editor”文件夾下,然后打開使用便是
27.Unity3D自定義Debug
Unity的Debug功能比較有限,有時候要自定義一些特殊的Debug,比如封裝一個Logger,在Editor平台時打印日志
到控制台,而在其他平台,則打印信息到屏幕或輸出到Log文件。功能倒挺容易實現,問題就是,這個封裝的Logger底層
部分也是通過Debug實現,在調試的時候要實現Unity控制台那種雙擊日志就跳到日志輸出的地方的功能,就比較麻煩了。
因為就是Logger封裝了Debug,雙擊的時候也不會跳到Logger.Log的地方,而是跳到內部用Debug實現的地方,導致調式
不方便。
解決方法就是,將Logger編譯成dll,然后再在Unity內部引用,這時候Debug輸出控制台的時候,雙擊日志就會直接跳到Logger.Log。
雨松也是這么做的:http://www.xuanyusong.com/archives/2782
28.Unity3D里的特殊文件夾以及腳本編譯順序
第一階段: Standard Assets, Pro Standard Assets 和Plugins文件夾之內的腳本,編譯好后為:Assembly-CSharp-firstpass.dll(C#)
第二階段: 在第一階段那些文件夾里任何名字為'Editor'的文件夾里的腳本,例如'Plugins/Editor'
第三階段: 所有其他不在'Editor'文件夾里的腳本,編譯好后為:Assembly-CSharp.dll(C#)
第四階段: 其他剩余腳本,例如'Editor'文件夾里的腳本(注,不是第二階段那些在特殊文件夾里的Editor),編譯好后為:Assembly-CSharp-Editor.dll(C#)
在U3D里,注意腳本的編譯順序有時候極其重要,例如:
要在C#腳本里引用UnityScprite腳本時,或者反過來,那么被引用的那些腳本,就必須在前一階段先編譯,不然會出現‘找不到腳本’的編譯錯誤。
29.Unity AssetBundle打包資源的一個坑:
就是Shader不會被打包進去,如果在對象身上掛有Shader,那么加載AB后,會出現Shader丟失的情況。
解決方法就是對通過AB動態加載的對象在Awake時做個判定,看看自身的Shader列表是否為空,如果為空,
則手動從本地加載。
30.通過Application.RegisterLogCallback來捕獲Debug輸出:
該方法可以監聽到unity中所有的log消息,然后你可以選擇保存到文件中或者顯示到gui上做調試來用。
通過LogType來判斷處理哪些類型的消息需要被保存或顯示。
LogType如下:
Error | LogType used for Errors. |
Assert | LogType used for Asserts. (These indicate an error inside Unity itself.) |
Warning | LogType used for Warnings. |
Log | LogType used for regular log messages. |
Exception | LogType used for Exceptions. |
using UnityEngine; using System.Collections; public class example : MonoBehaviour { public string output = ""; public string stack = ""; void OnEnable() { Application.RegisterLogCallback(HandleLog); } void OnDisable() { Application.RegisterLogCallback(null); } void HandleLog(string logString, string stackTrace, LogType type) { output = logString; stack = stackTrace; } }
31.物理射線檢測之BoxCast
在Physics下的RayCast已經很熟悉了,今天發現有一個BoxCast,起初以為它的意思就是檢測給定立方體內的物體,
如果物體在該Box內,則返回true:
如圖,但實際使用起來總是檢測失敗,仔細看下BoxCast的參數:
第一二個參數很好理解,立方體的中心,立方體的大小,第三個參數就讓我有點疑惑了,竟然都已經決定好Box的大小了,為何還有方向這個概念,
直接檢測是否在Box里不就好,知道去Google了下網上的用法才知道:
原來這個BoxCast的意思是,在給定的center處,建立一個給定大小的Box,然后往給定的方向一直往前拖/射
出這個Box,一直拉長,然后檢測這條路上的所有障礙物,大概效果如圖(中間的空隙是我加上便於理解的,實際中檢測是無縫的)
2D版
using UnityEngine; public class NewBehaviourScript : MonoBehaviour { public Vector2 pos; public Vector2 Size; public float angle; public Vector2 dir; public float dis; void Update() { var hit = Physics2D.BoxCast(pos, Size, angle, dir, dis); if (hit.collider != null) { Debug.Log(hit.point); } } private void OnDrawGizmos() { Gizmos.color = new Color(1, 0, 0, 0.3f); Quaternion rot = new Quaternion(); rot.eulerAngles = new Vector3(0, 0, angle); // 變換矩陣 Matrix4x4 trs = Matrix4x4.TRS(pos, rot, Vector2.one); Gizmos.matrix = trs; // 在該Transform的Vector3.zero處繪制一個位置、角度、縮放均與該Transform一樣的方塊,大小為size Gizmos.DrawCube(Vector3.zero, Size); // 復位Gizmos設置 Gizmos.matrix = Matrix4x4.identity; } }
32.U3D中的Time
一般Time中有兩個比較重要的參數:
Time.realTime: 表示現實中的時間
Time.time: 表示游戲中的時間(受TimeScale影響)
而我們游戲中的Time.fixedTime,Time.deltaTime都是與游戲時間對應,而不是與現實時間對應。
比如我們的fixedTime設置為0.2,那就表明游戲中每0.2“秒”會調用一次FixedUpdate,而這個0.2秒並不是現實中的0.2秒,
也就是說,FixedUpdate不能保證現實時間中每0.2秒調用一次,他的實際調用還會受到TimeScale等的影響。
如果我們在一個FixedUpdate里做太多的內容,那么,現實中每一個FixedUpdate的時長就會拉長,但是在游戲中,兩個FixedUpdate
的間隔仍然是0.2秒(可以理解為TimeScale變化了,但實際上不是),造成的結果就是,我們看到的游戲比較卡,幀數下降。
結論:
FixedUpdate與Update都不能保證在現實中多久調用一次,只能保證在游戲時間中FixedUpdate固定調用,而Update會受渲染影響
同樣的游戲,當FixedUpdate里處理的內容很少時
FixedUpdate處理的內容較多,可以明星地看到卡了
但是這兩個例子,Time.fixedTime都是0.2s
同理, deltaTime表示的是游戲中每兩個Update中的間隔時間,即完成最后一幀的時間,
所以如果你要讓一個物體勻速移動,你應該在Update里與Time.deltaTime相乘。當你乘以Time.deltaTime實際表示:每秒移動物體10米,而不是每幀10米。如果是每幀10米,游戲卡頓會造成物體移動變成非勻速。
33.物理中的Collider性能對比
首先,這是一個有700多顆樹的場景,每棵樹都掛有一個Rigibody組件
沒有任何Collider時:
Box: 可見大概漲了1ms
Shpere: 漲幅巨大,大概是Box的6-7倍
Capsule:雖然也漲幅比較大,但沒Sphere嚴重
由此可見,性能上Box >> Capsule > Shpere,
分別耗時:1ms 7.4ms 5ms,
網上有篇試驗教程,是09年的,結果跟我完全反過來,不知道是不是這幾年來U3D物理進行了大優化
http://forum.unity3d.com/threads/capsule-vs-box-colliders.34254/#post-222443
另外,以上是基於動態Collider(所謂動態Collider,即該Collider掛載在一個非靜止的物體的上,即有Rigibody且不為IsKinematic)
如果是在IsKinematic條件下,則性能消耗會進一步下降,比如以上情況,Box的情況為:
如果把Rigibody去掉,那么情況也差不多,主要消耗性能的部分是動態Collider
34. Unity里的Transform在SetPos或者SetRot時會造成很大的性能開銷的原因:
這種情況一般是因為該Transform的GameObject上掛有太多的Collider,Collider分為靜態與動態部分,
靜態的碰撞體,物理系統會批優化處理,他們基本不占多少資源。但如果這個Collider是動態運行的,比如一直在
改變位置或者旋轉,那么物理系統會不斷重新計算他,如果你的GameObject上掛有太多的Collider,那么在
改變Transform時就會造成額外的巨大開銷。
解決方法就是盡量減少運動的Collider,如果一個物體身上的Collider太多,可以嘗試使用MeshCollider的方式來優化,
在運動的情況下,一個MeshCollider的開銷比許多個小BoxCollider要小得多。
35.物理各種射線檢測性能對比
36.精簡縮小程序集
若想縮減移動端的游戲包大小,可以通過在打包選項里做手腳:
Api Compatibility Level 設置為 .NET 2.0 Subset的話,就會只將.NET的子集導出,僅有部分常用功能
Stripping Level的話也差不多,設置不同級別能精簡導出不同的程序集,越精簡的話,導出的函數越少,這樣雖然包小了,
但若用到的部分功能被剔除的話,說不定程序會crash,因此若要用這功能,先去Unity官方的列表里查哪些函數是包含在哪個級別里的:
https://docs.unity3d.com/410/Documentation/ScriptReference/MonoCompatibility.html
比如SetAccessControl這條函數,就只有在.NET 2.0原版的程序集中能用,其他的都不行。
縮減的效果很明顯(上為.NET 2.0 Subset,下位.NET 2.0):
37.通過WIFI調試安卓上的Unity項目
1.首先在手機上開啟USB調試功能,並安裝驅動(這一步很多手機助手都可以完成)。
2.用USB電纜連接手機和電腦。
3.確保手機和電腦在一個局域網內,簡單的說就是電腦和手機共用一個路由器,網段一樣。
4.打開電腦上CMD窗口,輸入以下命令:
adb tcpip 5555(該命令打開手機adb網絡調試功能)
正常情況下輸入命令后控制台會出現回顯
restarting in TCP mode port: 5555
打開手機查看手機的IP地址(不會請百度)假設手機的地址是192.168.1.x輸入命令
adb connect 192.168.1.x
如果一切正常控制台會回顯以下內容
connected to 192.168.1.x:5555
如果你想查看是否連接成功請輸入以下內容
adb devices
控制台會回顯連接的設備
5.如果一切連接成功,請拔掉USB電纜,選擇File->Build&Run,在編譯之前要勾選上Development Build 和Script Debugging這兩項(在build setting里面勾選不要忘記否則是不能調試的)電腦會自動編譯文件並將APK推送至手機,在手機上同意並安裝。
6.當程序運行后再Monodevelop里面打開Run->Attach to process 會發現你手機的選項,選擇手機,在腳本里面添加斷點,你發現可以調試了,那叫一個爽!出現問題再也不用去瞎猜,或者添加Debuglog了。
7.同時UnityEditor的Profiler也可以用了(要記得在上面輸入IP)
8.若想打印安卓的log,則可以在CMD里輸入adb logcat -s Unity -d > xxx.txt,“xxx.txt”為具體路徑,其中Unity是過濾用的tag,unity中的所有輸出都是“Unity”
注:除了第5步要用USB才能裝好,其他的都在WIFI環境下測試成功,而且貌似遠程調試不能用USB,只能用WIFI?
38.安卓游戲拆包
有時候在玩一些外國廠商做的比較大型的手游的時候,經常會發現一個情況,就是安裝包下來很小,一般也就20來M,然后還要下載一個很大的obb文件放到/SDcard/Android/obb/packName里,
那是因為一些平台限制上傳的apk容量,所以開發者無奈只能將游戲的資源分包出來,然后再在游戲運行時下載下來。用壓縮文件打開obb,會發現它其實也就是一個壓縮文件,像U3D的游戲,里面存放
的一般是/assets/data/里的一些資源,其實我們手動將obb里的資源解壓后放入apk里,然后重新回編譯apk,會發現也可以直接玩。
U3D拆包的做法:
勾上最后一項“Split Application Binary”就會將數據包和apk分離,然后在打開apk之后,進入選擇下載或直接下載界面,將你的.obb文件從服務器或者其他地方下載下來
先把apk安裝到Android設備,然后將對應obb文件改名為:main.<Bundle Version Code>.<包名>.obb
並拷貝到Android設備的“/android/obb/<包名>/ ”路徑下。
如在Unity3D編輯中,你可以在工程設置的如圖位置處,看到“Bundle Version Code”和包名(即“Bundle Identifier”).
假設“Bundle Version Code”值為2,包名為“com.Demo.ABC”:
- 首先,在Android設備上安裝ABC.apk;- 接着,將ABC.obb改名為“main.2.com.Demo.ABC.obb”;
- 然后,將文件“main.2.com.Demo.ABC.obb”拷貝到Android設備的“/android/obb/com. Demo.ABC/”路徑下;
- 啟動App,你會發現新安裝的APP已經可以正常使用了。

39.Smcs.rsp文件
可以在Unity Assets目錄下創建smcs.rsp文件,並向其中添加預編譯命令,其會在unity啟動時執行,比如新建一個smcs.rsp文件,向其中添加內容:
-define:MYDEF
然后就可以在腳本中加入宏判斷:
#if MYDEF .... #endif
其原理是啟動Unity時會執行unity目錄下的smcs.exe文件並添加預編譯命令,也可以通過cmd運行smcs.exe逐個添加預編譯命令。
另外還有可以創建gmcs.rsp文件,對應Editor腳本中的預編譯命令。
比如如果想要在C#語言中使用指針,必須標記為unsafe的,默認情況下unity中使用unsafe標記會報錯,可以在項目中添加smcs.rsp文件並加入-unsafe預編譯命令,就可以編譯通過。
注:Unity5.6貌似將smcs.rsp改成smc.rsp
40.Unity代碼的編譯運行流程
早期:
安卓:c# -> IL -> mono jit -> 機器碼
蘋果:c# -> mono aot -> 機器碼
現在
安卓:c# -> IL -> mono jit -> 機器碼
c# -> IL2CPP -> 機器碼
蘋果:c# -> IL2CPP -> 機器碼
未來?
安卓:c# -> IL -> LLVM jit -> 機器碼
蘋果:c# -> IL -> LLVM aot -> 機器碼
mono jit:在運行時,CLR動態將IL代碼解釋成機器碼,然后運行
mono aot:在編譯時,將IL代碼解釋成機器碼,運行時直接執行機器碼
il2cpp:在編譯時,將IL代碼轉成C++代碼,再將C++代碼編譯成機器碼
llvm(一種中間件,mono跟.net core都有對應的實現):http://www.mono-project.com/docs/advanced/runtime/docs/llvm-backend/
之所以早期蘋果用aot而現在換成il2cpp,是因為Unity的mono2.6只支持32位aot,而蘋果已經限制32位應用了
未來很有可能用微軟的.net core方案,然后用llvm實現對ios平台的支持
41.Unity編輯器監聽腳本重載事件
[InitializeOnLoad] public class UnityScriptCompiling:AssetPostprocessor { [UnityEditor.Callbacks.DidReloadScripts] static void AllScriptsReloaded() { } }
除此之外,編輯器還能監聽其他時間,例如監聽資源文件被打開:
42.Unity的RuntimeInitializeOnLoadMethod屬性
Unity 5.0開始增加了RuntimeInitializeOnLoadMethodAttribute,這樣就很方便在游戲初始化之前做一些額外的初始化工作,比如:Bulgy參數設置、SDK初始等工作。
用此屬性修飾某個靜態方法,則這個方法就會被Unity注冊到系統中,游戲啟動的時候,會自動檢測並運行該方法,就算打包了游戲,依然會生效
注意:該方法是Unity注冊的,也就是說,一個已經編譯好的游戲,就算你反編譯dll,並加入自己的靜態方法並用上該屬性,也不會生效
(注冊方式,估計是通過名字,寫在資源文件里了,看能不能找到是注冊在哪個文件里頭的)
using UnityEngine; public class Test { [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void OnBeforeSceneLoadRuntimeMethod () { Debug.Log("Before scene loaded"); } [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void OnAfterSceneLoadRuntimeMethod() { Debug.Log("After scene loaded"); } [RuntimeInitializeOnLoadMethod] static void OnRuntimeMethodLoad() { Debug.Log("RuntimeMethodLoad: After scene loaded"); } }
43.Unity中提供的Attribute有很多,如果自己寫程序擴展編輯器的功能,就需要了解這些屬性。常用的有:
1、AddComponentMenu 導航欄菜單
2、ContextMenu 右鍵菜單
3、HeaderAttribute
4、HideInInspector 可以讓public變量在Inspector上隱藏,無法在Editor中進行編輯
5、MultilineAttribute 支持輸入多行文本
6、RangeAttribute 限定輸入值的范圍
7、RequireComponent 組件依賴,使用該組件后自動添加依賴組件
8、RuntimeInitializeOnLoadMethodAttribute
9、SerializeField 強制對變量進行序列化,即使變量是private
10、SpaceAttribute 增加空位
11、TooltipAttribute 提示信息,當鼠標移到Inspector上時顯示相應的提示
12、InitializeOnLoadAttribute 在啟動Unity編輯器並打開項目的時候運行編輯器腳本
13、InitializeOnLoadMethodAttribute
14、MenuItem 導航欄的菜單項
44.Unity層級結構Hierarchy優化
Hierarchy Structure Guidelines
- If something moves every frame, make sure all its children care about position. Only rendering, physics, audio, or core systems like that should be there.
- When dynamically creating game objects at runtime, if they do not need to be children of the spawner for the above reasons, spawn things at the root of the scene.
- You can easily register everything you spawn and pass along the ActiveInHeirarchy state of the spawner using OnEnable and OnDisable.
- Try to group your moving transforms such that you have around 50 or so GameObjects per root. This lets the underlying system group your TransformChangeDispatch jobs into a fairly optimal amount of work per thread. Not so few that the thread overhead dominates; not so many that you are waiting on thread execution.