简体   繁体   English

在简单的2D游戏中,移动精灵的最佳方式是什么?

[英]What's the best way to move a sprite faster than the update rate in a simple 2d game?

At the moment, I have a sprite that I have arbitrarily set to move 1 pixel per second. 目前,我有一个精灵,我任意设置为每秒移动1个像素。 The code is basically this (The code isn't optimised at all, I could do it much nicer but it is the principle I am trying to solve first:): 代码基本上是这样的(代码根本没有优化,我可以做得更好,但这是我试图首先解决的原则:):

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

Perform movement is called in a loop as you'd expect, and it equates to updating around 10 times per second. 按照您的预期在循环中调用执行移动,它等同于每秒更新大约10次。 So if I lower the MOVEMENT_SPEED, my sprite speeds up, but it never gets any faster than 10 pixels per second. 因此,如果我降低MOVEMENT_SPEED,我的精灵会加速,但它永远不会超过每秒10个像素。 For projectiles and other stuff I obviously want it to update much faster than this. 对于射弹和其他东西,我显然希望它比这更快地更新。

If I alter the movement to 2 pixels or more, it creates issues with calculating collisions and suchlike, but these might be possible to overcome. 如果我将运动改变为2像素或更多,则会产生计算碰撞等问题,但这些可能有可能克服。

The other alternative is to store x and y as a float rather than an int, and increase the values as a fraction of the number of elapsed ticks. 另一种方法是将x和y存储为float而不是int,并将值增加为经过的tick数的一部分。 I am not sure if this will create smooth movement or not as there still has to be some rounding involved. 我不确定这是否会创造顺畅的运动,因为仍然需要进行一些舍入。

So my question is, does anyone know the standard way? 所以我的问题是,有没有人知道标准方式?

Should I increase the amount to more than 1 pixel and update my collision detection to be recursive, should I store X,Y as floats and move as a % of elapsed time, or is there a 3rd better way of doing it? 我应该将数量增加到多于1个像素并将我的碰撞检测更新为递归,我应该将X,Y存储为浮动并以经过时间的百分比移动,还是有第三种更好的方法?

The standard way is to not count down a timer to move, but instead the opposite: 标准的方法是不倒计时移动,而是相反:

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

Make the movement based on the time elapsed. 根据经过的时间进行移动。 The reason floats are commonly used is to get the fractional value of motion. 通常使用浮动的原因是获得运动的小数值。 Fixed-point is another common fractional representation but uses int s instead. 定点是另一种常见的小数表示,但使用的是int

As for collision, collision can be very tricky but in general you don't absolutely need to do it once per pixel of motion (as you suggested with recursion); 至于碰撞,碰撞可能非常棘手,但一般来说,每个运动像素并不是绝对需要做一次(正如你提出的递归); that's overkill and will lead to terrible performance in no time. 这太过分了,很快就会导致糟糕的表现。 If you are currently having trouble with 2-pixel motion, I would reevaluate how you're doing your collisions. 如果您目前在使用2像素运动时遇到问题,我会重新评估您是如何进行碰撞的。 In general, it becomes problematic when you're moving very fast to the point of skipping over thin walls, or even passing over to the "wrong side" of a wall, depending on how your collision is set up. 一般来说,当你快速移动到超薄墙壁上跳跃,甚至越过墙壁的“错误侧”时,根据你的碰撞设置方式,它会成为问题。 This is known as "tunnelling". 这被称为“隧道”。 There are many ways of solving this. 有很多方法可以解决这个问题。 Look here and scroll down to "Preventing Tunnelling". 在这里查看并向下滚动到“防止隧道”。 As the article states many people just cap their speed at a safe value. 正如文章所述,许多人只是将速度限制在一个安全的价值。 But another common method is to "step" through your algorithm in smaller time steps than is currently being passed in. For example, if the current elapsed time is 0.1, you could step by 0.01 within a loop and check each small step. 但另一种常见的方法是以比当前传入的更短的时间步长“逐步”遍历算法。例如,如果当前经过的时间为0.1,则可以在循环内逐步减去0.01并检查每个小步骤。

A way to do what you request, although not very recommended, is to increase your game's update frequency to a higher value than the usual 30 or 60 fps, but only draw to the screen every N frames. 尽管不是非常推荐,但是你要求的方法是将游戏的更新频率提高到比通常的30或60 fps更高的值,但每N帧只画一次屏幕。 You can do it by just having your graphics engine ignore Draw calls until a count or timer reaches the desired value. 您可以通过让图形引擎忽略Draw调用直到计数或计时器达到所需的值来实现。

Of course, this solution should be avoided unless it is specifically desired, because performance can degrade quite fast as the number of updated elements increases. 当然,除非特别需要,否则应避免使用此解决方案,因为随着更新元素数量的增加,性能会迅速降低。

For example, Proun (not an XNA game) uses this trick for exactly your reasons. 例如, Proun (不是XNA游戏)完全按照你的理由使用这个技巧

With the default of IsFixedTimeStep = true , XNA behaves in a similar fashion , skipping calls to Draw if Update takes too long. 默认情况下, IsFixedTimeStep = trueXNA以类似的方式运行 ,如果Update花费太长时间,则跳过对Draw的调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM