2010-08-20 61 views
2

最近我一直在玩XNA,我也一直在阅读游戏中垃圾收集的相关内容。我想我已经做了一个合理的工作,通过使用池并减少对foreach的依赖来减少垃圾数量。如何从另一个集合中生成一个游戏实体的集合而不会产生垃圾?

现在我有我的基础游戏实体存储在一个KeyedCollection中,它允许我遍历所有实体(如列表)并通过键(如字典)引用实体。

我希望能够查询我的集合并返回另一个集合而不会产生垃圾每个查询。我在下面列出,因为我觉得我在编码他们我在更好地解释这一概念的样本...

/// <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; 
    } 

我是否错过了什么?有没有更好的解决方案,我只是不知道。

+0

“我认为我已经做了一个减少垃圾量的合理工作” - 如果您还没有:您应该使用CLR Profiler,那么您肯定会知道*。 – 2010-08-20 13:32:39

+0

很难说不知道“将实体添加到结果集合”里面的逻辑,但我会说这很有可能是收益回报是你的朋友。 – 2010-08-20 13:35:49

+0

@Andrew:这个游戏还不够远,从探查器获得很多有用的信息,但是感谢你指引我走向CLR探查器。 @Nate:不幸的是yield关键字也会产生垃圾,与我的示例类似。它为每个函数调用创建一个新的IEnumerable 对象。 – Pixelfish 2010-08-20 13:48:37

回答

3

我觉得在这样的情况下,最好的解决办法是让你的函数是这个样子:

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

,使主叫方负责管理该列表。

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


如果您不需要存储列表,然后Skurmedel's answer可能是从性能的角度更好。

虽然,如果是我,我会做真的简单的事情,只是在实体直接迭代。同样的效果,也许快一点,最重要的是:代码少。

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

+0

我想我会把你,Skurmedel和Tergiver说到心里。当我需要时,我会担心优化,现在只使用收益回报,因为我没有特别需要将结果存储在列表中。我确实发现你的原始解决方案很有趣,但我有兴趣通过直接遍历我的实体来探索你的意思。 – Pixelfish 2010-08-20 15:38:27

0

我不认为你的要求是可能的 - 如果你想要一个不同的集合(即一个如该集合中的元素是从另一个集合有些不同)那么你需要的一些新的实例收集为了能够存储这些差异是什么。

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

不要误解我的意思 - 它好,你是知道这样的事情,我只是认为你不应该在这个层面上细心,因为你不太可能会看到任何影响。

+0

作为一名桌面开发人员,我同意。然而,XBox上的垃圾收集器在每次分配1MB后被调用,并且速度很慢。如果我粗心大意产生垃圾,那么我可能会从GC运行频繁的时候开始丢帧。 – Pixelfish 2010-08-20 13:54:19

1

我认为Andrew Russell有正确的想法,但如果你可以完全消除List,那会更好。您将创建一个IEnumerable(使用yield return或handwrite一个实例)来返回查询找到的项目。你不需要一个新的列表。你基本上也可以通过LINQ来实现这一点。

我将其视为在集合的内容中具有“窗口”或“视图”。

您需要注意的唯一事情是使IEnumerable无效,您无法修改列表,而其他东西正在迭代它。

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

相关问题