我有以下代码:
public class Character
{
public Vector2 WorldPixelPosition
{
get { return Movement.Position; }
}
public Vector2 WorldPosition
{
get { return new Vector2(Movement.Position.X / Tile.Width, Movement.Position.Y / Tile.Height); }
}
public Vector2 LevelPosition
{
get { return new Vector2(WorldPosition.X % Level.Width, WorldPosition.Y % Level.Height); }
}
}
现在我的代码中的其他地方,我在循环到Character.LevelPosition中进行了大约2500次调用。 这意味着每个更新周期,正在制作5000个“新”Vector2,而在我的笔记本电脑上,它确实会降低帧速率。
我已经通过创建
暂时修复了它var levelPosition = Character.LevelPosition;
在我开始循环之前,但是每次遇到类似的情况时,我都会感觉到它的丑陋代码。也许它是 - 可行的方式,但我想确定。
有更好或普遍接受的方法吗?
我正在使用XNA-Framework,它使用Vector2
。
答案 0 :(得分:5)
据我所知,你应该避免在XNA中从堆中分配大量对象,因为这会导致性能不佳。但由于Vector2
是struct
,我们在这里没有在堆上分配任何东西,所以这不应该是问题。
现在,如果你像在游戏中那样在性能关键型应用程序中进行紧密循环,那么你将始终不得不考虑性能,而不会出现这种情况。
如果我们查看LevelPosition
的代码,您可以将WorldPosition
的getter调用两次,可能还需要更多的getter。 WorldPosition
的getter可能会调用其他几个getter。 (如果没有源代码,很难说究竟发生了什么,因为getter调用和字段访问看起来完全一样。)
调用getter,实际上只是对特殊方法的调用,通常非常快,如果编译器决定使用内联,则可以更快。但所有的调用都加在一起,特别是如果你在一个循环中调用它们。
解决方案是某种缓存。一种选择是使LevelPosition
成为一个字段并设计一个系统以在必要时更新它。这可能有效,但如果你需要更频繁地更新它,它实际上也可能会损害性能。
正如您所发现的,另一种解决方案是将结果缓存在局部变量中。如果你知道这是正确的,即在执行循环期间属性的值不会改变,那就太棒了!您解决了性能问题,只需使用一行代码即可让任何程序员轻松理解。你还想要什么?
让我重申一下。您找到了解决性能问题的解决方案:
我认为这样的解决方案很难被击败。
答案 1 :(得分:4)
在循环中创建许多对象可能是一项昂贵的操作(*)。也许如果有助于提前创建Vector2
(例如当坐标发生变化时)以及将来只更改坐标。
示例:
public class Character
{
private Vector2 m_worldPosition = new Vector2(0, 0);
private Vector2 m_levelPosition = new Vector2(0, 0);
....
public Vector2 WorldPosition
{
get
{
m_worldPosition.X = ...;
m_worldPosition.Y = ...;
return m_worldPosition;
}
}
public Vector2 LevelPosition
{
get
{
m_levelPosition.X = ...;
m_levelPosition.Y = ...;
return m_levelPosition;
}
}
}
修改强>
对LevelPosition
属性也应该这样做。请参阅修改后的源代码。
<强>(*)强>
Tim Schmelter向我指出this question详细讨论了实例化对象的影响。我重写了我的第一句话,即对象创建总是昂贵。虽然创建对象并不总是一项昂贵的操作,但在某些情况下仍可能会降低性能。
答案 2 :(得分:2)
您可以创建一个私有字段来存储值,而不是每次都计算它。您可以创建一个方法来更新私有字段,并以某种方式订阅Movement.Position更改。这样,当位置改变时,该值仅计算一次。