总体特征
(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 注:TGTest类的工程编译需在使用该常量的工程之前或同处一个工程
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'
(7)枚举定义所在工程编译需在使用该枚举的工程之前或【同处一个工程且使用该枚举所在类名按字母升序比枚举定义所在类名靠后】
如果一个枚举在当前工程中广泛地使用,需要打破类名按字母升序的限制,可以将该枚举定义在一个单独从Object派生的类中(如:TGType)
若使用该枚举所在的类名按字母升序较靠前,可以使用dependson(TGType)来解决编译找到类型问题
静态数组
(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; };
(2)结构体中不能有函数方法,成员的初始化放在structdefaultproperties块中
(3)结构体只能在类中定义
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) } };
(4)支持结构体继承
// 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; };
(5)函数中对进行结构体赋值和拷贝
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"
(6)在类的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) )} }
(7)支持==与!=比较(两个结构体内容完全一致则相等,否则不相等)
(8)结构体定义所在工程编译需在使用该结构体的工程之前或【同处一个工程且使用该结构体所在类名按字母升序比结构体定义所在类名靠后】
如果一个结构体在当前工程中广泛地使用,需要打破类名按字母升序的限制,可以将该结构体定义在一个单独从Object派生的类中(如:TGType)
若使用该结构体所在的类名按字母升序较靠前,可以使用dependson(TGType)来解决编译找到类型问题
常用结构体 -- 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 | 可选参数,必须放到函数参数列表的最右边 |