标签: entity-component-system

基于组件的游戏引擎设计

我一直在研究游戏引擎设计(特别关注 2d 游戏引擎,但也适用于 3d 游戏),并且对如何进行的一些信息感兴趣。我听说现在许多引擎正在转向基于组件的设计,而不是传统的深层对象层次结构。

您是否知道有关此类设计通常如何实施的信息的任何良好链接?我已经看到了你的层次结构的进化,但我真的找不到更多的详细信息(他们中的大多数似乎只是说“使用组件而不是层次结构”,但我发现改变我的想法需要一些努力在两个模型之间)。

任何关于这方面的好的链接或信息都将不胜感激,甚至是书籍,尽管这里的链接和详细答案将是首选。

architecture game-engine entity-system entity-component-system

322
推荐指数
4
解决办法
16万
查看次数

将实体与实体组件系统中的系统匹配的有效方式

我正在研究一个面向数据的实体组件系统,其中组件类型和系统签名在编译时是已知的.


一个实体是一个组件的集合体.可以在运行时从实体添加/删除组件.

组件是一个小的逻辑少类.

一个签名是组件类型的编译时间列表.如果实体包含签名所需的所有组件类型,则称该实体与签名匹配.


一个简短的代码示例将向您展示用户语法的外观以及预期用途:

// User-defined component types.
struct Comp0 : ecs::Component { /*...*/ };
struct Comp1 : ecs::Component { /*...*/ };
struct Comp2 : ecs::Component { /*...*/ };
struct Comp3 : ecs::Component { /*...*/ };

// User-defined system signatures.
using Sig0 = ecs::Requires<Comp0>;
using Sig1 = ecs::Requires<Comp1, Comp3>;
using Sig2 = ecs::Requires<Comp1, Comp2, Comp3>;

// Store all components in a compile-time type list.
using MyComps = ecs::ComponentList
<
    Comp0, Comp1, …
Run Code Online (Sandbox Code Playgroud)

c++ optimization performance c++14 entity-component-system

22
推荐指数
3
解决办法
4686
查看次数

为系统循环创建高性能组件池

我正在设置一个非常小的Entity-Component-System示例,并且组件池存在一些问题.

目前我的实体只是ID(GUID),这很好.

每个系统都必须实现该ISystem接口

internal interface ISystem
{
    void Run();
}
Run Code Online (Sandbox Code Playgroud)

并存储在KeyedByTypeCollection.此集合确保每个系统都是唯一的.

每个组件都必须实现该IComponent接口.

internal interface IComponent
{
}
Run Code Online (Sandbox Code Playgroud)

通过这样做,我可以将所有不同的组件类型存储到其匹配的组件池中.每个游泳池都是Dictionary<Guid, IComponent>.键表示实体的ID,值是该实体的组件.每个池都存储在一个池中KeyedByTypeCollection,以确保组件池是唯一的.

目前我的EntityManager类包含核心逻辑.我不知道它是否需要静态但目前我的系统需要从中获取一些信息.

处理组件池的重要核心方法是:

internal static class EntityManager
{
    public static List<Guid> activeEntities = new List<Guid>();

    private static KeyedByTypeCollection<Dictionary<Guid, IComponent>> componentPools = new KeyedByTypeCollection<Dictionary<Guid, IComponent>>();

    public static KeyedByTypeCollection<ISystem> systems = new KeyedByTypeCollection<ISystem>(); // Hold unique Systems

    public static Guid CreateEntity() // Add a new GUID and return it
    {
        Guid entityId …
Run Code Online (Sandbox Code Playgroud)

c# entity-component-system

7
推荐指数
1
解决办法
208
查看次数

用事件连接系统

使用Entity-Component-System模式我想要将一些系统与事件连接起来.所以有些系统不应该在循环中运行,它们应该只是按需运行.

鉴于Health系统的示例,死亡系统应仅在组件低于1健康时运行.

我想过有两种类型的系统.第一种类型是周期性系统.这将运行每帧一次中,例如一个渲染运动系统.另一种类型是基于事件的系统.作为之间的连接之前提到的健康死亡.

首先,我创建了两种系统类型使用的基本接口.

internal interface ISystem
{
    List<Guid> EntityCache { get; } // Only relevant entities get stored in there

    ComponentRequirements ComponentRequirements { get; } // the required components for this system

    void InitComponentRequirements();

    void InitComponentPools(EntityManager entityManager);

    void UpdateCacheEntities(); // update all entities from the cache

    void UpdateCacheEntity(Guid cacheEntityId); // update a single entity from the cache
}
Run Code Online (Sandbox Code Playgroud)

我进一步创建了接口

internal interface IReactiveSystem : ISystem
{
// event …
Run Code Online (Sandbox Code Playgroud)

c# entity-component-system

7
推荐指数
1
解决办法
314
查看次数

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

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

我的图书馆的用户想创建一些组件,例如Cat:-

class Cat{ public:
    int hp;
    float flyPower;
};
Run Code Online (Sandbox Code Playgroud)

他可以通过以下方式修改hp每个内容cat

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

几天后,他想拆分CatHPFlyable:-

class HP{ public:
    int hp;
};
class Flyable{ public:
    float flyPower;
};
Run Code Online (Sandbox Code Playgroud)

因此,每次cat访问hp都会编译错误(例如,#1在上面的代码中)。

为了解决这个问题,用户可以将他的代码重构为:

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

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

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

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

c++ maintainability game-engine c++14 entity-component-system

7
推荐指数
1
解决办法
237
查看次数

与 OOP(或其他范式)相比,ECS(实体-组件-系统)架构模式有哪些缺点?

由于 Unity ECS,我最近阅读了很多有关 ECS 的文章。

ECS架构有很多明显的优势:

ECS 是面向数据的:数据倾向于线性存储,这是系统访问它的最佳方式。在体面的 ECS 实现中,数据是按顺序存储和处理的,对于任何给定的系统处理它的组件集几乎没有中断。

ECS 非常分割它自然地将数据与行为分开,强制执行“组合而非继承”(google it)等。

ECS 对并行处理和多线程非常友好:由于事物的结构方式,许多实体和组件可以避免冲突并与其他系统并行处理。


然而,ECS 的缺点(与 OOP 或实体组件 [没有系统] 相比,直到最近在包括 Unity 在内的游戏引擎中很常见)很少被谈论,如果有的话。它们存在吗?如果他们这样做了,他们是什么?

architecture oop unity-game-engine data-oriented-design entity-component-system

7
推荐指数
2
解决办法
3095
查看次数

实体组件系统的设计

我想知道如何在C++中实现最快版本的实体组件系统(从现在开始的ECS).

首先,关于术语:

  • 一个场景是一种用于容器的实体(和系统在某些实施方式中)
  • 一个组件是一个简单的数据存储(如位置,碰撞盒,图像呈现等)
  • 一个系统上执行的逻辑组件装配系统的要求(这可能是物理,玩家输入,简单渲染等)
  • 一个实体包含多个组件来弥补最后的行为

我列出了我们在下面提出的所有设计.


1."天真"的方式

场景包含所有无序的实体.
随着系统更新,每个系统都必须遍历所有实体并检查每个实体是否包含所有必需的组件,然后对这些实体执行更新.

显然,当拥有大量系统和/或许多实体时,这种方式并不太高效.


2.使用"位掩码枚举"和映射

每个Component包含一个位掩码形式的类型标识符(例如1u << 5/ binary [0...]100000).然后,每个实体可以组成所有Component的类型标识符(假设所有typeID在Entity中都是唯一的),所以它看起来像

1u << 5 | 1u << 3 | 1u << 1
binary [0...]101010
Run Code Online (Sandbox Code Playgroud)

场景包含某种类型的地图,系统可以轻松查找实体:

MovementSystem::update() {
    for (auto& kv : parent_scene_) { // kv is pair<TypeID_t, vector<Entity *>>
        if (kv.first & (POSITION | VELOCITY))
            update_entities(kv.second); // update the whole set of fitting entities
    } …
Run Code Online (Sandbox Code Playgroud)

theory c++11 entity-component-system

6
推荐指数
1
解决办法
2647
查看次数

实体组件系统的自定义堆预分配

我有一个OOP实体组件系统,目前的工作方式如下:

// In the component system
struct Component { virtual void update() = 0; }
struct Entity
{
    bool alive{true};
    vector<unique_ptr<Component>> components;
    void update() { for(const auto& c : components) c->update(); }
}

// In the user application
struct MyComp : Component
{
    void update() override { ... }
}
Run Code Online (Sandbox Code Playgroud)

要创建新的实体和组件,我使用C++的常规newdelete:

// In the component system
struct Manager
{
    vector<unique_ptr<Entity>> entities;
    Entity& createEntity() 
    { 
        auto result(new Entity);
        entities.emplace_back(result);
        return *result;
    }
    template<typename TComp, typename... TArgs>
        TComp& …
Run Code Online (Sandbox Code Playgroud)

c++ memory memory-management c++11 entity-component-system

5
推荐指数
2
解决办法
2116
查看次数

如何在ECS中处理动态分层实体

这是一个类比:我有一个由细胞组成的有机体,这些细胞可以进一步由混合的附件组成。

我目前拥有的是子/父级之间的一种事件链,用于处理附加和分离组件(这可能会影响链上的任何内容),根本不涉及 ecs,它们是实体中的函数。

现在我已经使用了事件组件(用于对象上的鼠标事件)。如果我希望系统是纯粹的,我会在附加组件等时创建一个附加组件吗?即使如此,我如何才能让所有必要的接收者到达使用该组件的系统?以这种方式而不是一系列函数来处理它是否值得?有没有更好的办法?

game-engine data-structures entity-component-system

5
推荐指数
1
解决办法
3119
查看次数

关于纯ECS(实体组件系统)和更新系统的问题

我已经写了一个ECS,但我对更新阶段有一些疑问。(在系统中)\n我读过很多文章,但没有找到此类问题的参考。

\n\n

为了从 ECS 中获益(例如缓存友好),需要满足以下要求:

\n\n
    \n
  • 实体必须只是一个 ID。
  • \n
  • 组件必须只是纯数据(没有逻辑的结构)。
  • \n
  • 系统包含逻辑和更新组件。
  • \n
  • 系统之间没有交互(而是系统通过将 \xe2\x80\x9cTag\xe2\x80\x9d 组件添加到实体来进行通信)。
  • \n
\n\n

因此,每个系统中应用的逻辑都很好,并且在没有 \xe2\x80\x9cuser 代码\xe2\x80\x9d 时一切正常。\n但是,当我们处理用户代码时(例如用户可以将 C++ 代码附加到一个对象(如 Unity、Unreal)),问题就来了:

\n\n
    \n
  1. 由于组件仅包含数据,因此当用户修改\n本地位置时,世界位置不会更新(当变换系统处理每个\n变换组件时,\n将计算世界位置。因此,如果用户请求世界位置\n修改其本地位置后\n,它将获得之前的世界位置\n而不是实际位置。
  2. \n
  3. 当一个实体被删除时,它的子实体也必须被删除。由于组件仅包含数据而不包含逻辑,因此子组件将不会被删除(它将在下一次父系统更新时删除)。因此,我们有一些 \xe2\x80\x9cdelay\xe2\x80\x9d (子级仍然可以访问,但将在下一次父系统更新时被删除)。
  4. \n
  5. 假设我们有实体 A、B、C。B 是 A 的子实体。在\n用户代码(附加到实体的 C++ 代码)中,用户设置 B 的父\n有 C,然后删除实体 A。当父系统将更新,它将检测到 A 已被删除(它还可以检测到实体 A 的父级已更改),但是系统如何知道实体 A 的父级更改后实体 A 是否已被删除B\也不是之前?
  6. \n
\n\n

将逻辑添加到组件中会破坏纯 ECS 的优势(以缓存友好的方式对所有相同的组件执行相同的操作),因此恕我直言,这不是一个解决方案。

\n\n

有人有解决办法吗?我想知道您在 ECS 实施中如何处理此类问题。

\n\n

谢谢!

\n

c++ architecture game-development game-engine entity-component-system

5
推荐指数
1
解决办法
2576
查看次数