我最近一直在玩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;
}
我只是错过了什么?那里有一个更优雅的解决方案,我只是不知道。
答案 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)
我不认为你的要求是可能的 - 如果你想要一个不同的集合(即那个集合中的元素与另一个集合有某种不同)那么你需要一些集合的新实例,以便能够存储这些差异。
您可能希望的最好的方法是使用数组而不是列表,因为内存开销可能更低,但是(我不是游戏开发人员)这一切都让我觉得过于优化 - 收集的开销实例相对较小。
不要误解我的意思 - 你很清楚意识到这样的事情,我只是觉得你不应该对这个细节水平有所顾忌,因为你不太可能看到任何影响