基本语法
- 基本语法的说明
基本语法的说明
-
编译期(compile-time)和运行时(run-time)
除声明变量外的任何非编译期代码都不可暴露在函数外
包含了任何运行时操作的函数为非编译期函数
所有的对变量的声明/初始化/读取/运算/赋值操作都是运行时操作
编译期的代码 if 条件不成立也会执行 -
大小写敏感
epScript 是大小写敏感的编程语言,A 和 a 意思不同
-
值类型
epScript 基本的
值类型只有一种,就是 32 位无符号整数 -
逻辑规则
整数
0逻辑为假(false)
整数非 0 值逻辑为真(true) -
字面量数字(literal number)
字面量数字包含 10 进制数字、16 进制数字、二进制数字
// 以下代码中的 15、0xf、0b1111 均表示同一个数 15,只是写法不同,所以 a、b、c 是相等的
var a = 15;
var b = 0xf; // 16 进制数以 0x 打头
var c = 0b1111; // 二进制数以 0b 打头 -
字面量字符串(literal string)
字面量字符串用成对单引号
'或者成对双引号"包裹字面值即可DisplayText("这是字面量字符串");
DisplayText('这是字面量字符串');
DisplayText("这是字面量\
字符串"); // 当字符串太长时,可以用反斜杠 \ 换一行继续这个字面量字符串,这并不表示在字符串中插入换行符,以上两个写法完全等价字面量字符串支持使用反斜杠转义符
描述 说明 示例 示例结果 \\ 表示 \ 本身 DisplayText("你好\\星际");你好\星际 \八进制数 表示 ASCII 编码对应的那个字符 DisplayText("星际\101\102\103");星际ABC \x十六进制数 表示 ASCII 编码对应的那个字符 DisplayText("星际\x41\x42\x43");星际ABC \ 换行表示续行,不换行 DisplayText("你好\星际");你好星际 \n 换行符,等同于 \x0A DisplayText("你好\n星际");你好
星际\t 横向制表符,等同于 \x09 DisplayText("你好\t星际");你好 星际 \r 回车符,等同于 \x0D,在游戏中没啥效果 DisplayText("你好\r星际");你好星际 \" 在双引号字符串中表示双引号本身 DisplayText("你好\"星际\"");你好"星际" \' 在单引号字符串中表示单引号本身 DisplayText('你好\'星际\'');你好'星际' -
字面量字节串(literal bytes)
字面量字节串用
b"和"包裹或者b'和'包裹字面值即可,字节串不以 \0 结尾,字面量字节串同样也支持字面量字符串中的转义符println("{}", b"ASCII\nliteral");
println("{}", b'ASCII\nliteral'); -
命名规则
变量名、常量名及函数名只能包含
非 ASCII 部分的 UTF-8 字符(中日韩文都可以)、ASCII 部分的字母/数字以及下划线_,并且不能以 ASCII 数字打头// 合法的变量名
var a;
var a_b;
var a_1;
var _a1;
var 这也行;
// 不合法的变量名
var 3y = 1;
var abc# = 2;变量名、常量名及函数名不能用关键字命名,哪些是关键字我也不清楚,但这些是
static var const object this function
return for foreach while switch epdswitch
break continue if else once import as
true false变量名、常量名不能以 Python 3 的关键字命名
False None True
and as assert break class continue
def del elif else except finally
for from global if import in
is lambda nonlocal not or pass
raise return try while with yield另外一个特例是函数名用 py_ 开头的情况下,调用它要用 py_py_ 开头才行
function py_函数名() {
}
function onPluginStart() {
py_py_函数名();
} -
引入其它模块
可以使用
import关键词引入其它模块,as给引入的模块一个别名,以下代码说明用法模块1.eps:const 模块1的一个常量 = 0;
static var 模块1的一个变量 = 0;
function 模块1的一个函数() {
模块1的一个变量++;
return 模块1的一个变量;
}模块2.eps:import 模块1;
function 模块2的一个函数() {
return 模块1.模块1的一个函数();
}模块3.eps:import 模块1 as m1;
import 模块2 as m2;
function onPluginStart() {
println("{}", m1.模块1的一个常量);
m2.模块2的一个函数();
} -
符号
所有涉及语法的符号都是纯英文状态下的半角符号,可以在 ASCII 表中找到
-
代码块
使用成对的大括号
{}把单句或多句代码包围起来成为一个代码块{
// 代码
}若希望单句代码为一个代码块时,也可省略掉大括号,以下示例是合法的
function 无聊的示例函数()
for (var i = 1; i < 10; i++)
if (i < 5)
println("{} 小于 5", i);
else if (i == 5)
println("{} 等于 5", i);
else
println("{} 大于 5", i); -
语法层换行符
语法层的换行符是分号
;,而不是换行符var a;var b; -
索引运算符
[]取索引访问或修改数组中的元素const a = EUDArray(10);
a[0] = 11;
var b = a[0]; -
赋值符
赋值符号是单个等号
=var a; // 声明变量 a
var b = 3; // 声明变量 b 并赋值为 3
a = 2; // 将变量 a 赋值为 2 -
行注释符
两个斜杠
//开始行注释var a = 1; // 注释是代码中不会执行的部分,从 // 开始到当前行的结尾的内容不会被认为是代码 -
块注释符
在
/*到*/之间的内容叫块注释var /* 注释是代码中不会执行的部分,块注释可以加在代码中间 */ a = 1; -
条件运算符
- 大于
> - 小于
< - 大于等于
>= - 小于等于
<= - 等于
== - 逻辑与
&& - 逻辑或
||
值得一提的是,对变量使用条件比较运算时,返回的是
条件表达式常量,而非条件表达式的结果
if 的参数是条件表达式列表,如果将变量直接传入 if,则 if 会将其转换成一个运行时的取值是否不等于 0 的表达式if (a == 2) // 逻辑相等比较是两个等号
单句; // 逻辑比较的是非代码块是单句可以省略掉大括号
if (a == 2) {
// a 等于 2
单句1;
单句2;
}
if (a != 2) {
// a 不等于 2
}
if (a > 2) {
// a 大于 2
}
if (a < 2) {
// a 小于 2
}
if (a >= 2) {
// a 大于或等于 2
}
if (a <= 2) {
// a 小于或等于 2
}
if (a > 2 && a < 10) {
// a 大于 2 并且小于 10
}
if (a > 10 || a <= 5) {
// a 大于 10 或小于等于 5
}
if ( !(a < 2) ) {
// a 不小于 2
}
var a = 3;
var b = a > 0; // 错误!它返回的并非 true,而是 a > 0 这个条件表达式本身
var c = l2v(a > 0); // 这样就对了,运行时 c 就会等于 true 或 false,取决于运行时 a 的状态
const d = a > 0; // 这里的 d 就代表 a > 0 这个条件表达式本身,不是 a > 0 的结果
var e = l2v(d); // 在运行时使用 l2v 将 d 表达式的结果赋值给 e- 逻辑非
!
对变量
a使用!会返回一个变量,它的值是l2v((a != 0) == 0)的结果
对变量a使用两倍连续的!例如!!a或!(!a)或!!!(!a)都是直接等于a本身
对常量或条件表达式使用!则仍然会返回条件表达式var four = 4;
var b = !four; // b == 0
if (!b != !!four) println("b is not !!four");
if (four == !!four) println("four is !!four"); - 大于
-
数学运算符
+-*/a = a + 1;
a = a - 1;
a = a * 2;
a = a / 2; // 整数除法运算符,向下取整
a = a % 2; // 取余运算符
a = a ** 3; // 这个是幂运算符,返回 a 的 3 次幂,杨幂的幂~
a = a << 1; // 左位移 1 位
a = a >> 1; // 右位移 1 位 -
自增/自减/自乘/自除运算符
a += 10; // 它相当于 a = a + 10;
a -= 10; // 它相当于 a = a - 10;
a++; // 它相当于 a = a + 1;
a--; // 它相当于 a = a - 1;
a *= 10; // 它相当于 a = a * 10;
a /= 10; // 它相当于 a = a / 10;
-
-
条件判断语法
-
if
if 语法的形式为
if (条件表达式1) {
条件表达式1 满足后执行;
} -
if else
if 的否则分支 else 语法
if (条件表达式1) {
条件表达式1 满足时执行;
} else {
条件表达式1 不满足时执行;
} -
条件串联
可以将 if 串联到另外一个 else 上
if (条件表达式1) {
条件表达式1 满足时执行;
} else if (条件表达式2) {
条件表达式1 不满足并且 条件表达式2 满足时执行;
} else {
条件表达式1 和 条件表达式2 都不满足时执行;
} -
条件嵌套
可以将 if 写到另外一个 if 的代码块中
if (条件表达式1) {
条件表达式1 满足时执行;
} else if (条件表达式2) {
if (条件表达式3) {
条件表达式1 不满足并且 条件表达式2 和 条件表达式3 都满足时执行;
} else {
条件表达式1 和 条件表达式3 都不满足并且 条件表达式2 满足时执行;
}
} else if (条件表达式4) {
条件表达式1 和 条件表达式2 都不满足并且 条件表达式4 满足时执行;
} else {
条件表达式1 和 条件表达式2 都不满足时执行;
} -
单次执行
故名思义就是在运行时条件满足执行了一次之后就不再执行,通常用于加在 beforeTriggerExec 或者 afterTriggerExec 中每一帧重复判断,直到达成条件则执行一次
once (条件表达式) { // 在运行时重复运行 once 代码块的情况下,会在条件表达式满足时仅仅运行一次它里面的代码
// 代码
}
once { // 无条件仅执行一次它里面的代码
// 代码
}
// 以下代码执行后将只打印一次 0
for (var i = 0; i < 100; i++) {
once {
println("{}", i);
}
}
// 以下代码将只会在有机枪兵进入编号 1 到 10 的区域的任何一个区域的时候触发一次,不会在分别进入每个区域的时候触发,不会触发 10 次
for (var i = $L("Location 1"); i <= $L("Location 10"); i++) {
once ( Bring(P1, AtLeast, 1, "Terran Marine", i) ) {
println("机枪兵进入区域 {}", i);
}
}
-
-
流程控制
-
for 循环
for 循环可设定循环初始化动作表达式、循环执行条件表达式以及每循环附加动作表达式
for (初始化动作表达式; 循环执行条件表达式; 每循环附加动作表达式) {
循环中的代码;
}
for (var i = 0; i < 10; i++) {
println("{}", i);
}
// 简单描述一下,上面的代码声明了一个计数变量 i,初始值为 0,当 i < 10 的情况下就一直循环执行,并且每执行一次都将 i 自增 1,i 的作用域就是后面那个大括号里,大括号是每次循环需要执行的内容
var i1, i8;
for (i1, i8 = 0, 0 ; i1 < 10 && i8 < 80 ; i1++, i8 += 8) {
printAll("{} x 8 = {}", i1, i8);
} -
while 循环
while 循环可以设定一个循环条件,假如条件满足就一直循环执行,直到条件不满足则不再继续
var i = 0;
while (i < 10) {
println("{}", i);
i++;
} -
break 跳出循环
可以使用 break 跳出一个运行时循环或 switch
var i = 0;
while (true) { // 设定一个永远成立的条件,也就是一直返回 true
println("{}", i);
if (i >= 10) {
break;
}
}它和上面的 while 循环等效
-
foreach 迭代器循环
编译期迭代器
py_range 和 py_enumerator 是编译期迭代器
Python 层的容器(list、tuple 等)属于编译期迭代器
使用编译期迭代器的情况下,foreach 是编译期循环,会在编译期静态展开,不能使用 break 和 continueforeach (i : py_range(5)) {
simpleprint(i + 1);
}
// 完全等价于以下代码
simpleprint(0 + 1);
simpleprint(1 + 1);
simpleprint(2 + 1);
simpleprint(3 + 1);
simpleprint(4 + 1);foreach (i : py_range(3)) {
once (ElapsedTime(AtLeast, i)) {
println("第 {} 秒", i);
}
}
// 完全等价于以下代码
once (ElapsedTime(AtLeast, 0)) {
println("第 {} 秒", 0);
}
once (ElapsedTime(AtLeast, 1)) {
println("第 {} 秒", 1);
}
once (ElapsedTime(AtLeast, 2)) {
println("第 {} 秒", 2);
}const arrs = py_list();
foreach (x : list(50, 100, 150)) {
arrs.append(EUDArray(x));
}
// 完全等价于以下代码
const arrs = py_list();
arrs[0] = EUDArray(50);
arrs[1] = EUDArray(100);
arrs[2] = EUDArray(150);运行时迭代器
名字以 EUDLoop 开头的迭代器函数通常是返回的是运行时迭代器EUDLoopPlayer、EUDLoopRange、EUDLoopUnit、EUDLoopUnit2、EUDLoopCUnit、EUDLoopNewUnit、EUDLoopNewCUnit、EUDLoopPlayerUnit、EUDLoopPlayerCUnit
其次是 EUDQueue、EUDDeque 容器也属于运行时迭代器,UnitGroup.cploop 也返回一个运行时迭代器
EUDDeque 演示
// dq3 是一个尺寸为 3 的 EUDDeque
const dq3 = EUDDeque(3)();
const ret = EUDCreateVariables(6);
// 如果 dq3 为空则运行时不会有任何代码被执行
foreach(v : dq3) {
ret[0] += v;
}
// 从右侧添加 1 和 2 两个值到 dq3 这个 EUDDeque 中
dq3.append(1); // dq3 : (1)
dq3.append(2); // dq3 : (1, 2)
foreach(v : dq3) {
ret[1] += v; // 3 = 1 + 2
}
// 从右侧添加 3 和 4 两个值到 dq3 这个 EUDDeque 中
dq3.append(3); // dq3 : (1, 2, 3)
dq3.append(4); // dq3 : (2, 3, 4)
foreach(v : dq3) {
ret[2] += v; // 9 = 2 + 3 + 4
}
// 从右侧添加 5 这个值到 dq3 这个 EUDDeque 中
dq3.append(5); // dq3 : (3, 4, 5)
foreach(v : dq3) {
ret[3] += v; // 12 = 3 + 4 + 5
}
// 从左侧弹出一个值,这里 3 被弹出
const three = dq3.popleft(); // dq3 : (4, 5)
foreach(v : dq3) {
ret[4] += v; // 9 = 4 + 5
}
// 从右侧添加 6 和 7 两个值到 dq3 这个 EUDDeque 中
dq3.append(6); // dq3 : (4, 5, 6)
dq3.append(7); // dq3 : (5, 6, 7)
foreach(v : dq3) {
ret[5] += v; // 18 = 5 + 6 + 7
} -
switch 变量值多重选择分支
对单个值的多种状态判断的条件分支
普通 switch
switch (day) {
case 1:
DisplayText("苦逼上班日子开始了");
break;
case 4:
case 5:
DisplayText("马上周末了");
break;
case 0, 6:
DisplayText("好嗨啊!");
break;
default:
DisplayText("期待周末");
}
// 上述 switch 代码可以看作以下 if 条件分支代码
if (day == 1) {
DisplayText("苦逼上班日子开始了");
} else if (day == 4 || day == 5) {
DisplayText("马上周末了");
} else if (day == 0 || day == 6) {
DisplayText("好嗨啊!");
} else {
DisplayText("期待周末");
}带 bitmask 的 switch
var x = 0x101;
switch (x, 0xff) {
case 0:
// (x & 0xff) == 0
break;
default:
// (x & 0xff) != 0
break;
} -
epdswitch 内存值多重选择分支
对单个运行时内存位置的值的多种状态判断的条件分支
const unitId = epd + 0x64/4;
epdswitch (unitId, 255) { // you can put constant epd in epdswitch too
// switch branching by unit kind
case $U("Terran Marine"):
// Run when unitType is marine
break;
case $U("Terran Ghost"):
// Run when unitType is ghost
break;
}
-