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

C++中如何为不同枚举类状态使用std::unordered_map实现精灵表

问题

我正在C++中实现一个精灵表(Sprite Sheet)类,这类文件包含多行多列的图像,每行对应游戏角色的一种动作——比如玩家角色的第一行是向左移动,第二行是向右移动;而鲨鱼角色的第一行可能是向上游动,不同角色的行对应的动作各不相同。

为了表示这些角色状态,我定义了不同的枚举类,比如玩家角色的状态:

enum class HeroState {
    Idle,
    MovingLeft,
    MovingRight,
};

其他角色还有各自的枚举类,比如SharkState、BombState等。

我希望实现一个可参数化的SpriteSheet类,能将精灵表的行与对应状态、列数关联起来,期望的用法大致如下:

heroSpriteSheet->setSpriteSheetData({{HeroState::Idle, 0, 19}, {HeroState::MovingLeft, 1, 14}});
sharkSpriteSheet->setSpriteSheetData({{SharkState::SwimLeft, 0, 12}, {SharkState::SwimRight, 1, 12}}); 

之后在代码中,只需传入状态就能查询到对应的行和列信息。

我原本考虑用std::unordered_map<State, RowAndColumnInfo>来存储映射,但不知道如何适配不同的状态枚举类(比如HeroState、VillainState)。应该用模板类,还是有更优的方案?目前还在规划阶段。


解决方案

1. 模板类方案(最直接推荐)

把SpriteSheet设计为模板类,以状态枚举类型作为模板参数,让每个角色的精灵表实例绑定到对应的枚举类,完美适配不同状态类型。

首先定义存储行列信息的结构体:

struct RowColumnInfo {
    int row;
    int columnCount;
};

然后实现模板化的SpriteSheet类:

template <typename StateEnum>
class SpriteSheet {
private:
    std::unordered_map<StateEnum, RowColumnInfo> stateMap;

public:
    // 批量设置状态与行列的映射
    void setSpriteSheetData(const std::initializer_list<std::pair<StateEnum, RowColumnInfo>>& data) {
        for (const auto& pair : data) {
            stateMap[pair.first] = pair.second;
        }
    }

    // 根据状态查询行列信息,返回std::optional避免空指针风险
    std::optional<RowColumnInfo> getInfo(StateEnum state) const {
        auto it = stateMap.find(state);
        if (it != stateMap.end()) {
            return it->second;
        }
        return std::nullopt;
    }
};

实际使用示例(修正原语法错误,枚举类成员用::访问):

// 玩家精灵表实例
auto heroSpriteSheet = std::make_unique<SpriteSheet<HeroState>>();
heroSpriteSheet->setSpriteSheetData({
    {HeroState::Idle, {0, 19}},
    {HeroState::MovingLeft, {1, 14}}
});

// 鲨鱼精灵表实例
auto sharkSpriteSheet = std::make_unique<SpriteSheet<SharkState>>();
sharkSpriteSheet->setSpriteSheetData({
    {SharkState::SwimLeft, {0, 12}},
    {SharkState::SwimRight, {1, 12}}
});

这个方案的优势:

  • 类型安全:编译期就能检查传入的状态是否匹配当前精灵表的枚举类,避免类型错误
  • 实现简单:无需额外基类或类型擦除逻辑,代码清晰直接
  • 性能优异:模板实例化后没有运行时类型开销,效率高

2. 类型擦除方案(适配统一存储场景)

如果需要把不同角色的SpriteSheet统一存储到同一个容器中(比如std::vector<std::unique_ptr<BaseSpriteSheet>>),模板类无法满足,这时可以用类型擦除:

先定义非模板基类:

class BaseSpriteSheet {
public:
    virtual ~BaseSpriteSheet() = default;
    // 可添加通用接口,比如绘制精灵等,但状态查询无法在基类统一定义
};

template <typename StateEnum>
class SpriteSheet : public BaseSpriteSheet {
    // 内部实现同模板类方案
};

不过这种方案下,状态查询的接口无法在基类统一(因为状态类型不同),所以如果不需要统一管理不同类型的精灵表,优先选模板类方案。

3. 枚举类哈希适配(注意点)

默认std::unordered_map不支持枚举类作为键,需要提供自定义哈希函数。可以通过特化std::hash实现:

template <typename Enum>
struct std::hash<Enum> {
    std::size_t operator()(Enum e) const {
        return static_cast<std::size_t>(e);
    }
};

或者在声明unordered_map时指定哈希函数:

std::unordered_map<HeroState, RowColumnInfo, std::hash<int>> stateMap;

前者更通用,推荐使用。

内容的提问来源于stack exchange,提问作者Sami Lakka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:37:26