跳转至内容

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);
	}
华夏公益教科书