Unity 3D 腳本教學 (三)

上一篇主題 下一篇主題 向下

Unity 3D 腳本教學 (三)

發表  aaa1218bbb 于 周五 11月 25, 2011 2:08 pm

概覽:實例化
實例化,複製一個物體。包含所有附加的腳本和整個層次。它以你期望的方式保持引用。到外部物體引用的克隆層次將保持完好,在克隆層次上到物體的引用映射到克隆物體。
實例化是難以置信的快和非常有用的。因為最大化地使用它是必要的。
例如, 這裡是一個小的腳本,當附加到一個帶有碰撞器的剛體上時將銷毀它自己並實例化一個爆炸物體。
var explosion : Transform;
// 當碰撞發生時銷毀我們自己
// 並生成給一個爆炸預設
function OnCollisionEnter (){
Destroy (gameObject);
var theClonedExplosion : Transform;
theClonedExplosion = Instantiate(explosion, transform.position, transform.rotation);
}
實例化通常與預設一起使用
概覽:Coroutines & Yield
在編寫遊戲代碼的時候,常常需要處理一系列事件。這可能導致像下面的代碼。
private var state = 0;
function Update()
{
if (state == 0) {
// 做步驟0
state = 1;
return;
}
if (state == 1) {
// 做步驟1
state = 2;
return;
}
// …
}
更方便的是使用yield語句。 yield語句是一個特殊類型的返回,這個確保在下次調用時該函數繼續從該yield語句之後執行。
while(true) {
// 做步驟0
yield; //等待一幀
// 做步驟1
yield; //等待一幀
// ...
}
你也可以傳遞特定值給yield語句來延遲Update函數的執行,直到一個特定的事件發生。
// 做一些事情
yield WaitForSeconds(5.0); //等待5秒
//做更多事情…
可以疊加和連接coroutines。
這個例子執行Do,在調用之後立即繼續。
Do ();
print ("This is printed immediately");
function Do ()
{
print("Do now");
yield WaitForSeconds (2);
print("Do 2 seconds later");
}
這個例子將執行Do並等待直到它完成,才繼續執行自己。
//鏈接coroutine
yield StartCoroutine("Do");
print("Also after 2 seconds");
print ("This is after the Do coroutine has finished execution");
function Do ()
{
print("Do now");
yield WaitForSeconds (2);
print("Do 2 seconds later");
}
任何事件處理句柄都可以是一個coroutine
注意你不能在Update或FixedUpdate內使用yield,但是你可以使用StartCoroutine來開始一個函數。
參考YieldInstruction, WaitForSeconds, WaitForFixedUpdate, Coroutine and MonoBehaviour.StartCoroutine獲取更多使用yield的信息。
概覽:用C#編寫腳本
除了語法,使用C#或者Boo編寫腳本還有一些不同。最需要注意的是:
1.從MonoBehaviour繼承
所有的行為腳本必須從MonoBehaviour繼承(直接或間接)。在Javascript中這自動完成,但是必須在C#或Boo腳本中顯示申明。如果你在Unity內部使用Asset -> Create -> C Sharp/Boo Script菜單創建腳本,創建模板已經包含了必需的定義。
public class NewBehaviourScript : MonoBehaviour {...} // C#
class NewBehaviourScript (MonoBehaviour): ... # Boo
2.使用Awake或Start函數來初始化
Javascript中放置在函數之外的代碼,在C#或Boo中要放置在Awake或Start中。
Awake和Start的不同是Awake在場景被加載時候運行,而Start在第一次調用Update或FixedUpdate函數之前被調用,所有Awake函數在任何Start函數調用之前被調用。
3.類名必須與文件名相同
Javascript中,類名被隱式地設置為腳本的文件名(不包含文件擴展名)。在c#和Boo中必須手工做。
4.在C#中Coroutines有不同語法。
Coroutines必有一個IEnumerator返回類型,並且yield使用yield return… 而不是yield…
using System.Collections;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour {
// C# coroutine
IEnumerator SomeCoroutine ()
{
// 等一幀
yield return 0;
//等兩秒
yield return new WaitForSeconds (2);
}
}
5.不要使用命名空間
目前Unity還不支持將代碼放置在一個命名空間中,這個需要將會出在未來的版本中。
6.只有序列化的成員變量會顯示在檢視面板中
私有和保護成員變量只在專家模式中顯示,屬性不被序列化或顯示在檢視面板中。
7.避免使用構造函數
不要在構造函數中初始化任何變量,使用Awake或Start實現這個目的。即使是在編輯模式中Unity也自動調用構造函數,這通常發生在一個腳本被編譯之後,因為需要調用構造函數來取向一個腳本的默認值。構造函數不僅會在無法預料的時刻被調用,它也會為預設或未激活的遊戲物體調用。
單件模式使用構造函數可能會導致嚴重的後果,帶來類似隨機null引用異常。
因此如果你想實現,如,一個單件模式,不要使用構造函數,而是使用Awake。其實上,沒有理由一定要在繼續自MononBehaviour類的構造函數中寫任何代碼。
概覽:最重要的類
Javascript中可訪問的全局函數或C#中的基類
移動/旋轉物體
動畫系統
剛體
FPS或第二人稱角色控制器
概覽:性能優化
1.使用靜態類型
在使用Javascript時最重要的優化是使用靜態類型而不是動態類型,Unity使用一種叫做類型推理的技術來自自動轉換Javascript為靜態類型編碼而不需要你做任何工作。
var foo=5;
在上面的例子裡foo會自動被推斷為一個整型值。因此,Unity可能使用大量的編譯時間來優化。而不使用耗時的動態名稱變量查找等。這就是為什麼Unity比其他在JavaScript的實現平均快20倍的原因之一。
唯一的問題是,有時並非一切都可以做類型推斷。 Unity將會為這些變量重新使用動態類型。通過回到動態類型,編寫JavaScript代碼很簡單。但是這也使得代碼運行速度較慢。
讓我們看一些例子:
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
這裡foo將是動態類型,因此調用DoSomething函數將使用較長時間,因為foo的類型是未知的,它必須找出它是否支持Do​​Something函數,如果支持,調用它。
function Start ()
{
var foo : MyScript = GetComponent(MyScript);
foo.DoSomething();
}
這裡我們強制foo為指定類型,你將獲得更好的性能。
2.使用#pragma strict
當然現在問題是,你通常沒有意識到你在使用動態類型。 #pragma strict解決了這個!簡單的在腳本頂部添加#pragma strict。然後,unity將在腳本中禁用動態類型,強制使用靜態類型,如果一個類型未知。 Unity將報告編譯錯誤。那麼在這種情況下foo將在編譯時產生一個錯誤:
#pragma strict
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}
3.緩存組件查找
另一個優化是組件緩存。不幸的是該優化需要一點編碼,並且不一定是值得的,但是如
果你的腳本是真的用了很長時間了,你需要把最後一點表現出來,這是一個很好的優化。
當你訪問一個組件通過GetComponent或訪問變量,Unity會通過遊戲對象找到正確的組件。這一次可以很容易地通過緩存保存在一個私有變量裡引用該組件。
簡單地把這個:
function Update ()
{
transform.Translate(0, 0, 5);
}
變成:
private var myTransform : Transform;
function Awake ()
{
myTransform = transform;
}
function Update ()
{
myTransform.Translate(0, 0, 5);
}
後者的代碼將運行快得多,因為Unity沒有找到變換在每一幀遊戲組件中的對象。這同樣適用於腳本組件,在你使用GetComponent代替變換或者其它的東西。
4.使用內置數組
內置數組的速度非常快,所以請使用它們。
而整列或者數組類更容易使用,因為你可以很容易地添加元素,他們幾乎沒有相同的速度。內置數組有一個固定的尺寸,但大多數時候你事先知道了最大的大小在可以只填寫了以後。關於內置數組最好的事情是,他們直接嵌入在一個結構緊湊的緩衝區的數據類型沒有任何額外的類型信息或其他開銷。因此,遍歷是非常容易的,作為一切緩存在內存中的線性關係。
private var positions : Vector3[];
function Awake ()
{
positions = new Vector3[100];
for (var i=0;i<100;i++)
positions[i] = Vector3.zero;
}
5.如果你不需要就不要調用函數
最簡單的和所有優化最好的是少工作量的執行。例如,當一個敵人很遠最完美的時間就是敵人入睡時可以接受。直到玩家靠近時什麼都沒有做。這是種緩慢的處理方式的情況:
function Update ()
{
// 早期進行如果玩家實在是太遙遠。
if (Vector3.Distance(transform.position, target.position) > 100)
return;
perform real work work...
}
這不是一個好主意,因為Unity必須調用更新功能,而你正在執行工作的每一個幀。一個比較好的解決辦法是禁用行為直到玩家靠近。有3種方法來做到這一點:
1.使用OnBecameVisible和OnBecameInvisible。這些回調都是綁到渲染系統的。只要任何相機可以看到物體,OnBecameVisible將被調用,當沒有相機看到任何一個,OnBecameInvisible將被調用。這種方法在很多情況下非常有用,但通常在AI中並不是特別有用,因為只要你把相機離開他們敵人將不
可用。
function OnBecameVisible () {
enabled = true;
}
function OnBecameInvisible ()
{
enabled = false;
}
2.使用觸發器。一個簡單的球形觸發器會工作的非常好。一旦你離開這個影響球你將得到OnTriggerEnter/Exit調用。
function OnTriggerEnter (c : Collider)
{
if (c.CompareTag("Player"))
enabled = true;
}
function OnTriggerExit (c : Collider)
{
if (c.CompareTag("Player"))
enabled = false;
}
3.使用協同程序。 Update調用的問題是它們每幀中都發生。很可能會只需要每5秒檢檢查一次到玩家的距離。這應該會節省大量的處理週期。
概覽:腳本編譯(高級)
Unity編譯所有的腳本為.NET dll文件,.dll將在運行時編譯執行。
這允許腳本以驚人的速度執行。這比傳統的javascript快約20倍。比原始的C++代碼慢大約50%。在保存的時候,Unity將花費一點時間來編譯所有腳本,如果Unity還在編譯。你可以在Unity主窗口的右下角看到一個小的旋轉進度圖標。
腳本編譯在4個步驟中執行:
1.所有在"Standard Assets", "Pro Standard Assets" 或"Plugins"的腳本被首先編譯。
在這些文件夾之內的腳本不能直接訪問這些文件夾之外腳本。
不能直接引用或它的變量,但是可以使用GameObject.SentMessage與它們通信。
2.所有在"Standard Assets/Editor", "Pro Standard Assets/Editor" 或"Plugins/Editor"的腳本被首先編譯。
如果你想使用UnityEditor命名空間你必須放置你的腳本在這些文件夾中,例如添加菜單項或自定義的嚮導,你都需要放置腳本到這些文件夾。
這些腳本可以訪問前一組中的腳本。
3.然後所有在"Editor"中的腳本被編譯。
如果你想使用UnityEditor命名空間你必須放置你的腳本在這些文件夾中。例如添加菜單單項或自定義的嚮導,你都需要放置腳本到這些文件夾。
這些腳本可以訪問所有前面組中的腳本,然而它們不能訪問後面組中的腳本。
這可能會是一個問題,當編寫編輯器代碼編輯那些在後面組中的腳本時。有兩個解決方法:1、移動其他腳本到"Plugins"文件夾2、利用JavaScript的動態類型,在javascript中你不需要知道類的類型。在使用GetComponent時你可以使用字符串而不是類型。你也可以使用SendMessage,它使用一個字符串。
4.所有其他的腳本被最後編譯
所有那些沒有在上面文件夾中的腳本被最後編譯。
所有在這裡編譯的腳本可以訪問第一個組中的所有腳本("Standard Assets","Pro
Standard Assets" or "Plugins")。這允許你讓不同的腳本語言互操作。例如,如果你想創建一個JavaScript。它使用一個C#腳本;放置C#腳本到"Standard Assets"文件夾並且JavaScript放置在" Standard Assets"文件夾之外。現在JavaScript可以直接引用c#腳本。
放置在第一個組中的腳本,將需要較長的編譯時間,因為當他們被編譯後,第三組需要被重新編譯。因此如果你想減少編譯時間,移動那些不常改變的到第一組。經常改變的到第四組。

_________________
歡迎大家多多交流~

avatar
aaa1218bbb
社員
社員

文章數 : 65
積分 : 2144
注冊日期 : 2011-11-22

檢視會員個人資料 http://design.gugebb.com

回頂端 向下

上一篇主題 下一篇主題 回頂端


 
這個論壇的權限:
無法 在這個版面回復文章