目前,我有一个精灵,我任意设置为每秒移动1个像素。代码基本上是这样的(代码根本没有优化,我可以做得更好,但这是我试图首先解决的原则:):
private const long MOVEMENT_SPEED = 10000000; // Ticks in 1 second
private long movementTimeSpan = MOVEMENT_SPEED;
protected void PerformMovement(GameTime gameTime)
{
movementTimeSpan -= gameTime.ElapsedGameTime.Ticks;
if (movementTimeSpan <= 0)
{
// Do the movement of 1 pixel in here, and set movementTimeSpan back to MOVEMENT_SPEED
}
}
按照您的预期在循环中调用执行移动,它等同于每秒更新大约10次。因此,如果我降低MOVEMENT_SPEED,我的精灵会加速,但它永远不会超过每秒10个像素。对于射弹和其他东西,我显然希望它比这更快地更新。
如果我将运动改变为2像素或更多像素,则会产生计算碰撞等问题,但这些可能有可能克服。
另一种方法是将x和y存储为float而不是int,并将值增加为经过的tick数的一部分。我不确定这是否会创造顺畅的运动,因为仍然需要进行一些舍入。
所以我的问题是,有没有人知道标准方式?
我应该将数量增加到多于1个像素并将我的碰撞检测更新为递归,我应该将X,Y作为浮动存储并以经过时间的百分比移动,还是有第三种更好的方法?
答案 0 :(得分:8)
标准方法是不倒计时器来移动,而是相反:
private const float MOVEMENT_SPEED = 10.0f; //pixels per second
private float time;
protected void PerformMovement(GameTime gameTime)
{
time = (float)gameTime.ElapsedGameTime.TotalSeconds;
character.X += MOVEMENT_SPEED * time;
}
根据经过的时间进行移动。通常使用浮动的原因是获得运动的小数值。 Fixed-point是另一种常见的小数表示形式,但使用int
代替。
至于碰撞,碰撞可能非常棘手,但一般来说,每个运动像素并不是绝对需要做一次(正如你提出的递归);这太过分了,很快就会导致糟糕的表现。如果您目前在使用2像素运动时遇到问题,我会重新评估您是如何进行碰撞的。一般来说,当你快速移动到超薄墙壁上跳跃,甚至越过墙壁的“错误一侧”时,根据你的碰撞设置方式,它会成为问题。这被称为“隧道”。有很多方法可以解决这个问题。查看here并向下滚动到“防止隧道”。正如文章所述,许多人只是将速度限制在一个安全的价值。但另一种常见的方法是以比当前传入的更短的时间步长“逐步”遍历算法。例如,如果当前经过的时间为0.1,则可以在循环内逐步减去0.01并检查每个小步骤。
答案 1 :(得分:1)
尽管不是非常推荐,但您可以将游戏的更新频率提高到比通常的30或60 fps更高的值,但是每N帧只能绘制到屏幕上。您可以通过让图形引擎忽略Draw调用直到计数或计时器达到所需的值来实现。
当然,除非特别需要,否则应避免使用此解决方案,因为随着更新元素数量的增加,性能会迅速降低。
例如,Proun(不是XNA游戏)uses this trick完全是出于您的原因。
使用默认值IsFixedTimeStep = true
,XNA behaves in a similar fashion,如果更新需要太长时间,则跳过调用Draw。