非整数速度值-是否有更干净的方法可以做到这一点?


21

通常,我会希望使用速度值(例如2.5)在基于像素的游戏中移动角色。但是,如果这样做,碰撞检测通常会更加困难。所以我最终做了这样的事情:

moveX(2);
if (ticks % 2 == 0) { // or if (moveTime % 2 == 0)
    moveX(1);
}

每当我不得不写那封信时,我都会畏缩一次,是否有一种更清洁的方式来移动具有非整数速度值的角色,或者我会永远坚持这样做吗?


11
您是否考虑过使整数单位变小(例如,显示单位的1/10),然后2.5转换为25,因此您仍然可以将其视为所有检查的整数并始终如一地对待每一帧。
DMGregory

6
您可以考虑采用仅可使用整数实现的Bresenham线算法
n0rd

1
这在旧的8位控制台上通常是完成的。有关使用定点方法如何实现亚像素移动的示例,请参见此类文章:tasvideos.org/GameResources/NES/BattleOfOlympus.html
Lucas,

Answers:


13

布雷森纳姆

在过去,当人们仍在编写自己的基本视频例程以绘制线条和圆形时,使用Bresenham线条算法并不是没有听说过的。

布雷森纳姆解决了这个问题:您想在屏幕上画一条线,该线在dx水平方向上移动像素,同时在dy垂直方向上跨越像素。线条具有固有的“蓬松性”。即使您具有整数像素,您最终也会得到合理的倾斜度。

但是,该算法必须快速,这意味着它只能使用整数算法。它也没有任何乘法或除法,只有加法和减法就消失了。

您可以根据自己的情况进行调整:

  • 您的“ x方向”(根据Bresenham算法而言)就是您的时钟。
  • 您的“ y方向”是您要增加的值(即角色的位置-小心,这实际上不是您的精灵的“ y”或屏幕上的任何内容,更不是抽象的值)

“ x / y”不是屏幕上的位置,而是您的一个尺寸随时间变化的值。显然,如果您的精灵在屏幕上沿任意方向运行,则将有多个不来梅奔跑,其中2个用于2D,三个用于3D。

假设您要沿着一个轴从0到25的简单移动中移动角色。当它以速度2.5移动时,它将到达那里的第10帧。

这与从(0,0)到(10,25)的“画线”相同。抓住布雷森纳姆的线算法,让它运行。如果您做对了(并且在学习时会很快清楚地知道如何做对),那么它将为您生成11个“点”(0,0),(1,2),(2, 5),(3,7),(4,10)...(10,25)。

适应提示

如果您搜索该算法并找到一些代码(维基百科上有相当大的条约),则需要注意以下几点:

  • 这显然适用于所有类型的dxdy。但是,您对一种特定情况感兴趣(即,您永远不会有dx=0)。
  • 通常的实现将有几种不同的情况下,在屏幕上象限,这取决于是否dxdy是积极的,消极的,以及是否abs(dx)>abs(dy)与否。您当然也可以在这里选择所需的东西。您必须特别确保1每个刻度线增加的方向始终是您的“时钟”方向。

如果应用这些简化,结果的确将非常简单,并且完全摆脱了任何现实。


1
这应该是公认的答案。在80年代在C64上编写游戏,在90年代在PC上编写分形游戏后,我仍然不愿在任何可以避免的地方使用浮点数。或者,当然,在当今的处理器中普遍使用FPU时,性能的争论主要是争论不休,但是浮点算术仍然需要更多的晶体管,消耗更多的功率,并且许多处理器将在不使用时完全关闭其FPU。因此,避免浮点会使您的移动用户感谢您不要如此快地吸尽电池电量。
Guntram Blohm支持Monica

@GuntramBlohm虽然使用定点时,可接受的答案也可以很好地工作,我认为这是一个很好的方法。您对定点数字有何看法?
leetNightshade

在发现这是他们在8位和16位工作日中的处理方式后,将其更改为可接受的答案。
累加器

26

有一种很好的方法可以精确地执行您想要的操作。

除了float速度之外,您还需要第二个float变量,该变量将包含并累积实际速度和舍入速度之间的差。然后将这种差异与速度本身结合起来。

#include <iostream>
#include <cmath>

int main()
{
    int pos = 10; 
    float vel = 0.3, vel_lag = 0;

    for (int i = 0; i < 20; i++)   
    {
        float real_vel = vel + vel_lag;
        int int_vel = std::lround(real_vel);
        vel_lag = real_vel - int_vel;

        std::cout << pos << ' ';
        pos += int_vel;
    }
}

输出:

10 10 11 11 11 12 12 12 12 13 13 13 14 14 14 15 15 15 15 16


5
为什么相对于对速度和位置都使用浮点数(或定点数),而只在最后将位置四舍五入为整数,您为什么更喜欢这种解决方案?
CodesInChaos

@CodesInChaos我不喜欢我的解决方案。当我写这个答案时,我并不知道。
HolyBlackCat

16

将浮点值用于移动,将整数值用于碰撞和渲染。

这是一个例子:

class Character {
    float position;
public:
    void move(float delta) {
        this->position += delta;
    }
    int getPosition() const {
        return lround(this->position);
    }
};

移动时,您可以使用move()累计小数位。但是通过使用该getPosition()函数,碰撞和渲染可以处理积分位置。


请注意,在联网游戏的情况下,使用浮点类型进行世界模拟可能会比较棘手。参见例如gafferongames.com/networking-for-game-programmers/…
liori

@liori如果您有一个定点类,像浮点型的替代品一样,那不是可以解决大多数问题吗?
leetNightshade

@leetNightshade:取决于实现。
liori

1
我会说这个问题在实践中不存在,能够运行现代网络游戏的硬件没有IEEE 754浮点数?
Sopel
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.