Ricreare la fisica in stile retrò / NES con imprecisione intenzionale


16

Sfondo:

Sto avendo problemi a ottenere la curva di salto corretta per un mio progetto di remake platform retrò. Il gioco originale è per il NES e la velocità del giocatore è memorizzata in due parti separate: un byte per il numero intero e un altro per la parte frazionaria.

La gravità viene aggiunta alla velocità Y del giocatore ad una velocità di 0,25 / frame.

Quando il giocatore salta, la sua velocità Y è impostata su -4,64453125. Il resto della curva di salto è lasciato alla gravità.

Mentre il giocatore sale la sua velocità verticale converge a 0 a una velocità di 0,25 / frame. Quando la velocità del giocatore raggiunge un valore inferiore a zero, tuttavia, la velocità cambia seguendo uno schema diverso. Invece di diminuire costantemente di 0,25 ogni fotogramma, segue questo schema:

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

Sembra avere qualcosa a che fare con l'overflow di numeri interi.

Dati:

Ecco un dump dei dati dall'originale. È una tabella della velocità.

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

Problema:

Nel mio gioco non sono stato in grado di ottenere questo effetto. Quando la velocità è inferiore a zero, continua a diminuire regolarmente di 0,25 anziché lo schema sopra descritto. Invece di conservare le parti intere e frazionarie separatamente, le sto conservando insieme in un singolo galleggiante.

Come si può ottenere questo effetto?


1
Ad essere sincero, prenderei screenshot per calcolare la sua altezza / lunghezza di salto massima in pixel e modificare la tua funzione attuale per assomigliare il più possibile. Dici che l'imprecisione è intenzionale, quindi questo non dovrebbe causare problemi?
Jonathan Connell,

Penso che devi pubblicare la parte che stai modificando la velocità e descrivere esattamente il problema e le tue esigenze sul codice.
Ali1S232,

2
@ Gajet cosa? Ha descritto esattamente il problema.
Maik Semder,

@maikSemder: sono solo curioso di sapere come ha implementato il motore fisico per fornire una soluzione basata sul suo codice.
Ali1S232,

Fammi sapere se hai bisogno di maggiori dettagli, non volevo scrivere un post gigantesco per paura che avrò le risposte del dott.
Zack The Human,

Risposte:


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

Fondamentalmente devi solo sottrarre 64 da lowper sottrarre 0,25, perché un valore di 8 bit può avere 256 valori, quindi 256 * 0,25 = 64 Quando c'è un underflow lowanche sottrai 1 da high.

Disclaimer: questo codice è intenzionalmente errato quando si tratta di numeri negativi, si suppone che modelli le anomalie numeriche descritte nella domanda. Per motivi di confronto, l'implementazione di un numero negativo adeguato che gestisce la classe a virgola fissa è riportata in fondo a questa risposta.

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

EDIT : ho anche aggiunto la conversione in float e da float e l'output

L'output generato è lo stesso della tabella:

 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

Al contrario, questa classe a virgola fissa gestisce correttamente i numeri negativi:

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

1
@Zack sì certo, vedi la mia struttura di posizione, ho aggiunto una conversione alla funzione float che fa esattamente questo.
Maik Semder,

1
@Zack ha anche aggiunto una conversione fromFloat
Maik Semder l'

1
@Maik signore, siete un gentiluomo. Grazie per l'aiuto. Questo mi riporta in pista.
Zack The Human,

1
@Zack sei il benvenuto, felice di aiutarti, specialmente per una domanda così bella :)
Maik Semder

1
@Zack nel caso tu sia interessato, ho aggiunto una classe a virgola fissa che gestisce correttamente i numeri negativi per il confronto
Maik Semder
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.