如何自动枚举游戏继承类的状态常量并满足多技术要求?
游戏对象状态常量的自动枚举方案需求
我正在开发一款游戏,现有一个GameObject类,包含unsigned int类型的成员变量表示对象状态,同时定义了状态常量:
GAME_OBJECT_STATE_IDLE GAME_OBJECT_STATE_SPAWN GAME_OBJECT_STATE_MAX
实现了各状态对应的成员函数,并用vector存储函数指针,每帧通过(this->*state_function[state])();执行状态逻辑。
还有两个继承自GameObject的子类:
Character新增状态:CHARA_STATE_WALK CHARA_STATE_JUMP CHARA_STATE_MAXProjectile新增状态:PROJECTILE_STATE_MOVE PROJECTILE_STATE_MAX
其中CHARA_STATE_WALK和PROJECTILE_STATE_MOVE的值等于GAME_OBJECT_STATE_MAX(该常量无对应行为)。三个类构造时会把state_function的大小调整为各自_MAX常量的值。
核心要求
- 每组内的常量值依次递增1(如
GAME_OBJECT_STATE_IDLE=0,GAME_OBJECT_STATE_SPAWN=1) Character和Projectile的首个状态常量值等于GAME_OBJECT_STATE_MAXCHARA_STATE_WALK和PROJECTILE_STATE_MOVE值相同- 常量可直接存入
std::any无需强制转换,且能统一通过any_cast取出;若用强类型枚举,子类状态需能隐式转换为父类状态的类型 - 常量可用于
switch语句
现有方案的问题
枚举+const int方案
可行但代码冗余,调整常量顺序繁琐(实际有70+个常量):
// GameObjectState.h #pragma once enum { _GAME_OBJECT_STATE_IDLE, _GAME_OBJECT_STATE_SPAWN, _GAME_OBJECT_STATE_MAX }; const int GAME_OBJECT_STATE_IDLE = _GAME_OBJECT_STATE_IDLE; const int GAME_OBJECT_STATE_SPAWN = _GAME_OBJECT_STATE_SPAWN; const int GAME_OBJECT_STATE_MAX = _GAME_OBJECT_STATE_MAX;
// CharaState.h #pragma once #include "GameObjectState.h" enum { _CHARA_STATE_WALK = GAME_OBJECT_STATE_MAX, _CHARA_STATE_JUMP, _CHARA_STATE_MAX }; const int CHARA_STATE_WALK = _CHARA_STATE_WALK; const int CHARA_STATE_JUMP = _CHARA_STATE_JUMP; const int CHARA_STATE_MAX = _CHARA_STATE_MAX;
constexpr方案
不够简洁,且常量无法被识别为编译期常量,不能用于switch:
// GameObjectState.h #pragma once inline int GAME_OBJECT_STATE_NUM = 0; constexpr int game_object_state_start() { GAME_OBJECT_STATE_NUM = 0; return GAME_OBJECT_STATE_NUM++; } constexpr int game_object_state() { return GAME_OBJECT_STATE_NUM++; } const int GAME_OBJECT_STATE_IDLE = game_object_state_start(); const int GAME_OBJECT_STATE_SPAWN = game_object_state(); const int GAME_OBJECT_STATE_MAX = game_object_state();
// CharaState.h #pragma once #include "GameObjectState.h" inline int CHARA_STATE_NUM = GAME_OBJECT_STATE_MAX; constexpr int chara_state_start() { CHARA_STATE_NUM = GAME_OBJECT_STATE_MAX; return CHARA_STATE_NUM++; } constexpr int chara_state() { return CHARA_STATE_NUM++; } const int CHARA_STATE_WALK = chara_state_start(); const int CHARA_STATE_JUMP = chara_state(); const int CHARA_STATE_MAX = chara_state();
请问是否有更简洁的自动枚举方案满足所有要求,还是只能沿用第一种方案?
推荐方案:宏辅助的强类型枚举(带底层类型)
这个方案既满足编译期常量要求,又能简化常量定义,同时覆盖所有需求:
1. 基础宏定义(公共头文件StateMacros.h)
#pragma once // 定义状态枚举的核心宏,包含基础值偏移 #define DEFINE_STATE_ENUM(EnumName, UnderlyingType, BaseValue, ...) \ enum class EnumName : UnderlyingType { \ __VA_ARGS__, \ MAX \ }; \ \ namespace StateValues { \ inline constexpr auto EnumName##_BASE = BaseValue; \ inline constexpr auto EnumName##_MAX = static_cast<UnderlyingType>(EnumName::MAX) + BaseValue; \ } \ \ // 为单个状态生成全局常量 #define EXPAND_STATE_VALUE(EnumName, Value) \ inline constexpr auto EnumName##_##Value = static_cast<UnderlyingType>(EnumName::Value) + StateValues::EnumName##_BASE; \ \ // 遍历状态列表生成常量 #define FOREACH_STATE(EnumName, FUNC, ...) \ FUNC(EnumName, __VA_ARGS__)
2. 定义GameObject状态(GameObjectState.h)
#pragma once #include "StateMacros.h" // 定义枚举:底层类型unsigned int,基础值0 DEFINE_STATE_ENUM(GameObjectState, unsigned int, 0, IDLE, SPAWN ) // 生成全局常量 FOREACH_STATE(GameObjectState, EXPAND_STATE_VALUE, IDLE, SPAWN, MAX ) // 对齐原有命名习惯 inline constexpr unsigned int GAME_OBJECT_STATE_IDLE = GameObjectState_IDLE; inline constexpr unsigned int GAME_OBJECT_STATE_SPAWN = GameObjectState_SPAWN; inline constexpr unsigned int GAME_OBJECT_STATE_MAX = GameObjectState_MAX;
3. 定义Character状态(CharaState.h)
#pragma once #include "GameObjectState.h" #include "StateMacros.h" // 基础值设为GAME_OBJECT_STATE_MAX,确保起始值对齐 DEFINE_STATE_ENUM(CharacterState, unsigned int, GAME_OBJECT_STATE_MAX, WALK, JUMP ) FOREACH_STATE(CharacterState, EXPAND_STATE_VALUE, WALK, JUMP, MAX ) // 对齐原有命名习惯 inline constexpr unsigned int CHARA_STATE_WALK = CharacterState_WALK; inline constexpr unsigned int CHARA_STATE_JUMP = CharacterState_JUMP; inline constexpr unsigned int CHARA_STATE_MAX = CharacterState_MAX;
4. 定义Projectile状态(ProjectileState.h)
#pragma once #include "GameObjectState.h" #include "StateMacros.h" // 基础值同样设为GAME_OBJECT_STATE_MAX DEFINE_STATE_ENUM(ProjectileState, unsigned int, GAME_OBJECT_STATE_MAX, MOVE ) FOREACH_STATE(ProjectileState, EXPAND_STATE_VALUE, MOVE, MAX ) // 对齐原有命名习惯 inline constexpr unsigned int PROJECTILE_STATE_MOVE = ProjectileState_MOVE; inline constexpr unsigned int PROJECTILE_STATE_MAX = ProjectileState_MAX;
方案合规性验证
- 自动递增:枚举成员默认从0开始递增,加上基础值后形成连续序列;
GameObject状态值为0、1、2;子类首个状态值为GAME_OBJECT_STATE_MAX(2),后续依次+1。 - 子类起始值正确:通过
BaseValue参数指定为GAME_OBJECT_STATE_MAX,确保首个状态值对齐。 - 互斥状态值共享:
CHARA_STATE_WALK和PROJECTILE_STATE_MOVE都等于GAME_OBJECT_STATE_MAX,满足要求。 - std::any兼容:所有常量均为
unsigned int类型(与GameObject状态成员变量类型一致),可直接存入std::any,且能通过std::any_cast<unsigned int>统一取出。 - switch可用:所有常量都是编译期
constexpr值,可直接作为switch的case标签。
优势
- 调整状态顺序/新增状态只需修改宏内的列表,无需手动维护数值,大幅减少冗余代码。
- 所有常量都是编译期常量,性能无损耗,且兼顾类型安全。
- 命名规则统一,可通过宏自动生成对齐原有习惯的常量名。
内容的提问来源于stack exchange,提问作者Henry Nissen
相关产品推荐
相关产品推荐

