如何在不产生垃圾的情况下从另一个集合中生成游戏实体集合?

时间:2010-08-20 13:27:39

标签: c# garbage-collection xna

我最近一直在玩XNA,而且我也在阅读很多关于游戏垃圾收集的内容。我认为通过使用池并避免依赖foreach,我已经做了合理的工作来减少垃圾量。

现在我将我的基本游戏实体存储在KeyedCollection中,它允许我遍历所有实体(如List)并按键引用实体(如字典)。

我希望能够查询我的集合并返回另一个集合,而不会在每个查询中产生垃圾。我已经在下面列出了这个概念的样本,因为我认为我更擅长编码,我正在解释......

/// <summary>
/// Defines a sample entity.
/// </summary>
public class SampleEntity
{
    public uint Id;
    public Vector2 Position;
}

/// <summary>
/// Defines a collection of sample entities.
/// </summary>
public class EntityCollection : KeyedCollection<uint, SampleEntity>
{
    /// <summary>
    /// Return the key for the supplied item.
    /// </summary>
    protected override uint GetKeyForItem(SampleEntity item)
    {
        return item.Id;
    }
}

/// <summary>
/// Defines the sample game class.
/// </summary>
public class GameSample
{
    /// <summary>
    /// Create a new instance of the GameSample class.
    /// </summary>
    public GameSample()
    {
        Entities = new EntityCollection();
    }

    /// <summary>
    /// Get the collection of game entities.
    /// </summary>
    public EntityCollection Entities { get; private set; }

    /// <summary>
    /// Return the collection of entities within a radius of the supplied point.
    /// </summary>
    public List<SampleEntity> Query(Vector2 center, float radius)
    {
        List<SampleEntity> results = new List<SampleEntity>() // BAD, BAD, BAD!!!!!

        //
        // add the entities to the results collection
        //

        return results;
    }
}

这个(过于简化的)示例会产生大量垃圾,因为它会在每次调用时创建一个新的List对象。我也玩过创建全局结果列表并清除每个电话,但这看起来很难看。

    /// <summary>
    /// Return the collection of entities within a radius of the specified point.
    /// </summary>
    public List<SampleEntity> Query(Vector2 center, float radius)
    {
        _globalResults.Clear();

        //
        // add the entities to the global results collection
        //

        return _globalResults;
    }

我只是错过了什么?那里有一个更优雅的解决方案,我只是不知道。

3 个答案:

答案 0 :(得分:3)

我认为在这种情况下最好的解决方案是让你的功能看起来像这样:

 public void Query(Vector2 center, float radius, List<SampleEntity> result)
 {
     result.Clear();
     result.Add(/*...*/);
     // ...
 }

让调用者负责管理该列表。

另外:制作游戏时,不要害怕编写简单的代码。如果全局结果列表有效 - 那么这是一个很好的解决方案。


如果您不需要存储列表,那么从性能角度来看,Skurmedel's answer可能更好。

虽然,如果是我,我会做真的简单的事情,而直接迭代实体。相同的效果,可能更快一点,最重要的是:更少的代码。

在编写复杂的空间分区数据结构后编写复杂的枚举查询函数,只有在实际需要它提供的性能后才能编写。

答案 1 :(得分:1)

我认为Andrew Russell有正确的想法,但如果你能完全取消List,那就更好了。您将创建一个IEnumerable(带有yield返回或手写实例),它返回查询找到的项目。你不需要新的清单。基本上你也可以用LINQ实现这个目的。

我把它想象成在集合的内容中有一个“窗口”或“视图”。

你需要注意的唯一一件事就是使IEnumerable无效,你不能修改列表而其他东西在迭代它。

如果您害怕每次都创建一个新的IEnumerable,请重复使用它。您也可以重用枚举器,但这会限制一次执行查询的调用者数量。如果两个呼叫者同时开始使用同一个枚举器,那么地狱之门就会打开。

答案 2 :(得分:0)

我不认为你的要求是可能的 - 如果你想要一个不同的集合(即那个集合中的元素与另一个集合有某种不同)那么你需要一些集合的新实例,以便能够存储这些差异。

您可能希望的最好的方法是使用数组而不是列表,因为内存开销可能更低,但是(我不是游戏开发人员)这一切都让我觉得过于优化 - 收集的开销实例相对较小。

不要误解我的意思 - 你很清楚意识到这样的事情,我只是觉得你不应该对这个细节水平有所顾忌,因为你不太可能看到任何影响