算术运算符一览,按优先级排序:
| 表达式 | 说明 |
|---|---|
x ** y | 幂(乘方), 表示 x 的 y 次方 |
+x -x | 取同取负(相反数) |
x * y x / y x % y | 乘法除法取模(求余)/ 格式化字符串这三者相同优先级 |
x + y x - y | 加法 / 连接字符串减法这两者相同优先级 |
x = y x += y x -= y x *= y x /= y x **= y x %= y | 赋值运算,优先级最低x += y 等效于 x = x + y,其他同理 |
如果进行整数除法,则结果将会自动“向下取整”截断小数
print(5 / 2) # 输出2,而不是2.5要解决这一问题,只需要将其中一个整数转化为浮点数即可,比如转换类型,乘以
1.0,如果是常量则在其后方加上.05.0 / 2float(5) / 25 * 1.0 / 2
取模运算符
%只能用在整数上,如果要对小数进行求余,可以使用数学函数fmod()对于负值,取模运算符和数学函数
fmod()的结果并非数学意义上的余数,因为会使用截断小数位,而非向负无穷大舍入,此时的余数会带有符号(如-5 % 2 = -1,而不是预期的1),如果想要得到数学意义上的余数(非负,且小于除数的绝对值)应改用posmod()和fposmod()可以使用括号
()来改变表达式的运算优先级
1 + 2 * 3 = 7
(1 + 2) * 3 = 9
常用数学函数
点击对应函数将自动跳转到官方文档
clamp(x, min, max)/clampi(x, min, max)/clampf(x, min, max):将值限制在指定范围内clamp(15, 0, 10)➜10lerp(from, to, weight)/lerpf(from, to, weight):线性插值(从from到to的过渡),weight应该在0.0-1.0之间(包含)lerp(0, 100, 0.3)➜30snapped(x, step)/snappedi(x, step)/snappedf(x, step):将x对齐到最近的 step 倍数snapped(2.3, 0.5)➜2.5fmod(x, y):浮点数模运算(和%类似,但能处理小数)fmod(5.75, 2.0)➜1.75posmod(x, y):带正号的取模,保证结果非负posmod(-3, 4)➜1fposmod(x, y):浮点正号模fposmod(-3.5, 4.0)➜0.5atan2(y, x):返回点 (x, y) 与 x轴的夹角,常用于旋转atan2(1, 0)➜PI / 2min(...)/mini(a, b)/minf(a, b)/max(...)/maxi(a, b)/maxf(a, b):返回较小/较大的值wrap(value, min, max)/wrapf(value, min, max)/wrapi(value, min, max):让值在[min, max]范围内循环wrapf(11.5, 0.0, 10.0)➜1.5
二进制运算符
| 表达式 | 含义 | 示例 | 效果说明 |
|---|---|---|---|
~x | 按位取反(NOT) | ~0000 0001 ➜ 1111 1110 | 把每一位都反过来(1变0,0变1) |
x << y | 左移 y 位 | 1 << 3 ➜ 8(即 0001 ➜ 1000) | 相当于乘以 2 的 y 次方 |
x >> y | 右移 y 位 | 8 >> 2 ➜ 2(即 1000 ➜ 0010) | 相当于除以 2 的 y 次方(向下取整) |
x & y | 按位与(AND) | 1100 & 1010 ➜ 1000 | 两位都为1才为1 |
x | y | 按位或(OR) | 0100 | 0011 ➜ 0111 | 有任意一位为1就为1 |
x ^ y | 按位异或(XOR) | 0110 ^ 0011 ➜ 0101 | 相同为0,不同为1 |
x &= y x |= y x ^= y x <<= y x >>= y | 二进制赋值运算符 | 同上 | x &= y等效于 x = x & y其余同理 |
标志位(flag)
在GDScript中,有很多flag标志位
比如表示属性使用情况元数据的标志位PropertyUsageFlags
常见的PropertyUsageFlags标志位
| 标志名 | 值 | 意义 |
|---|---|---|
PROPERTY_USAGE_SCRIPT_VARIABLE | 4096/ 0000 0001 0000 0000 0000 | 表示这个属性是脚本中的变量 |
PROPERTY_USAGE_NIL_IS_VARIANT | 131072/0010 0000 0000 0000 0000 | 属性类型是 Variant(动态类型) |
PROPERTY_USAGE_NO_EDITOR | 2 / 0000 0000 0000 0000 0010 | 不显示在编辑器中(即使是导出变量) |
PROPERTY_USAGE_CLASS_IS_ENUM | 65536 / 0001 0000 0000 0000 0000 | 属性是枚举类型变量 |
PROPERTY_USAGE_NONE | 0 / 0000 0000 0000 0000 0000 | 属性不被存储,也不会显示在编辑器中(非导出属性的默认值) |
PROPERTY_USAGE_STORAGE | 2 / 0000 0000 0000 0000 0010 | 将属性序列化并保存到场景文件中(用于导出属性的默认值) |
PROPERTY_USAGE_EDITOR | 4/ 0000 0000 0000 0000 0100 | 属性会显示在检查器中(用于导出属性的默认值) |
组合标志:按位或|
按位或 | 用于 组合多个标志
print(PROPERTY_USAGE_SCRIPT_VARIABLE) # 输出 4096
print(PROPERTY_USAGE_NIL_IS_VARIANT) # 输出 131072
var usage = PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_NIL_IS_VARIANT
print(usage) # 输出 135168这是说,“这个属性既是用户脚本变量也是类型为Variant的动态类型变量”,所以两个值加起来就是 4096 + 131072 = 135168,用按位或表示就是 :
0010 0000 0000 0000 0000(131072)
| 0000 0001 0000 0000 0000(4096)
= 0010 0001 0000 0000 0000 (135168)结果就是同时拥有两个标志
print(PROPERTY_USAGE_SCRIPT_VARIABLE) # 输出 4096
print(PROPERTY_USAGE_CLASS_IS_ENUM) # 输出 65536
var usage = PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_NIL_IS_VARIANT
print(usage) # 输出 69632这是说,“这个属性既是用户脚本变量也是类型为枚举类型的变量”,用按位表示就是
0000 0001 0000 0000 0000
| 0001 0000 0000 0000 0000
= 0001 0001 0000 0000 0000 (4096 | 65536 = 69632)检查标志:按位与 &
你可以使用 按位与(&)判断某个标志是否存在
if usage & PROPERTY_USAGE_SCRIPT_VARIABLE:
print("这个变量是用户脚本变量")
if usage & PROPERTY_USAGE_CLASS_IS_ENUM:
print("这个属性被当做枚举了!")
if usage & PROPERTY_USAGE_STORAGE:
print("这个属性会被存储到场景或资源文件里")
if usage & PROPERTY_USAGE_NO_EDITOR:
print("这个属性不会显示在编辑器中(即使是导出变量)")如果结果非零,就说明这个标志存在!
如果你想检查多个标志是否全部存在,可以这样:
if (usage & (PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_NIL_IS_VARIANT)) == (PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_NIL_IS_VARIANT):
print("两个标志都有设定喔~!")比如说 :
usage = 0000 0001 0000 0000 0000(PROPERTY_USAGE_SCRIPT_VARIABLE)我们想要检查PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_NIL_IS_VARIANT:
0000 0001 0000 0000 0000 (4096) PROPERTY_USAGE_SCRIPT_VARIABLE
| 0010 0000 0000 0000 0000 (131072) PROPERTY_USAGE_NIL_IS_VARIANT
= 0010 0001 0000 0000 0000 (135168)然后来看
usage & 135168 =
0000 0001 0000 0000 0000 (4096) PROPERTY_USAGE_SCRIPT_VARIABLE
& 0010 0001 0000 0000 0000 (135168)
= 0000 0001 0000 0000 0000 (4096)计算结果如上,转换为十进制就是4096,这表明它存在PROPERTY_USAGE_SCRIPT_VARIABLE标志位,但不存在PROPERTY_USAGE_NIL_IS_VARIANT
再比如:
usage = PROPERTY_USAGE_SCRIPT_VARIABLE # 4096 -> 0000 0001 0000 0000 0000我们要检查PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_CLASS_IS_ENUM:
0000 0000 0000 0000 0010 (2) PROPERTY_USAGE_NO_EDITOR
| 0001 0000 0000 0000 0000 (65536) PROPERTY_USAGE_CLASS_IS_ENUM
= 0001 0000 0000 0000 0010 (65538)然后来看:
usage & 65538 =
0000 0001 0000 0000 0000 (4096) PROPERTY_USAGE_SCRIPT_VARIABLE
& 0001 0000 0000 0000 0010 (65538)
= 0000 0000 0000 0000 0000 (0)结果为0,说明usage 没有包含任何一个我们要检查的两个标志位
清除标志:按位取反 ~ + 按位与 &
比如我们不想要动态类型这个标志了:
usage = usage & ~PROPERTY_USAGE_NIL_IS_VARIANT
usage &= ~PROPERTY_USAGE_NIL_IS_VARIANT这就会把 131072 从 usage 中移除:
~ 0010 0000 0000 0000 0000 (131072) PROPERTY_USAGE_NIL_IS_VARIANT
= 1101 1111 1111 1111 1111 (917503)
0010 0000 0000 0000 0000 (131072) PROPERTY_USAGE_NIL_IS_VARIANT
& 1101 1111 1111 1111 1111 (917503)
= 0000 0000 0000 0000 0000 (0)清除一个不存在的标志什么也不会发生:
usage = PROPERTY_USAGE_SCRIPT_VARIABLE # 0000 0001 0000 0000 0000(4096)我们试着清除PROPERTY_USAGE_NIL_IS_VARIANT
~ 0010 0000 0000 0000 0000 (131072) PROPERTY_USAGE_NIL_IS_VARIANT
= 1101 1111 1111 1111 1111 (917503)
0000 0001 0000 0000 0000(4096) PROPERTY_USAGE_SCRIPT_VARIABLE
& 1101 1111 1111 1111 1111 (917503)
= 0000 0001 0000 0000 0000 (4096) PROPERTY_USAGE_SCRIPT_VARIABLE翻转标志:按位异或 ^
如果我们想“切换”枚举标志的开启状态:
usage = usage ^ PROPERTY_USAGE_CLASS_IS_ENUM
usage ^= PROPERTY_USAGE_CLASS_IS_ENUM如果原本有这个标志,它就会被移除;
如果原本没有,它就会被加上!
以上面的代码为例,比如说usage现在是PROPERTY_USAGE_SCRIPT_VARIABLE | PROPERTY_USAGE_NIL_IS_VARIANT
0000 0001 0000 0000 0000 (4096) PROPERTY_USAGE_SCRIPT_VARIABLE
| 0010 0000 0000 0000 0000 (131072) PROPERTY_USAGE_NIL_IS_VARIANT
= 0010 0001 0000 0000 0000 (135168)
0010 0001 0000 0000 0000 (135168)
^ 0001 0000 0000 0000 0000 (65536)PROPERTY_USAGE_CLASS_IS_ENUM
= 0011 0001 0000 0000 0000 (200704)然后用按位与检查一下
0011 0001 0000 0000 0000 (200704)
& 0001 0000 0000 0000 0000 (65536)
= 0001 0000 0000 0000 0000 (65536)不为 0,表示具有该标志
再检查一下是否同时具有三个标志
0000 0001 0000 0000 0000 (4096) PROPERTY_USAGE_SCRIPT_VARIABLE
| 0010 0000 0000 0000 0000 (131072) PROPERTY_USAGE_NIL_IS_VARIANT
| 0001 0000 0000 0000 0000 (65536)PROPERTY_USAGE_CLASS_IS_ENUM
= 0011 0001 0000 0000 0000 (200704)
0011 0001 0000 0000 0000 (200704)
& 0011 0001 0000 0000 0000 (200704)
= 0011 0001 0000 0000 0000 (200704)满足条件,(usage & (a | b | c)) == (a | b | c)因此同时具有三个标志。
我们再试着用一次异或,看看是不是真的能够移除
0011 0001 0000 0000 0000 (200704)
^ 0001 0000 0000 0000 0000 (65536)PROPERTY_USAGE_CLASS_IS_ENUM
= 0010 0001 0000 0000 0000(135168)135168也就是PROPERTY_USAGE_SCRIPT_VARIABLE|PROPERTY_USAGE_NIL_IS_VARIANT
所以,异或可以对标志进行开关
小练习
- 下面的代码会输出什么?为什么?
print(100 / 2)
print(100 / 2.0)
print(float(100) / float(2))- 下面代码的输出结果是?
print(-7 % 3)
print(posmod(-7, 3))
print(fmod(-7.5, 3.0))- 最终的计算结果是多少?
var result = 2
result *= 3 + 1- 下面的代码会运行成功吗?如果不会,哪里错了?
var a = 5
var b = a % 2.0
print(b)实践
你正在制作一个房间温控系统。房间的初始温度是 15°C,每秒加热 2.5°C,最多升到 25°C 为止。
使用下面的模板,写一段代码,模拟每秒之后的温度变化(共模拟10秒,即
print10次),并在每秒输出当前温度。(提示:使用加法运算符和
min()函数,防止超过最大温度)gdscriptvar last_time: float = 0 func _process(delta: float) -> void: if Time.get_ticks_msec() / 1000.0 - last_time > 1.0: # 替换这里,这里的代码每一秒都会执行 last_time = Time.get_ticks_msec() / 1000.0