【问题标题】:create performant component pooling for system loops为系统循环创建高性能组件池
【发布时间】:2019-06-05 09:35:52
【问题描述】:

我正在设置一个非常小的实体-组件-系统示例,但组件池存在一些问题。

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

每个系统都要实现ISystem接口

internal interface ISystem
{
    void Run();
}

并存储在KeyedByTypeCollection 中。这个集合确保每个系统都是独一无二的。

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

internal interface IComponent
{
}

通过这样做,我可以将所有不同的组件类型存储到它们匹配的组件池中。每个池都是一个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 = Guid.NewGuid();
        activeEntities.Add(entityId);
        return entityId;
    }

    public static void DestroyEntity(Guid entityId) // Remove the entity from every component pool
    {
        activeEntities.Remove(entityId);

        for (int i = 0; i < componentPools.Count; i++)
        {
            componentPools[i].Remove(entityId);
        }
    }

    public static Dictionary<Guid, TComponent> GetComponentPool<TComponent>() where TComponent : IComponent // get a component pool by its component type
    {
        return componentPools[typeof(TComponent)];
    }

    public static void AddComponentPool<TComponent>() where TComponent : IComponent // add a new pool by its component type
    {
        componentPools.Add(new Dictionary<Guid, TComponent>());
    }

    public static void RemoveComponentPool<TComponent>() where TComponent : IComponent // remove a pool by its component type
    {
        componentPools.Remove(typeof(TComponent));
    }

    public static void AddComponentToEntity(Guid entityId, IComponent component) // add a component to an entity by the component type
    {
        componentPools[component.GetType()].Add(entityId, component);
    }

    public static void RemoveComponentFromEntity<TComponent>(Guid entityId) where TComponent : IComponent // remove a component from an entity by the component type
    {
        componentPools[typeof(TComponent)].Remove(entityId);
    }
}

我创建了一个小型运动系统用于测试目的:

internal class Movement : ISystem
{
    public void Run()
    {
        for (int i = 0; i < EntityManager.activeEntities.Count; i++)
        {
            Guid entityId = EntityManager.activeEntities[i];

            if (EntityManager.GetComponentPool<Position>().TryGetValue(entityId, out Position positionComponent)) // Get the position component
            {
                positionComponent.X++; // Move one to the right and one up ...
                positionComponent.Y++;
            }
        }
    }
}

这应该没问题,因为字典查找。我不确定是否可以优化它,但我认为我必须先遍历所有实体。

问题出在这里:

我无法使用KeyedByTypeCollection&lt;Dictionary&lt;Guid, IComponent&gt;&gt;,因为我需要类似的东西

KeyedByTypeCollection&lt;Dictionary&lt;Guid, Type where Type : IComponent&gt;&gt;

我的 GetComponentPoolAddComponentPool 方法抛出错误。

GetComponentPool:无法将 IComponent 隐式转换为 TComponent

调用GetComponentPool 时,我必须将字典值从IComponent 转换为TComponent

AddComponentPool:无法从 TComponent 转换为 IComponent

调用AddComponentPool 时,我必须将TComponent 转换为IComponent

我不认为强制转换是一种选择,因为这似乎会降低性能。

有人介意帮我解决类型问题吗?


更新:

在调试时我使用此代码来测试整个 ECS

internal class Program
{
    private static void Main(string[] args)
    {
        EntityManager.AddComponentPool<Position>();
        EntityManager.AddComponentPool<MovementSpeed>();

        EntityManager.Systems.Add(new Movement());

        Guid playerId = EntityManager.CreateEntity();
        EntityManager.AddComponentToEntity(playerId, new Position());
        EntityManager.AddComponentToEntity(playerId, new MovementSpeed(1));

        foreach (ISystem system in EntityManager.Systems)
        {
            system.Run();
        }
    }
}

我已经展示了Movement系统,这里是缺少的组件

internal class Position : IComponent
{
    public Position(float x = 0, float y = 0)
    {
        X = x;
        Y = y;
    }

    public float X { get; set; }
    public float Y { get; set; }
}

internal class MovementSpeed : IComponent
{
    public MovementSpeed(float value = 0)
    {
        Value = value;
    }

    public float Value { get; set; }
}

【问题讨论】:

  • 我不能完全回答,但我看到你犯了一个错误。在某些地方,您使用的是typeof(TComponent),第二个是component.GetType()。这些不是等效键!

标签: c# entity-component-system


【解决方案1】:

问题的核心是Dictionary&lt;Guid, TComponent&gt;Dictionary&lt;Guid, IComponent&gt; 是完全不相关的类型。它们不能相互转换。这是因为字典可以读写。如果您可以将List&lt;Giraffe&gt; 转换为List&lt;Animal&gt;,则可以存储Tiger,这会破坏类型安全。如果您可以反过来转换,您可以读取Tiger 实例并将它们视为Giraffe,这又会破坏类型安全。您可以在网上搜索.net covariance contravariance 以查找更多信息。


一个简单的解决方案是从

KeyedByTypeCollection<Dictionary<Guid, IComponent>> componentPools

Dictionary<Type, object> componentPools

.

这使您可以在集合中存储Dictionary&lt;Guid, TComponent&gt;,这在以前是不可能的。缺点是你现在需要施法。但是您只需要转换相当便宜的字典实例。您不需要在 O(N) 时间内转换整个字典。

访问者:

public static Dictionary<Guid, TComponent> GetComponentPool<TComponent>() where TComponent : IComponent // get a component pool by its component type
{
    return (Dictionary<Guid, TComponent>)componentPools[typeof(TComponent)]; //cast inserted
}

public static void AddComponentPool<TComponent>() where TComponent : IComponent // add a new pool by its component type
{
    componentPools.Add(typeof(TComponent), (object)new Dictionary<Guid, TComponent>()); //unneeded cast inserted for clarity
}

也许您根本不喜欢我们需要投射的内容。我看不出有什么办法。实践经验表明,当您对类型系统(泛型、方差)进行详细说明时,您会获得更糟糕的解决方案。所以不用担心。做有效的事。

【讨论】:

  • 嘿,非常感谢您的帮助 :) 我更新了我的帖子以概述完整的代码。我按照您的要求编辑了我的代码。这些是我的更改pastebin.com/AiUGWQmM 在调试时,我在AddComponentToEntity 得到一个无效的强制转换异常。你介意检查一下吗?
  • 有一个new new Dictionary&lt;Guid, TComponent&gt;,应该是object。我建议您学习使用调试器来查找像这样的简单错误。经验丰富的开发人员需要大约 3 秒的时间来使用调试器找到此错误,但阅读代码可能需要几分钟。 @totalBeginner
  • 很抱歉没有得到您的解释。我试图改进我的代码,我想我会将代码分成多个类,但目前我有这个pastebin.com/k16BQJ63 我使用调试器并看到调用AddComponentToEntity 时出现错误,但我仍然无法修复例外:/你能帮忙吗?
  • 我想我自己也很困惑 :) 我明天会回答你@totalBeginner
  • 没问题。这会很棒:)
猜你喜欢
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
  • 2020-08-25
  • 1970-01-01
  • 2018-12-12
  • 1970-01-01
  • 2010-09-13
  • 2010-11-04
相关资源
最近更新 更多