### 4. 枚举 enum
> 配套示例:`examples/04_enum.gd`
枚举用来定义"一组命名常量",比写一堆 `const` 整齐得多。
```gdscript
enum State { IDLE, RUN, JUMP, ATTACK }
# 这里的状态 翻译成中文,对应为4个状态,空闲,跑,跳,攻击
var current := State.IDLE
func update_state(s):
current = s
match current:
State.IDLE: print("在发呆")
State.RUN: print("在跑")
State.JUMP: print("在跳")
State.ATTACK: print("在攻击")
```
也可以显式指定值:
```gdscript
enum Dir { LEFT = -1, RIGHT = 1 }
#枚举类型通常是自动按0,1...取值,但也可以指定数值
```
**底层原理**:枚举值实际就是 int。`State.IDLE` 在内部是 `0`、`State.RUN` 是 `1`,依此类推。
## 第 4 节:枚举 enum
##
## 演示用 enum 表示"一组命名常量"。
## 配合 match 写状态分支非常顺手。
extends Node
#int float String 这里enum算是一个新的类型,但本质还是int类型
enum State { IDLE, RUN, JUMP, ATTACK }
enum Direction { LEFT = -1, RIGHT = 1 }
var current_state := State.IDLE
var facing := Direction.RIGHT
func _ready() -> void:
print("当前状态:", current_state)
print("State.IDLE 实际是数字:", State.IDLE)
print("State.RUN 实际是数字:", State.RUN)
current_state = State.RUN
describe_state()
current_state = State.ATTACK
describe_state()
print("朝向:", facing)
func describe_state() -> void:
match current_state:
State.IDLE:
print("在发呆")
State.RUN:
print("在跑")
State.JUMP:
print("在跳")
State.ATTACK:
print("在攻击")
_:
print("未知状态")
## 第 4 节附加:为什么要用枚举?
##
## 同一个"角色状态机"分别用 3 种写法实现:
## 方案 A:直接用 int "魔法数字"
## 方案 B:用一堆 const 常量
## 方案 C:用 enum 枚举(推荐)
##
## 把脚本挂到任意 Node 上,F6 运行场景看输出。
## 然后建议你自己改两行代码,体会哪种写法最不容易出错。
extends Node
# =========================================================
# 方案 A:用裸数字(最差写法)
# =========================================================
# 约定:0 = 待机,1 = 跑步,2 = 跳跃,3 = 攻击
# 缺点:
# 1. 看到 "2" 完全不知道是啥
# 2. 写错数字(比如 5)编辑器不会报错
# 3. 以后想在中间插一个新状态,所有数字都要重排
var state_a: int = 0
# =========================================================
# 方案 B:用一堆 const 常量
# =========================================================
# 比 A 好多了,至少看得懂名字。
# 但还是有坑:
# 1. 类型还是 int,state_b = 999 不会报错
# 2. 没"组"的概念,常量列表会越来越乱
# 3. 不能一次性遍历"所有状态"
const STATE_IDLE := 0
const STATE_RUN := 1
const STATE_JUMP := 2
const STATE_ATTACK := 3
var state_b: int = STATE_IDLE
# =========================================================
# 方案 C:用 enum(推荐)
# =========================================================
# 好处:
# 1. State 是独立类型,编辑器有自动补全
# 2. State.RUNN 这种拼写错误会立刻飘红
# 3. 天然有"分组"概念,相关常量挤在一起
# 4. 可以用 State.keys() / State.values() 遍历
enum State { IDLE, RUN, JUMP, ATTACK }
var state_c: State = State.IDLE
func _ready() -> void:
print("========== 方案 A:裸数字 ==========")
state_a = 1
describe_a(state_a)
# 下面这一行是 bug,但编辑器不会拦你
state_a = 999
describe_a(state_a)
print("\n========== 方案 B:const 常量 ==========")
state_b = STATE_RUN
describe_b(state_b)
# const 也只是 int,这种错误还是逃不掉
state_b = 999
describe_b(state_b)
print("\n========== 方案 C:enum(推荐)==========")
state_c = State.RUN
describe_c(state_c)
state_c = State.ATTACK
describe_c(state_c)
# enum 自带遍历能力,A 和 B 都做不到
print("\n所有状态名:", State.keys())
print("所有状态值:", State.values())
# 下面这一行如果取消注释,编辑器会直接飘红
# state_c = State.RUNN # ❌ 拼写错误,Godot 立刻报错
# state_c = 999 # ❌ 类型不匹配,Godot 立刻报错
# ---------------------------------------------------------
# 方案 A 的处理函数:必须靠"魔法数字"判断
# ---------------------------------------------------------
func describe_a(s: int) -> void:
# 看到这些数字,三个月后的你会一脸懵
match s:
0: print("[A] 待机")
1: print("[A] 跑步")
2: print("[A] 跳跃")
3: print("[A] 攻击")
_: print("[A] 未知状态(值=%d),但编辑器没发现错误" % s)
# ---------------------------------------------------------
# 方案 B 的处理函数:好一点,但还是 int
# ---------------------------------------------------------
func describe_b(s: int) -> void:
match s:
STATE_IDLE: print("[B] 待机")
STATE_RUN: print("[B] 跑步")
STATE_JUMP: print("[B] 跳跃")
STATE_ATTACK: print("[B] 攻击")
_: print("[B] 未知状态(值=%d)" % s)
# ---------------------------------------------------------
# 方案 C 的处理函数:参数类型就是 State,传错直接报错
# ---------------------------------------------------------
func describe_c(s: State) -> void:
match s:
State.IDLE: print("[C] 待机")
State.RUN: print("[C] 跑步")
State.JUMP: print("[C] 跳跃")
State.ATTACK: print("[C] 攻击")
_: print("[C] 未知状态")
