实体组件系统中的拆分组件需要太多重构

2023-12-19

我有一个使用实体组件系统 (ECS) 的现有工作 C++ 游戏库。

我的库的用户想要创建一些组件,例如Cat :-

class Cat{ public:
    int hp;
    float flyPower;
};

他可以修改hp每一个的cat例如:-

for(SmartComponentPtr<Cat> cat : getAll<Cat>()){
    cat->hp-=5;  //#1
}

几天后,他想分手Cat to HP and Flyable :-

class HP{ public:
    int hp;
};
class Flyable{ public:
    float flyPower;
};

因此,每一个cat那个访问hp将编译错误(例如在#1在上面的代码中)。

要解决这个问题,用户可以将此代码重构为:-

for(MyTuple<HP,Flyable> catTuple : getAllTuple<HP,Flyable>()){
    SmartComponentPtr<HP> hpPtr=catTuple ; //<-- some magic casting
    hpPtr->hp-=5; 
}

它可以工作,但需要在用户代码中进行大量重构(调用的各个地方cat->hp).

ECS拆分组件时如何编辑框架/引擎解决可维护性问题?

我从未找到任何不受此问题困扰的方法,例如:-

  • https://github.com/skypjack/entt https://github.com/skypjack/entt
    (开源 - 搜索vel.dx = 0.; line)
  • https://medium.com/@savas/nomad-game-engine-part-2-ecs-9132829188e5 https://medium.com/@savas/nomad-game-engine-part-2-ecs-9132829188e5
    (博客 - 搜索int currentHealth; line)
  • https://www.randygaul.net/2013/05/20/component-based-engine-design/ https://www.randygaul.net/2013/05/20/component-based-engine-design/
    (博客 - 搜索comp->DoStuff( dt ); line)
  • (C#、Unity3D)http://www.sebaslab.com/learning-svelto-ecs-by-example-the-unity-survival-example/ http://www.sebaslab.com/learning-svelto-ecs-by-example-the-unity-survival-example/
    (参考的博客https://codereview.stackexchange.com/questions/48536/an-ecs-model-for-game-development https://codereview.stackexchange.com/questions/48536/an-ecs-model-for-game-development ;
    搜索playerGunComponent.timer += _time.deltaTime;)

赏金原因

Yuri 的答案是一个很酷的技术,但它仍然需要一些重构。

我当前糟糕的解决方案(pimpl)

如果我想创建Cat,我将创建 6 个组件:-

  • Hp_, Hp_OO
  • Flyable_, Flyable_OO
  • Cat_, Cat_OO

这是一个代码示例:-

class Hp_ : public BaseComponent{
    int hp=0;
};
class Hp_OO : public virtual BaseComponent{
    Hp_* hpPimpl;
    public: void damage(float dmg){ hpPimpl->hp-=dmg;}
};
class Flyable_  : public BaseComponent{ public:
    float flyPower;
};
class Flyable_OO: public virtual BaseComponent{
    Flyable_* flyPimpl;
    //other function
};
class Cat_: public virtual BaseComponent{};
class Cat_OO: public virtual Hp_OO , public virtual Flyable_OO{
   Cat_* catPimpl;
};

现在,拨打电话是有效的:-

SmartComponentPtr<Cat_OO> catPtr;
catPtr->damage(5);   //: so convenient - no need to refactor 

执行:-

  1. 如果用户添加Cat_OO对于一个实体,我的游戏引擎会自动将其父类添加到该实体,例如Hp_, Hp_OO ,Flyable_, Flyable_OO, and Cat_.
  2. 还必须分配正确的 pimpl 指针/句柄。

  3. ^ 两个操作都可以使用回调。

缺点是 :-

  • 需要创建很多组件。 (浪费内存)
  • 如果有一个共同的基类,例如BaseComponent,我需要虚拟继承。 (浪费大量内存)

优点 are :-

  • 如果用户查询getAll<Hp_OO>(), Hp_OO每一个的Cat_OO也将在返回的列表中。
  • 无需重构。

会员救援指点:

#include <tuple>

template <typename... Components>
struct MultiComponentPtr {
    explicit MultiComponentPtr(Components*... components)
        : components_{components...}
    {}

    template <typename Component, typename Type>
    Type& operator->*(Type Component::* member_ptr) const {
        return std::get<Component*>(components_)->*member_ptr;
    }

private:
    std::tuple<Components*...> components_;
};

struct Cat {
    int hp;
    float flyPower;
};

struct HP {
    int hp;    
};

struct Flyable {
    float flyPower;    
};

int main() {
    {
        Cat cat;
        MultiComponentPtr<Cat> ptr(&cat);
        ptr->*&Cat::hp += 1;
        ptr->*&Cat::flyPower += 1;
    }

    {
        HP hp;
        Flyable flyable;
        MultiComponentPtr<HP, Flyable> ptr(&hp, &flyable);
        ptr->*&HP::hp += 1;
        ptr->*&Flyable::flyPower += 1;
    }
}

从技术上讲,您仍然需要重构,但自动替换很简单&Cat::hp with &HP::hp, etc.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

实体组件系统中的拆分组件需要太多重构 的相关文章

  • WPF DataGrid 多选

    我读过几篇关于这个主题的文章 但很多都是来自 VS 或框架的早期版本 我想做的是从 dataGrid 中选择多行并将这些行返回到绑定的可观察集合中 我尝试创建一个属性 类型 并将其添加到可观察集合中 它适用于单个记录 但代码永远不会触发多个
  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • C++ 中的 include 和 using 命名空间

    用于使用cout 我需要指定两者 include
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template

随机推荐

  • 我可以从 R 中访问 Mac Numbers (.num) 文档吗?

    我知道我可以使用 RODBC 库从 Windows 中访问 excel xls 文档 但是 iWorks 附带的 Numbers 程序是否有类似的功能 如果没有 还有哪些其他解决方案可以轻松编辑电子表格 如查找表 并在 R 中访问它 我知道
  • 如何用Java发现文件的创建时间?

    有没有一种简单的方法可以用Java发现文件的创建时间 File 类只有一个方法来获取 最后修改 时间 根据我在 Google 上找到的一些资源 File 类不提供 getCreationTime 方法 因为并非所有文件系统都支持创建时间的概
  • wp_kses_post 函数中允许使用哪些 HTML?

    我需要清理 admin notices 的输出 它使用某些东西 例如 a id href title strong 使用是个好主意吗wp kses post功能 阅读文档我不确定允许哪些 HTML 标签https developer wor
  • 将 XML 字符串转换为对象

    我正在通过套接字接收 XML 字符串 并希望将它们转换为 C 对象 消息的形式如下
  • 让 Python 的 `assert` 抛出一个我选择的异常

    我可以做吗assert抛出一个我选择的异常AssertionError UPDATE 我将解释我的动机 到目前为止 我已经进行了断言式测试 这些测试引发了我自己的异常 例如 当您创建一个Node具有某些参数的对象 它会检查参数是否适合创建节
  • 沿着一系列 CGPoint 移动图像

    我将路径存储在 CGPoints 数组中 我想沿着它移动图像 这是我到目前为止的一般代码 void movePic id sender for int i 0 i lt self array count i CGPoint location
  • Android - 应用程序在棒棒糖之前的设备上崩溃

    我的应用程序在 Lollipop 设备上运行良好 但在 Lollipop 之前的版本上一直崩溃 我只是通过谷歌文档使用以下代码在我的应用程序中实现了横幅添加 Request for Ads AdRequest adRequest new A
  • 将带有模板参数的方法传递给宏

    我无法使用 Google 测试ASSERT THROW 宏与多个模板参数相结合 考虑到我想确保Matrix lt 5 1 gt throws ASSERT THROW Matrix lt 5 1 gt std runtime error 这
  • 如何将 Joomla 参数传递到 iframe(包装器)页面?

    我尝试使用 getUser 函数和位于 Joomla iframe 包装器 中的 php 脚本来获取我的用户信息 似乎将参数传递给 iframe 代码时出现问题 我捕获用户信息的唯一方法是将代码插入到基本文章 不是 iframe 中 var
  • 用于编译的 RAM 驱动器 - 有这样的东西吗?

    An answer https stackoverflow com questions 354160 what do you do while your codes compiling 354176 see below to one of
  • 匹配空格但不匹配换行符

    我有时想匹配空格而不是换行符 到目前为止我一直在求助于 t 有没有不那么尴尬的方法呢 Summary Use h匹配水平空白 自 v5 10 0 起使用 Perl https perldoc perl org perl5100delta V
  • 如何在Android平台上启用SSL调试?

    有没有类似设置的 D javax net debug ssl对于 Java 桌面应用程序 可以在命令行中使用 但是对于 Android 来说呢 我尝试通过以下方式在代码中设置它System setProperty javax net deb
  • C# 中如何测试线程是否持有某个对象的锁?

    有没有办法测试当前线程是否持有对象的监视器锁 IE 相当于 Java 中的 Thread holdsLock Thanks 我不相信有 你可以做一些糟糕的黑客事情 比如打电话Monitor Wait monitor 0 并抓住Synchro
  • MVC5 和 Ninject 的依赖注入失败

    我试图在控制器中注入几个类 但失败了 这就是我所做的 Added Ninject Web WebApi WebHost and WebActivatorExNuGet 包 在下面创建了以下类App Start NinjectWebCommo
  • 在 Python 中,在另一个类中定义一个类有什么好处吗?

    我这里所说的是嵌套类 本质上 我正在建模两个类 一个 DownloadManager 类和一个 DownloadThread 类 这里最明显的 OOP 概念是组合 然而 组合并不一定意味着嵌套 对吗 我的代码看起来像这样 class Dow
  • 如果只是端口不同,AJAX调用是否跨域?

    主页是 www myserver com 80 ajax 引用 www myserver com 8081 状态码为 0 文本为空 这是跨域拒绝吗 是的 这违反了同源政策 http en wikipedia org wiki Same or
  • 在 JavaScript 中在画布中生成随机图像

    大家好 我正在尝试使用画布制作一个 javascript 游戏 我想生成随机的敌方物体 到目前为止 我发现这是一个生成示例 JSFiddle 演示 http jsfiddle net m1erickson RCLtR 如何加载图像而不是球
  • 如何将 itertools“石斑鱼”对象转换为列表

    我正在尝试学习如何在 Python 中使用 itertools groupby 并且我想找到每组字符的大小 起初我尝试看看是否能找到单个组的长度 from itertools import groupby len list list gro
  • 如何在 ASP.net C# 中单击数据网格中的单元格

    我正在将数据表中的一列导入到我的网格中 现在我想导航到一个新页面 通过获取所选值来选择网格中的单元格 我已经尝试过在网格中包含绑定字段 例如
  • 实体组件系统中的拆分组件需要太多重构

    我有一个使用实体组件系统 ECS 的现有工作 C 游戏库 我的库的用户想要创建一些组件 例如Cat class Cat public int hp float flyPower 他可以修改hp每一个的cat例如 for SmartCompo