2012年12月30日 星期日

在C++中存取UnrealScript函式

在C++中要存取UnrealScript函式時,一般的做法是宣告此類別和函式為原生類別和函式,這樣在編譯UnrealScript時會自動產生對應的 C++類別和函式定義,就可讓C++認得此函式了。不過如果是非原生類別,難道就沒有其他方法可以存取函式嗎?答案是當然可以,不然用C++實作的 UnrealScript虛擬機就寫不出來了。

由UnrealScript定義的函式必須放在一個繼承自Object的類別內,UnrealScript並沒有支援非成員函式。Object類別在C++中的對應型別為UObject。由於Object這個名稱容易混淆,所以接下來 皆以UObject稱之。每個UObject都會記得自己所屬類別以及相關成員,包括函式在內。可以用UObject::FindFunction()函式取得對應的UFunction物件,並且用ProcesEvent()函式執行UFunction。

UnrealScript類別的狀態(state)內也可定義函式,會遮蔽同名的非狀態函式。FindFunction()預設會先找目前作用中的狀態是否有同名函式,然後再找父狀態,都沒有才找同名的非狀態函式。如果不想要狀態函式的話可以讓第二個參數為TRUE。

使用ProcessEvent()函式時要注意如果要執行的UFunction有參數或傳回值,第二個參數必須傳入一塊代表參數和傳回值的區塊,用來傳遞給UFunction實際的參數並且傳回執行結果。例如若要在C++執行以下UnrealScript函式:
function int foo(int i);

2012年12月22日 星期六

自訂樹狀結構編輯器

許多Unreal資源都以樹狀或圖(一種以相連的結點構成的數學模型)表示,例如AnimTree和Kismet。為了方便撰寫這類編輯器,UEd提供一組以LinkedObj為名的類別,在此翻譯為鏈結物件。

鏈結物件以方形繪出,左右下三邊可能會有一個方塊或三角形代表接點。鏈結物件有四種接點:輸入、輸出、變數和事件。輸入在左,輸出在右,變數在左下,事件在右下。請參考下圖:
以下列出鏈結物件相關的類別:

2012年11月30日 星期五

自訂預覽視窗

先前提過如何撰寫資源編輯器的主視窗,接著本篇來介紹如何撰寫預覽視窗。許多資源編輯器都有預覽視窗,例如AnimSetViewer和AnimTreeEditor。UEd提供FEditorLevelViewportClient類別方便開發者撰寫預覽畫面,以及FPreviewScene類別方便管理預覽場景,接下來說明一下幾個相關的類別。

FEditorLevelViewportClient


在一般的資源編輯器中,預覽畫面大多是繼承FEditorLevelViewportClient類別加以改寫,它代表用來看預覽畫面的一個視埠,可說是撰寫預覽視窗時的主要框架。其實它不限用於預覽,Matinee編輯器的軌道視窗也是用它寫的。FEditorLevelViewportClient類別並不包括視窗的部分,所以需要把它放進一個視窗。它以虛擬函式的方式提供輸入、繪製、更新的擴充界面。以下列出幾個主要的成員:

2012年11月27日 星期二

自訂資源編輯器

自訂新的資源時,通常也需要在UEd裡撰寫相應的編輯器。就好比AnimSet有AnimSetViewer,AnimTree有AnimTreeEditor。UEd的GUI是用wxWidgets寫的,這是一個第三方跨平台的GUI函式庫。

為了結合UE系統以及寫碼方便,UEd繼承wxWidgets的類別進行擴充。原本wxWidgets的類別前綴小寫的wx,UEd繼承後變成首字大寫的Wx,所以可以依此區分是否為原wxWidget內建的型別。

以下列出幾個撰寫編輯器主視窗時常用的類別。當然編輯器不是只有主視窗而已,只是篇幅已經太長,其他像顯示3D物件的部份就另外再寫一篇好了。

2012年10月31日 星期三

利用ini檔存放全域參數

開發專案時,有時會需要開放全域參數給美術或企劃人員設定。程式員可以把這些參數放在一個UnrealScript類別,然後建立一個物件儲存在固定的package檔裡,這其實就跟一般的Unreal資源很像。也可以把參數儲存在Unreal的ini檔裡,格式跟Windows的ini檔很像,一樣也是用section分類存放key=value設定。它也支援UnrealScript類別,一個section存放一個類別的屬性預設值設定。下表列出這三種方法的比較:

存放方式 固定路徑資源 ini檔的section方式 ini檔的屬性預設值方式
程式存取方式 載入物件後存取 呼叫存取函式 建立物件後存取
文字檔存取 不可
屬性視窗存取 不可 可,但需另外寫程式
執行時期更新 可,但需另外寫程式 可,但需另外寫程式 在遊戲中可用set指令修改

以方便性考量,建議將參數放在一個UnrealScript類別後利用ini檔儲存。這樣可以在文字檔內編輯參數,也可在遊戲執行時使用set指令修改參數。set指令的格式如下:
set <類別名稱> <屬性名稱> <數值>

2012年10月10日 星期三

在C++中存取UnrealScript物件屬性

在C++中要存取UnrealScript物件的屬性時,一般的做法是宣告此類別為原生類別,這樣在編譯UnrealScript時會自動產生對應的C++類別定義,就可讓C++認得此類別的成員。不過如果是非原生類別,難道就沒有其他方法可以存取其屬性嗎?答案是當然可以,不然用C++實作的UnrealScript虛擬機就寫不出來了。

由UnrealScript定義的類別必須繼承自Object,它在C++中的對應型別為UObject。由於Object這個名稱容易混淆,所以接下來皆以UObject稱之。每個UObject都會記得自己當初建立時的UnrealScript類別,在UnrealScript中可以用Object:Class屬性取得,在C++中可以用UObject::GetClass()函式取得。它在C++中是一個型別為UClass的物件,記錄著對應UnrealScript類別的相關資訊,可以從這裡得知這個類別包括屬性在內的所有成員。

取得屬性


以下程式碼展示如何在一個UObject中取得其所有屬性:
for( UProperty* Property = GetClass()->PropertyLink; Property; Property = Property->PropertyLinkNext )
{
    ...
}
UnrealScript屬性在C++中的對應型別稱為UProperty,它是一個基底類別,依據屬性的型別有各自的具象類別:

2012年9月26日 星期三

動態存取UnrealScript的enum

雖然對於UnrealScript來說,enum一旦編譯完是不能修改的。但對C++而言就不是這麼一回事了,畢竟UnrealScript是用C++實作出來的。

例如AudioDevice類別的ESoundClassName列舉,在uc檔裡只宣告了一個Master項目:
enum ESoundClassName
{
     Master
};
實際上編輯SoundMode時,型別為ESoundClassName的SoundClassName卻會列舉出所有的SoundClass。其實這是因為在C++中可以動態修改UnrealScript的enum。

2012年9月16日 星期日

自訂骨架控制

UE的動畫樹(AnimTree)提供動畫混合、變形混合、骨架控制三種功能。它可以讓開發者自訂節點來擴充新的功能,在此篇簡單說明一下如何自訂骨架控制節點。

骨架控制節點的基礎類別叫做SkelControlBase,其他的骨架控制節點都會直接或間接繼承自此類別。它定義數個C++虛擬函式以供子類別擴充,以下列出重要的成員:
  • GetAffectedBones(BoneIndex, SkeletalMeshComponent, OutBoneIndices):可覆寫此函式傳回要修改的骨頭之索引值。
  • CalculateNewBoneTransforms(BoneIndex, SkeletalMeshComponent, OutBoneAtoms):可覆寫此函式傳回修改後的骨頭位置和方向。傳回的陣列必須和GetAffectedBones()相對應。
  • CalculateNewBoneScales(BoneIndex, SkeletalMeshComponent, OutBoneScales):可覆寫此函式傳回修改後的骨頭縮放值。傳回的陣列必須和GetAffectedBones()相對應。
  • GetBoneScale(BoneIndex, SkeletalMeshComponent):可覆寫此函式傳回此骨架控制的骨頭縮放值。
  • TickSkelControl(DeltaSeconds, SkeletalMeshComponent):可覆寫此函式每畫面更新骨架控制節點。

2012年8月30日 星期四

自訂變形節點

UE的動畫樹(AnimTree)提供動畫混合、變形混合、骨架控制三種功能。它可以讓開發者自訂節點來擴充新的功能,在此篇簡單說明一下如何自訂變形節點。

變形節點的基礎類別叫做MorphNodeBase,其他的變形節點都會直接或間接繼承自此類別。它定義數個C++虛擬函式以供子類別擴充,以下列出重要的成員:
  • NodeName [name] :使用者可自行定義的節點名稱。可以呼叫SkeletalMeshComponent的FindMorphNode()函式找到指名的變形節點。
  • InitMorphNode(SkeletalMeshComponent) :動畫樹會呼叫此函式初始化變形節點。
  • GetNodes(TArray<UMorphNodeBase*>& OutNodes):列出此節點和其下所有節點。
  • GetActiveMorphs(TArray<FActiveMorph>& OutMorphs):列出以此節點算起子樹的所有節點裡,作用中的變形目標和其權重。

2012年8月18日 星期六

自訂編輯器引擎

UEd在開啟編輯器前會先建立一個型別為EditorEngine的編輯器引擎物件,然後將它的位址存放在全域指標GEditor裡。UEd提供讓開發者自訂編輯器引擎的功能,只要繼承UnrealEdEngine自訂引擎類別,然後在DefaultEngine.ini裡指定自訂的類別即可:
[Engine.Engine]
EditorEngine=MyEditor.MyEditorEngine 
EditorEngine類別提供許多虛擬函式可供覆載,以下列出重要的函式:
  • Init():引擎初始化。
  • PreExit():關閉引擎前會呼叫這個函式。
  • Tick( DeltaSeconds ):更新遊戲時會呼叫這個函式。
  • Exec( Command, OutputDevice=GLog ):執行控制台指令。
  • PlayInEditor():在編輯器中啓動試玩功能時會呼叫這個函式。
  • EndPlayMap() :在編輯器中關閉試玩功能時會呼叫這個函式。

2012年7月31日 星期二

自訂遊戲引擎

UE在執行遊戲前會先建立一個型別為GameEngine的遊戲引擎物件,然後將它的位址存放在全域指標GEngine裡。UE提供讓開發者自訂遊戲引擎的功能,只要繼承GameEngine自訂引擎類別,然後在DefaultEngine.ini裡指定自訂的類別即可:
[Engine.Engine]
GameEngine=MyGame.MyGameEngine 
GameEngine類別提供許多虛擬函式可供覆載,以下列出重要的函式:
  • Init():引擎初始化。
  • PreExit():關閉引擎前會呼叫這個函式。
  • Tick( DeltaSeconds ):更新遊戲時會呼叫這個函式。
  • Exec( Command, OutputDevice=GLog ):執行控制台指令。

2012年7月16日 星期一

優化AnimTrail

AnimTrail讓粒子跟隨動作拉出拖曳效果,可以用來製作武器揮動的特效。它需要指定一個資料型別為AnimTrail的粒子系統,並且在動作資料AnimSequence加上AnimNotify_Trails來指定特效的起始和結束時間。在AnimNotify_Trails上還要指定三個跟隨骨架移動的socket名稱,用來定位粒子。在編輯AnimNotify_Trails時,就會以指定的更新頻率取樣socket位置並且記錄下來,以供播放特效時內插粒子多邊形的位置。

在使用AnimTrail時,我發現幾個不盡理想的地方。我把其中兩個的問題和解法記錄在此,以供參考。

已建粒子系統元件的判斷方法

2012年6月30日 星期六

自訂Kismet條件節點

Kismet可以讓開發者透過圖形界面編排遊戲腳本,例如當玩家碰觸開關時把門打開,或是進入門口時播放過場動畫。Kismet的基本結構是以各種不同功能的節點連接構成,節點可分為事件、行動、條件、變數。除了UE內建的功能外,開發者也可自行擴充需要的節點。在此篇簡單說明一下如何自訂條件節點。

條件節點的基礎類別是SequenceCondition,重要的屬性有:
  • ObjName [string]:編輯器顯示的名稱。
  • ObjCategory [string]:編輯器選單的分類。在右鍵選單的New Condition的子選單裡會看到同名的分類選單。指定新的分類名稱會產生新的分類選單。 
雖然SequenceCondition是條件節點的基礎類別,實際上它並沒有為條件節點增加什麼功能,它的父類別SequenceOp就已經提供了條件節點所需的功能。子類別必須覆寫Activated函式或Activated事件來輸出訊號。前者是是C++虛擬函式,要有原始碼授權才能覆載。UDK使用者可以使用Activated事件。

2012年6月17日 星期日

自訂UnrealScript迭代子

UnrealScript的迭代子(iterator)提供簡潔的foreach語法循序取得一個陣列中的所有元素。而且它不限只能用於陣列,開發者可以自訂迭代子取出任何列表的元素,即使這個列表實際上並不存在。在Actor.uc裡即可找到許多迭代子的宣告,例如:
native(307) final iterator function TouchingActors( class<Actor> BaseClass, out Actor OutActor );
迭代子宣告在一個原生UnrealScript類別裡,由C++實作,實際上是一個成員函式。要在UnrealScript中使用一個迭代子,需要透過foreach語法。下列程式碼示範如何使用一個迭代子:
function PrintTouchingActors()
{
    local Actor TouchedActor;
    foreach TouchingActors(class'Actor', TouchedActor)
    {
        `log(TouchedActor.Name);
    }
}

2012年6月9日 星期六

自訂UnrealScript運算子

UnrealScript提供自訂運算子的功能,可以不修改引擎原始碼而在自訂專案中添加新的運算子。運算子宣告在一個原生UnrealScript類別裡,由C++實作,實際上是一個成員函式。在Object.uc裡即可找到許多運算子的宣告,連最簡單的整數加法都有:
native(146) static final operator(20) int + ( int A, int B );
關鍵字native後面括弧裡面的數字是靜態查表索引,索引值不可重覆。靜態查表不限用於運算子,只要是原生函式都可使用。它的查詢速度比一般的動態查表快,通常是用在常用的函式。關鍵字operator用來宣告運算子,後面括弧裡面的數字代表運算優先權,用來建立先乘除後加減的規則。優先權越高的越先運算。

UnrealScript運算子只能使用固定組合的字符,以下列出其中幾個可用的字符以及它們在程式碼中的名稱:
+ Add
- Subtract
* Multiply
/ Divide
+= AddEqual
-= SubtractEqual
*= MultiplyEqual
/= DivideEqual
++ AddAdd
-- SubtractSubtract
== EqualEqual

2012年6月3日 星期日

動態列表

動態列表(Dynamic List)是一個連UE本身也尚未使用的功能,UDN上也沒有相關的網頁,在此簡單的說明一下。它用於屬性視窗,原本字串或名稱屬性的預設控制項是打字輸入,使用動態列表的話,可以把控制項改成是自訂選項的下拉式清單。使用這個功能需要作三個設定:
  • 想要動態列表的字串或名稱屬性要加上元資料設定。
  • 在DefaultEditor.ini裡設定自訂屬性控制項為UnrealEd.PropertyInputCombo
  • 覆寫UObject的GetDynamicListValues()函式以自訂選項。第一個參數即為元資料設定的值。第二個參數為要傳回的選項。

2012年5月26日 星期六

自訂Matinee軌道

UE的Matinee提供開發者在預定時間點變更物件屬性的功能,它是以軌道為基本單位來編排,例如一條軌道代表位置,另一條軌道代表顏色。它可以讓開發者自訂軌道來擴充新的功能。Matinee軌道的基礎類別是InterpTrack,編輯器會尋找所有的InterpTrack類別並且列舉出來。所以如果你自訂一個InterpTrack類別,編譯完就可以在下次開啟Matinee編輯器時看到它。

在Matinee編輯器中,左邊看到軌道名稱是寫在InterpTrack類別的TrackTitle屬性,左邊的圖示則由GetTrackIcon()函式指定。而按右鍵新增軌道時顯示的名稱是寫在當地化組態檔Descriptions.int裡。所以當自訂軌道時,記得要去新增一個名稱設定。

InterpTrack可以指定另外兩個擴充類別:
  • 其中一個是實例類別,用來維護執行狀態,由TrackInstClass屬性指定。
  • 另外一個是輔助類別,用來擴充編輯功能,由GetEdHelperClassName()函式指定。

2012年5月13日 星期日

開關Unreal屬性編輯

開發者可以在UnrealScript中為一個屬性指定另一個布林屬性作為可編輯開關。當指定的布林屬性為false時,在UEd的屬性視窗中被指定的屬性控制項會變成不可編輯的失能狀態。

指定的方法是透過設定UnrealScript的EditCondition元資料。以下程式碼展示如何設定EditCondition元資料:
class TestEditCondition extends Object;

var() bool bSwitch1;

var() int Int1 <EditCondition = bSwitch1>;

var bool bSwitch2;

var() int Int2 <EditCondition = bSwitch2>;

2012年5月7日 星期一

自訂Kismet事件

Kismet可以讓開發者透過圖形界面編排遊戲腳本,例如當玩家碰觸開關時把門打開,或是進入門口時播放過場動畫。Kismet的基本結構是以各種不同功能的節點連接構成,節點可分為事件、行動、條件、變數。除了UE內建的功能外,開發者也可自行擴充需要的節點。在此篇簡單說明一下如何自訂事件節點。

事件節點的基礎類別是SequenceEvent,重要的屬性有:
  • ObjName [string]:編輯器顯示的名稱。
  • ObjCategory [string]:編輯器選單的分類。在右鍵選單的New Event的子選單裡會看到同名的分類選單。指定新的分類名稱會產生新的分類選單。
  • bAutoActivateOutputLinks [bool]:當事件發生時,是否自動啓動輸出連結。
  • bPlayerOnly [bool]:是否限定玩家才能啟動這個事件。

2012年4月28日 星期六

自訂Kismet行動

Kismet可以讓開發者透過圖形界面編排遊戲腳本,例如當玩家碰觸開關時把門打開。Kismet的基本結構是以各種不同功能的節點連接構成,節點可分為事件、行動、條件、變數。除了UE內建的功能外,開發者也可自行擴充需要的節點。此篇簡介一下如何自訂行動節點。

行動節點的基礎類別是SequenceAction,重要的屬性有:
  • ObjName [string]:編輯器顯示的名稱。
  • ObjCategory [string]:編輯器選單的分類名稱。在右鍵選單的New Action的子選單裡會看到同名的分類選單。指定新的分類名稱會產生新的分類選單。
  • HandlerName [name]:當此節點啟動時,呼叫指定名稱的UnrealScript函式。指定的函式要寫在節點作用的Actor類別上或操作該Actor的Controller類別上,而不是在節點類別上。若沒有指名,則以節點類別名稱自動產生,例如SeqAct_XXX會呼叫OnXXX函式。

2012年4月21日 星期六

在Visual Studio建立UnrealScript專案

nFringe是一套可以讓Visual Studio支援UnrealScript的軟體,它可以建立UnrealScript專案,支援IntelliSense和除錯器,有分要收費的商業授權和免費的非商業授權。非商業授權可以免費註冊並且使用所有腳本相關功能,對於不想花錢的UDK使用者而言,能免費使用中斷點和單步執行等除錯功能絕對是一大福音。

安裝


  • 首先安裝好Visual Studio 2008並且確認可以使用。
  • 下載nFringe,並且安裝。
  • 註冊nFringe。一定要註冊才能正常運作

建立UnrealScript專案


從主選單點選File>New>Project..,會出現一個對話窗:

2012年4月14日 星期六

UnrealScript起步

UnrealScript是UE所提供的腳本語言,透過它你可以控制角色移動、運鏡、播放動畫和特效、甚至畫出簡單的UI。UDK的使用者只需要文字編輯器就可編寫UnrealScript了。每次起動UDK時會自動偵測腳本是否有被修改,所以寫完UnrealScript要測試時只要重新起動UDK就可以了。本篇為UDK的使用者簡介如何入門UnrealScript。請注意本篇使用的UDK版本為2012年3月版。

撰寫腳本


安裝完UDK後,可以在Development\Src\UTGame\Classes路徑下找到副檔名為uc的腳本檔,在Src下的其他子目錄如果有Classes目錄裡面也有放uc檔。在先前提到的UTGame\Classes路徑下找到UTCheatManager.uc檔,然後用文字編輯器打開檔案,在尾端鍵入以下程式碼:

2012年4月8日 星期日

子原型

目前UDN上還沒有子原型(Sub-Archetype)的說明,因此在此簡單地介紹一下。

子原型指一個繼承另一個原型的原型。原本修改原型的屬性也會跟著修改所有實例的對應屬性,子原型也是一樣。修改母原型的屬性也會反應到子原型身上,當然也會反應到由子原型所建立的實例上。PhysicalMaterial的Parent屬性就有點類似子原型的功能。

使用者介面


其實UE一直都有支援子原型功能,只是沒有寫使用者介面讓開發者可以直接使用。直到2012年2月版的UDK才在內容瀏覽器的內容選單裡增加了建立子原型的選項。

2012年4月7日 星期六

自訂動畫元資料

UE提供讓開發者在動畫上附加額外自訂資料的功能,方法大致如下:
  • 擴充AnimMetaData類別撰寫自訂功能。
  • 開啟AnimSetViewer,找到要加資料的AnimSequence,然後在它的MetaData陣列新增自訂的元資料。

範例


以下程式碼示範如何自訂一個可以提早結束動畫的元資料:

2012年4月1日 星期日

取得socket或bone的位置

UE提供socket功能方便開發者指定一個相對於bone的位置。例如像手上拿著武器的時候,就可以將武器模型附著在人物模型的某個socket上。

而撰寫特效程式時,時常會需要取得骨架上的某個位置。其實可以只用一個name欄位來指定socket或bone,只要骨架上socket和bone的名稱不重覆就不會有問題。

不過UE沒有提供方便的函式直接取得socket或bone的位置,需要自行撰寫一個:

自訂聲音節點

UE的SoundCue提供混音、變調、衰減等多種音效功能,它可以讓開發者自訂節點來擴充新的功能。

聲音節點的基礎類別叫做SoundNode,它有一個陣列存放下一層子節點。多次播放同一個SoundCue並不會產生多份SoundNode來播放,所以像SoundNodeLooping這種需要額外記錄目前播放次數的情況,會把額外資料放在AudioComponent::SoundNodeData裡,它是一個位元陣列,由 AudioComponent::SoundNodeOffsetMap記錄不同SoundNode資料在該位元陣列的起始位置。為了方便寫碼,UE提供以下三個巨集:

自訂動畫節點

UE的動畫樹(AnimTree)提供動畫混合、變形混合、骨架控制三種功能。它可以讓開發者自訂節點來擴充新的功能,在此篇簡單說明一下如何自訂動畫節點。

動畫節點的基礎類別叫做AnimNode,其他的動畫節點都會直接或間接繼承自此類別。它的子類別主要可分為:
  • 代表動畫的葉節點
  • 混合子節點動畫的中間節點
前者通常繼承AnimNodeSequence類別而來
後者通常繼承AnimNodeBlendBase類別(或其子孫)而來

Unreal的繪圖底層界面

UE提供一個跨平台的繪圖底層界面,叫做Render Hardware Interface,簡稱RHI。它是以全域函式的形式給子系統去呼叫,例如RHISetViewport()、RHISetDepthState()、RHISetRenderTarget()等。在RHI.h的#include "RHIMethods.h"那行宣告這些全域函式。

而這些RHI全域函式的定義形式分成StaticRHI和DynamicRHI兩種。差別在於這些函式是轉呼靜態成員函式還是虛擬函式,根據不同平台擇一使用。

Windows DynamicRHI
Mac DynamicRHI
XBox360 StaticRHI
PS3 StaticRHI
PS Vita StaticRHI
WiiU StaticRHI

2012年3月11日 星期日

為內建的Unreal資源新增右鍵選項

在UEd的內容瀏覽器裡的資源圖示上按右鍵會出現選單。依照資源的種類會出現不同選項,可以自訂GenericBrowserType類別來為自訂的資源客製選項。

但是如果要為內建的Unreal資源新增右鍵選項呢?其實還是一樣自訂GenericBrowserType類別就可以了,UEd會優先使用專案覆寫的而不是內建的GenericBrowserType。以下程式碼示範如何為內建的SkeletalMesh新增屬性視窗選項:

2012年3月8日 星期四

自訂動畫事件

在播放骨架動畫時,AnimNotify用來讓開發者在指定的時間觸發事件。例如:
  • AnimNotify_Sound可用來播放音效
  • AnimNotify_PlayParticleEffect可用來播放粒子特效

開發者可以自行擴充AnimNotify系統,透過繼承AnimNotify類別體系就可以自訂動畫事件。編輯器會偵測所有的AnimNotify,所以不需修改編輯器的原始碼就可看到新增的動畫事件。AnimNotify是存放在AnimSequence裡,所以不同模型實例播放同一個動畫時用的是相同的AnimNotify,這點在撰寫AnimNotify時要特別留意。

當解析度變更時通知ActionScript

當解析度變更時,Swf原本會根據Stage.scaleMode的設定自動變更大小和位置。但是如果想要自行在ActionScript內調整,就會需要在ActionScript中取得解析度變更的通知。

雖然本來ActionScript就有提供Stage.onResize函式,不過只有在Stage.scaleMode設為noScale才會通知。然而實際使用時通常是不會設為noScale,所以只好另外從UE去通知ActionScript。

UE在解析度變更時有提供一個事件CALLBACK_ViewportResized。Scaleform GFx的介接碼也有使用這個事件,請參考UGFxInteraction::Send()。所以其實只要在這個函式中通知ActionScript就好了。呼叫GGFxEngine->GetOpenMovie()可取得目前開啟的GFxMoviePlayer,然後呼叫Invoke()來通知某個自訂的ActionScript函式就可以了。

更新Unreal的屬性視窗

在撰寫編輯器時,如果使用屬性視窗顯示UnrealScript物件,那麼在程式碼中去修改物件屬性時,會需要通知屬性視窗更新。雖然UE有提供更新的方法:
GCallbackEvent->Send(CALLBACK_ObjectPropertyChanged, Obj);

不過有兩個問題:
  • 如果第二個參數傳入NULL,那整個屬性視窗樹都會重建(請參考WxPropertyWindow::Rebuild()),這個動作可能會很慢。
  • 如果指定物件,WxPropertyWindow::Rebuild()只會檢查指定物件是否在第一層才更新(請參考WxPropertyWindow::ContainsObject())。但如是是第一層以下的子物件才擁被修改的屬性,那麼將不會更新視窗。

Unreal編輯器的兩個世界

UE的程式碼使用一個全域變數GWorld指向遊戲世界,其型別為UWorld。開發者可以透過這個全域指標呼叫UWorld的函式SpawnActor()來產生Actor,或者呼叫SingleLineCheck()進行射線檢查看看有沒有打中物件。

從Actor:Spawn()不需指定世界可以看得出UE是假設世界只有一個,Actor也沒有提供得知自己身處於哪個世界的方法。或者至少可以退一步說,就算有複數個世界,在呼叫UE前得先把GWorld設定好。

PIE的另一個世界


然而編輯器的PIE(Play in Editor)功能需要有另一個世界以供試玩,才不會在試玩過程中破壞了編輯到一半的關卡。PIE是採用偷天換日的寫法來達成,在同一個frame中適時切換GWorld指向編輯世界和試玩世界,讓兩者輪流更新。主要的相關變數或函式有:

2012年3月7日 星期三

Unreal中ActionScript的讀檔路徑

ActionScript的讀檔路徑 

 

在Unreal中的swf執行其ActionScript時,當呼叫到如MovieClipLoader.loadClip()之類的讀檔函式,讀檔路徑會對應到該Swf的Unreal package路徑。

例如:在package P的group G中swf A呼叫MovieClipLoader.loadClip("B.swf")時,會去載入Unreal content中的"P.G.B"。

這代表這些在ActionScript中會互相載入的swf必須放在同一個package裡路徑才會正確。然而如果是使用一個swf集中載入其他swf的管理方法,這個限制可能會造成多個swf集中在一個package檔,造成版本控管不便。

UnrealScript和C++之間的函式呼叫

UnrealScript是用C++實作的腳本語言。雖然有些功能可以用UnrealScript實作,不過在需要效能的場合還是得跟C++溝通。UnrealScript類別若宣告成native,會在自動生成的C++標頭檔裡產生對應的C++類別。當然這功能需要有原始碼授權才能正常運作。

UnrealScript呼叫C++函式


UnrealScript不能直接存取C++的函式。不過可以在UnrealScript宣告native function,在C++實作,使得UnrealScript函式可以呼叫native function來使用C++函式。例如在Actor.uc檔內:
class Actor extends Object
    native
...
native final function bool Move( vector Delta );

Unreal的ini檔

Unreal的ini檔可以用來變更許多設定,格式跟Windows的ini檔有點像,但多了許多自訂的功能。它一樣也是用Section、Key=Value的格式來排列分類,不過型別變成是UnrealScript支援的型別。

UObject的ini格式


例如UDKInput.ini的前兩行:
[Engine.PlayerInput]
MoveForwardSpeed=1200
第一行中括號內的Engine.PlayerInput對應到Engine package內的PlayerInput類別。點的前面是script package的名稱,後面是類別的名稱。

第二行則是對應到PlayerInput類別的MoveForwardSpeed屬性,可以在PlayerInput.uc中找到這個屬性: