3

我有一个实现 ICollidable 接口的实体列表。该接口用于解决实体之间的冲突。因此,我的实体是:

  • 球员
  • 敌人
  • 弹丸
  • 项目
  • 瓷砖

在每次游戏更新(大约 60 t/s)时,我都会清除列表并根据游戏状态添加当前实体。我通过以下方式完成此操作:

collidableEntities.Clear();
collidableEntities.AddRange(players);
collidableEntities.AddRange(enemies);
collidableEntities.AddRange(projectiles);
collidableEntities.AddRange(items);
collidableEntities.AddRange(camera.VisibleTiles);

一切正常,直到我将可见图块添加到列表中。运行游戏循环的前约 1-2 秒会导致可见的打嗝延迟绘制(因此我可以看到渲染中的抖动)。我可以从字面上删除/添加添加图块的行并查看抖动发生和不发生,因此我将其缩小到该行。

我的问题是,为什么?VisibleTiles 的列表大约有 450-500 个图块,所以实际上并没有那么多数据。每个图块包含一个 Texture2D(图像)和一个 Vector2(位置),以确定渲染的内容和位置。我会继续看,但从头顶看,我不明白为什么只有前 1-2 秒打嗝,但从那里开始就很顺利。

任何建议表示赞赏。

更新

我尝试将初始容量增加到近似数量的元素,但没有观察到差异。

更新

根据要求,这是 camera.VisibleTiles 的代码

public List<Tile>
{
    get { return this.visibleTiles; }
}
4

3 回答 3

5

每当您遇到这样的性能问题时,正确的反应是不要猜测可能的原因;正确的反应是分析应用程序。有多种选择;我相信您可以在 StackOverflow 上找到建议。

不过,我今天感觉很幸运,所以我将无视我刚刚给你的建议,胡乱猜测。我将解释我的推理,希望对将来诊断此类问题有所帮助。

您描述的问题仅在游戏开始时仅发生一次,这一事实使我相信这不是列表本身的功能或碰撞逻辑所固有的任何东西。当然,这是假设列表中的对象数量在这段时间内基本保持不变。如果它由其中任何一个引起的,我希望它每帧都会发生。

因此我怀疑垃圾收集器。您可能会在游戏开始时看到 GC 发生——换句话说,在您将所有大量资产加载到内存之后。您也可能会在代码中看似随机的点看到它,因为您分配的任何对象理论上都可以将其推入集合中。

我的猜测是:当你加载游戏时,你创建的资源会产生很大的收集压力,但这不足以触发收集。当您在游戏过程中分配对象时(在这种情况下,由于调整列表的大小),它会增加收集压力到 GC 最终决定激活的点,从而导致您观察到的抖动。一旦 GC 运行,并且所有适当的对象都已降级到正确的代,抖动就会停止。

正如我所说,这只是一个猜测,尽管是受过教育的。但是,它很容易测试。GC.Collect(2)在进入渲染循环之前添加一个调用。如果我是对的,抖动应该会消失。

如果你想比这更彻底——我强烈推荐它——微软提供了一个对调试内存问题很有用的工具,即CLR Profiler。该工具将准确地向您显示收集发生的时间以及原因。它对 XNA 开发非常有用。

于 2012-09-12T16:20:06.477 回答
1

1-2秒可能是因为GC调整了每一代的大小。查看相应的性能计数器,看看第一秒内是否有大量的 Gen1/2 收集(最小化 GC 是游戏的有用目标)

额外的随机猜测:你所有的对象都是struct出于某种原因。而且很大。所以复制它们需要很长时间。(不会解释第一秒后的平滑)

笔记:

  • 与其合并单个列表中的项目,不如考虑创建避免所有复制的迭代器。
  • 获取分析器
  • 学习查看您的流程的性能计数器。有趣的类别是内存、GC、CPU、处理程序。
于 2012-09-12T16:06:01.700 回答
0

您可以使用重载的构造函数List<T>来初始化具有预定义大小的列表:

var collidableEntities = new List<object>(500);
于 2012-09-12T15:50:13.410 回答