Skip to content

常量和枚举

在学习match语句之前,我们需要先了解什么是常量以及什么是枚举

常量

常量是游戏运行时不可更改的值。与变量不同,常量在定义后,其值不允许再被修改。尝试为常量重新赋值会触发编译错误。

因此,建议使用常量来储存那些不会发生改变的值,比如游戏规则参数、魔法数字等。

常量的定义语法: const 常量名 = 常量表达式

gdscript
const A = 5
const B = Vector2(20, 20)
const C = 10 + 20 # 常量表达式
const D = Vector2(20, 30).x # 常量表达式: 20.
const E = [1, 2, 3, 4][0] # 常量表达式: 1.
const F = sin(20) # ‘sin()’可用于常量表达式。
const G = x + 20 # 无效赋值,这不是一个常量表达式(标识符 `x` 不是一个常量)
const H = A + 20 # 常量表达式: 25 (`A` 是一个常量).

你也可以显式指定类型,但通常类型可以自动推断:

const: 数据类型 常量名 = 常量表达式

gdscript
const A: int = 5
const B: Vector2 = Vector2()

若指定的类型与实际值不匹配,会触发错误。

常量也可以用于函数内部,声明一些局部魔法数字,帮助提升可读性。

常量名应使用有效标识符,常量一般使用全大写命名:MAX_HEALTH_POINT

常量表达式

常量表达式的值必须在编译时即可确定,因此它不能包含变量引用或任何运行时决定的值,也不能依赖对象实例或其成员

  • 常量字面量

    直接使用不可变的固定值:

    • 整数字面量:1100

    • 浮点数字面量:1.42.1e9

    • 字符串字面量:"Hello""World"

    • 唯一字符串字面量:&"Hello"&"World"

    • 节点路径字面量:^"Hello"^"World"

    • 布尔值字面量:truefalse

  • 值类型构造函数

    可用于创建如向量、颜色、矩形等值类型对象,但构造参数必须全为常量

    • Vector2(0, 0)

    • Color(0, 0, 0, 1)

    • Rect2(0, 0, 100, 100)

  • 集合字面量

    仅由常量组成的数组或字典

    • 数组:[1, 2, 3, 4, 5, 6]

    • 字典:{"key" : 1, "key_2" : 2, "key_3" : 3}

  • 常量间的运算表达式

    运算中所用的全部值都必须是常量:

    • Vector2(0, 10) * 10

    • 1 + 1

    • "Hello" + "World"

如果表达式中出现了变量或运行时值(如对象属性、函数参数等),就不能作为常量表达式,将导致编译错误。

内置常量

在GDScript中有很多内置常量,下面列举一部分:

  • GDScript的全局常量:

    • PI= 3.14159265358979

      • 圆周率,表示圆的周长是直径的多少倍。相当于 TAU/ 2,即 180 度旋转。
    • TAU = 6.28318530717959

      • 圆常量,单位圆的周长,单位为弧度。相当于 PI * 2,即 360 度旋转。
    • INF = inf

      • 正无穷,用于浮点数除以 0 的场景。负无穷为 -INF
    • NAN= nan

      • 非数(Not A Number)。如 0.0 / 0.0 会得到 nan。具有特殊性质,如:NAN != NANtrue,而 NAN == NANfalse

    注意:INF 和 NAN 是浮点数特有的概念,整数除以 0 会直接触发运行时错误。

  • Color类中还有很多有意思的颜色常量,这里列举部分:

    • Color.BLACK = Color(0, 0, 0, 1)

      • 黑色。在 GDScript 中,这是所有颜色的默认值。
    • Color.ALICE_BLUE = Color(0.941176, 0.972549, 1, 1)

      • 爱丽丝蓝。
    • Color.VIOLET= Color(0.933333, 0.509804, 0.933333, 1)

      • 紫罗兰色
  • Vector2中提供了一些常用的向量常量

    • Vector2.ZERO= Vector2(0, 0)

      • 零向量,所有分量都设置为 0 的向量。
    • Vector2.ONE = Vector2(1, 1)

      • 单位向量,所有分量都设置为 1 的向量。
    • Vector2.INF = Vector2(inf, inf)

      • 无限向量,所有分量都设置为 @GDScript.INF 的向量。
    • Vector2.LEFT = Vector2(-1, 0)

      • 左单位向量。代表左的方向。
    • Vector2.RIGHT = Vector2(1, 0)

      • 右单位向量。代表右的方向。
    • Vector2.UP = Vector2(0, -1)

      • 上单位向量,Y轴向上(在Godot中是负方向)。在 2D 中 Y 是向下的,所以这个向量指向 -Y。
    • Vector2.DOWN = Vector2(0, 1)

      • 下单位向量, Y轴向下(在Godot中是正方向)。在 2D 中 Y 是向下的,所以这个向量指向 +Y

枚举

枚举(enum)用于定义一组相关联的常量值,通常可用来表示状态、种类、键值等。枚举的每个成员都会自动赋予一个整数值,默认从 0 开始,依次递增。

gdscript
enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

# 等效于
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

具名枚举

如果为枚举指定名称,它会生成一个只读字典,可通过键名访问值,也可使用字典的常方法进行遍历查询。

常方法也就是无副作用,不会修改原始对象实例的方法,一般会在文档中标注const

gdscript
enum WeekDays
{
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

# 等效于一个常量字典
const WeekDays = {
    MONDAY = 0, 
    TUESDAY = 1, 
    WEDNESDAY = 2, 
    THURSDAY = 3, 
    FRIDAY = 4,
    SATURDAY = 5,
    SUNDAY = 6
}

func _ready():
    # 使用 Name.KEY 来访问常量值, 打印 '5'
    print(WeekDays.SATURDAY)
    # 使用字典的方法:
    # 打印'["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]'
    print(WeekDays.keys())
    # 打印'{ "MONDAY": 0, "TUESDAY": 1, "WEDNESDAY": 2, "THURSDAY": 3, "FRIDAY": 4, "SATURDAY": 5, "SUNDAY": 6 }'
    print(WeekDays)
    # 打印 '[0, 1, 2, 3, 4, 5, 6]'
    print(WeekDays.values())

指定值

可以为枚举成员显式指定值。后续未指定的成员会在其基础上递增:

gdscript
enum State {
    STATE_IDLE,         # 默认是 0
    STATE_JUMP = 5,     # 显式设为 5
    STATE_SHOOT         # 自动为 6(前一项 +1)
}

print(State.values())   # 输出: [0, 5, 6]

允许重复值

多个成员可拥有相同的值(虽然不推荐,除非你故意需要这样做):

gdscript
enum Behavior
{
    ATTACK = 1
    JUMP = 1
    RUN = 1
}

print(Behavior.values()) # 输出: [1, 1, 1]

虽然值可以重复,但键名必须唯一,不能重复定义相同的枚举

枚举类型变量

你可以声明一个变量,然后指定其类型为你声明的枚举名,赋值时可以用Name.Key或者整数常量赋值

枚举类型变量也可以和整数类型互相转换

gdscript
enum WeekDays
{
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

var day: WeekDays = WeekDays.SUNDAY # 等同于 var day :WeekDays = 6
var day := WeekDays.FRIDAY # 等同于 var day :WeekDays = 4
var day: WeekDays = 0 # 等同于 WeekDays.MONDAY

给枚举值变量赋值时,推荐使用Name.Key的形式赋值,更具可读性

进行if判断时,也只需要将该枚举变量与对应的枚举值进行比较即可,也可将枚举类型变量和整数常量比较,但可读性差,不推荐

gdscript
enum WeekDays
{
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

var day: WeekDays = WeekDays.SUNDAY # 等同于 var day :WeekDays = 6

if day == WeekDays.MONDAY:
    print("周一了,要上班了")
elif day == WeekDays.FRIDAY:
    print("周五了,解放了")
elif day == WeekDays.SATURDAY or day == WeekDays.SUNDAY:
    print("周末了,好好玩")
else:
    print("在上班了TAT")

枚举类型变量的比较,实际上就是整数常量间的比较

  • 枚举名一般使用全大写,单词间用下划线分隔。

  • 使用具名枚举可以提高可读性,也便于调试和遍历。

  • 枚举是常量,因此不可被修改。

练习

  1. 下面哪些常量定义是合法的?哪些会报错?请说明原因

    gdscript
    const A = 5
    const B = Vector2(2, 3)
    const C = A + 10
    const D = x + 10
    const E = Color(1, 1, 1, alpha)
    const F = sin(PI)
  2. 以下哪一行会触发类型不匹配错误?

    gdscript
    const A: int = 10
    const B: float = 3.14
    const C: String = 123
    const D: Vector2 = Vector2(1, 1)
  3. 以下哪些是合法的枚举定义?如果合法,它们分别对应的值是?

    gdscript
    enum Example1 = { A, B, C }
    enum Example2 { A = 3, B, C }
    enum Example3 { A = 3, B = 3, C = 3 }
    enum Example4 { A = 1, A = 2 }
  4. 如下给定的枚举,输出的结果分别是什么?

    gdscript
    enum Colors {
        RED = 10,
        GREEN = 12,
        BLUE
    }
    
    func _ready():
        print(Colors.BLUE)
        print(Colors.keys())
        print(Colors.values())
  5. 反向查找

    已知如下枚举:

    gdscript
    enum Difficulty {
        EASY = 1,
        NORMAL = 3,
        HARD = 5
    }

    请写一段逻辑,查找值为 3 的枚举项名(即找到字符串 "NORMAL")并打印出来。 提示:可以用find_key方法来查找指定值的键

  6. 请定义一个名为 PlayerState 的枚举,表示以下角色状态:

    • 待机(IDLE)

    • 移动(MOVE)

    • 攻击(ATTACK)

    • 死亡(DEAD)

    然后编写一段逻辑,在 _ready() 中模拟以下行为:

    如果当前状态是 ATTACK,打印 "Player is attacking!"
    否则如果是 DEAD,打印 "Player has fallen..."
    否则打印 "Player is doing something else."

    提示:你可以使用 PlayerState.ATTACK 来比较当前状态变量