跳轉至內容

Trainz/情景(指令碼化活動)

來自 Wikibooks,開放世界中的開放書籍
logo
Trainz 培訓基礎
TOC | 開始樂趣 | AM&C | 創作 | 書內引用 ORP 引用:  • 索引 • 容器 • 種類 • 標籤 | 附錄  • 版本
 詞彙表
 HKeys-CM
 HKeys-DVR
 HKeys-SUR
 HKeys-WIN
 滑鼠使用
 符號
Trainz-情景將在 Trainz/Trainz:新紀元 (TANE) (TS12 後 64 位版本,由 2014 年 12 月下載的 Beta '社群版' 釋出,標準版(也是 Beta 版)於 2015 年 5 月釋出;預計 2015 年夏季末釋出第一個服務包改進。

情景介紹

[編輯 | 編輯原始碼]
高地谷煤礦情景開始。九個內建的 TRS2004 情景最初都捆綁在 Trainz UTC 中,然後重新發布。這是一個具有挑戰性的調車和謎題情景,對過度使用交叉口調車進行扣分,因此在行動之前在腦海中將事情“做好”是至關重要的。
 • 由 Chuck 'Cee Bee' Barkman 編寫的最初的高地谷地圖,路線/地圖本身被修改為高地谷工業,並用於演示新式的 會話 技術和 互動產業
Trainz 中的第一次遊戲玩法

一個虛構中的情景  有一個眾所周知的含義,指的是一個包含背景故事和情境的事件,主角必須以某種方式應對。這個廣泛的定義雖然在 Trainz 中以同樣的廣泛意義適用,但實際上有兩種機制來呈現 Trainz 的遊戲玩法,即軟體套件中的遊戲玩法操作員在駕駛員會話中應該執行的任務,一個目標,或者問題。簡而言之,一個有使命要完成的任務。一個寫得好的任務會提供某種“積分”評分,讓你可以將自己的表現與標準和自己的最高得分進行比較。寫得好的任務也會清晰地概述任務、結束條件,並假定駕駛員(我們)事先不知道路線/佈局。大多數任務沒有滿足這些標準。此外,寫得好的任務允許你重新開啟說明並重新整理記憶,並將名稱(有時聽起來很奇怪)與小地圖標籤進行比較。

它們還提供了情境修正器,有一次將貨物列車開往時間表,結果卻發現對列車編組位置的誤判(導致倒車速度降低了大約四五分鐘),要求駕駛員趕上時間——但為了小心地保持低於公佈的速度限制,會話編寫者並沒有費心告訴我們在那個國家,當速度達到一定範圍時,速度限制會自動降低,這意味著貨物列車必須在達到這些範圍時低於公佈的速度限制 10、15 或 25 英里。像許多這樣的問題任務一樣,這個任務會因為超速而扣分。你是否會永遠記住將貨物列車按時開到最後幾個站點的經歷,完成它並發現你的最終得分是-384(從 800+ 上次注意到/檢查時!)下降的?

這些遊戲玩法任務序列中的較舊序列實際上在 2001-2002 年由 Auran 稱為情景(以下稱為“Trainz-情景”),而較新的模式被稱為會話。從 Trainz 中駕駛員的遊戲玩法的角度來看,除了一個區別之外,幾乎沒有區別:它們在駕駛員選單中的顯示方式不同。從建立兩者之一的角度來看,差異很大。

Trainz-情景是使用者在 Trainz 駕駛員 GUI 模組中操作的互動式指令碼化活動——這是該軟體系統提供的各種娛樂形式中最像遊戲的一種。它們總是會有特定的完成目標或狀態,並且許多情景的編寫都提供了評分,讓駕駛員有機會與其他人競爭或打破她之前的最高得分。它們在本質上差異很大,一些情景為操作的每個階段提供逐步說明(例如,關於如何執行 Trainz 的教程),而另一些情景只是顯示目標的詳細資訊,並將其留給使用者去想出一個實現目標的方法。“調車謎題情景”,也許是最現實的 Trainz 駕駛員“任務”或“問題”模型  “典型的鐵路運營”  傾向於採用後一種風格。一些“時間表保持挑戰”遵循簡潔的模式,在駕駛員情景啟動時只提供一個時間表。各種各樣的事件可以在 Trainz-情景中觸發。可以顯示 HTML 文字和影像,播放聲音,在“天外飛來”的情況下新增列車(在幾分鐘前在地圖上不可見),或者改變機車的裝載狀態。

在最早的 Trainz 中,使用 TrainzScript.exe 編寫的 Trainz-情景,這是一個單獨的實用程式,不再作為 TRS2006 及以後版本的一部分提供,基於指令碼語言的情景對於在“鏡頭外”裝載煤炭車或其他貨物至關重要,因此非常適合反映許多短途或調車機車“調車”操作的現實情況,在這種情況下,機車在各個地點裝卸貨車,並將裝載或解除安裝的機車運回集結場或分類場,在那裡將其適當的調入或調出較長的區域列車。但 TRS2004 還引入了互動產業(在 2003 年)和會話編輯器以及駕駛員規則改進,同時仍然完全支援 Trainz-情景。從那時起,版本可能最好被視為“只讀”實現,其中下載的 Trainz-情景仍然可以播放,但不能編寫。

Trainz 會話規則是 TrainzScript '模組',對非程式設計師來說更加友好,它們被實現為圖形圖示,並且輸入或輸出模式數量有限。



Trainz-情景是在 服務包 3 釋出時引入的,該服務包面向原始 Trainz 版本以及 Trainz UTC,並且是讓許多資深 Trainz 使用者認為 Trainz 1.3 版本是第二個獨立的主要版本的主要改進之一。情景在 TRS2004 中得到了完全支援,作為一種替代的向後相容的指令碼化活動模式……即使該版本引入了新的、更通用的 會話'。它們只在 TRS2006 和 Trainz Classics 到最新的 Trainz Trainz 2012 中得到部分支援,這意味著現有的會話(通常)仍然可以在匯入或下載時執行,但 TrainzScript 軟體實用程式既沒有更新,也沒有作為這些更新版本的一部分提供,因為'會話'更加靈活,並且可以執行舊的 Trainz-情景技術可以執行的所有操作。換句話說(比較上面的內容與情景):Trainz-會話是使用者在 Trainz 駕駛員 GUI 模組中操作的互動式指令碼化活動——這是該軟體系統提供的各種娛樂形式中最像遊戲的一種”

一些建立情景的方法

[編輯 | 編輯原始碼]
  • TRS2004 使用者可以使用內建的方法建立情景的初始結構,然後使用文字編輯器程式編寫必要的程式碼。這種方法涉及使用 GameScript 程式設計,GameScript 是一種類似“C”的語言,具有許多擴充套件。
  • TRS2004 和 TRS2006 使用者都可以使用 SCS(情景建立系統)程式,該程式由第三方 Trainz 愛好者網站 Trainz Pro Routes 免費提供。TRS2004 和 TRS2006 有單獨版本的 SCS。SCS 的目標是使情景建立過程儘可能簡單,但即使這樣,也需要付出相當大的努力。SCS 有完整的使用者手冊。
Trainz 頁面過時或需要重新組織和改進,因此已列入待清理列表,以便我們儘快處理。它已被列入 Category:Trainz pages needing attention,對於您可能遇到的任何不便,我們深感抱歉,因為我們的 vlunteer 員工沒有時間將其改進。待辦事項:
檢視是否可以更新並連結,或決定是否應將其放置在 附錄 中作為“過時”內容。


TRS2004 情景建立概述

[編輯 | 編輯原始碼]

GameScript API(應用程式程式設計介面)文件可以在“Trainz Railroad Simulator 2004 使用者活動建立指南”中找到,該文件可以從Auran的網站下載。熟悉本檔案中列出的概念和各個函式至關重要。

以下是對這個本質上非常複雜的過程的高度簡化的概述。為了儘可能地簡化,它省略了許多細節。

在Surveyor中開啟相關佈局。

新增任何所需的軌標、觸發器和其他路邊物體。可以在軌標處建立火車。當火車進入觸發器時將觸發事件。不要儲存對佈局的更改。

在Surveyor選單中,呼叫“匯出場景資料”選項,併為新場景選擇一個合適的名字。

關閉Surveyor。同樣,不要儲存對佈局的更改。

一個新的資料夾將在C:\Program Files\Auran\TRS2004\World\Custom\scenarios資料夾中建立,名稱與您在Surveyor中指定的名稱相同。這個新建立的資料夾將包含構成場景基礎的檔案。

TSO(路邊物體)檔案包含對佈局的修改細節,這些細節是特定於這個場景的,例如您在Surveyor中新增的額外的軌標和觸發器。

使用文字編輯器開啟場景的config.txt檔案。kuid-table將已經包含佈局。您需要新增場景使用的滾動庫存資產,例如:(如何!!!)

kuid-table
	{
	testscenario		<kuid:154110:5701376>
	AN_830_Class		<kuid:-1:100737>
	QR_QLX			<kuid:-1:101154>
	}

如果您願意,也可以美化描述文字。

使用文字編輯器開啟gs(GameScript)檔案。預設情況下,此檔案包含大量的程式碼,幾乎所有程式碼都應該保持不變。您需要在正確的位置新增額外的程式碼部分。

在“建立編組規格”部分,新增程式碼以指定火車最初放置在佈局上的滾動庫存。以下示例包含一列由AN830 Class機車牽引的QLX百葉窗貨車。

KUID[] PlayerTrainKuids = new KUID[ 2 ];
PlayerTrainKuids[ 0 ] = World.FindKUID( "AN_830_Class" );
PlayerTrainKuids[ 1 ] = World.FindKUID("QR_QLX" );

在“建立編組”部分,新增程式碼以將火車放置在軌標處。以下示例將由“PlayerTrainKuids”編組規格組成的火車放置在名為TM01的軌標處。

Train PlayerTrain = World.CreateTrain( PlayerTrainKuids, "TM01", true );

在“遊戲玩法”部分,在“scenarioDone = true;”行上方,新增程式碼以設定一些初始引數並執行場景的操作。以下是一個非常簡單的示例,它僅僅允許使用者駕駛PlayerTrain,而沒有其他操作。

World.SetGameTimeRate( World.TIME_RATE_1X );
World.SetGameTime( 0.875 );	// 9am
World.SetWeather( World.WEATHER_TYPE_CLEAR, World.WEATHER_CHANGEABILITY_NONE );
World.SetCamera( PlayerTrain, World.CAMERA_EXTERNAL );
World.SetCameraAngle( 75, -15, 50 );
PlayerTrain.SetAutopilotMode( Train.CONTROL_MANUAL );

while( 1 == 1 )
	{
	Sleep( 0.1 );
	}

在上面的示例中,“while”迴圈將永遠等待,直到使用者退出場景。一個真正的場景通常會正常完成並關閉,而無需使用者干預。

不需要編譯。一旦gs檔案正確建立,就可以執行場景。在實踐中,通常需要在編輯和測試迴圈中進行多次迭代。

與程式語言的比較

[編輯 | 編輯原始碼]
編輯說明:GameScript 的實用性不僅限於 Trainz 場景 生成,還適用於編寫場景替換規則、遊戲 會話種類配置檔案)。TrainzScript 的變體是製作各種互動和動畫內容的核心,例如裝載和解除安裝貨車,以及其他動畫,包括在 種類移動道口種類工業、繪製、擺動和升降橋、汽船、可駕駛車輛,以及世界中基本所有移動的物體,都依賴於指令碼檔案。
  • 簡而言之,此頁面絕不過時,對於任何需要有關 TrainzScript 或 GameScripting 的更多資訊的人來說,它都很重要。
一些關於 GameScript & TrainzScript 的評論,針對那些只熟悉傳統 BASIC 或 Visual BASIC,而沒有其他高階計算機語言的人。

提示:本節末尾有一些簡單的示例。

以下內容適用於用於建立 TRS2004 場景的 GameScript。它在規則、命令、資產動作指令碼程式、指令碼化資產行為等方面的使用在某些重要方面與以下內容不同,但都共享相同的基礎要求和基本語法。指令碼化資產,例如路邊訊號、動畫伐木機、聯動道岔或訊號,都必須與執行時軟體進行互動,因此它們都在同一個“房子”裡,這樣說吧。

對於那些不熟悉除 BASIC 之外的任何形式的程式語言的人來說,學習 GameScript 可能會很困難。本節試圖列出一些可能讓初學者感到困惑的“陷阱”。

GameScript 沒有專門的程式編輯器或 IDE(整合開發環境)。相反,使用文字編輯器程式,例如記事本或更高階的替代方案(Notepad++Programmer's NotepadConTEXTCrimson 在本文中被介紹)來編輯程式原始檔,並且與IDE 中的“專案”不同,使用者必須手動完成所有檔案管理。在 GameScript 中,火車、訊號、道岔等實體被認為是 具有屬性的物體,這些屬性可以由程式指定和訪問。(計算機科學中屬性的另一種說法是屬性——“特徵”的限定。)

GameScript 基於“C”語言。
“C”與 BASIC 有很多區別,例如
  • 在 GameScript 中,所有程式碼都儲存在一個檔案中。由於沒有整合開發環境,因此在編輯時所有程式碼部分都是可見的。
  • 變數必須提前宣告,就像它們一樣,並且必須在大多數程式語言中,並且在使用之前,通常還要初始化(定義值)。
    • 如果需要,可以在首次賦予值時宣告它們;語法是指定變數型別(int、float 等),然後是它的名稱,然後是等於號,然後是給出變數初始值的表示式。
  • 陣列下標用方括號括起來,而不是圓括號。
  • 宣告陣列的語法對於 BASIC 程式設計師來說會顯得非常奇怪。請參閱下面的示例。
  • 註釋行開頭有兩個正斜槓,而不是 REM。這通常被稱為“hack-hack”註釋,在大多數 C 語言衍生語言中,它們可以出現在行的任何位置,並影響從它們出現的位置到換行符(行尾、換行符、CR+LF 或其他等效術語)字元序列結束文字行為止的所有內容。
  • 依賴語句組(或 程式碼塊)用匹配的花括號對括起來,這些花括號可以或應該被視為邊界(邊界)關鍵字——開啟的“花括號 '{''用於標記塊的開始(就像 PASCAL 的 'begin' 關鍵字一樣)。
  • 這些包括 FOR 迴圈、WHILE 迴圈和 IF 語句中的順序處理語句(過程)組。
  • 因此,在“C”中沒有與 BASIC 的 NEXT、WEND 或 ENDIF 語句“對映”的等效語句,但關閉的“花括號 '}''用於標記任何分組語句塊的結束。
  • 邏輯上,關閉的“花括號 '}''也用於標記按順序分組和執行的語句塊的結束,在基於 C 的語言分支中,這些語句被稱為 '函式'或“過程”或“過程”或 BASIC 的“子程式”)。
  • 大多數有效語句的末尾必須有分號,但並非所有語句都需要。在需要時遺漏分號是常見的錯誤原因,會導致意外結果
  • 例外情況是分支識別符號(標籤),以及通常後續的依賴語句用花括號括起來的地方,例如 while、do、for 迴圈和 if-then-else 條件塊之類的塊運算子。

提示:在 C 中,幾乎所有重要的程式碼塊,甚至過程或子程式(在 C 中都稱為“函式”)都將用一對花括號括起來。如今,這些也可能被稱為“包裝器”——含義借鑑了與 HTML 標籤對的相似之處。

必須沒有分號的語句示例包括
  • case
  • default
  • else {...}
  • FOR 迴圈標題、WHILE 迴圈標題和 IF 語句標題,其中後續的依賴語句用花括號括起來。
  • include
  • switch {...}(它的作用域塊包括 default 和 case 塊)
  • 函式宣告(不是定義)標題行(它的作用域包含邊界結束塊(關閉的花括號 '}')之前的全部內容)
  • 標籤(goto 語句的無條件分支目標——必須在函式包裝器內。)
  • 大多數操作都是使用函式或具有與函式相同語法的語句執行的。因此,在許多情況下,引數必須用圓括號括起來,而在 BASIC 中不需要圓括號。這會導致類似以下的語法
result=14*myfunction( new-input-value ); 其中 'myfunction{...}' 是其他地方的程式碼,可能是一個獲取特定狀態並將其值返回到表示式並儲存值到變數 'result' 的程式碼。
  • 賦值語句使用一個等於號,
    • IF 語句中的關係表示式(相等性測試)使用兩個等於號 (if(result==0){...})。
    • 在這樣的求值表示式中:不相等是 !=
  • “C” 具有許多使用兩個相鄰算術運算子來遞增和遞減變數的晦澀方法。由於這些方法很難記住,因此容易混淆,初學者最好避免使用它們。不使用這些方法造成的效能差異與 Trainz 生成每一幀所需的數百萬次計算相比,微不足道。
 

 

  • 與使用 STR$ 和 NUM 等函式,或像在 Visual BASIC 中那樣自動將變數從一種型別轉換為另一種型別不同,在“C”中,使用 <cast> 語句。
  • 與 Visual BASIC 不同,GameScript 本身不是事件驅動的。
    • 沒有“點選此物件以執行這段程式碼”的概念。但是,可以建立事件處理程式(當事件發生時執行的程式碼段)。這些對於處理 AI 火車事件特別有用,在某些情況下,這些事件可以與場景處理和使用者的操作非同步發生。
    • 但是,GameScript 還具有 Schedule 機制,用於獨立於場景執行來控制 AI 火車。
  • 以 void 開頭的函式表示它被宣告為不返回值的函式。因此,賦值:result=14*myfunction(); 甚至只是數字 * 函式()部分將(並且應該)產生編譯錯誤。
  • 在 Auran 的 API 文件中,帶有“括號引數 ( void )”的函式宣告不接收任何傳遞引數(void myfunction(void); 因此宣告一個子程序,它不需要傳遞值,每當“呼叫程式碼” myfunction(); 在行內出現時,它就會執行),但如所示,它確實需要一對空圓括號。
    • 宣告時,也需要使用 void 關鍵字。
  • "C" 沒有指數運算子。但是編寫一個用於立方的 C 函式是
float fcube(float finput;){ return finput*finput*finput; }
  • 注意,return 語句既需要一個終止分號,又需要在一個 '非 void 宣告' 函式中返回一個表示式,以將一些值 *傳遞迴* 呼叫程式碼塊。
布林(邏輯)表示式
  • GameScript 在複雜的關聯 布林運算 表示式中使用 andor,與 BASIC 中的方式相同。普通的 "C" 語言則完全不同。位布林運算雖然是可行的,但對於初學者來說最好忽略它們。
  • GameScript 使用 truefalse 表示字面布林值。
  • GameScript 使用 + 用於字串連線,與 BASIC 中的方式相同。
  • 而 Visual BASIC 一直使用 *'Object.Property' 語法*,GameScript 則只在有限程度上使用該語法。
  • GameScript 廣泛(可能是普遍地)使用 SetOfFunctions.SpecificFunction 語法。
  • 在 GameScript 中,大多數情況下,*物件的屬性* 必須使用以 Set... 開頭的函式來指定,而物件的屬性必須使用以 Get... 開頭的函式來獲取。
  • 而在 Visual BASIC 中,經常執行 *DoEvents 語句* 至關重要,以重新整理螢幕並防止 Windows 出現鎖定,在 GameScript 中,只需要避免緊密迴圈並在這種情況下插入 Sleep 函式。
  • GameScript 函式在暫停場景執行等待事件時不會鎖定 Trainz 或 Windows。
  • GameScript 中的一些函式可以接收數量不確定的引數,通常省略的引數會賦予合理預設值。
  • GameScript 中的一些函式根據其接收(傳遞)的引數的 *變數型別* 執行不同的操作。
  • 例如,用於指定攝像機角度的函式將它的引數視為
如果它們是 *整數*,則為 '度數'
但如果它們是 *浮點數*,則為 '弧度'。

與 Visual BASIC 一樣,對於初學者來說,關於哪些方面由作業系統或程式語言處理,哪些方面必須由程式設計師處理,存在相當大的困惑。在 Trainz 中,使用者列車和 AI 列車以非同步方式執行列車運動,這使得情況更加複雜。

  • Windows 和 Trainz 處理輸入和輸出的所有硬體方面。
  • Trainz 會在場景啟動時自動呼叫程式的 main() 部分。(這是 C 標準函式/任務/確定性)
  • 一旦使用者的列車建立完畢,並把控制權交給使用者,使用者就可以根據自己的意願向前或向後駕駛列車。場景需要對事件做出反應,例如使用者的列車進入觸發器或交叉口。
  • Trainz 會在使用者列車超速事件發生時、發生碰撞事件時等情況下 *自動* 呼叫一些其他 '樣板'(標準庫)*程式部分*。
  • 建立新場景時,會建立用於處理這些事件的預設程式碼,但是如果擁有足夠的知識,可以對其進行修改。
  • GameScript 中提供了一些函式,可以在程式碼中的某個點暫停場景執行,直到事件發生,例如使用者的列車進入交叉口或觸發器。

與 Visual BASIC 一樣,除了相對簡單的核心語言之外,還需要學習大量物件、它們的屬性和函式。這些都在 Auran GameScript API 中有記錄。

一項重要的實用說明:在 Auran GameScript API 文件中,一對匹配的相鄰方括號可能顯示得過於靠近,以至於看起來像是外框。

編者注: *'網路搜尋'* 引用的短語:"The C Language""The C Programming Language" 用於線上語法和運算子來源。強烈建議任何嘗試學習 C 或 C 派生語言(如 C/C++/C#、Java、Java script(實際上是大多數現代和最近的指令碼語言))的人獲取 Kernighan 和 Ritchie(該語言的發明者)的開創性著作 "The C Programming Language"。沒有哪本書比這本更能讓你更快地入門。

在 Trainz 中,物件在內部相互傳遞訊息。這些內部訊息使用者是看不到的。GameScript 程式碼可以生成內部訊息,還可以檢測和接收內部訊息。每個內部訊息都有四個屬性:源、目標、主要(即其主要訊息型別)和次要(即其次要訊息型別)。雖然初學者可以忽略這種訊息傳遞的概念,但它經常出現在 Auran 文件中,因此值得在這裡提及。

最後一條至關重要的建議:從一些真正非常簡單的東西開始。讓列車出現在軌道上本身對於剛開始學習 GameScript 的人來說就是一項非常了不起的成就。

示例 #1:IF 語句

//Equivalent to IF A=10 THEN...
if (a==10)
	{
	//dependent statements go here
	}

示例 #2:FOR 迴圈

// Equivalent to FOR N=1 TO 5 STEP 1
for (n=1; n<=5; n=n+1)
	{
	//statements within the loop go here
	}

示例 #3:WHILE 迴圈

while (a<50)
	{
	//statements within the loop go here
	}

示例 #4:變數宣告和初始化

int x=8;

示例 #5:宣告陣列

//Equivalent to DIM A(12) - possible subscript values will run from 0 to 12
float[] A = new float[ 13 ];

示例 #6:定義使用者定義函式

int Cube(x)
	{
	//local variable declarations go here
	//function statements go here
	return (x*x*x);
	}
華夏公益教科書