정수가 아닌 속도 값-더 깔끔한 방법이 있습니까?


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

답변:


13

브레 센햄

예전에는 사람들이 여전히 선과 원을 그리기 위해 자신의 기본 비디오 루틴을 작성하고 있었지만 브레 센햄 선 알고리즘을 사용하는 것은 들어 본 적이 없었습니다.

Bresenham은이 문제를 해결합니다. 화면에 선을 그려 dx가로 방향으로 픽셀 을 이동 시키면서 동시에 dy세로 방향으로 픽셀에 걸쳐 있습니다. 선에는 고유 한 "floaty"문자가 있습니다. 정수 픽셀이 있더라도 합리적인 기울기로 끝납니다.

그러나 알고리즘은 빠르므로 정수 산술 만 사용할 수 있습니다. 또한 곱셈이나 나눗셈없이 더하기와 빼기만으로 도망칩니다.

귀하의 경우에 맞게 조정할 수 있습니다.

  • Bresenham 알고리즘의 관점에서 "x 방향"은 시계입니다.
  • "y 방향"은 증가시키고 자하는 값입니다 (예 : 캐릭터의 위치-주의 : 이것은 실제로 스프라이트의 "y"가 아니거나 화면의 어떤 것이지, 더 추상적 인 값이 아닙니다)

여기서 "x / y"는 화면상의 위치가 아니라 시간에 따른 치수 중 하나의 값입니다. 분명히 스프라이트가 화면을 가로 질러 임의의 방향으로 움직이면 여러 개의 Bresenham이 별도로 실행됩니다 (2D는 2D, 3D는 3D).

축 중 하나를 따라 0에서 25 사이의 간단한 움직임으로 캐릭터를 움직이고 싶다고 가정 해 봅시다. 속도 2.5로 움직이면서 프레임 10에 도착합니다.

이것은 "0,0)에서 (10,25)까지"선 그리기 "와 동일합니다. Bresenham의 라인 알고리즘을 잡고 실행하십시오. 제대로하면 (그리고 공부할 때 어떻게해야하는지 빨리 알 수 있습니다), (0,0), (1,2), (2, 5), (3,7), (4,10) ... (10,25).

적응에 대한 힌트

해당 알고리즘을 Google로 검색하고 코드를 찾은 경우 (Wikipedia에 꽤 큰 조약이 있음)주의해야 할 사항이 있습니다.

  • 분명히 모든 종류의 dx및에 적용 dy됩니다. 그래도 하나의 특정 사례에 관심이 있습니다 (예 :) 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
속도와 위치 모두에 부동 소수점 (또는 고정 소수점)을 사용하고 위치를 전체 정수로 반올림하는 것보다이 솔루션을 선호하는 이유는 무엇입니까?
코드 InChaos

@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 float의 드롭 인 대체 역할을하는 고정 소수점 클래스가 있다면 그 문제가 대부분 해결되지 않습니까?
leetNightshade

@leetNightshade : 구현에 따라 다릅니다.
liori

1
실제로 문제가 존재하지 않는다고 말하고 싶습니다. 현대 네트워크 게임을 실행할 수있는 하드웨어에는 IEEE 754 수레가 없습니다. ??
Sopel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.