微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将字符串文字映射到 C++

如何解决如何将字符串文字映射到 C++

我正在编写一个小型 2D 游戏,目前正在为其添加脚本功能(使用 Lua 或 Python),我偶然发现了这个问题(我认为这将引导我为我的游戏):

我使用实体组件系统模式,实体的定义由脚本(Lua 表或 Python 字典)提供,因此每当我想构建实体时,我都会运行脚本:

player = {
     transformComponent = { 
            position = {1.0,2.0,0.0},scale = {1.0,1.0}
     },spriteComponent = {
            fileName = 'imageFile.png',numRows = 4,numCols = 6
     }
}

等等。 在 EntityFactory 中,我有一个 EntityFactoryFunctions 的映射,以实体的名称(例如“玩家”)为键,当我需要构造这样的命名实体时,我会调用它们。

现在,每个工厂函数将读取实体的表(dict)并获取它需要添加到实体的所有组件的名称

Entity *CreateEntity(const std::string entityType) // table / dictionary name in script
{
    Entity *newEntity = Scene::GetInstance().AddEntity();
        
    return mEntityFactories[entityType](newEntity);
}

typedef Entity *(*EntityFactoryFunction)(Entity*);
std::map<std::string,EntityFactoryFunction> mEntityFactories;

问题是,我的 ECS 使用类型为 enity.AddComponent():

Entity *PlayerFactory(Entity *entity)
{
    // read components from Lua table / Python dictionary
    // get strings of components' names and store them into vector
    Vector<std::string> componentNames;

    // create components and add to entity
    for (const auto &componentName : componentNames)
    {
        Component *component = mComponentFactories[componentName](/* pass a reference to component table / dictionary */);
        entity->AddComponent<......>(component);  // I must kNow the component type
    }

    return entity;
}

如何获取要传递给函数模板的组件名称?我需要某种反射系统吗?

解决方法

我可以想到一些解决您问题的方法。

  1. 不同类型的组件不是不同的 C++ 类型。

在这种情况下,您的组件只是一组属性。代码会查看您拥有哪些包,并且行为会有所不同。

  1. 您在 C++ 中有一组固定的组件类型。

在这里,脚本命名了各种组件。这些是 C++ 类型。组件名称和类型之间的映射存储在 C++ 中。关联可能就像一两个硬编码的 switch 语句一样简单。

  1. 您拥有 C++ 中组件类型的动态 st。

为了添加更多的组件类型,您可以加载另一个动态库,它注册新的组件类型以及组件名称和类型之间的关联。

  1. 更疯狂的事情。

例如,您发布了动态构建组件类型并动态加载它们的 C++ 编译器。或者,您编写自己的语言,而您的 C++ 代码实际上只是一个解释器。


我会排除 #4。

现在,在第 1 种情况下,您无事可做。

在第 2/3 种情况下,您仍然需要将该字符串映射到类型。

最简单的基于 #2 的方法是一堆硬编码的 switch 语句,它们采用您的类型字符串并编写处理具体类型的自定义代码。这很快,但不能很好地扩展。这是解决方案(a)。

另一个步骤是抽象 switch 语句并让它在多个地方使用。调用此解决方案 (b)。

另一种选择是将整个类型视为一个对象本身;您编写一个描述类的元类,并构建从字符串到元类的映射。元类本身对于您的所有类都是相同的类型。调用此解决方案 (c)。

我认为 (a) 很简单,但很无聊。你真的做了一个

if (componentName=="bob") {
  /* code assuming the type is Bob */
} else if (componentName=="blue") {
  ...

(b) 的一个例子:

template<class T>struct tag_t{using type=T;};
template<class Tag>using type_t = typename T::type;
template<class T>constexpr tag_t<T> tag={};

template<class...Ts>
using tags_t = std::variant<tag_t<Ts>...>;

namespace Components{
  using ComponentTag = tags_t<Transform,Sprite,Physics>;
  ComponentTag GetTagFromName(std::string_view str) {
    if(str=="transformComponent") return tag<Transform>;
    if(str=="spriteComponent") return tag<Sprite>;
    // ...
  }
}

现在我们得到:

// create components and add to entity
for (const auto &componentName : componentNames)
{
    Component *component = mComponentFactories[componentName](/* pass a reference to component table / dictionary */);
    auto tag = Components::GetTagFromName(componentName);
    std::visit([&](auto tag) {
      using Type = type_t<decltype(tag)>;
      entity->AddComponent<Type>(component);  // I must know the component type
    },tag);
}

在最终版本 (c) 中,我们可以:

for (const auto &componentName : componentNames)
{
    IMetaComponent* meta = mComponentMetaFactory[componentName];
    Component *component = meta->Create(/* pass a reference to component table / dictionary */);
    meta->Add(entity,component);
}

此处,IMetaComponent 为需要在需要知道类型的组件上执行的每个操作获取虚拟方法。

MetaComponent 实现本身可以使用模板编写 90% 以上的代码,但它有一个不是模板的基础 IMetaComponent

(c) 具有许多优点,例如扩展能力以及对 MetaComponent 本身进行单元测试的能力。

(b) 的优点是一旦设置,您只需编写代码来完成您需要完成的事情。它确实需要 才能获得良好的变体和 lambda 语法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。