天天看點

Unreal腳本筆記

原文:

http://udn.epicgames.com/Two/UnrealScriptLanguageReference.html

  将遊戲邏輯放在腳本這樣的沙盒(Sandbox)中,保護環境用戶端調用,防止程式崩潰   虛幻腳本是: 類似java的無指針,自動垃圾回收機制 比C++更進階的時間,狀态,屬性和網絡     引擎程式員使用C++寫功能子產品,并使用腳本封裝成腳本可通路對象方法,腳本程式員根據遊戲邏輯需求書寫邏輯代碼,并暴露可供調試和設定的屬性,通過編輯器可以很友善的編輯和修改這些屬性   需要花費時間的做事情的函數叫做延時函數,類似Sleep FinishAnim MoveTo等。延時函數隻能在狀态函數中調用,而不能是普通函數。普通函數隻有在延時函數挂起時才能被執行   虛幻隻是模拟windows的線程概念而非照抄,windows線程效率很低。   所有虛幻的對象腳本執行都是并行的,對象獨立的   所有對象都繼承于Object Actor(繼承于Object)定義了一些列諸如可控制的移動,互動和影響的行為 Pawn(繼承于Actor) 一個擁有高階AI和玩家控制級别的對象 Class(繼承于Object) 定義了一類物體(有别于C++的class)   類定義修飾符: Native: 表示其被C++背景支援,如果你有一個類為native Robots,虛幻将會在磁盤上需找Robots.dll實作腳本功能,前提是,C++類必須提供 IMPLEMENT_CLASS 宏定義   Abstrat:          抽象類,類似于C++的接口類,接口類本身不能被執行個體化,子類必須實作其方法方可執行個體化。在虛幻腳本中有一個Pawn類,其本身隻有用類似Brute類才能被執行個體化   Guid(a,b,c,d):          現階段沒有被使用,在以後支援COM時将會被添加到虛幻中 Transient:          表明這個類的不必儲存到磁盤,也就是說這種類并不是永久使用的,而是暫時的。類似的有Players,Windows Config(section_name):          一些有必要儲存的變量,全局變量。一個類就是一個ini檔案   虛幻腳本的name類型可以是函數,狀态或者類等等的預定義名稱,它和字元串的差別就在于:字元串可以動态修改,而name一次确定,不再修改 變量修飾符: Input:可以被虛幻輸入系統識别的變量,類似于将鍵盤或者搖桿的輸入映射于某個變量   Native:          從C++裝載或者儲存的類型   虛幻隻支援1維數組,多元數組請自行實作   函數修飾符 Static:          與C++不同的是:虛幻腳本靜态函數隻能通路靜态函數,一些變量的預設值,不能調用非靜态,執行個體化了的函數。并且不能被重載   Singular:          一種防止函數被遞歸調用的修飾。當你寫一個Actor碰撞另外一個Actor的邏輯時,函數遞歸調用将會導緻棧溢出。添加這個修飾符後,已經被調用的函數就不會被重複調用。   Latent:          延時函數:在狀态中調用一個延時函數,在過一定的tick後,該函數才會被傳回   Iterator:          定義一個函數為疊代器,用于使用foreach周遊一個actor list   Simulated:          當一個actor是模拟代理(simulated proxy)或者獨立代理( autonomous proxy )時決定一個函數在用戶端運作   Operator:          與C++類似   Event:          隻是一個編譯器修飾,在使用 unreal -make –h 可以将腳本事件函數與 C++ 連接配接代碼對應起來。   腳本中的 event Touch( Actor Other ) { ... }   在被列出源碼為 void Touch(class AActor* Other) {     FName N("Touch",FNAME_Intrinsic);     struct {class AActor* Other; } Parms;     Parms.Other=Other;     ProcessEvent(N,&Parms); }     狀态 虛幻腳本是唯一一個于語言層支援狀态的語言 使用狀态的好處:          可以很友善的書寫基于狀态的函數(邏輯),一些函數和邏輯就可以被不同的狀态共享,而其執行結果隻是決定于actor實際在做什麼。          狀态中可以使用延時函數,這是一種特殊類型的函數,C++和java都沒有實作。例如:你可以實作以下邏輯:打開一個門,等待2秒後播放音效然後再打開另外一道門,釋放一些怪,讓它們打玩家,你可以以非常普通常見,線性的邏輯來書寫這些代碼。而具體的細節将有虛幻底層來實作         3種主要的延時函數 Sleep:          暫停腳本執行一段時間,後繼續執行   FinishAnim          等待目前播放的動畫完畢後繼續執行。使用這個函數可以很友善的書寫動畫驅動的邏輯,AI邏輯就是嚴格的的動畫驅動(有别于時間驅動),平滑的動畫控制是一個AI系統的關鍵   FinishInterpolation          等待目前茶緻電動作完成後繼續     狀态的流程控制:          使用Goto語句在不同的代碼段間切換   auto state MyState { Begin:      Log( "MyState has just begun!" );      Sleep( 2.0 );      Log( "MyState has finished sleeping" );      goto Begin; }   使用GotoState轉入執行某個狀态     執行個體 // This is the automatic state to execute. auto state Idle {      // When touched by another actor…      function Touch( actor Other )      {               log( "I was touched, so I’m going to Attacking" );               GotoState( ‘Attacking’ );               Log( "I have gone to the Attacking state" );      } Begin:      log( "I am idle…" );      sleep( 10 );      goto ‘Begin’; }   // Attacking state. state Attacking { Begin:      Log( "I am executing the attacking state code" );      //... }   運作結果 I am idle... I am idle... I am idle... I was touched, so I’m going to Attacking I have gone to the Attacking state I am executing the attacking state code       狀态繼承和可視規則   // 父類 class MyParentClass expands Actor;   // 非狀态函數 function MyInstanceFunction() {          log( "Executing MyInstanceFunction" ); }   // A 狀态 state MyState {          // A 狀态函數          function MyStateFunction()          {                    Log( "Executing MyStateFunction" );          } // Begin标簽 Begin:          Log("Beginning MyState"); }   // 子類實作 class MyChildClass expands MyParentClass;   // 重載非狀态函數 function MyInstanceFunction() {          Log( "Executing MyInstanceFunction in child class" ); }   // 重定義狀态,并且重載函數 state MyState {          // 被重載的函數          function MyStateFunction()          {                    Log( "Executing MyStateFunction" );          } // 标簽被重載 Begin:          Log( "Beginning MyState in MyChildClass" ); }     規則: 1.       如果對象是一個狀态,無論被定義過多少次,最後重載的那個類将會被執行 2.       否則,按照重載次數,并且是非狀态版本的函數被執行   狀态可以同樣使用expand來繼承狀态 // Base Attacking state. state Attacking {          }   // 上段攻擊 state MeleeAttacking expands Attacking {          }   //遠距離攻擊. state RangeAttacking expands Attacking {          }     使用ignores指定忽略的函數 state Retreating {          // 忽略以下函數的調用          ignores Touch, UnTouch, MyFunction;   }     預設屬性:          預設屬性可以在物體在編輯器或者建立的時候給出一些列的預設值。這是一種很好的可重入性表現和代碼規範的标準       技術細節:          虛幻的垃圾回收采用與JAVA 虛拟機類似的樹跟随方式(tree-following),回收器使用Uobject的序列化功能遞歸的擷取到哪些物體引用了其他的對象            虛幻腳本是基于bytecode的,編譯時,腳本将類似于java一樣被編譯為虛拟機代碼。這使得其可以做到友善的跨平台            虛幻腳本是2次pass,第一遍pass确定變量,狀态和函數定義,第二遍編譯為p-code。這種機制使得複雜的循環依靠的邏輯繼承結構可以完整的編譯并且連結                   可恢複的玩家狀态:虛幻可以在遊戲進行的任何時刻儲存所有actor的狀态和運作情況到磁盤,并在必要時恢複。因為其狀态和函數執行隻是運作于低端的虛拟機,運作結果隻是依賴于棧資料,是以,儲存棧資料是非常關鍵的   虛幻檔案包:這是一種包含索引,序列化dump和對象細節的二進制格式包。其很類似于DLL,可以擁有一個引用其他包的清單。這樣可以很友善的将預定義包放在Internet提供下載下傳,已經下載下傳的包将不會在下載下傳以減少下載下傳次數(manifest機制)       開發政策 C++腳本大約每秒能執行5千萬條指令,虛幻腳本能執行250萬條指令,相差20倍,整個遊戲中,腳本執行需要花費CPU 5~10%的時間。使用虛幻腳本做一些你實際感興趣的時間,或者需要自定義的部分。而不是将一些基礎移動的代碼重新書寫一遍,實體系統可以為你做一切。   大量使用延時函數,如果依然按照時間驅動的傳統方式書寫這類邏輯将是低效的   代碼書寫中減少無限遞歸的出現:在Move指令執行中,actor将發生碰撞,然後調用Bump函數,如果在Bump中依然有Move指令将形成無限遞歸。無限遞歸和死循環是虛幻腳本不能很好解決的地方(所有語言都是-_-! Tim太謙虛)   建立和銷毀actor将是伺服器端一個非常耗費資源的操作,特别是在網絡遊戲中,銷毀和銷毀中需要發包,占用一定的網絡帶寬。   盡量使用面向對象特性。在寫新功能時盡可能的重載已有函數和狀态讓代碼清爽,友善修改,便于和其他人的成果內建。避免使用傳統的C技巧,類似于使用switch判斷actor的狀态并執行不同的操作。這樣的操作阻止你加入新的類和修改一些東西。