Odtwarzanie fizyki w stylu retro / NES z celową niedokładnością

16

Tło:

Mam problem z poprawieniem krzywej skoku dla mojego projektu remake'u retro platformówki. Oryginalna gra przeznaczona jest dla NES, a prędkość gracza jest przechowywana w dwóch oddzielnych częściach: jeden bajt dla liczby całkowitej i drugi dla części ułamkowej.

Grawitację dodaje się do prędkości Y gracza z szybkością 0,25 / klatkę.

Kiedy gracz skacze, jego prędkość Y jest ustawiona na -4,64453125. Reszta krzywej skoku pozostawiona jest grawitacji.

W miarę wznoszenia się gracza jego prędkość pionowa zbliża się do 0 z szybkością 0,25 / klatkę. Gdy prędkość gracza osiągnie wartość mniejszą od zera, prędkość zmienia się według innego wzoru. Zamiast stale zmniejszać się o 0,25 na każdą klatkę, stosuje się następujący wzór:

[1.75, -0.25, -0.25, -0.25, 1.75, -0.25, -0.25, -0.25, 1.75, ...]

Wygląda na to, że ma to coś wspólnego z przepełnieniem liczb całkowitych.

Dane:

Oto zrzut danych z oryginału. To jest tabela prędkości.

Jump Curve

Y-Hi Y-Lo    Decimal        Change/Frame
4    165     4.64453125     ?
4    101     4.39453125     -0.25
4    37      4.14453125     -0.25
3    229     3.89453125     -0.25
3    165     3.64453125     -0.25
3    101     3.39453125     -0.25
3    37      3.14453125     -0.25
2    229     2.89453125     -0.25
2    165     2.64453125     -0.25
2    101     2.39453125     -0.25
2    37      2.14453125     -0.25
1    229     1.89453125     -0.25
1    165     1.64453125     -0.25
1    101     1.39453125     -0.25
1    37      1.14453125     -0.25
0    229     0.89453125     -0.25
0    165     0.64453125     -0.25
0    101     0.39453125     -0.25
0    37      0.14453125     -0.25
-1   229     -1.89453125    1.75
-1   165     -1.64453125    -0.25
-1   101     -1.39453125    -0.25
-1   37      -1.14453125    -0.25
-2   229     -2.89453125    1.75
-2   165     -2.64453125    -0.25
-2   101     -2.39453125    -0.25
-2   37      -2.14453125    -0.25
-3   229     -3.89453125    1.75
-3   165     -3.64453125    -0.25
-3   101     -3.39453125    -0.25
-3   37      -3.14453125    -0.25
-4   229     -4.89453125    1.75
-4   165     -4.64453125    -0.25
-4   101     -4.39453125    -0.25
-4   37      -4.14453125    -0.25
-5   229     -5.89453125    1.75
-5   165     -5.64453125    -0.25
-5   101     -5.39453125    -0.25
-5   37      -5.14453125    -0.25
-6   229     -6.89453125    1.75

Problem:

W mojej grze nie byłem w stanie osiągnąć tego efektu. Gdy prędkość jest mniejsza od zera, nadal zmniejsza się regularnie o 0,25 zamiast wzoru opisanego powyżej. Zamiast osobnego przechowywania całości i części ułamkowych, przechowuję je razem w jednym pływaku.

Jak można osiągnąć ten efekt?

Zack The Human
źródło
1
Szczerze mówiąc, po prostu robię zrzuty ekranu, aby obliczyć jego maksymalną wysokość / długość skoku w pikselach i po prostu ulepszyć bieżącą funkcję, aby wyglądała tak, jak to możliwe. Mówisz, że niedokładność jest zamierzona, więc nie powinno to powodować problemu?
Jonathan Connell,
Myślę, że musisz opublikować część, w której zmieniasz prędkość i dokładnie opisać problem i swoją potrzebę w kodzie.
Ali1S232,
2
@Gajet co? Dokładnie opisał problem.
Maik Semder
@maikSemder: Jestem tylko ciekawy, jak zaimplementował silnik fizyki, aby dać rozwiązanie oparte na jego kodzie.
Ali1S232,
Daj mi znać, jeśli potrzebujesz więcej informacji, nie chciałem pisać gigantycznego posta, boję się, że dostanę odpowiedzi na pytania; dr.
Zack The Human

Odpowiedzi:

16
one byte for the whole number and another for the fractional part

Zasadniczo wystarczy odjąć 64 low, aby odjąć 0,25, ponieważ 8-bitowa wartość może mieć 256 wartości, więc 256 * 0,25 = 64 Gdy występuje niedopełnienie, lowodejmij także 1 high.

Uwaga: Ten kod jest celowo niepoprawny, jeśli chodzi o liczby ujemne, ma on modelować anomalie liczbowe opisane w pytaniu. Dla celów porównawczych wdrożenie właściwej liczby ujemnej obsługującej klasę punktów stałych można znaleźć na dole tej odpowiedzi.

struct velocity
{
    char high;
    unsigned char low;

    // fall -0.25
    void fall()
    {
        if(low < 64) --high;
        low -= 64;;
    }

    // convert to a float
    float toFloat() const
    {
        float ret = high;
        float frac = (float)low / 256.0f;
        if(high >= 0) ret += frac;
        else ret -= frac;
        return ret;
    }

    // convert from float
    void fromFloat(float f)
    {
        high = (char)f;
        float frac = f - high;
        low = (unsigned char)(frac * 256.0f);
    }
};

velocity v;
v.high = 4;
v.low = 165;    
for(int i = 0; i < 30; ++i)
{
    printf("%2d     %3d   %f\n", v.high, v.low, v.toFloat());
    v.fall();
}

EDYCJA : Dodałem również konwersję do float oraz z float i wyjścia

Wygenerowane dane wyjściowe są takie same jak w tabeli:

 4     165   4.644531
 4     101   4.394531
 4      37   4.144531
 3     229   3.894531
 3     165   3.644531
 3     101   3.394531
 3      37   3.144531
 2     229   2.894531
 2     165   2.644531
 2     101   2.394531
 2      37   2.144531
 1     229   1.894531
 1     165   1.644531
 1     101   1.394531
 1      37   1.144531
 0     229   0.894531
 0     165   0.644531
 0     101   0.394531
 0      37   0.144531
-1     229   -1.894531
-1     165   -1.644531
-1     101   -1.394531
-1      37   -1.144531
-2     229   -2.894531
-2     165   -2.644531
-2     101   -2.394531
-2      37   -2.144531
-3     229   -3.894531
-3     165   -3.644531
-3     101   -3.394531
-3      37   -3.144531
-4     229   -4.894531
-4     165   -4.644531
-4     101   -4.394531
-4      37   -4.144531
-5     229   -5.894531
-5     165   -5.644531
-5     101   -5.394531
-5      37   -5.144531
-6     229   -6.894531

Natomiast ta klasa punktów stałych prawidłowo obsługuje liczby ujemne:

#include <iomanip>
#include <iostream>

struct fixed_point
{
    union
    {
        struct
        {
            unsigned char low;
            signed char high;
        };
        short s;
    };

    float toFloat() const
    {
        fixed_point tmp;
        if(high < 0) tmp.s = ~s;
        else tmp.s = s;

        float ret = tmp.high;
        float frac = (float)tmp.low / 256.0f;
        ret += frac;
        if(high < 0) ret = 0 - ret;
        return ret;
    }

    void fromFloat(float f)
    {
        float tmp;
        if(f < 0.0f) tmp = -f;
        else tmp = f;

        high = (char)tmp;
        float frac = tmp - high;
        low = (unsigned char)(frac * 256.0f);

        if(f < 0.0f) s = ~s;
    }

    fixed_point operator+(const fixed_point &fp) const
    {
        fixed_point ret;
        ret.s = s + fp.s;
        return ret;
    }

    fixed_point operator-(const fixed_point &fp) const
    {
        fixed_point ret;
        ret.s = s - fp.s;
        return ret;
    }

    void print(const char *msg) const
    {
        std::cout << msg << ":" << std::endl;
        std::cout << std::hex << std::uppercase;
        // cout'ing the hex value for a char is kind of a pain ..
        unsigned int _high = 0;
        memcpy(&_high, &high, 1);
        std::cout << "  high : 0x" << std::setfill('0') << std::setw(2) << _high << std::endl;
        unsigned int _low = 0;
        memcpy(&_low, &low, 1);
        std::cout << "  low  : 0x" << std::setfill('0') << std::setw(2) << _low << std::endl;
        std::cout << "  all  : 0x" << std::setfill('0') << std::setw(4) << s << std::endl;
        std::cout << "  float: " << toFloat() << std::endl;
        std::cout << std::endl;
    }
};
Maik Semder
źródło
1
@Zack tak, zobacz moją strukturę pozycji, dodałem konwersję do funkcji float, która robi dokładnie to.
Maik Semder
1
@Zack dodał także konwersję fromFloat
Maik Semder
1
@ Maik, proszę pana, jesteś dżentelmenem. Dzięki za pomoc. To przywróci mnie na właściwe tory.
Zack The Human
1
@Zack jesteś bardzo mile widziany, cieszę się, że mogę pomóc, szczególnie w przypadku tak miłego pytania :)
Maik Semder
1
@Zack, jeśli jesteś zainteresowany, dodałem klasę punktów stałych, która poprawnie obsługuje liczby ujemne dla porównania
Maik Semder