不是什麼技術文章,純粹是我我的學習是遇到一些以爲須要注意的要點,當成筆記。php
1.關於調試,在Android下沒法斷點,Debug也沒法查看,已修正參考第37條
查看日誌方法能夠啓動adb的log功能,或者本身寫個GUI控件直接在屏幕上顯示Info 參考30html
2.全部自定義的編輯器擴展插件腳本必須放在Editor文件夾裏,否則會致使編譯程序時出錯,
放到Editor文件下,編譯成遊戲時纔會忽略這些腳本node
3.打包資源時,假設是在移動設備上使用,打包方式務必選擇成:BuildTarget.Android
BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.Android)
不然到安卓上會讀不出資源。PC則無此問題。android
4.從新打包資源後,加入在LoadAssetBunldes時沒給指定讀取新版本,例如原來爲:LoadFromCacheOrDownload(path, 1);
如今仍然爲LoadFromCacheOrDownload(path, 1);那麼是沒法讀取到新包的。由於系統會先從緩存檢查有沒有版本爲1的同名bundle,若是有,則直接使用緩存的,
若是沒有,則讀取這個新的包。所以,從新打包文件後,應該給文件包升1級,讀取的同時也多讀一級。這樣纔會讀出來。ios
5.若是在測試的時候爲了不頻繁打包頻繁換級形成麻煩,則能夠在每次加載時或者打包時選擇手動清空緩存:Caching.CleanCache();這樣就會強制讀取最新的包。
但千萬不要在正式版使用,由於這樣是清空所有數據,頻繁的讀取會形成性能消耗。PC就無所謂了。小程序
6.想要控制磁盤緩存不超標. 只要設置Caching.maximumAvailableDiskSpace 的值爲你預期的容量大小就能夠(byte爲單位)例如, 想要限定200M的緩存空間能夠這樣:Caching.maximumAvailableDiskSpace = 200*1024*1024;c#
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有不少,若是本身寫程序擴展編輯器的功能,就須要瞭解這些屬性。經常使用的有:
一、AddComponentMenu 導航欄菜單
二、ContextMenu 右鍵菜單
三、HeaderAttribute
四、HideInInspector 可讓public變量在Inspector上隱藏,沒法在Editor中進行編輯
五、MultilineAttribute 支持輸入多行文本
六、RangeAttribute 限定輸入值的範圍
七、RequireComponent 組件依賴,使用該組件後自動添加依賴組件
八、RuntimeInitializeOnLoadMethodAttribute
九、SerializeField 強制對變量進行序列化,即便變量是private
十、SpaceAttribute 增長空位
十一、TooltipAttribute 提示信息,當鼠標移到Inspector上時顯示相應的提示
十二、InitializeOnLoadAttribute 在啓動Unity編輯器並打開項目的時候運行編輯器腳本
1三、InitializeOnLoadMethodAttribute
1四、MenuItem 導航欄的菜單項
44.Unity層級結構Hierarchy優化