我想用unreal 游戏做独立游戏,但电脑配置太低了,是否要重买电脑?电脑配置能不能改

(转载)虚幻引擎3--UnrealScript语言参考
时间: 17:15:08
&&&& 阅读:587
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&档概要: UnrealScript 介绍和简要参考。最初作者是&()
请一定要查看UnrealScript 的和指南&。
本文档的目的
这是一篇描述UnrealScript编程语言的技术文档。它不是指南,也没有提供有用的UnrealScript代码的详细例子。要获得UnrealScript的例子,读者可以参考引擎的源代码,它提供了成千上万行的有效代码,用来解决许多例如AI、运动、武器装备和触发事件等很多问题。最好的入门方式是查看"Actor"、"Object(物体)"、 "Controller(控制器)"、 "Pawn(士兵)" "Weapon(武器)"的脚本。
本文档假设读者使用过C/C++或&编程语言、熟悉面向对象程序设计、已经玩过Unreal的游戏并使用过UnrealEd的编辑环境。
对于是面向对象程序设计的新手的程序员,我强烈推荐您去Amazon.com或者书店买一本介绍java编程方面的书。Java和UnrealScript很相似,并且由于它的干净和简单的方法使它成了一种值得学习的极好的语言。
UnrealScript的设计目标
UnrealScript是为开发团队和第三方Unreal开发人员创建的,是一种强大的内置的编程语言,它自然地满足了游戏编程的需要和细小差别。
UnrealScript的主要设计目标:
UnrealScript支持传统的编程语言没有提到的时间、状态、属性、网络等主要的概念。这大大地简化了UnrealScript代码。基于C/C++的AI和游戏逻辑编程的主要复杂性是处理要花费一定量的游戏时间来完成的事件以及处理依赖物体状态的各个方面的事件。在C/C++中,这将会导致冗长的混乱的代码,使代码难于书写、理解、维护和调试的。UnrealScript包含了对时间、状态和网络复制的内部支持,这大大地简化了游戏编程。
提供一种像Java类型编程语言一样简单的、面向对象的并在编译时进行错误检查的语言。就像Java为Web开发人员提供了一个干净的开发平台,UnrealScript为3D游戏提供了一个同样干净的、简单的、强大的编程语言。UnrealScript从Java语言中衍生的主要编程观念有:
没有指针并自动进行垃圾回收的环境;
一个简单的单继承类图;
编译时进行强类型检查;
安全的客户端执行的"sandbox"
像C/C++/Java代码一样熟悉的外观和感觉。
UnrealScript为了在游戏对象和交互方面而不是位和像素方面提供丰富的高层次的编程语言,所以在设计上必须有一些妥协,我们为了获得开发的简单性和强大性,从而牺牲了执行速度。毕竟, Unreal的底层的、对性能起重要作用的代码是由C/C++书写的,在那里所获得的性能提高的价值超出了所增加的复杂性。UnrealScript是在那个层次的基础上进行运作的,在物体和交互性的层次上而不是位和像素的底层上。-
在UnrealScript开发的早期,我们探索了几个主要的不同的编程语言范例,然后又放弃了,最终开发出了目前的UnrealScript。首先,我们研究使用Sun 和 Microsoft为Windows设计的VM(虚拟机) 作为Unreal脚本语言的基础。但最终证明在Unreal的情境中Java并没有提供比C/C++更大的编程好处,反而由于缺乏所需要的语言功能(比如 运算符重载)而增加了一些令人沮丧的限制,另外也证明由于VM过多的任务转换开销和Java垃圾回收器在处理大的对象图形时的低效率导致运行速度特别的慢。其次,我们设计了基于Visual Basic变量的UnrealScript初期实现,它可以工作的很好,但是对于已经习惯于C/C++的程序员来说,它不是那么友好。最终基于我们想将游戏的特定概念映射到语言定义本身的愿望以及获得速度和熟悉度的需要,我们决定将UnrealScript设置为基于C++/Java变种的语言。结果证明这是一个好主意,因为它在很多方面大大地简化了Unreal代码。
虚幻引擎3中UnrealScript的新功能
对于已经熟悉UnrealScript的人来说,这里是一个自虚幻引擎2后在UnrealScript中所发生的主要改变的简短概要。
Replication(复制)-在UE3中replication(复制)语句已经改变:
现在复制(replication)语句块只适用于变量。
函数复制(replication)现在通过以下方法定义:&(Server, Client, Reliable&)
&- 你现在可以使状态压入栈或弹出栈。
&- 支持宏和条件编译。
&- 添加了新的和调试相关的功能。
&- defaultproperties块的处理已经被改变/改进。
&- structs现在也可以有默认属性。
不再允许给配置或者局部变量设定默认值。
在运行时系统中是随机的,它不再允许执行class‘MyClass‘.default.variable = 1的操作。
&- 动态数组现在有一个新功能&find()&,用于查找一个元素的索引。
&- Foreach操作符现在可以应用于动态数组。
&- UE3现在允许 delegates作为函数的传递参数。
&- 添加了对接口的支持
从其它的类中访问常量:class‘SomeClass‘.const.SOMECONST
&- 现在可以指定操作函数参数的默认值。
支持工具提示- 如果在UnrealScript中属性声明的上面有一个=/** tooltip text */=形式的注释,则在编辑器的属性窗口中,当您将鼠标放到一个属性上时可以显示工具提示文本。
&- 通过将属性和各种类型的元数据相关联,扩展了in-game(游戏中)和in-editor(编辑器中)的功能。
代码结构示例
这个示例说明了一个典型的简单的UnrealScript类,并且它囊括了UnrealScript的语法和功能。注意由于该文档没有和代码同步,所以这个代码可能和现在的Unreal源代码的呈现不一样。
//=====================================================================
// TriggerLight.
// 一个可以通过触发进行开关的光源
//=====================================================================
class TriggerLight extends L
//---------------------------------------------------------------------
var() float ChangeT //光源从开到关所需要的时间
var() bool bInitiallyOn; //它在最初是否是亮的
var() bool bDelayFullOn; // 在到完全的点亮之前是否有延迟
var ELightType InitialT // 光源的初始类型
var float InitialB //光源的初始亮度
var float Alpha, D
var actor T
//---------------------------------------------------------------------
//引擎函数
//在游戏开始播放时调用
function BeginPlay()
// 记忆光源的初始类型并为它设置一个新的类型
Disable( ‘Tick‘ );
InitialType = LightT
InitialBrightness = LightB
if( bInitiallyOn )
Alpha = 1.0;
Direction = 1.0;
LightType = LT_N
Alpha = 0.0;
Direction = -1.0;
// 当时间经过时调用
function Tick( float DeltaTime )
LightType = InitialT
Alpha += Direction * DeltaTime / ChangeT
if( Alpha & 1.0 )
Alpha = 1.0;
Disable( ‘Tick‘ );
if( Trigger != None )
Trigger.ResetTrigger();
else if( Alpha & 0.0 )
Alpha = 0.0;
Disable( ‘Tick‘ );
LightType = LT_N
if( Trigger != None )
Trigger.ResetTrigger();
if( !bDelayFullOn )
LightBrightness = Alpha * InitialB
else if( (Direction&0 && Alpha!=1) || Alpha==0 )
LightBrightness = 0;
LightBrightness = InitialB
//---------------------------------------------------------------------
//公有状态
//触发打开光源
state() TriggerTurnsOn
function Trigger( actor Other, pawn EventInstigator )
Trigger = N
Direction = 1.0;
Enable( ‘Tick‘ );
//触发关闭光源
state() TriggerTurnsOff
function Trigger( actor Other, pawn EventInstigator )
Trigger = N
Direction = -1.0;
Enable( ‘Tick‘ );
//触发切换光源的开关
state() TriggerToggle
function Trigger( actor Other, pawn EventInstigator )
log("Toggle");
Trigger = O
Direction *= -1;
Enable( ‘Tick‘ );
// 触发控制光源
state() TriggerControl
function Trigger( actor Other, pawn EventInstigator )
Trigger = O
if( bInitiallyOn ) Direction = -1.0;
else Direction = 1.0;
Enable( ‘Tick‘ );
function UnTrigger( actor Other, pawn EventInstigator )
Trigger = O
if( bInitiallyOn ) Direction = 1.0;
else Direction = -1.0;
Enable( ‘Tick‘ );
在这个脚本中要看的主要元素是:
类的声明。每个类"extends" (继承)一个父类,并且每个类属于一个"package(包),",一组对象一起发布。属于一个类的所有功能和变量,仅属于那个类的actor(物体)可以访问。没有整个系统范围可以使用的全局函数或变量。
变量声明。UnrealScript支持多种变量类型,包括大多数基于C/Java的类型、对象引用、structs (结构体)和数组。另外,变量可以被设置成为可以编辑的属性,这样设计人员在UnrealEd中即使不用任何编码就可以访问这些变量了。这些属性通过使用&var()&语法来指定,而不是&var&。
函数。函数可以有一系列的参数,并且可以随意地返回一个值,也可以有局部变量。某些函数是由虚幻引擎本身调用的(例如 BeginPlay),有些是由其它的脚本代码进行调用的。(比如Trigger)。
代码。支持所有的标准的C和Java关键字,比如&for=、 =while=、 =break=、 =switch=、 =if&等等。
Actor和object(对象)引用。这里你看到了几个在另一个对象中使用对象引用调用一个函数的情况。
关键词"state"。这个脚本定义了几个"states",它们由函数、变量和仅当这个物体在那个状态时所执行的代码组成。
注意:在UnrealScript中,所有的关键词、变量名、函数和对象名都是大小写不敏感的。对于UnrealScript来说,=Demon=,&demON, 和&demon&是同样的事情。
Unreal虚拟机
Unreal虚拟机有以下几部分组成:服务器、客户端、渲染引擎及引擎支持代码。
Unreal控制着所有的玩家和物体间的游戏性和交互。在一个单人游戏中,Unreal客户端和Unreal服务器在同一台机器上运行;在一个网络游戏中,有一个机器用于专用服务器;所有连接到这个机器上的玩家是客户端。
所有的游戏播放都发生在一个"关卡"中,它是一个包含着几何体和物体的独立环境。尽管UnrealServer可以同时运行多个关卡,每个关卡独立运作并且彼此屏蔽:物体(actors)不能在不同关卡间穿行,而且一个关卡中的物体不能和另一个关卡中的物体进行交流。
在地图中的每个物体都可以或者在玩家的控制下(在网络游戏中可以有很多玩家)或者在脚本的控制下。当一个物体在脚本的控制下时,那么该脚本完全地定义了物体如何移动及如何与其它物体进行交互。
对于世界中所有这些物体的跑动、执行脚本、事件发生,你也许会问它们是如何能理解UnrealScript的执行流程哪。答案如下所示:
为了管理时间,Unreal将游戏播放的每秒钟分隔为"Ticks"。一个Tick是关卡中所有物体更新所使用的最小时间单位。一个tick一般是一秒钟的1/100到 1/10。Tick时间仅受到CPU功率的限制,机器速度越快,tick持续时间越短。
在UnrealScript中的某些命令的执行只需要使用零tick(也就是:它们的执行没有占有任何游戏时间),也有些命令需要占用很多ticks。需要占用游戏时间的函数称为"latent functions(潜伏的函数)"。一些latent functions函数的例子包括&Sleep&,&FinishAnim&, and&MoveTo&。UnrealScript中的Latent functions仅可以从在一个状态的代码中被调用(所以也称作"state code(状态代码)"),而不能从一个函数的代码中(包括在一个状态中定义的函数)被调用。
当一个actor在执行一个latent函数,那个actor的状态执行不会继续直到latent函数执行完毕。然而,其它的actor或者VM可能会调用actor内部的函数。最终的结果是所有的UnrealScript的函数可以在任何时间被调用,甚至在latent 函数没有执行完毕的情况下。
按照传统的编程术语来说,UnrealScript就像在关卡中的每个物体有它们自己的执行"thread(线程)"一样工作。在内部,Unreal不使用Windows线程,因为那将是非常低效的(Windows 95和Windows NT不能高效地处理同时发生的成千上万的线程)。然而,UnrealScript 可以模拟线程。虽然这个事实对于UnrealScript代码是透明的,但当您书写和UnrealScript交互的C++代码时则变得非常显然的。
所有的UnrealScripts将彼此独立地执行,如果有100个鬼怪正在关卡中走动,那么所有的这100个鬼怪的脚本在每个"Tick"中都正在同时地且独立地执行着。
在使用UnrealScript进行工作之前,理解Unreal中物体的高层次的关系是非常重要的。Unreal的和其它大多数的游戏架构是有较大的背离的:Unreal是完全地面向对象的(非常像 COM/ActiveX),它有一个定义明确的对象模型,可以支持高层次的面向对象的概念比如对象图、序列化、对象生命周期和多态性。从游戏历史上来说,尽管很多游戏比如Doom和Quake,已经证明在内容方面有较大的可扩展性,但大多数游戏都被设计成为集成化的,它们的主要功能都是被硬编码的并且是在对象的层次上不能扩展的。Unreal的面向对象的形式有一个很大的好处:主要的新功能和对象类型可以在运行时添加到Unreal中,并且这个扩展是以划分子类的方式进行的,而不是(比如)通过修改一串已有的代码。这种扩展的形式是非常强大的,因为它鼓励Unreal团队共同来创建可以所有人都可以操作的改善。
Object是Unreal中所有对象的父类。在Object类中的所有函数可以在任何地方访问,因为任何东西都继承于Object。Object是一个抽象的基类,它没有做任何有用事情。所有的功能都是由子类提供,比如Texture (一个贴图),TextBuffer(文字块)和Class (它描述了其它对象的类)。
Actor(继承Object)是Unreal中所有独立运行的游戏对象的父类。Actor包含了所有的一个物体所需要的用于四处运动、和其它物体进行交互、影响环境及做其它与游戏相关的有用的事情的功能。
Pawn(继承Actor)是Unreal中所有的可以使用高层次AI和玩家控制的创造物和玩家的父类。
Class (继承 Object)是一种描述对象的类的特殊类型的object。刚看到时可能是令人疑惑的:一个类是一个对象,而其一个类描述某些的对象。但是这概念是合理的,并且在很多情况下您将会用到Class的对象。例如,当你在UnrealScript产生一个新的actor,你可能会通过Class对象来指定actor的类。
使用UnrealScript,你可以为任何Object类书写代码,但是99%的时候您都是在为从Actor继承的类来写代码。大多数有用的UnrealScript功能是游戏相关的并且是处理actors的。
每个脚本明确地对应一个类,并且脚本是通过声明类、类的父类及任何和该类相关的其它附加信息开始的。最简单的形式是:
class MyClass extends MyParentC
这里我声明了一个名为"MyClass"的新类,它继承了"MyParentClass"类的功能。另外,这个类存储在名为"MyPackage"的包中。
每个类继承它父类的所有的变量、函数和状态。然后它可以添加新的变量声明、添加新的函数(或者重写已有函数)、添加新的状态(或者为已有的状态添加功能)。
在UnrealScript中典型的设计类的方法是构造一个继承了具有您所需要的大部分功能的现有类(比如Pawn类,即所有怪物的基类)的新类(比如一个”人身牛头怪物”)。使用这个方法,你从来不需要重新发明新的东西---你可以简单地增加您想自定义的新功能,然而保持所有你不需要进行自定义的现有的功能。这个方法在Unreal中实现AI时显得尤其有用,因为在内置的AI系统中提供了大量的基础功能,您可以把它们作为您自定义的创造物的组成部分。
类的声明可以带有几个可选的影响类的修饰符:
Native(包名称)意味着“这个类使用C++作为幕后支持”。Unreal期望native类在.EXE文件中包含着一个C++实现。只有native类可以声明native函数或者实现native接口。Native类必须继承于另一个native类。Native类创建一个自动生成的具有必要的 _ glue_ 的C++头文件来和脚本变量及指定函数进行交互。NativeReplication意味着对于该类的变量值的复制是在C++实现中处理的。仅对native类有效。DependsOn(ClassName[,ClassName,...])意味着ClassName是在这个类之前进行编译。ClassName必须在同一个(或前一个)包中指定一个类。多个依赖的类可以通过使用一个单独的&DependsOn&代码行并且类之间通过逗号分界来指定,或者通过为每个类使用单独的&DependsOn&来指定。
Abstract声明类为一个"abstract base class(抽象的基类)"。这可以防止用户在UnrealEd的世界中添加这个类中的actor或者在游戏中创建这个类的实例,因为这个类本身没有任何意义。例如,"Actor(物体)"基类是抽象的,然而它的子类"Ladder(梯子)"不是抽象的—那么你可以在世界中放置一个Ladder,但你不可以在世界中放置一个Actor。这个关键词可以传播到内在的子类,但是不能传播到脚本的子类。
Deprecated导致该类的所有对象可以被加载但不能被保存。当关卡设计者在编辑器中加载地图时,地图中任何已放置的deprecated actors的实例将会给他们产生一个警告。Transient说明”属于这个类的对象将永远不会被保存到磁盘上” 。 仅在和某些类型的netive类结合时才有作用,这些netive类天生不需要持久存在的比如玩家或者窗口。这个关键字可以传播到子类,子类可以使用NotTransient&关键字覆盖这个标志。NonTransient取消从基类继承的&Transient&关键字。Config(IniName)意味着允许这个类在.ini文件中存储数据。如果在类(以"config"或 "globalconfig"声明)中有任何可配置的变量,则可以把这些变量被保存到指定的配置文件中。这个标志可以传播到所有的子类且不能取消传播,但子类可以通过重新声明&Config&关键字 并指定一个不同的IniName来改变.ini文件。正常情况下IniName指定了用于存储数据的.ini文件,但是有几个名称具有特殊的意义:
Config(Engine): 使用引擎配置文件,它的名称是您的游戏的名称后附加上"Engine.ini"。比如,ExampleGame的引擎配置文件名称是ExampleEngine.ini。
Config(Editor): 使用编辑器配置文件,它的名称是您的游戏名称后附加"Editor.ini"。比如,ExampleGame的编辑器配置文件名称是ExampleEditor.ini。
Config(Game): 使用游戏配置文件,它的名称是您的游戏名称后附加"Game.ini"。比如,ExampleGame的游戏配置文件名称是ExampleGame.ini。
Config(Input): 使用输入配置文件,它的名称是您的游戏名称后附加"Input.ini"。比如ExampleGame的输入配置文件是ExampleInput.ini。
PerObjectConfig为每个对象存储这个类的配置信息,每个对象在.ini文件中有一个在该对象后以[ObjectName ClassName]的形式命名的部分。这个关键字可以传播到子类。PerObjectLocalized以每个对象为基础为这个类定义本地化数据,每个对象在本地化文件中都有一个在该对象后以[ObjectName ClassName]形式命名的部分。这个关键字可以传播到子类。
EditInlineNew编辑器。意味着这个类的对象可以从UnrealEd的属性窗口中创建(默认行为是仅已存在的对象的引用可以通过属性窗口进行分配)。这标志可以传播到所有子类,子类可以使用&NotEditInlineNew&关键字覆盖这个标志。NotEditInlineNew编辑器。取消从基类继承&EditInlineNew&关键字。如果父类没有使用&EditInlineNew&关键字,则该关键字没有影响。Placeable编辑器。意味着这个类可以在UnrealEd中创建并可以被放置到关卡、UI scene或者kismet窗口中(依赖于类的类型)。这个标志可以传播到所有子类,子类可以通过使用&NotPlaceable&关键字覆盖这个标志。NotPlaceable编辑器。取消从基类继承&Placeable&关键字,意味着这个类不能放置UnrealEd中的关卡中等。HideDropDown编辑器。阻止这个类在UnrealEd的属性窗口的复选框中显示。HideCategories(Category[,Category,...])编辑器。为这个类的对象指定一个或多个应该在UnrealEd属性窗口中隐藏的类别。为了隐藏声明时的没有类别的变量,可以使用声明变量时所使用的类的名称。ShowCategories(Category[,Category,...])编辑器。取消从基类继承&HideCategories&关键字。AutoExpandCategories(Category[,Category,...])编辑器。为这个类的对象指定一个或多个应该在UnrealEd属性窗口中自动展开的类别。为了自动展开声明时的没有类别的变量,可以使用声明变量时所使用的类的名称。Collapsecategories编辑器。意味着这个类的属性不应该在UnrealEd的属性窗口中以类别分组。这个关键字可以传播到子类,子类可以使用&DontCollapseCategories&关键字来覆盖这个标志。DontCollapseCategories编辑器。 取消从基类继承&CollapseCatogories&关键字。Within&ClassName高级。 意味着这个类的对象在没有ClassName的实例的情况下不能存在。为了创建这个类的对象,你必须指定一个ClassName的实例作为&Outer&对象。这个关键字必须紧跟在类的声明本身后面。Inherits(ClassName[,ClassName,...]):高级。用于多重继承-指定额外的基类。多个被继承的基类可以通过使用一个&Inherits&并在多个基类之间用逗号分开来指定,或者通过为每个基类分别使用一个=Inherits= 来指定。仅对native类有效。不支持来自两个UObject-derived类的多重继承。Implements(ClassName[,ClassName,...])高级。指定一个或多个这个类要实现的接口类。多个接口可以通过使用一个单独的=Implements=且多个接口使用逗号分隔来指定,或者通过为每个接口类使用一个=Implements=来指定。NoExport高级。意味着这个类的C++声明不应该被脚本编译器包含到自动生成的C++头文件中。该C++类的声明必须在一个单独的头文件中手动地定义。
这里是在UnrealScript中的一些实例变量声明的例子:
//声明了一个整型变量,
名称为"A"的
var byte Table[64];
//声明了一个64个字节的静态数组,名为"Table"的。
var string PlayerN
// 声明了一个字符串变量,名称为"PlayerName"。
var actor O
//声明了一个可以被赋值为一个Actor实例的引用的变量。
var() float MaxTargetD
// 声明了名称为"MaxTargetDist"的一个浮点变量,并且它的值可以从UnrealEd的属性窗口中进行修改。
变量在UnrealScript能出现在两种地方:实例变量,它应用于整个对象,在类声明后或者在struct声明内立即出现。局部变量,出现在函数中,仅在函数执行时有效。实例变量使用关键字&var&进行定义。局部变量使用关键字&local进行定义,比如:
function int Foo()
local int C
Count = 1;
这里是UnrealScript中支持的内置的变量类型:
byte(字节型): 一个字节值的范围是&0&到&255
int(整型): 32位的整型值
bool(布尔型): 布尔值:=真= 或&假
float(浮点型): 32位的浮点数。
string(字符串型): 一个字符串(请查看)
constant(常量): 不能修改的变量。
enumeration(枚举型): 一个能够代表多个预先设定的整数值中其中一个值的变量。比如,在Actor脚本中定义的枚举类型ELightType描述了一个动态光源,它可以具有像&LT_None=、 =LT_Pulse=、 =LT_Strobe&等值。
集合数据类型
array&Type&:一个&Type&类型的数组。
struct和C中的结构体类似,UnrealScript的structs可以让您创建包含子变量的新的变量类型。比如,两个常用的Unreal structs是由X坐标、Y坐标及Z坐标组成的&vector(向量)&;及由倾斜度、偏转度、旋转度组成的rotator&。
Unreal类型
Name在Unreal中一个术语的名称(比如函数名、状态名、类名等)。Names(名称)会作为一个索引存储到全局名称表中。Names(名称)和简单的字符串相对应,最多可以包含64个字符。Names(名称)不像字符串那样一旦创建不能改变(请查看获得更多的信息)。Object&and&Actor&references一个指向世界中另一个object 或 actor的变量。比如,Pawn类有一个 "Enemy" 物体引用,它指定了玩家应该攻击的物体。Object 及actor引用是非常强大的工具,因为它们使您可以访问另一个actor的变量及函数。比如,在Pawn的脚本中,你可以用&Enemy.Damage(123)&来调用您的敌人的Damage函数—从而导致敌人受到伤害。Object 引用也可能包含着一个特殊的叫&None&的值,它和C 语言中的&NULL&指针是等同的:它的意思是这个变量不能指向任何物体。Delegate保存着到一个Unrealscript函数的引用。
变量修饰符
变量也可以具有其它的额外的修饰符来对其进一步的描述,比如&const&。事实上,将会有很多你在一般的编程语言中没有见过的修饰符,这主要是因为我们想使UnrealScript支持许多游戏-和特定环境中-的专用概念:
config这个变量是可以配置的。当前的值将被保存到一个ini文件中并在创建时加载。这个变量不能在默认属性中赋值。globalconfig和config的使用基本相同,除了不可以在子类中覆盖它所定义的变量并且它不能在默认属性中进行赋值。意味着它是常量。[这里的理解可能有歧义,解释下:config的数据可以在派生类中重写,globalconfig不可以,mark by dongbo]。localized这个变量的值将会定义一个局部变量,主要用于字符串。意味着是常量。在&及&获得关于此的更多信息。const把变量的内容当成一个常量。在UnrealScript,你可以读取常量的值,但是你不能对其写入值。"Const"仅用于引擎来负责更新的变量及不能从UnrealScript中安全地进行更新的变量,比如一个物体的位置(仅能通过调用MoveActor函数来进行设置)。private这个变量是私有的,仅能通过类的脚本来进行访问;其它的类(包括子类)不能访问该变量。protected这个变量仅能由此变量的类及其子类进行访问,其它类不能访问。repnotify当这个属性通过拷贝被赋值时,Actors应该被通知(通过 ReplicatedEvent 函数)。deprecated意味着这个变量在不久的将来将会被删除,并且将不能在编辑器中访问。Deprecated属性可以被加载,但不能被保存。instanced仅对对象属性。当创建了一个这个类的实例,将会默认地为这个变量赋予一个这个对象的唯一拷贝。用于实例化在类的默认属性中定义的子对象。databinding这个属性可以通过数据仓库系统进行操作。editoronly属性值仅在运行UnrealEd或者一个命令开关是被加载。在游戏中,这个属性值将被丢弃。notforconsole这个属性值将仅在运行PC时加载。在游戏控制台中,该属性值被丢弃。editconst编辑器。这个变量可以在UnrealEd中被看见,但不能被编辑。一个editconst变量并&不&暗示着它是"const"变量。editfixedsize编辑器。仅用于动态数组。这将防止用户通过UnrealEd属性窗口来改变数组的长度。editinline编辑器。允许用户编辑在UnrealEd属性窗口中编辑这个变量所引用的物体属性。(仅用于对象引用,包括数组对象的引用)。editinlineuse编辑器。除了执行&editinline&的相关行为外,在编辑器中这个对象引用的旁边添加一个"Use"按钮。noclear编辑器。允许从编辑器中把这个对象引用设为None。interp编辑器。意味着这个值可以受到Matinee中的浮点或向量属性轨迹的驱动从而随时间改变。
input高级。使变量在Unreal的输入系统中是可以访问的,从而使输入(比如按钮的押下和操作杆的运动)能够和变量直接地进行映射。transient高级。说明这个变量是临时使用的,且不是对象永久状态的一部分。 Transient变量不会被保存到磁盘。当加载一个对象时,Transient变量被初始化为类的默认值。duplicatetransient高级。意味着当创建对象的一个二进制副本时,变量的值将被重置为类的默认值(通过StaticDuplicateObject).noimport高级。意味着当导入T3D文本时,将跳过这个变量。换句话说,当导入或者复制/粘帖对象时,这个变量的值将不会被传递到新的对象实例中。native高级。声明了这个变量通过C++代码进行加载和保存,而不是通过UnrealScript。export高级。仅对对象属性(或对象数组)有用,意味着当一个对象被复制(复制/粘帖)或导出到T3D中而不是仅仅输出对象引用本身时,赋予给这个属性的对象应该作为一个子对象块整体地导出。noexport高级。仅用于native类。这个变量不能包含在自动生成的类声明中。nontransactional高级。意味着这个变量值的改变将不会被包含到编辑器的取消/重做历史中。pointer{type}高级。这个变量是一个指向&type(数据类型)&的指针(&type(数据类型)&是随意的)。注意语法是:pointer varname(指针变量名){type(数据类型)}init高级。这个属性将被以一个FString或TArray而不是一个FStringNoInit or TArrayNoInit导出到头文件中。仅适用于在native类中声明的字符串和动态数组。‘Init‘属性不能被赋予默认值,因为当创建对象时,默认值将会被清除。(请查看&&和&)
out:这个修饰符仅对函数参数有效。请查看来获取详细信息。&coerce:这个修饰符仅对函数参数有效。请查看来获取详细信息。&optional:这个修饰符仅对函数参数有效。请查看来获取详细信息。
skip这个修饰符仅对运算符函数参数有效。仅用于逻辑运算符比如&& 和 ||。如果一个表达式的输出已经确定则防止赋值。比如:FALSE && ++b==10 ().
在UnrealScript中,你可以使一个实例变量变得可编辑,以便用户可以在UnrealEd中编辑变量的值。这个机制负责了UnrealEd的"Actor Properties"对话框的全部内容,您在那里所看到的全部内容仅是UnrealScript中的一个声明为可以编辑的变量。
声明一个可以编辑的变量的语法如下所示:
var() int MyI //在默认目录中声明了一个可以编辑的整型
var(MyCategory) bool MyB //中声明一个可以编辑的整型"MyCategory"
Y您也可以声明一个变量为&editconst&,这意味着这个变量将可以在UnrealEd中看见但&不&可以编辑。注意这仅仅是为了防止变量在编辑器中被改变而不是在脚本中。如果您想使一个变量为真正的&const&但是仍然可以在编辑器中可见,您必须声明它为&const editconst&:
// MyBool在UnrealEd中是可以看见的但却不可编辑
var(MyCategory) editconst bool MyB
// MyBool变量在UnrealEd中是可见的但不能编辑而且不能通过脚本来改变它的值
var(MyCategory) const editconst bool MyB
// MyBool变量在UnrealEd中是可见的且可以设置,但不能在脚本中改变他的值
var(MyCategory) const bool MyB
数组使用以下语法声明:
var int MyArray[20]; //声明了一个长度为20的整形数组
UnrealScript仅支持一维数组,但你可以通过自己来设置行/类的元素从而来模拟多维数组。要获得关于动态数组的信息,请查看以下部分
UnrealScript struct是把一串变量一起塞入到一个新类型称为struct的超变量的一种方法。UnrealScript structs和中的结构体很像,它可以包含变量、数组及其它的结构体,但UnrealScript structs中不可以包含函数。
你可以按照以下方法来声明一个struct:
//在3D空间的一个点或方向向量
struct Vector
var float X;
var float Y;
var float Z;
一旦您定义了一个struct,你便可以开始定义那个struct类型的特定变量了:
//声明了一组Vector类型的变量
var Vector P
var Vector D
要想访问struct中的分量 ,可以按照以下方式使用代码:
function MyFunction()
Local Vector A, B, C;
//向量相加
C = A + B;
//仅向量的X成员相加
C.X = A.X + B.X;
// 将向量C传递到一个函数中
SomeFunction( C );
//将向量的某个成员传递到函数中
OtherFunction( A.X, C.Z );
你可以像操作其它变量一样来操作Struct变量:你可以赋值变量给它们、也可以传递它们给函数、而且您也可以访问它们的成员。
在Object类中定义了几个Structs,这些在整个的Unreal将一直会使用。你必须熟悉它们的操作,因为它们是脚本的基础构建模块。
Vector在空间中的一个唯一的三维点或者向量,具有X、 Y、Z分量。Plane在三维空间内定义了一个的唯一的平面。平面是通过它的X、Y、Z分量(假设它们都是归一化的)加上它的W分量来定义的,W代表了沿着平面的法线平面到原点的距离 (它是从平面到原点的最短的线)。Rotator一个rotation(旋转定)义了一个唯一的正交坐标系统。一个rotator包括Pitch(倾斜度)、Yaw(偏转度)及Roll(旋转度)分量。Coords在三维空间中的任意一个坐标系统。Color一个RGB颜色值。Region定义了关卡中的一个凸起区域。
Structs也有少许修饰符来影响struct的所有实例。
atomic意味着这个struct要一直作为一个单独的单元进行序列化;如果在struct中的任何属性和它的默认值不同,那么struct中的所有元素都将被序列化。atomicwhencookedapplies the ‘atomic‘ flag only when working with cooked package data.仅在使用已烘焙的包的数据时才应用‘atomic‘标志。immutable意味着这个结构体使用二进制序列化(减少磁盘空间占用并提高序列化性能);在没有增加包的版本的情况下从这个struct中增加/移除成员是不安全的。&immutablewhencooked:仅当使用已烘焙的包数据进行工作时才能应用‘immutable‘标志。strictconfig意味着当一个struct属性有‘config/globalconfig‘修饰符时,仅在这个结构体中标记为config/globalconfig的属性才能被从.ini文件中读取。(如果没有此标志,在struct中的所有属性都是可配置的)
在UnrealScript中枚举类型的存在,可以方便地声明能够包含一串关键字中的任意一个值的变量。比如,actor类包含着枚举变量=EPhysics=,它描述了Unreal要应用到actor上的物理。这个物理可以设置为已经预先定义的值中的一个,比如&PHYS_None=、 =PHYS_Walking=、& =PHYS_Falling&等。
在内部,枚举变量是作为字符变量存储的。在设计UnrealScript时,并没有认为枚举类型是必需的,但是当看到一个actor得物理模型被设置为&PHYS_Swimming&而不是(比如) =3=,发现它使代码变得如此的易读。
这里是定义枚举类型的示例代码。
//定义了一个具有三个值的枚举类型EColor。
enum EColor
//现在定义EColor类型的两个变量
var EColor ShirtColor, HatC
//可替换地,你可以像这样一起声明变量和枚举类型:
var enum EFruit
FRUIT_Apple,
FRUIT_Orange,
FRUIT_Bannana
} FirstFruit, SecondF
在Unreal代码中,我们经常要声明枚举值,像&LT_Steady=、 =PHYS_Falling&等,而不是简单地声明为"Steady" 或 "Falling"。这只是编码风格的问题而不是语言的要求。
UnrealScript仅在枚举类型定义的类及其子类中可以识别没有明确枚举变量名的枚举标签(比如&FRUIT_Apple)。如果你需要引用一个在类的层次的其它地方定义的枚举标签,你必须使它符合要求:
FRUIT_Apple
// 如果Unreal不能找到这个枚举标签…
EFruit.FRUIT_Apple
//那么像这样来使它符合要求
在UnrealScript,你几乎可以为所有的数据类型来指定常量值:
整型和字节型常量可以使用简单的数字来指定,比如&123. 如果你必须以16进制的方式来指定一个整型或字节型常量,使用:&0x123
使用十进制指定浮点型常量:&456.789
字符串常量必须加上双引号,比如:="MyString"=
Name常量必须加上单引号,比如:&‘MyName‘
Vector常量要像这样来了包含X, Y, 和 Z的值:&vect(1.0,2.0,4.0)
Rotator常量要像这样来包含Pitch, Yaw, 和 Roll的值:&Rot(0x0,0)
=None=常量指向"no object(没有对象)"(或者等同地"no actor")
=Self=常量指向"this object"(或等同地,"this actor"),也就是正在执行的脚本所属的对象。
一般对象的常量通过用对象的类型并跟随带单引号的对象的名称来指定,比如:&texture‘Default‘
EnumCount=指定了一个枚举类型的元素个数,比如: =ELightType.EnumCount
ArrayCount=指定了一个静态数组中的元素的个数,比如:=ArrayCount(Touching)
你可以使用"const"关键字来声明一个常量,以后便可以通过常量的名称来引用了。比如:
const LargeNumber=123456;
const PI=3.14159;
const MyName="Tim";
const Northeast=Vect(1.0,1.0,0.0);
常量可以在类或structs中定义。
如果要访问在另一个类中定义的常量,请使用"class‘classname‘.const.constname"语法,比如:
class‘Pawn‘.const.LargeNumber
Object和actor引用变量
你可以声明一个指向一个actor或object的变量,像这样:
var actor A; //一个actor引用
var pawn P; //一个到Pawn类中的一个actor的引用
var texture T; //一个到贴图对像的引用
上面的变量"P"是一个到Pawn类的一个actor的引用。这个变量可以指向属于Pawn的子类的任何actor。比如,P可以指向一个Brute,或者一个Skaarj或Manta。它可以是任何类型的Pawn。然而,P永远不能指向一个Trigger actor(因为Trigger不是Pawn的子类)。
使变量指向一个actor从而获得便利的例子是在Pawn类中的Enemy变量,它指向Pawn正在试图攻击的actor。
当您有一个指向actor的变量时,你可以访问那个actor的变量并且调用它的函数。例如:
//声明指向一个pawn的两个变量
var pawn P, Q;
//这里是一个使用P对象的函数,它显示了关于P对象的信息。
function MyFunction()
// 设置P的敌人为Q
P.Enemy = Q;
//告诉P播放这个跑动的动画
P.PlayRunning();
指向actors的变量通常或者指向一个有效的actor(任何在关卡中实际存在的actor)或者指向&None&值。None等同于C/C++中的&NULL&指针。然而,在UnrealScript中访问具有&None&引用的变量和函数是安全的;结果通常为0。
注意一个object 或 actor引用是“指向”另一个actor 或 object,它并不“包含”一个真正的actor 或 object对象。在C语言中,一个actor引用等同于指向一个AActor类的对象的指针(在C语言中,你会称它为AActor*)。例如,你可以在世界中有两个鬼怪(monsters)Bob和Fred,它们相互打斗。Bob 的"Enemy"变量将“指向”Fred,并且Fred的"Enemy"变量将“指向”Bob。
和C语言中的指针不同,UnrealScript的对象引用总是安全的可靠的。一个对象引用指向一个不存在的对象或者无效的对象是不可能的(除了特殊情况:&None&值)。在UnrealScript中,当一个actor被销毁,那么它的所有引用将被设置为&None&。
类引用变量
在Unreal中,类就像actors、textures(贴图)及 sounds(声音)一样是对象。Class 对象属于名称为"class"的类。在很多情况下,您将需要存储一个到类对象的引用,以便您可以产生一个属于那个类的actor(在编译时不知道那个类是什么)。比如:
var() class C;
var actor A;
A = Spawn( C ); // 产生一个属于某个任意类C的actor.
现在,一定不要搞乱类C和属于类C的对象O(作为类C的一个“实例”)的角色作用。打一个不太靠谱的比喻,一个类就像一个胡椒粉的研磨工,而那个类的实例就像是胡椒粉。你可以使用胡椒粉的研磨工(类)来转动磨制机器的曲柄(调用Spawn函数)来创建胡椒粉(类的对象)…但是,一个胡椒粉研磨工(一个类)不是胡椒粉(属于这个类的对象),所以你一定不要尝试去吃它!
当定义一个引用这个类对象的变量时,你可以有选择性地使用语法&class&metaclass&&来限制这个类不能被metaclass&类型(及它的子类)的变量所引用。比如在以下声明中:
var class&actor& ActorC
变量ActorClass仅能引用继承类"actor"的类。这对改善编译时的类型检查是有帮助的。比如,Spawn函数采用一个类作为参数,但是仅当这个类是Actor的子类时才有效, class&classlimitor&语法使编译器来执行那个要求。
关于使用动态对象类型转换,你可以这样来动态地转换类的类型:
// 转换SomeFunctionCall()的结果作为Actor类型的一个类(或Actor类的子类)
class&actor&( SomeFunctionCall() )
要给一个变量赋值,请使用 "=":
function Test()
local vector v,
//给一个整型变量i赋值
s = "Hello!"; //给字符型变型变量s赋值
//赋值向量q的值到v
在UnrealScript中,当一个函数或其它表达式需要一个某种类型的数据(比如:"float")时,而您指定了一个不同的数据类型(比如:”int”),编译器将会自动转换你给的值为适当的类型。所有数字数据类型(byte、 int 和 float)之间的转换是自动进行的,不需要您做任何操作。
UnrealScript也可以转换许多内置的数据类型为其它的类型,如果在代码中明确地转换它们,用于转换的语法是:
function Test()
local vector v,
s = string(i);
//转换字符型i为一个字符串,并赋值给s
s = string(v);
//转换向量v为一个字符串,不把它赋值给s
v = q + vector(r); //转换rotator类型的r为向量类型,不把它和q进行相加
这里是在UnrealScript您可以使用的不能自动地进行类型转换的完整集合:
String转换为Byte 、Int、 Float:尝试转换一像&"123"&的字符串为&123&。如果字符串不代表一个值,则它的值为&0&。
转换Byte, Int, Float, Vector, Rotator为String:转换数字为文本格式。
转换String为Vector, Rotator:尝试解析vector 或 rotator的文本格式。
转换String 为 Bool:转换大小写不敏感的单词&"True"&或&"False"&为&True&和&False=;将会使任何非零数值转换为 =True&;其它任何值转换为&False&。
转换Bool为String:结果不是&"True"&就是&"False"&。
转换Byte, Int, Float, Vector, Rotator类型为Bool类型:转换非零值为&True&;零值为&False&。
转换Bool类型为Byte, Int, Float类型:转换&True&为&1&;=False= 为&0&。
转换Name为String:转换名称为等同的文本。
转换Rotator为Vector:根据rotator返回一个向前的向量。
转换Vector 为Rotator:返回一个以向量的方向进行倾斜、偏转的rotator;旋转度为0.
转换Object (或 Actor)为Int:返回一个针对于那个object的唯一的整型数值。
转换Object (或 Actor) 为 Bool类型:如果object是&None&则返回&False=;否则为 =True&。
转换Object (或 Actor) 为String:返回那个object的文本格式。
在类之间转换对象引用
就像上面的简单数据类型的转换函数一样,在UnrealScript中你可以在各种类型之间转换actor和object引用。比如,所有的actors有一个叫"Target"的变量,它是到另一个actor的引用。假如您正在写一个脚本,在脚本中您需要检查并查看Target是否属于"Pawn" actor类的,并且你需要对您的target做一些特殊的处理,但仅当它是一个pawn类时才有效--比如,您需要调用Pawn函数的其中一个。actor的类型转换操作符可以让您完成这个目的。这里是一个例子:
var actor T
function TestActorConversions()
local Pawn P;
//转换Target的类型为Pawn并把它的值赋予P。如果Target不是一个Pawn(或者Pawn的子类),那么赋给P的值将会是None。
P = Pawn(Target);
if( P != None )
// Target是一个pawn,所以让它的Enemy为Self。
P.Enemy = S
// Target不是一个pawn。
为了执行一个actor转换,输入类的名称并在后面加上你希望转换的actor表达式,并使用圆括号括起。根据这个转换是否合理,转换将或者成功或者失败。在上面的例子中,如果您的Target指向的是一个Trigger对象而不是一个Pawn,表达式Pawn(Target)将会返回"None"值,因为不能将一个Trigger转换为Pawn。然而,如果您的Target指向一个Brute对象,转换将会成功并返回那个Brute,因为Brute是Pawn的子类。
因此,actor类型转换有两个目的:第一,你可以使用它们来判断某个actor引用是否属于某个类。第二,你可以使用它们来将一个actor引用从一个类转换为一个更加明确的类。注意这些转换不会影响您正在转换的actor—它们只是使UnrealScript将对待那个actor引用就像它是一个更加明确的类型一样并且允许您访问这个更加子类中所声明的属性和方法。
另一个类型转换的列子是在Inventory脚本中。每个Inventory actor由一个Pawn所有,尽管它的Owner变量可以指向任何Actor(因为Actor.Owner是一个Actor类型的变量)。所以在Inventory代码中我们会将常看到将一个Owner转换为Pawn类型的现象,比如:
//当销毁时,由engine调用
function Destroyed()
//从拥有者的武器装备中移除
if( Pawn(Owner)!=None )
Pawn(Owner).DeleteInventory( Self );
在UnrealScript中,你可以声明新的函数及为已存在的函数书写一个新的版本(重写这些函数)。函数可以带有一个或多个参数(可以是UnrealScript支持的任何变量类型),并且可以可选择地返回一个值。尽管大多数函数是直接在UnrealScript中书写的,您也可以声明可以从UnrealScript中调用的函数,但是在C++中实现并存在于一个DLL文件中。Unreal支持所有可能函数调用组合:C++引擎可以调用脚本函数;脚本可以调用C++函数;而且脚本之间也可以相互调用。
这里是一个简单的函数声明。这个函数有一个vector(向量)作为参数并返回一个浮点型值:
// 用于计算一个vector的大小的函数
function float VectorSize( vector V )
return sqrt( V.X * V.X + V.Y * V.Y + V.Z * V.Z );
关键词&function&总是在函数声明的前面,后面可以跟随任意的函数返回类型(在这里是&float&),然后是函数名及在括号里所包含的函数参数。
当调用一个函数时,在括弧内的代码将会被执行。在函数内部,您可以声明局部变量(使用关键字&local&),并且可以执行任何UnrealScript代码。可选择的&return&关键字可以使函数立刻地返回一个值。
你可以传递任何UnrealScript类型到一个函数(包括数组)中,并且这个函数可以返回任何类型。
默认情况下,在函数中声明的任何局部变量都将被初始化为0.
函数可以进行递归调用。比如,下面的函数计算了一个数的阶乘
//计算数的阶乘的函数
function int Factorial( int Number )
if( Number &= 0 )
return Number * Factorial( Number - 1 );
某些UnrealScript函数是在某些事件发生时由引擎来调用的。比如,当一个actor和另一个actor接触,引擎会调用它的&Touch&函数来告诉它是谁正在接触它。通过书写自定义的&Touch&函数,你可以在接触事件发生时采取特殊的行动:
//当某些东西接触这个actor时则调用这个函数
function Touch( actor Other )
Log( "I was touched!")
Other.Message( "You touched me!" );
上面的函数解释了几件事情。首先,函数使用&Log&命令向日志文件中写入了一个消息(除了格式规则以外,这和Basic语言的"print"及C语言的"printf"是类似的)。第二,它调用了在Other actor中的"Message"函数。调用在其它actors中的函数在UnrealScript是一个常用的操作,大体上和面向对象的语言比如Java是一致的,因为它为actors间彼此的交流提供了一个简单的方法。
函数参数修饰符
通常当您的调用一个函数时,UnrealScript会制作一个您传入函数的参数的一个本地副本。如果函数修改了某些参数,这对您传入的参数没有任何影响。比如,以下的代码:
function int DoSomething( int x )
x = x * 2;
function int DoSomethingElse()
local int a,
log( "The value of a is " $ a );
b = DoSomething( a );
log( "The value of a is " $ a );
log( "The value of b is " $ b );
当调用了DoSomethingElse函数后,产生了以下输出结果:
The value of a is 2
The value of a is 2
The value of b is 4
换句话说,函数DoSomething处理的是我们传递给它的变量a的本地副本,并且它不会影响真正的变量"a"。
out&修饰符告诉函数它应该修改传递给它的变量,而不是修改一个本地副本。这是有用的,比如,如果您有一个需要返回给调用者几个值的函数,你可以让调用者传递几个变量给带有&out&值的函数。
//计算一个向量中的最大值和最小值
function VectorRange( vector V, out float Min, out float Max )
//计算最小值
if ( V.X&V.Y && V.X&V.Z ) Min = V.X;
else if( V.Y&V.Z ) Min = V.Y;
else Min = V.Z;
//计算最大值
if ( V.X&V.Y && V.X&V.Z ) Max = V.X;
else if( V.Y&V.Z ) Max = V.Y;
else Max = V.Z;
如果没有关键字&out&,那么尝试写出必须返回多个函数值的函数是个令人痛苦的事情。Out参数是通过引用传入的,所以在函数中修改参数的值将会立即影响原始值。这也可以用于多个值的优化,类似于C++中通过指定"const out"类型来实现。
使用&optional&关键字,你可以使某些函数的参数是可选的,这为调用者提供了便利。在UnrealScript函数中,调用者没有指定的可选参数的值时,将被设置为在函数声明是指定的默认值或如果在函数声明时没有指定值时则设置它为0(也就是 0、false、""、none)。对于native函数,可选参数的默认值依赖于函数。比如,Spawn函数有一个可选的参数location 和 rotation,它们默认为actor的位置和旋转度。可选参数的默认值可以通过添加&= value&来指定。比如,function myFunc(optional int x = -1)。
关键字&coerce&强制使调用函数的参数转换为指定的类型(即使通常情况下UnrealScript将不会执行自动转换)。这对处理字符串的函数是有用的,函数将为您自动地将参数转换为字符串。
"函数重写"是指在子类中写一个函数的新的版本。比如,您正在为一个新类型的叫Demon的鬼怪书写脚本。您所创建的Demon类继承了Pawn类。现在,当一个pawn第一次看到一个玩家时,将会调用pawn的"SeePlayer"函数,从而pawn可以开始袭击玩家。这是一个很好的概念,但是当您想在您的新的Demon类中以不同的方式处理"SeePlayer"函数时,你该怎么实现哪?答案便是函数重载。
要重写一个函数,只需要从它的父类中将函数的定义剪切并复制到您的新类中。比如,你可以添加SeePlayer函数到您的新类Demon中。
//类Demon 中的新的Touch函数版本
function SeePlayer( actor SeenPlayer )
log( "The demon saw a player" );
//在这里添加中定义的功能…
函数重载是有效地创建新的UnrealScript类的关键。您可以创建一个继承已有类的新类。然后,你所需做的便是重载那些你需要进行不同处理的函数。这可以使您在不必写大量的代码的情况下创建一种新的对象。
在UnrealScript中的某些函数是声明为&final&。=final=关键字(在关键字&function&后出现)是说:”这个函数不能被子类重载”。这可以在没有人重载的函数中使用,因为它可以提供更快的脚本代码执行速度。比如,你有一个VectorSize&函数用于计算一个向量的大小。因为绝对没有原因导致任何人来重载它,所以声明它为&final&。另一方面,像&Touch&这样的函数是非常依赖于情境的,所以不能声明它为final。
高级函数修饰符
Static静态函数就像C语言中的全局函数一样,在那里你可以在没有类的对象的引用的情况下来调用它。静态函数可以调用其它的静态函数,并且可以访问变量的默认值。静态函数不能调用非静态函数并且他们不能访问实例变量(因为它们的执行不是针对于对象的实例的)。和C++语言不一样,静态函数是虚函数并且可以在子类中重载。这在当您想在一个变量类(在编译时不能确定的但是通过变量或者表达式来引用的类)中调用一个静态函数时是有用的.
Singularsingular&关键字,紧接在函数声明的前面出现,可以阻止函数对其本身进行递归调用。规则是:如果某个actor已经在一个singular函数中,任何对这个singular的后续调用将会被跳过。这在某些情况下来避免无限的递归循环bug是有利的。例如,如果你尝试着在您的&Bump&函数中移动一个actor, 那个actor将会在移动中撞击另一个actor是有很大的可能的,从而导致了另一个对&Bump&函数的调用等等。你必须细心地避免这种行为,但是如果您没有足够的信心来通过书写代码避免这种潜在的递归情况,那么请使用&singular&关键字。
Native:你可以声明UnrealScript函数为&native&,这意味着该函数是可以从UnrealScript中调用的,但是事实上是在C++中实现的。比如,Actor类包含着许多native函数定义,比如:
native(266) final function bool Move( vector Delta );
在关键字&native&后的圆括号内的数字和在C++(使用&AUTOREGISTER_NATIVE&macro)中定义的函数的数字是相对应的,并且这仅在运算符函数中有要求。Native函数将存在于DLL文件中,该DLL文件的名称和包含了UnrealScript定义的类的包的名称是一致的。
NoExport仅用于native函数。说明了将不导出这个native函数的C++函数定义,仅导出该函数的在脚本版本的定义。
Exec意味着这个函数可以通过在控制台输入函数的名称来执行。仅在某些类中有效。
Latent说明了一个native函数是latent,意味着它仅能从状态代码中被调用,并且可能在游戏过去一段时间后返回。
Iterator说明了一个native函数是一个iterator,它可以通过使用&foreach&命令来在一系列的actors中循环。
Simulated不管一个actor是simulated代理或一个autonomous代理时,函数可以在客户端执行。所有不管是native还是final函数都可以自动地被simulated。
Server说明这个函数必须被发送到服务器上执行,而不是在本地客户端运行。
Client说明这个函数必须被发送到客户端执行,而不是在服务器上运行。这个标志也隐含着为该函数设置了simulated&标记。
Reliable说明了一个复制(replicated)函数(标记为&server(服务器)&或&client(客户端))是可靠发送,意味着相对于那个Actor上的其它复制来说,它将最终地按照顺序到达另一端。
Unreliable说明了一个复制函数(标记为&服务器&或&客户端)是不可靠发送,意味着它将不能保证会以任何特定的顺序到达另一端并且如果没有足够的带宽它可能会被完全地跳过。
Private,&Protected这些关键字作为相应的变量关键字时具有同样的意思。
Operator,&PreOperator,&PostOperator这些关键字用于声明一个称为运算符重载函数(operator)的特殊函数(和C++中的运算符函数类似)。这是UnrealScript如何知道所有的内置运算符像"+", "-", "==", 和 "||"的方式。我将不会在这篇文档中详细说明运算符函数是如何工作的,但是运算符函数的概念和C++中是类似的,所以你可以像声明UnrealScript函数或native函数一样来声明一个新的运算符重载函数。
Eventevent=关键字和UnrealScript中的 =function&具有相同的意思。然而,当您可以使用&unreal -make -h&来从Unreal中导出一个C++头文件时,UnrealEd为每个”event”自动生成一个C++ -& UnrealScript调用存根。这可以自动地保持C++代码和UnrealScript函数同步并消除了向一个UnrealScript函数中传递无效参数的可能性。比如,这些UnrealScript代码:
event Touch( Actor Other )
生成类似于以下的在EngineClasses.h中的代码:
void eventTouch(class AActor* Other)
FName N("Touch",FNAME_Intrinsic);
struct {class AActor* O } P
Parms.Other=O
ProcessEvent(N, &Parms);
因此您可以从C++中像这样来调用UnrealScript函数了:
AActor *SomeActor, *OtherA
SomeActor-&eventTouch(OtherActor);
Const仅用在声明为native的函数中,并且这个修饰符加在函数声明的&后面&。这个修饰符决定了这个函数是否应该在生成的头文件中作为‘const‘导出。举例应用:
native function int doSomething(string myData)
UreanScript支持C/C++/Java中所有的标准的流程控制语句。
"For"循环指在没有满足某个条件之前一直执行循环。例如:
// "for"循环举例
function ForExample()
log( "Demonstrating the for loop" );
for( i=0; i&4; i++ )
log( "The value of i is " $ i );
log( "Completed with i=" $ i);
这个循环的输出是:
Demonstrating the for loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4
在for循环中,你必须指定3个表达式且它们之间使用分号分隔。第一个表达式用于初始化一个值作为它的初值;第二个表达式给出一个条件用于在每次循环执行前进行条件判断;如果这个表达式为真,则执行循环。如果它为假,则终止循环。第三个条件给出了一个增加循环计数器的表达式。
尽管大多数”for”循环仅仅是更新一个计数器,你也可以通过使用合适的初始化值、终止条件和增量表达式,从而使用”for”循环来做像遍历链表这样的高级的事情。
在所有的流程控制语句中,你可以或者执行一个没有括弧的单独的语句,如下所示:
for( i=0; i&4; i++ )
log( "The value of i is " $ i );
或者你可以执行由括弧包围的多个语句,像这样:
for( i=0; i&4; i++ )
log( "The value of i is" );
"Do"循环是指在某个结束表达式为真的情况下,您可以不断的进行循环。注意Unreal使用&do-until&语法,这和C/Java (它们使用&do-while&)是不同的。
// "do"循环举例
function DoExample()
log( "Demonstrating the do loop" );
log( "The value of i is " $ i );
i = i + 1;
} until( i == 4 );
log( "Completed with i=" $ i);
这个循环的输出结果:
Demonstrating the do loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4
While 循环
"While"是指当某个起始表达式为真时,您可以不断地进行循环。
// "while"循环举例
function WhileExample()
log( "Demonstrating the while loop" );
while( i & 4 )
log( "The value of i is " $ i );
i = i + 1;
log( "Completed with i=" $ i);
这个循环的输出是:
Demonstrating the do loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4
"continue"命令将会跳回到循环的开始位置,所以任何continue之后的命令将不会被执行。这可以用于在某些情况下跳过循环代码。
function ContinueExample()
log( "Demonstrating continue" );
for( i=0; i&4; i++ )
if( i == 2 )
log( "The value of i is " $ i );
log( "Completed with i=" $ i );
这个循环的输出是:
Demonstrating break
The value of i is 0
The value of i is 1
The value of i is 3
Completed with i=4
"break"跳出最近一层循环("For", "Do", 或 "While")。
function BreakExample()
log( "Demonstrating break" );
for( i=0; i&10; i++ )
if( i == 3 )
log( "The value of i is " $ i );
log( "Completed with i=" $ i );
这个循环的输出是:
Demonstrating break
The value of i is 0
The value of i is 1
The value of i is 2
Completed with i=3
注意"break"命令也可以用于跳过执行一个条件语句("switch")的剩余代码。
If-Then-Else语句
"If", "Else If", and "Else"使您在满足某些条件是执行相应代码。
if( LightBrightness & 20 )
log( "My light is dim" );
// "if-else"举例
if( LightBrightness & 20 )
log( "My light is dim" );
log( "My light is bright" );
// if-else if-else"举例
if( LightBrightness & 20 )
log( "My light is dim" );
else if( LightBrightness & 40 )
log( "My light is medium" );
else if( LightBrightness & 60 )
log( "My light is kinda bright" );
log( "My light is very bright" );
//执行语句带有括弧的"if"判断举例
if( LightType == LT_Steady )
log( "Light is steady" );
log( "Light is not steady" );
"Case", "Default", 和 "Break"可以使您的轻易地处理一系列的条件判定。
// switch-case举例
function TestSwitch()
// 基于LightType的值,执行以下case语句的任意一条,
switch( LightType )
case LT_None:
log( "There is no lighting" );
case LT_Steady:
log( "There is steady lighting" );
case LT_Backdrop:
log( "There is backdrop lighting" );
log( "There is dynamic" );
"switch"由一个或多个"case"语句及一个可选择的"default"语句组成。在一个switch语句后,如果和"case"语句的某一个相匹配则执行该Case语句;如果没有相匹配的case则执行"default"语句;如果既没有相匹配的case语句也没有写default语句,则执行到"select"语句的末尾。
当您在代码中写好了以下的"case"标签,你必须是有一个"break"语句来使执行继续执行到"switch"语句的末尾。如果您不使用"break",将会继续执行接下来的"case"处理语句。
// Example of switch-case.
function TestSwitch2()
switch( LightType )
case LT_None:
log( "There is no lighting" );
case LT_Steady:
// 将一直执行到case: LT_Backdrop
case LT_Backdrop:
log( "There is lighting" );
log( "Something else" );
"Goto"命令可以跳转到当前函数或状态的某处的标签。
// "goto"举例
function GotoExample()
log( "Starting GotoExample" );
log( "At Yon" );
log( "At Hither" );
Elsewhere:
log( "At Elsewhere" );
The output is输出结果是:
Starting GotoExample
At Elsewhere
内置的操作符和它们的优先级
UnrealScript提供了为各种操作提供了很多种类似于C/C++/Java类型的操作符,比如数字相加的操作、比较值的操作以及自增操作等。操作符的完整的集合在Object.u中定义,但是这里有一个简明的概括。这里使一些标准操作符,以优先级为顺序。注意所有的C类型的操作符的优先级和它们在C语言中的优先级是一致的。
string(字符串)
字符串连接,在两个字符串间有一个附加的空格"string1"@"string2" = "string1 string2"
string(字符串)
字符串连接,在两个字符串间有一个附加的空格,连接并赋值(版本3323 及其后续版本)
string(字符串)
字符串连接
string (字符串)
字符串连接,连接并赋值(版本3323 及其后续版本)
byte, int, float, vector, rotator
相乘并赋值
byte, int, float, vector, rotator
相除并赋值
byte, int, float, vector
相加并赋值
byte, int, float, vector
相减并赋值
逻辑“或“
逻辑“与“
All (所有类型)
All(所有类型)
byte, int, float, string
byte, int, float, string
byte, int, float, string
byte, int, float, string
float, string
约等于(精确到0.0001),大小写不敏感等式
int, vector
左移(int),正向向量转换(vector)
int, vector
左移(int), 反向向量转换(vector)
byte, int, float, vector
byte, int, float, vector
float, int, byte
求模(除法的余数)
byte, int, float, vector, rotator
byte, int, float, vector, rotator
ClockwiseFrom
int (rotator elements)
当从第一个参数可以通过第二个参数顺时针旋转得到,则返回true
上面的表格按照优先顺序列出了操作符(具有相同优先级的分为一组)。当您输入一个复杂的表达式,比如"1*2+3*4",UnrealScript会自动地按照优先级为操作符分组。因为乘法的优先级比加法高,这个表达式等同于"(1*2)+(3*4)"。
"&&" (逻辑与) 和 "||" (逻辑或)操作符是短路的:如果表达式的结果能够单独地由第一个表达式决定(比如,如果 && 的第一个参数是false),第二个表达式则不需要进行计算。
关于向量的正向和反向转换,反向变换&&是从本地空间转换为世界空间而正向变换&&是从世界空间转换为本地空间。
比如,如果您有一个向量指向玩家前面64个单位的地方,vect(64,0,0)是在本地玩家坐标的值。如果您想让它在世界空间中,您必须使用玩家的旋转来把它转换为世界坐标,所以您必须通过以下方式来进行进算:
myWorldSpaceVect = my64Vect && playerPawn.
当您有一个世界空间的向量并且你想让它呈现在本地玩家空间时,万一您想使用一个向前的rotation。比如,您可能想转换一个汽车actor的世界空间速度到本地空间,从而您可以获得X(前进速度)来把它输出到一个HUD中。
除了标准的操作符,UnrealScript支持一下一元操作符:
! (bool)逻辑非
- (int, float) 负数
~ (int)按位取反
++, --自增,自减(或者在一个变量前或者在一个变量后使用)
新的操作符会不断地增加到引擎中。要获得操作符的完整列表,请查看最新的UnrealScript源代码-尤其是Object类。
为了在UnrealScript中创建一个新的对象,您将会根据这个对象是否是一个Actor来使用这两个函数其中的一个。对于Actors,您将会使用Spawn函数,声明在Actor.uc文件中。对于不是从Actor类派生的类,您必须使用&new&操作符。New操作符的语法和其它的函数不同。除了一个可选择的参数列表,你必须指定新的对象的类及可选择的模板对象。new操作符没有UnrealScript声明,但是这里是函数声明的样子:
native final operator function coerce Object new
InTemplate
InOuter(可选的) 用于作为新创建的对象的Outer的对象 。如果没有指定,则此对象的Outer将被设置为一个仅当游戏运行时存在的被称为"transient package"的特殊包。InName(可选的)新对象的名称。如果没有指定,此对象将以ClassName_##的形式给定一个唯一的名称,每次创建一个这个类的实例,这里的##会不断增加。InFlags(可选的,目前还不能使用,因为对象标志是64位的)创建对象时所要使用的对象标志。
0x0000: 支持编辑器撤销/重复(RF_Transactional)。
0x0000: 可以被外部文件所引用(RF_Public)。
0x0000: 不能被保存到磁盘(RF_Transient)。
0x0000: 请不要再游戏客户端加载对象(RF_NotForClient)。
0x0000: 请不要再游戏服务器加载对象(RF_NotForServer)。
0x0000: 不要再编辑器内加载对象。(RF_NotForEdit)
x0000:即使对象没有被引用,也要保持对象以便编辑。(RF_Standalone)
InClass用于创建一个实例的类。InTemplate用于初始化新的对象的属性值的object。
新的操作符的实际的语法如下所示:
ObjectVar = new[(InOuter, InName, InFlags)] &class‘InClass‘&[(InTemplate)];
创建类LightFunction的一个对象
function CreateALight()
local LightFunction NewO
NewObj = new class‘Engine.LightFunction‘;
创建一个叫"NewLight"的LightFunction对象,分配这个对象作为它的Outer。
function CreateALight()
local LightFunction NewO
NewObj = new(Self,‘NewLight‘) class‘Engine.LightFunction‘;
在transient包中创建一个叫"NewLight"的新的LightFunction对象,使这个对象作为LightFunctionTemplate变量的值来初始化新的对象的属性:
var LightFunction LightFunctionT
function CreateALight()
local LightFunction NewO
NewObj = new(None,‘NewLight‘) class‘Engine.LightFunction‘ (LightFunctionTemplate);
defaultproperties
Begin Object class="LightFunction" Name=MyLightFunctionArchetype
End Object
LightFunctionTemplate=MyLightFunctionArchetype
int Rand( int Max ); 返回一个0到Max-1的随机数
int Min( int A, int B );返回两个数中最小的数
int Max( int A, int B );返回两个数中最大的数
int Clamp( int V, int A, int B );返回A到B间隔内所包含的第一个数值。
警告-Min和Max在整数上的应用和C或C++不同。当针对浮点型数据使用这两个函数时不会产生警告 – 您的数字会默认地进行四舍五入处理!对于浮点型,你需要使用函数FMin 和 FMax来进行操作。
浮点型数据函数
float Abs( float A );返回数值的绝对值
float Sin( float A ); 返回在半径中所表示的数值的正弦函数值。
float Cos( float A );返回弧度数值的余弦值
float Tan( float A ); 返回弧度数值的正切值
float ASin( float A );返回半径所表示的数值的反正弦值。
float ACos( float A );返回半径所表示的数值的反余弦值
float Atan( float A );返回弧度数值的反正切值
float Exp( float A ); 返回A的幂数的常量指数“e“
float Loge( float A ); 返回A的对数
float Sqrt( float A );返回A的平方根
float Square( float A ); 返回A的平方的值
float FRand();返回从0.0 到 1.0的一个随机数
float FMin( float A, float B );返回两个数最小的值
float FMax( float A, float B ); 返回两个数最大的值
float FClamp( float V, float A, float B ); 返回A到B的间隔区间内的第一个数的值。
float Lerp( float A, float B, float Alpha );返回A和B之间的线性插值。
float Smerp( float Alpha, float A, float B );返回A和B之间的一个Alpha-smooth非线性插值。
float Ceil ( float A ); 向上舍入
float Round ( float A ); 正常舍入
字符串函数
int Len( coerce string S ); 返回字符串的长度
int InStr( coerce string S, coerce string t); 如果第一个字符串包含第二个字符串,则返回第二个字符串在第一个字符串中的偏移量;如果不存在,则返回值为-1.
string Mid ( coerce string S, int i, optional int j );返回字符串S的中间部分,即从字符i开始后连续j个字符的字符串(或者如果没有指定j,则返回字符i后面的所有字符)。
string Left ( coerce string S, int i );返回字符串S的最左边的字符i。
string Right ( coerce string] S, int i ); 返回字符串S的最右边的字符i。
string Caps ( coerce string S );返回到转换为大写字母的字符串S。
string Locs ( coerce string S);返回转换为小写字母的字符串S (v3323 and up)。
string Chr ( int i );从ASCII表格中返回一个字符
int Asc ( string S );返回一个字符的ASCII码值(仅使用字符串的第一个字符)
string Repl ( coerce string Src, coerce string Match, coerce string With, optional bool bCaseSensitive );在源代码中使用&With&代替&Match&。
string Split(coerce string Text, coerce string SplitStr, optional bool bOmitSplitStr); 当第一个&SplitStr&发生时分隔&Text&并返回&Text&的剩余部分。如果=bOmitSplitStr=为真,=SplitStr=将会从返回的字符串中被忽略。
array SplitString( string Source, optional string Delimiter=",", optional bool bCullEmpty );使用一个单独的表达式来分隔一个字符串为一个字符串数组的封装函数。
JoinArray(array StringArray, out string out_Result, optional string delim = ",", optional bool bIgnoreBlanks = true);从一个字符串数组中创建一个单独的字符串,使用指定的分界符,可以选择是否忽略空格。
ParseStringIntoArray(string BaseString, out array Pieces, string Delim, bool bCullEmpty);分解一个具有分解符的字符串为一个字符串数组的多个元素。
A == B; 比较,如果两个字符串相同则返回true(区分大小写)。
A ~= B; 比较,如果两个字符串相同则返回true(不区分大小写)
A != B; 比较,如果字符串不相同返回true.
请查看来获取更多的信息。
Vector函数
vector vect( float X, float Y, float Z );使用给定的组成元素创建一个新的向量。
float VSize( vector A );返回向量的欧几里得大小(元素开平方后值的总和的平方根)
vector Normal( vector A );返回一个大小为1.0的向量,面向指定向量的方向。
Invert ( out vector X, out vector Y, out vector Z ); I通过3个坐标轴向量来反转坐标系统。
vector VRand ( );返回一个均匀分布的随机向量。
vector MirrorVectorByNormal( vector Vect, vector Normal );根据指定的法线向量镜像一个向量。
计时器函数
计时器函数仅在Actor的子类中存在。
您可以创建具有不同触发速率的多个计时器。每个计时器有一个唯一的目标函数(默认为&Timer()&)。
function SetTimer(float inRate, optional bool inbLoop, optional Name inTimerFunc);在被触发&inRate&后启动一个计时器。如果&inbLoop&为真则计时器将会循环。_inTimerFunc_ 定义了要调用的函数,默认情况下这个函数是&Timer()&,这个值也用于识别多个计时器。
ClearTimer(optional Name inTimerFunc);停止一个正在运行的计时器
bool IsTimerActive(optional Name inTimerFunc);如果给定的计时器是出于启动状态,则返回true.
float GetTimerCount(optional Name inTimerFunc);返回计时器的计数器的值。也就是自从上一次执行计时器开始所过的秒数。如果计时器没有启动,则返回-1。
float GetTimerRate(optional name TimerFuncName = ‘Timer‘);返回计时器的速率,GetTimerRate(‘SomeTimer‘) - GetTimerCount(‘SomeTimer‘)&将返回计时器剩余的时间。
以下函数可以帮助您调试代码
LogEx( ELoggingSeverity Severity, name Category, coerce string Msg );使用给定的安全级别和类别来记录一个消息。这个函数比标准的&log()&函数提供了更多的控制,它允许您在运行时根据安全级别和目录来过滤日志信息。
LogFatal( name Category, coerce string Msg );用于调用&LogEx(LOG_FATAL, Category, Msg)&函数的缩写。
LogError( name Category, coerce string Msg );
function LogWarn( name Category, coerce string Msg );
LogInfo( name Category, coerce string Msg );
LogDebug( name Category, coerce string Msg );
LogTrace( name Category, coerce string Msg );
注意,正如变更列表134102所示,以上的日志功能已经不能获得。它们已经被由处理的一个关于日志的宏所取代。请查看版本更新信息来获取详情:
ScriptTrace();存储当前的脚本调用堆栈到一个日志文件中
Name GetFuncName();返回当前正在调用的函数的名称
DumpStateStack();记录当前的状态栈
UnrealScript处理器
要获得更多信息,请查看页面。
UnrealScript工具和使用
Script Profiler(脚本分析器)
能够帮助您了解脚本执行的那些部分占有了大部分的时间。
脚本调试器
请查看Unreal的页面获得更多信息。
虚幻开发环境(UDE)
如果想获得更多的使用UDE来进行UnrealScript开发和调试的信息,请查看页面。
高级的语言功能
计时器是一种用于安排一个事件随时间变化发生、重复发生的一种机制。以这种方式,当一段固定时间过后,一个Actor能使一个计时器来注册它本身到一个游戏引擎上,从而可以使一个&Timer()&函数被调用或者一次、或者重新发生。
UnrealScript计时器仅仅是在每个Actor内部作为一个structs数组来实现的(一个Actor可以有多个还没有被触发的计时器) 。这个struct包含了在计时器过期前所剩余的时间的数量、计时器到期时所要调用的函数等。
一般游戏主循环每帧每个Actor一次,并且每个Actor的&Tick()&函数包含一个调用函数&UpdateTimers()&,它会检查任何将要过期的计时器并调用它们适当的UnrealScript代码。
时间间隔大小限于每个帧之间的时间间隔长度,但是对硬件和OS资源没有要求。所有的这些是在C++代码中实现的,所以您可以在不必担心可能引起的任何后果的情况下安全地更新成百个UnrealScript计时器。当然,您不会想使所有的计时器同时地过期或者在每一个帧过期,因为当这是计时器被激活时要以较慢的速度执行脚本代码。
从游戏历史来说,游戏开发人员自从游戏发展到"pong"阶段后便开始使用状态这个概念。状态(也被称为“状态机编程”)是使复杂的物体行为更加容易管理的一种常用方式。然而在UnrealScript出现之前,状态在编程语言的层次上还没有得到支持,需要开发人员基于物体的状态来创建C/C++"switch"语句,这样的代码是难于书写和更新的。
UnrealScript在编程语言的层次上支持状态。
在UnrealScript中,世界中的每个actor都处于并仅处于一个状态。它的状态反映了它要执行的动作。比如,移动画刷有几个状态像"StandOpenTimed" 和 "BumpOpenTimed"。Pawns有一些像"Dying"、 "Attacking"、 和 "Wandering"等状态。
在UnrealScript中,你可以针对一个特定的状态来书写函数和代码。这些函数和代码仅当actor在那个状态时被调用。比如,你正在写一个关于怪物的脚本,并正在思考如何处理"SeePlayer"函数,当您在到处巡逻的时候,您想攻击您看到的玩家,当您正在攻击玩家时,您想不停地进行连续攻击。
实现这个目的最容易的方式便是通过定义几个状态(Wandering(到处走动) 和 Attacking(攻击)),并且在每个状态写一个不同版本的"Touch"函数。UnrealScript支持这样做。
在对状态进行深入研究之前,你需要知道状态的两个主要好处和一个复杂性:
好处:状态提供了一个用于来书写针对状态的函数的简单方法,所以你可以根据actor的行为来不同的方式来处理同一个函数。
好处: 使用状态,您可以使用完整的规范的UnrealScript命令和几个以"latent functions"著称的专用函数来书写专用的"state code(状态代码)"。一个latent函数的执行是比较慢的(也就是非阻塞的),并且可能要在一定的游戏时间过后才能返回。这可以使您进行基于时间的编程 – 这个主要的好处是无论是C, C++, 还是Java都没有提供的。也就是,你可以按照您想的方式来书写代码;比如,你书写一个脚本来表达"打开这个门; 暂停2s中; 播放这个音效;打开那个门;发放一个怪物来攻击玩家"。你可以通过使用简单的线性的代码来完成它,并且虚幻引擎会负责管理基于时间的代码执行的细节。
复杂性:现在您可以多个状态及子类中重载函数(像&Touch&),这对您明确地分辨出在哪个特定的状态来调用哪个"Touch"函数增加了负担。UnrealScript提供了一个规则来清楚地描述这个过程,它是在您创建类及状态的复杂层次时必须考虑的。
这里是一个TriggerLight脚本中的一个状态的例子:
// Trigger打开光源
state() TriggerTurnsOn
function Trigger( actor Other, pawn EventInstigator )
Trigger = N
Direction = 1.0;
Enable( ‘Tick‘ );
// Trigger关闭光源
state() TriggerTurnsOff
function Trigger( actor Other, pawn EventInstigator )
Trigger = N
Direction = -1.0;
Enable( ‘Tick‘ );
这里声明了两个不同的状态(TriggerTurnsOn和TriggerTurnsOff),并且在每个状态中书写了一个不同版本的Trigger函数。尽管您可以在不使用状态的情况下努力地完成这个实现,但是状态使代码更加的具有模块性和可扩展性:在UnrealScript中,你可以很容易地创建一个现有类的子类、增加新的状态及增加新的函数。如果过你尝试不使用状态来完成这个功能,最终的代码在以后将更加的难于扩展。
一个状态可以声明为可编辑的,意味着用户可以在UnrealEd中设置actor的状态,或者不能设置其状态。如果想定义一个可以编辑的状态,请按照以下方式:
state() MyState
如果要声明一个不可编辑的状态,请按照:
state MyState
您也可以通过"auto"关键字来指定一个actor的自动的或者初始的状态。这会导致当新的actor初次被激活时都会被置于那个状态。
auto state MyState
状态标签和Latent函数
除了函数,一个状态可以包含一个或多个标签,在其后面可以书写UnrealScript代码,比如:
auto state MyState
Log( "MyState has just begun!" );
Sleep( 2.0 );
Log( "MyState has finished sleeping" );
goto(‘Begin‘);
上面的状态代码输出了信息&"MyState has just begun!"&,接着它停止了2秒钟,然后它输出了信息&"MyState has finished sleeping"&。这个例子中的一个有趣的事情便是调用latent函数"Sleep":这个函数的调用并不是立即返回,而是在一定量的游戏时间过去后才返回。Latent函数仅能在状态代码中并且不能从函数中调用。Latent函数可以帮助您管理包括一段时间的经过在内的一系列复杂的事件。
所有的状态代码都是以标签定义开始;在上面的例子中标签命名为"Begin"。标签提供了一个进入状态代码的方便的入口点。你可以在状态代码中使用任何标签名称,但是"Begin"标签式专用的:它是那个状态的默认入口点。
所有的actor都有三个主要的latent函数可以使用:
Sleep( float Seconds )使状态的执行暂停一段时间,然后再继续执行。
FinishAnim()等待指导当前你正在播放的动画序列播放完成,然后继续执行。这个函数使编写由动画驱动的脚本也就是执行是由网格物体动画控制的脚本变得容易。比如,大多数AI脚本都是由动画驱动的(相对于有时间驱动的脚本),因为平滑过度的动画是AI系统的主要目标。
FinishInterpolation()等待当前的InterpolationPoint移动完成然后继续。
Pawn类为动作定义了几个重要的latent函数比如在世界中导航及短期运动。请查看单独的AI文档来获得它们用途的描述。
3个native UnrealScript函数在书写状态代码时尤其地有用:
"Goto(‘标签名‘)"函数(和C/C++/Basic中的goto类似),该函数使状态代码在指定的标签处继续执行。
特殊的Goto(‘‘)命令,它会导致状态代码停止执行。状态代码在您到达一个新的状态或者到达当前状态中的一个新的标签之前将不会继续执行。
"GotoState"函数会使actor跳转到一个新的状态,然后在指定的标签处继续执行(如果您没有指定一个标签,默认则是"Begin"标签)。你可以在状态代码中调用GotoState函数,它将会立刻跳转到目标状态。你也可以在actor中的任何函数中调用GotoState函数,但是它不能立即生效:它只有在函数执行返回到状态代码时才会生效。
这里是到目前为止所讨论的状态概念的示例:
//这是一个自动的状态
auto state Idle
//当被另一个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" );
log( "I am idle..." );
sleep( 10 );
goto ‘Begin‘;
//攻击状态
state Attacking
Log( "I am executing the attacking state code" );
当您运行这个程序然后触摸那个actor,你会看到:
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
请确保您理解了GotoState的这个重要的方面:当你从一个函数中调用GotoState函数,它不会立刻跳转到目标状态,而是当给函数的执行返回到状态代码时才会跳转到目标状态。
状态继承和范围规则
在UnrealScript中,当您创建一个现有类的子类时,您的新类继承了父类的所有变量、函数及状态。这很好理解。
然而,对UnrealScript编程模型的状态抽象给继承及其范围规则增加了额外的复杂性。
一个新的子类继承它的父类的所有变量。
一个新子类继承它的父类的所有的非状态函数。你可以冲在任何这些继承过来的非状态函数。你可以增加全新的非状态函数。
一个新的子类继承它的父类的所有状态,包括在这些状态中的函数和标签。你可以重载任何继承的状态函数及状态标签,你可以增加新的状态函数及新的状态标签。
这里是所有重载规则的一个例子:
// 这里是一个示例父类
class MyParentClass extends A
// 一个非状态函数
function MyInstanceFunction()
log( "Executing MyInstanceFunction" );
// 一个状态
state MyState
//状态函数
function MyStateFunction()
Log( "Executing MyStateFunction" );
// "Begin"标签
Log("Beginning MyState");
// 这里是一个示例子类
class MyChildClass extends MyParentC
// 这里我重载了一个非状态函数
function MyInstanceFunction()
Log( "Executing MyInstanceFunction in child class" );
//这里我重新声明了MyState状态,以便我可以重载MyStateFunction函数。
state MyState
//这里我重载了MyStateFunction函数
function MyStateFunction()
Log( "Executing MyStateFunction" );
//这里我重载了"Begin"标签
Log( "Beginning MyState in MyChildClass" );
当您在一个或多个状态中、及在一个或多个父类中有一个全局执行的函数,你需要理解在给定的环境中应该调用哪个版本的函数。范围规则解决了这些复杂的情况:
如果对象在一个状态中,并且函数的实现存在于那个状态的某处(不管是在actor类中还是在某个父类中),则调用最子类的状态函数版本。
否则,调用最底层派生函数的非状态版本。
高级的状态编程
如果一个状态没有重载父类中具有相同名称的状态,那么你可以随意地使用"extends"关键字来使该状态在当前类的所存在的状态上进行扩展。这是有用的,比如,在您有一组类似的状态的情况时(比如MeleeAttacking 和 RangeAttacki

我要回帖

更多关于 unreal4 电脑配置 的文章

 

随机推荐