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


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.
  • 일반적인 구현 여부에 따라 화면의 사분면에 대한 여러 가지 사례를해야합니다 dx및 dy긍정적, 부정적, 또한인지 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.