You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自动枚举游戏继承类的状态常量并满足多技术要求?

游戏对象状态常量的自动枚举方案需求

我正在开发一款游戏,现有一个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_MAX
    
  • Projectile新增状态:
    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_MAX
  • CHARA_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;

方案合规性验证

  1. 自动递增:枚举成员默认从0开始递增,加上基础值后形成连续序列;GameObject状态值为0、1、2;子类首个状态值为GAME_OBJECT_STATE_MAX(2),后续依次+1。
  2. 子类起始值正确:通过BaseValue参数指定为GAME_OBJECT_STATE_MAX,确保首个状态值对齐。
  3. 互斥状态值共享:CHARA_STATE_WALK和PROJECTILE_STATE_MOVE都等于GAME_OBJECT_STATE_MAX,满足要求。
  4. std::any兼容:所有常量均为unsigned int类型(与GameObject状态成员变量类型一致),可直接存入std::any,且能通过std::any_cast<unsigned int>统一取出。
  5. switch可用:所有常量都是编译期constexpr值,可直接作为switch的case标签。

优势

  • 调整状态顺序/新增状态只需修改宏内的列表,无需手动维护数值,大幅减少冗余代码。
  • 所有常量都是编译期常量,性能无损耗,且兼顾类型安全。
  • 命名规则统一,可通过宏自动生成对齐原有习惯的常量名。

内容的提问来源于stack exchange,提问作者Henry Nissen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:02:34