总体特征
(1)大小写不敏感。关键字、宏、变量名、函数名以及类名不区分大小写;变量名可以与关键字同名
(2)局部变量、成员变量未初始化时,会被编译器初始化
(3)没有全局变量、全局函数,所有东西必须写入类中
(4)一个uc文件中,有且只能写一个类,且文件名与类名要一致
(5)变量和函数默认为public类型,函数默认为虚函数
(6)不支持函数重载,但支持运算符重载和缺省参数
(7)不支持异常处理,不支持模板泛型
(8)无需显示导入外部包,uc文件可以使用当前包及之前编译的包中定义的类型和函数
(9)没有构造函数,没有析构函数,通过引用计数机制进行垃圾回收
(10)没有指针类型;数值型、bool型、名称、字符串、枚举、数组、结构体为值类型;类、接口为引用类型
(11)只支持一维数组
(12)没有静态变量,没有union类型
(13)所有对象都从Object类单根继承,支持接口多继承
(14)解释执行,运行时安全
预处理
编译命令参数
make -intermediate // 编译脚本,并将预处理结果保存到 UDKGame\PreProcessedFiles\<package>\<class>.uc
make -debug // 定义名为debug的宏变量,启用`logd宏函数,并编译脚本
make -final_release // 定义名为final_release的宏,禁用`log、`logd、`warn、`assert宏函数,并编译脚本
注:
`log(string OutputString, optional bool bRequiredCondition, optional name LogTag);
`logd(string OutputString, optional bool bRequiredCondition, optional name LogTag);
`warn(string OutputString, optional bool bRequiredCondition);
`assert(bool bCondition);
作用范围
1. 宏的作用范围是在文件内,意味着当前文件中的 X 行上的宏变量只能在该文件的 X+n 行中使用
2. 为了能让工程中多个文件使用宏变量,一般是将宏变量定义在.uci文件中(如:TGDefine.uci),然后各个文件的起始处`include(TGDefine.uci)
3. 定义在工程的根目录下的Globals.uci文件中的宏,能直接被这个包和依赖该包的包使用
注:增量编译不会侦测uci文件的修改,因此修改uci文件后要进行全量编译
条件编译
`if(`notdefined(USE_ASYNCHRONOUS_MOVE)) // ... `endif `if(`isdefined(USE_ASYNCHRONOUS_MOVE)) // ... `else // ... `endif
宏定义、宏取消
`define USE_ASYNCHRONOUS_MOVE `define WEAPON_CONFIG config // 花括号用于确保宏周围的空白是有效的 如:var `{WEAPON_CONFIG} bool m_bConfigTest; `define GRAPH_INCOSIZE 8 `define LOCATION "(" $ Name $ ") `{ClassName}::" $ GetStateName() $ ":" $ GetFuncName() `define MAX(a, b) ( ((`a)<(`b))?(`b):(`a) ) `define ShowVar(expr,name) "`if(`name) `name `else `expr `endif:‘" $ `expr $ "‘" // ShowVar换行写法如下 `define ShowVar(expr,name) "\ `if(`name) `name`else `expr`endif:‘" $ `expr $ "‘" `define LogWarn(cat, msg) `log("WARN:" @ `msg,,`cat) // 取消宏定义 `undefine(INCLUDE_GAME_STATS)
文件包含
`include(TGOnlineLogicMacros.uci)
特殊宏
`define MyMacro(p1, p2, p3, p4) “This macro contains `# parameters.”// `#为4,为宏参数的个数
常量
作用范围
1. 常量在运行时不可改变,可以定义在类中、成员函数内、状态块内,但编译器会将其统一调整到类中;好的编码习惯是将常量定义在类的起始处。
2. 定义在结构体中的常量将只能在结构体中使用,外部无法访问。
3. 当前类中定义的常量可直接访问:PI;访问外部类TGTest中定义的常量:class‘TGTest‘.const.PI
4. 对于公共常量,一般会将这些常量集中写入到一个单独的.uci文件中(如:TGConst.uci),然后类的起始处`include(TGConst.uci)
举例
const a = 10; // 十进制 a=10 const b = 0x10; // 十六进制 转成十进制为:b=16 const c = 010x; // 八进制 转成十进制为:c=8 const d = true; const f = 3.14; const s = "Hello World!"; // 字符串需加上双引号 const n = ‘James‘; // name需加上单引号 const o = none; // 空对象 相当于C/C++中的NULL const oc = class‘UTGame.UTPawn‘; // 类模板(引号中不能有空格) class<UTPawn> const oc2 = class‘UTBot‘; // 类模板(引号中不能有空格) class<UTBot> const or = Texture2D‘EngineResources.DefaultTexture‘;//纹理对象(引号中不能有空格) const v = vect (1.2, 3.4 , 5.6); // vector常量 const r = rot(1234, 5678 , 9012); // rotator常量
变量
数值型
类型 | 长度 | 范围 | 举例 |
byte |
8位无符号整型 | [0, 255] |
十进制:2 八进制:012x 十六进制:0xFF |
int | 32位整型 | [-2^31, 2^31-1] |
十进制:-500 八进制:018000x 十六进制:0x12FC |
float | 32位浮点(单精度) |
[-3.403e+38, 3.403e+38] 有效精度:6-7位有效数字 |
-3.25 5.0f 10. 1.23E2(不支持科学计数法,编译失败) 1.0/0.0=1.#INF00(比任何浮点数都大) -1.0/0.0=-1.#INF00(比任何浮点数都小) |
数值型间转换规则
1. 自动隐式转换
2. 精度高向精度低转换:
(a) 内存截断 (b) 精度丢失
>>>补零右移运算符
local int a, b; a = -1; // 0xFFFF FFFF b = a>>2; // b=0xFFFF FFFF=-1 b = a>>>2; // b=0x3FFF FFFF=1073741823
float ~= 约等于符号
local float f; local bool b; f = 0.00001; b = (f~=0); // b=true 等价于 abs(f)<0.0001
bool
(1)bool型只能为True或False
(2)bool型与数值型间不支持隐式转换
local int a; local bool b; a = 10; b = a; // 编译错误 b = bool(a); // b=true a = b; // 编译错误 a = int(b); // a=1
(3)^^ 异或
local bool b1, b2, b3; b1 = true; b2 = false; b3 = b1^^b2; // b3=true b1 = true; b2 = true; b3 = b1^^b2; // b3=false
name
(1)name字符:“a..z”、“A...Z”、"0...9"、“_”、“-”、空格,最大允许长度为1023字符
如: ‘Nico Chen‘ ‘123‘ ‘‘ ‘#逆战@123‘ name ("#逆战@123")
(2)name不区分大小写 ‘nico‘ 和 ‘Nico‘是相等的
(3)name在函数中用单引号包含,defaultproperties块中无需包含或使用双引号包含
defaultproperties { m_name1=‘Hello‘//Warning, Invalid property value m_name1=‘‘ m_name2=Hello_123//m_name2=‘Hello_123‘ m_name3=Hello World//m_name3=‘Hello‘ m_name4=Hello#World//m_name4=‘Hello‘ m_name5="Hello World!"//m_name5=‘Hello World!‘ m_name5=Hello// Warning, redundant data m_name5仍然为‘Hello World!‘ }
(4)通过NameOf()伪函数在编译时,将变量名和函数名转换成name
(5)name只能强制转换成string,不能转换成其他类型;同时string可以强制转换成name
(6)可以修改name,但无法修改name的内容
(7)name只支持两种比较运算符:==、!=
string
(1)string中的字符为16位unicode,最大允许长度为1023字符
如: "NicoChen" "Hello World!\n" "" "逆战"
(2)string区分大小写 "nico"和 "Nico"是不相等的
(3)可以修改string,但无法修改string的内容
(4)string在函数中用双引号包含,defaultproperties块中无需包含或使用双引号包含
defaultproperties { m_string1=‘Hello‘//Warning, Invalid property value m_string1="" m_string2=Hello_123//Warning, Missing " m_string2="Hello_123" m_string3=Hello World//Warning, Missing " m_string3="Hello" m_string4=Hello#World//Warning, Missing " m_string4="Hello#World" m_string5="Hello World!"//m_string5=‘Hello World!‘ m_string5=Hello// Warning, redundant data m_string5仍然为‘Hello World!‘ }
(5)除了数组与结构体外,其他类型都可强制转成string
local string s; // 其他类型转成字符串 s = string(‘‘); // s="" s = string(‘James‘); // s="James" s = string(50); // s="50" s = string(-1234); // s="-1234" s = string(-13.89); // 保留6位有效数字 s="-13.8900" s = string(true); // s="True" s = string(false); // s="False" s = string(ENetRole.ROLE_Authority); //s="3" s = string(GetEnum(Enum‘ENetRole‘, ENetRole.ROLE_Authority)); //s="ROLE_Authority" s = string(self); //代码位于MyPawn类中,s="MyPawn_1" s = string(none); //s="None"
(6)string可强制转成数值型、bool和name
local name m; local bool b; local byte n; local int a; local float f; // 字符串转成name m = name("test"); // m=‘test‘ m = name("Hello World!"); // m=‘Hello World!‘ // 字符串:整型 b = bool("-1"); // b=true a = int("-1"); // a=-1 n = byte("-1"); // n=255 f = float("-1");// f=-1.000000 // 字符串:浮点数 b = bool("-300.23"); // b=true a = int("-300.23"); // a=-300 n = byte("-300.23"); // n=byte(-300)=212 f = float("-300.23");// f=-300.230011 // 字符串:布尔值 b = bool("True"); // b=true a = int("True"); // a=0 n = byte("True"); // n=0 f = float("True");// f=0.000000 // 字符串:起始为数字子串 b = bool("210fAlse300"); // b=true a = int("210fAlse300"); // a=210 n = byte("210fAlse300"); // n=210 f = float("210fAlse300"); // f=210.000000 // 字符串:起始不为数字子串 b = bool("True335Test"); // b=false a = int("True335Test"); // a=0 n = byte("True335Test"); // n=0 f = float("True335Test"); // f=0.000000
(7)string支持所有的比较运算符 >、>=、<、<=、==、!=、~=
(8)string特有连接运算符 @ $ @= $= 注:@ 、$会将其左右操作数执行coerce转换成string类型;@= 、$=会将右操作数执行coerce转换成string类型
(9)string特有运算符 -= 注:-=会将右操作数执行coerce转换成string类型;a -= b等价于a = Repl(a, b, "", false)
local bool b1; local string s1; // string操作符 s1 = "Hello"$"World"; // "HelloWorld" s1 = "Hello"@"World"; // "Hello World" s1 = "Hello"; s1 $= "World"; // "HelloWorld" s1 = "Hello"; s1 @= "World"; // "Hello World" //s1 = "Hello"+"World"; // 编译错误 //s1 += "World"; // 编译错误 s1 = "Hello WOrld"; s1 -= "o"; // 去掉子串中所有的小写字母o(大小写敏感) s1="Hell WOrld" b1 = "Hello"<"World"; // 大写字母<小写字母 b1=true b1 = "Hello">"hello"; // 大写字母<小写字母 b1=false b1 = "Hello"!="hello"; // 大小写敏感 b1=true b1 = "Hello"~="hello"; // 大小写敏感 b1=true b1 = "Hello"=="Hello"; // b1=true b1 = "Hello"<="Hello"; // b1=true b1 = "Hello">="Hello"; // b1=true b1 = "Hello"!="Hello"; // b1=false b1 = "Hello"~="Hello"; // b1=true
(10)string函数
local int a1; local array<string> v1, v2, v3, v4; local string s1; // Object函数 a1 = Len("Hello"); // a1=5 a1 = Instr("THIS is my book!", "is"); // 返回第1个is的位置(大小写敏感) a1=5 s1 = Mid("Hello", 0, 2); // 返回从0索引开始后2个长度的子串 s1="He" s1 = Mid("Hello", 2); // 返回从2索引开始到结尾长度的子串 s1="llo"; s1 = Left("World", 2); // s1="Wo" s1 = Right("Hello", 4); // s1="ello" s1 = Caps("Hello"); // 返回字符串的大写版本 s1="HELLO" s1 = Locs("World"); // 返回字符串的大写版本 s1="world" s1 = Chr(97); // 返回给定int的字符串表示形式(可以是Unicode:0-65535内的任何值)s1="a" a1 = Asc("Hello"); // 返回字符串第一个字母的Unicode数值 a1=72 // 将"This is a test"中的"is"替换成"was" 大小写不敏感 s1="THwas was a test" s1 = Repl("THIS is a test", "is", "was"); // 将"Two be or not two be"中的"two"替换成"to" 大小写敏感 s1="Two be or not to be" s1 = Repl("Two be or not two be", "two", "to", true); // 将"Two be or not two be"中的"two"替换成"to" 大小写不敏感 s1="to be or not to be" s1 = Repl("Two be or not two be", "two", "to", false); // 返回从第1次出现"is"(大小写敏感)到结尾的子串(含is) s1="is is a test" s1 = Split("This is a test", "is", false); // 返回从第1次出现"is"(大小写敏感)到结尾的子串(不含is) s1="a test" s1 = Split("THIS is a test", "is", true); // 返回"Test_45_50_me20"最右边"_"后的子串 s1="me20" s1 = GetRightMost("Test_45_50_me20"); v1[0] = "Hello "; v1[1] = ""; v1[2] = "World"; // 使用#连接数值v1中的各个元素(空串不跳过) s1="Hello ##World" JoinArray(v1, s1, "#", false); // 使用#连接数值v1中的各个元素(空串跳过) s1="Hello #World" JoinArray(v1, s1, "#", true); // 使用"M"分隔字符串(大小写敏感),并将结果保存到数组v2中(空串不跳过) ParseStringIntoArray("HelloMMmWorld", v2, "M", false);//v2[0]="Hello" v2[1]="" v2[2]="mWorld" // 使用"M"分隔字符串(大小写敏感),并将结果返回到数组v2中(空串不跳过) v2 = SplitString("HelloMMmWorld", "M", false); //v2[0]="Hello" v2[1]="" v2[2]="mWorld" // 使用"M"分隔字符串(大小写敏感),并将结果保存到参数数组v2中(空串跳过) ParseStringIntoArray("HelloMMmWorld", v3, "M", true);//v3[0]="Hello" v3[1]="mWorld" // 使用"M"分隔字符串(大小写敏感),并将结果返回到数组v2中(空串跳过) v3 = SplitString("HelloMMmWorld", "M", true); //v3[0]="Hello" v3[1]="mWorld" // Actor函数 s1 = "This is a test"; // 等价于:s1=Repl(s1, "is", "was", true); 将字符串中的"is"替换成"was" 大小写敏感 s1为输出参数 ReplaceText(s1, "is", "was"); // 返回s1="Thwas was a test" s1 = "Two be or not two be"; // 等价于:s1=Repl(s1, "two", "to", true); 将字符串中的"two"替换成"to" 大小写敏感 s1为输出参数 ReplaceText(s1, "two", "to"); // 返回s1="Two be or not to be" // 等价于:v4=SplitString("www.qq.com", ".", true);s1=v4[v4.Length-1]; s1="com" s1 = GetItemName("www.qq.com");
enum
(1)枚举只能在类中定义,不能在函数中定义 如:ENetRole
// Net variables. enum ENetRole { ROLE_None, // No role at all. ROLE_SimulatedProxy, // Locally simulated proxy of this actor. ROLE_AutonomousProxy, // Locally autonomous proxy of this actor. ROLE_Authority, // Authoritative control over the actor. };
(2)枚举第一个元素的值为0,后续依次+1;不能随意地指定新的起始值
(3)在defaultproperties中对枚举成员变量初始化时,不能带枚举类型前缀,否则会失效
(4)枚举类型(等于枚举元素的个数)、类型.前缀_MAX、类型.EnumCount及类型.枚举元素(一定要带上类型)可作为静态数组的大小
local int a1[ENetRole]; // 等价于a1[4] local int a2[ENetRole.EnumCount]; // 等价于a2[4] local int a3[ENetRole.ROLE_MAX]; // 前缀_MAX 等价于a3[4] local int a4[ENetRole.ROLE_Authority]; // 等价于a4[3]
(5)整型与enum之间支持隐式转换
local byte a; local ENetRole nr; nr = 3; // nr=ROLE_Authority nr = -1; // nr=INVALIDE nr = 10; // nr=INVALIDE a = ENetRole.ROLE_SimulatedProxy; // a=1
(6)使用GetEnum函数将enum转换成name
local name m; m = GetEnum(Enum‘ENetRole‘, ENetRole.ROLE_Authority); // m=‘ROLE_Authority‘
静态数组
(1)不支持size为1的静态数组定义 local int a[1];
(2)不支持bool型静态数组定义 local bool b[5];
(3)不支持多维静态数组 local int a[2][2];
(4)无法修改静态数组的值,但可以修改静态数组的内容 如:b=a; // 编译错误
(5)越界访问情况,不会导致崩溃
local int a[5]; a[6] = 8; // a[4]=8 a[-1] = 2; // a[0]=2 a[1] = a[-1]; // a[1]=2
(6)通过ArrayCount()伪函数在编译时,得到静态数组大小
(7)静态数组元素访问在函数中用[],defaultproperties块中用()或[]均可
var int m_sa1[5]; defaultproperties { m_sa1(-1)=2 // m_sa1(0)仍然为0 m_sa1(0)=3 // m_sa1(0)=3 m_sa1(3)=1 m_sa1(3)=5 // Warning, redundant data m_sa1(3)仍然为1 m_sa1(6)=8 // Warning, Out of bound array m_sa1(4)仍然为0 }
(8)静态数组作为函数参数
simulated function Test() { local int xx[3]; StaticArrayTest(xx); } function StaticArrayTest(int a[3]) { a[0] = 5; a[1] = 12; a[2] = 20; }
(9)静态数组不能作为函数返回值
动态数组
(1)可以定义bool类型的动态数组
(2)动态数组既可以作为函数参数,也可以作为函数返回值
(3)避免写>>,否则编译不过 local Array<class<Actor>>; local Array<delegate<OnFailed>>
(4)不支持动态数组嵌套 local Array<Array<int> > vva;
(5)既可以修改动态数组的值,又可以修改动态数组的内容
(6)函数中通过Length字段,获取动态数组size(defaultproperties块中不能访问Length)
(7)函数中修改Length字段,调整动态数组size
local array<int> va; va.Length = 5; // va size=5: 0 0 0 0 0 va.Length = 0; // va size=0 va.Length = va.Length+3; // va size=3: 0 0 0 va.Length = -1; // va size=-1 //va.Length += 1; // 编译出错 不能对Length进行+=运算 //va.Length -= 1; // 编译出错 不能对Length进行-=运算 //va.Length++; // 编译出错 不能对Length进行后自增运算 //va.Length--; // 编译出错 不能对Length进行后自减运算 //--va.Length; // 编译出错 不能对Length进行前自减运算 //++va.Length; // 编译出错 不能对Length进行前自增运算 // function ChangeArrayLength(out int x){x=10;} //ChangeArrayLength(va.Length); // ChangeArrayLength中修改va.Length,会导致内存泄露和崩溃
(8)动态数组元素访问在函数中用[],defaultproperties块中用()或[]均可
(9)函数中使用索引修改元素的值时,若索引>=Length,将引发动态数组size增长
local array<int> va; local array<color> vb; local color c; va[2] = 10; // va size=3: 0 0 10 va[-1] = 5; // 无效语句 va size=3: 0 0 10 va[1] = 3; // va size=3: 0 3 10 va[2] = va[5]+8; // va size=3: 0 3 8 vb[1].R = 255; // vb size=0 注:不会引发size增长 c.G = 128; vb[2] = c; // vb size=3 [r=0 g=0 b=0 a=0] [r=0 g=0 b=0 a=0] [r=0 g=128 b=0 a=0]
(10)动态数组在defaultproperties中初始化
var Array<int> m_da1, m_da2; defaultproperties { m_da1[0]=2 // m_da1:2 m_da1(-1)=3 // Warning, Invalid property value m_da1:2 m_da1(1)=5 // m_da1:2 5 m_da1(1)=10 // Warning, redundant data m_da1:2 5 m_da1(3)=7 // m_da1:2 5 0 7 m_da2(4) = 10 //m_da2: 0 0 0 0 10 m_da2 = (1,,3) //m_da2: 1 0 3 m_da2 = () // Warning, redundant data m_da2: 1 0 3 m_da2(1) = 5 // m_da2: 1 5 3 m_da2(4) = 8 // Warning, redundant data m_da2: 1 5 3 }
(11)函数中清空动态数组
va.Length = 0;
(12)defaultproperties中清空动态数组
defaultproperties { m_da1.Empty }
(13)使用函数来修改动态数组size,追加、删除、插入元素到动态数组中
local array<int> va; va.AddItem(1); // 末尾增加值为1的元素 size=1 a: 1 va.Add(3); // 末尾扩充3个元素 size=4 a: 1 0 0 0 va.Insert(2, 2); // 在索引为2处插入2个元素 size=6 a: 1 0 0 0 0 0 va.Remove(1, 3); // 从索引为1处起删除2个元素 size=3 a: 1 0 0 va.AddItem(5); // 末尾增加值为5的元素 size=4 a: 1 0 0 5 va.RemoveItem(0); // 删除所有为0的元素 size=2 a: 1 5 va.InsertItem(1, 7); // 在索引为1处插入值为7的元素 size=3 a: 1 7 5
(14)defaultproperties中使用函数来追加、删除元素
var Array<int> m_da1; defaultproperties { m_da1.Add(1) // 末尾增加元素为1的元素 size=1 m_da1: 1 m_da1.Add(2) // 末尾增加元素为2的元素 size=2 m_da1: 1 2 m_da1.Add(5) // 末尾增加元素为5的元素 size=3 m_da1: 1 2 5 m_da1.Add(2) // 末尾增加元素为2的元素 size=4 m_da1: 1 2 5 2 m_da1.Remove(5) // 删除所有值为5的元素 size=3 m_da1: 1 2 2 m_da1.RemoveIndex(1) // 删除索引值为1的元素 size=2 m_da1: 1 2 }
(15)元素查找
local int n; local Rotator r; local Object o1, o2; local array<int> va; local array<Rotator> vr; local array<Object> vo; va[0]=1; va[1]=2; va[2]=6; va[3]=2; n = va.Find(2); // n=1 第1个为2的元素的索引值为1 n = va.Find(3); // n=INDEX_NONE(即:-1) 未找到为3的元素 r.Pitch=1000; r.Roll=2000; r.Yaw=3000; vr.AddItem(r); r.Pitch=4000; r.Roll=5000; r.Yaw=6000; vr.AddItem(r); n = vr.Find(‘Pitch‘, 4000); // n=1 第1个Pitch=4000的元素的索引值为1 n = vr.Find(‘Roll‘, 500); // n=-1 未找到Roll=500的元素 n = vr.Find(‘Yaw‘, 3000); // n=0 第1个Yaw=3000的元素的索引值为0 o1 = new(self) class‘ObjectEx‘; vo.AddItem(o1); o2 = new(self) class‘ObjectEx‘; vo.AddItem(o2); n = vo.Find(o2); // n=1 第1个为o2的元素的索引值为1
(16)元素排序
// 返回值<0,则交换a和b;否则不交换 delegate int IntSortAscDelegate(int a, int b) { if (a <= b) // 等于情况,一定不要交换,否则会死循环 return 0; // 不进行交换 return -1; // 进行交换 } simulated function DynamicArrayTest5() { local array<int> va; va[0] = 8; va[1] = 6; va[2] = 0; va[3] = 5; va[4] = 9; va[5] = 3; va[6] = 2; va[7] = 6; va.Sort(IntSortAscDelegate); // va: 0 2 3 5 6 6 8 9 }
(17)元素遍历
local int i, n, s1, s2, s3; local array<int> va; va[0] = 8; va[1] = 6; va[2] = 0; va[3] = 5; va[4] = 9; va[5] = 3; va[6] = 2; va[7] = 6; foreach va(n, i) { s1 += i; // s1=0+1+2+3+4+5+6+7=28 } foreach va(n) { s2 += n; // s2=8+6+0+5+9+3+2+6=39 } for(i=0; i<va.Length; i++) { s3 += va[i]; // s3=8+6+0+5+9+3+2+6=39 }
(18)静态数组与动态数组拷贝比较
local int i; local int a[3], ac[3]; local Array<int> va, vac; a[0] = 2; a[1] = 5; // a: 2 5 // ac = a; // 编译失败,静态数组不能通过赋值方式拷贝 // 循环拷贝静态数组a中的值到ac中 for (i=0; i<ArrayCount(a); i++) { ac[i] = a[i]; } va.AddItem(2); va.AddItem(5); vac = va; // 拷贝动态数组 vac: 2 5
struct
(1)结构体中可以存放常量和任意类型变量(小心避免直接或间接包含自身类型的变量,会引起编译崩溃 如下:)
struct STRTest { var STRTest c; };
结构体中不能有函数方法,成员的初始化放在structdefaultproperties块中
(2)结构体只能在类中定义
struct Student { var string sname; var int age; var float height; var byte scores[3]; var Array<int> history; var Color clr; structdefaultproperties // 初始化变量的默认值 { sname = "James" age = 20 height = 1.83 scores(0)=87.5 scores(1)=96.5 history=(95,100,88) clr= (r=255,g=32,b=128,a=0) } };
(3)支持结构体继承
// A point or direction vector in 3d space. struct immutable Vector { var() float X, Y, Z; }; // A plane definition in 3d space. struct immutable Plane extends Vector { var() float W; };
(4)函数中对进行结构体赋值和拷贝
local Student s1, s2; s1.sname = "Tom"; s1.age = 30; s1.height = 1.90; s1.clr.r = 255; s2 = s1; // 结构体赋值 s2.sname = "Jack"; // 结构体为值类型 s1.sname仍然为"Tom"
(5)在类的defaultproperties块中对结构体成员变量进行初始化
var Student m_student1, m_student2; defaultproperties { m_student1 = (sname="Lucy",age=8,height=1.77, clr=(r=200,b=150)) m_student2 = {( sname="Lily", age=10, height=1.68, clr=(r=200,b=150), scores[0]=80,scores[1]=90,scores[2]=85, history=(100,120,150,180,200) )} }
(6)支持==与!=比较(两个结构体内容完全一致则相等,否则不相等)
常用结构体 -- vector
运算符
(1)向量与向量 加减乘 + - * += -= *=
local vector v1, v2, v3; v1.x = 1.0; v1.y = 2.0; v1.z = 3.0; v2.x = 1.0; v2.y = 5.0; v2.z = 2.0; v3 = v1 + v2; // v3.x=2.0 v3.y=7.0 v3.z=5.0 v3 = v1 - v2; // v3.x=0.0 v3.y=-3.0 v3.z=1.0 v3 = v1 * v2; // v3.x=1.0 v3.y=10.0 v3.z=6.0 //v3 = v1 / v2; // 编译失败 没有除法 v3 = v1; v3 += v2; // v3.x=2.0 v3.y=7.0 v3.z=5.0 v3 = v1; v3 -= v2; // v3.x=0.0 v3.y=-3.0 v3.z=1.0 v3 = v1; v3 *= v2; // v3.x=1.0 v3.y=10.0 v3.z=6.0 //v3 = v1; v3 /= v2; // 编译失败 没有除法
(2)向量比较运算符 == !=
(3)- 负号运算符
local vector v1, v2; v1.x = 1.0; v1.y = 2.0; v1.z = 3.0; v2 = -v1; // v2.x=-1.0 v2.y=-2.0 v2.z=-3.0
(4)缩放
local vector v1, v2; v1.x = 1.0; v1.y = 2.0; v1.z = 3.0; v2 = 2.0*v1; // v2.x=2.0 v2.y=4.0 v2.z=6.0 v2 = v1*2.0; // v2.x=2.0 v2.y=4.0 v2.z=6.0 v2 *= 2.0; // v2.x=4.0 v2.y=8.0 v2.z=12.0 v2 /= 2.0; // v2.x=2.0 v2.y=4.0 v2.z=6.0 v2 = v1/2.0; // v2.x=0.5 v2.y=1.0 v2.z=1.5
(5)dot 点乘
local vector v1, v2, v3; local int n; v1.x = 1.0; v1.y = 2.0; v1.z = 3.0; v2.x = 0.0; v2.y = 5.0; v2.z = 2.0; n = v1 dot v2; // n=v1.x*v2.x+v1.y+v2.y+v1.z*v2.z=16.0
(6)cross 叉乘
local vector v1, v2, v3; v1.x = 1.0; v1.y = 2.0; v1.z = 3.0; v2.x = 0.0; v2.y = 5.0; v2.z = 2.0; v3 = v1 cross v2; // v3.x=v1.y*v2.z-v1.z*v2.y= -11.0 // v3.y=v1.z*v2.x-v1.x*v2.z= -2.0 // v3.x=v1.x*v2.y-v1.y*v2.x= 5.0
(7)旋转 << >>
local vector v1, v2; local rotator r1; v1.y = 1.0; r1.yaw = 65536/4; // 方位角 0-360分担到0-65536上 90 左手坐标系 v2 = v1 >> r1; // 正向旋转 v2.x=-1.0 v2 = v1 << r1; // 反向旋转 v2.x=1.0
(8)与string进行类型转换
local vector v; local string s; v = vect(1.23456,2.12,3.5688);//v=(1.234560,2.120000,3.568800) s = string(v);//s="1.23,2.12,3.57" 四舍五入,保留小数点后两位 s = "-3.7856235,,20,15.688"; v = vector(s);//v=(-3.785624,0.000000,20.000000)//按逗号进行分割,每个子块按照string转float规则进行转换
函数
(1)长度 VSize(v) VSize2D(v)
(2)长度的平方 VSizeSq(v) VSizeSq2D(v)
(3)单位化 Normal(v)
(4)随机向量 v=VRand()
(5)是否为0向量 IsZero(v)
(6)忽略z的点乘 NoZDot(v1, v2)
常用结构体 -- rotator
pitch (think nodding to say "yes") 俯仰角 绕-y轴 -->可通过下列公式:(65536+pitch%65536)%65536归一化到[0,65535]
yaw(shaking your head to say "no") 方位角 绕+z轴 -->可通过下列公式:(65536+yaw%65536)%65536归一化到[0,65535]
roll (tilting your head sideways while looking in a specific direction) 横滚角 绕-x轴 -->可通过下列公式:(65536+roll%65536)%65536归一化到[0,65535]
范围: [0,360)-> [0, 65536) 公式:y = 360.0*x/65536 注:y为度数,x为pitch、yaw或roll
(1)与string进行类型转换
local rotator r; local string s; r.Pitch = 65536/16;//r1 [Pitch=4096 yaw=0 roll=0] r.yaw = 65536/8;//r1 [Pitch=4096 yaw=16384 roll=0] s = string(r);//s="4096,16384,0" s = "-21.36,70000,65535,100"; r = rotator(s);//[Pitch=-21 yaw=70000 roll=65535] //按逗号进行分割,每个字块按照string转int规则进行转换
(2)运算符
a. rotator与rotator加减 + - += -=
b. rotator比较运算符 == !=
c. 缩放
(3)函数
a. 长度 RSize(r)
b. 求夹角(单位:度 范围:[0, 180]) RDiff(r1, r2)
控制语句
(1)do...until循环 c++: do...while
(2)新增foreach循环类型
函数
关键字 | 说明 |
simulated | 能被ENetRole=ROLE_SimulatedProxy的对象执行 |
final | 不允许被子类重写 |
native | 使用C++实现 |
event | 生成脚本的C++调用函数,实现C++调用脚本的功能 |
latent | 延迟函数(只能在state的label块中调用) |
client | client远程函数,调用后立即返回,无返回值 |
server | server远程函数,调用后立即返回,无返回值 |
参数
关键字 | 说明 |
coerce | 自动进行类型转换 |
out | 使用引用传入值类型变量,输出型参数 |
const | 常量参数,函数中不允许改变参数 |
optional | 可选参数,必须放到函数参数列表的最右边 |