Neuerstellung der Physik im Retro- / NES-Stil mit absichtlicher Ungenauigkeit

16

Hintergrund:

Ich habe ein Problem damit, die Sprungkurve für ein Retro-Platformer-Remake-Projekt von mir zu korrigieren. Das ursprüngliche Spiel ist für das NES und die Geschwindigkeit des Spielers wird in zwei getrennten Teilen gespeichert: einem Byte für die ganze Zahl und einem anderen für den Bruchteil.

Die Schwerkraft wird zur Y-Geschwindigkeit des Spielers mit einer Rate von 0,25 / Frame addiert.

Wenn der Spieler springt, ist seine Y-Geschwindigkeit auf -4,64453125 eingestellt. Der Rest der Sprungkurve bleibt der Schwerkraft überlassen.

Wenn der Spieler aufsteigt, konvergiert seine vertikale Geschwindigkeit mit einer Geschwindigkeit von 0,25 / Bild gegen 0. Wenn die Geschwindigkeit des Spielers einen Wert unter Null erreicht, ändert sich die Geschwindigkeit jedoch nach einem anderen Muster. Anstatt sich mit jedem Frame stetig um 0,25 zu verringern, folgt es diesem Muster:

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

Es scheint etwas mit Integer-Überlauf zu tun zu haben.

Daten:

Hier ist ein Dump der Daten aus dem Original. Es ist eine Tabelle der Geschwindigkeit.

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:

In meinem Spiel konnte ich diesen Effekt nicht erzielen. Wenn die Geschwindigkeit kleiner als Null ist, nimmt sie statt des oben beschriebenen Musters regelmäßig um 0,25 ab. Anstatt die gesamten und gebrochenen Teile getrennt zu lagern, lagere ich sie zusammen in einem einzigen Schwimmer.

Wie kann dieser Effekt erzielt werden?

Zack der Mensch
quelle
1
Um ehrlich zu sein, mache ich einfach Screenshots, um seine maximale Sprunghöhe / Sprunglänge in Pixeln zu ermitteln und ändere einfach deine aktuelle Funktion, um sie so ähnlich wie möglich zu gestalten. Sie sagen, dass die Ungenauigkeit absichtlich ist, also sollte dies kein Problem verursachen?
Jonathan Connell
Ich denke, Sie müssen den Teil, in dem Sie die Geschwindigkeit ändern, posten und das Problem und Ihren Bedarf im Code genau beschreiben.
Ali1S232
2
@Gajet was? Er hat das Problem genau beschrieben.
Maik Semder
@maikSemder: Ich bin nur neugierig, wie er die Physik-Engine implementiert hat, um eine Lösung zu finden, die auf seinem Code basiert.
Ali1S232
Lassen Sie mich wissen, wenn Sie weitere Informationen benötigen. Ich wollte keinen riesigen Beitrag verfassen, aus Angst, dass ich die richtigen Antworten bekomme.
Zack The Human

Antworten:

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

Grundsätzlich müssen Sie nur 64 von lowsubtrahieren, um 0,25 zu subtrahieren, da ein 8-Bit-Wert 256 Werte haben kann, also 256 * 0,25 = 64 Wenn ein Unterlauf vorliegt, lowsubtrahieren Sie auch 1 von high.

Haftungsausschluss: Dieser Code ist absichtlich falsch, wenn es um negative Zahlen geht. Er soll die in der Frage beschriebenen numerischen Anomalien modellieren. Zu Vergleichszwecken finden Sie am Ende dieser Antwort die Implementierung einer korrekten Negativzahl für die Behandlung von Festkommaklassen.

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 : Ich habe auch die Konvertierung in float und from float und die Ausgabe hinzugefügt

Die generierte Ausgabe ist dieselbe wie in Ihrer Tabelle:

 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

Im Gegensatz dazu behandelt diese Festkommaklasse die negativen Zahlen richtig:

#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
quelle
1
@Zack ja sicher, siehe meine Positionsstruktur, ich habe eine Konvertierung in Float-Funktion hinzugefügt, die genau das macht.
Maik Semder
1
@Zack fügte auch eine fromFloat-Konvertierung hinzu
Maik Semder
1
@Maik Sie Sir, sind ein Gentleman. Danke für die Hilfe. Das bringt mich wieder auf Kurs.
Zack The Human
1
@Zack ihr seid herzlich willkommen, freut euch zu helfen, besonders bei solch einer netten Frage :)
Maik Semder
1
@Zack, falls Sie interessiert sind, habe ich eine Festkomma-Klasse hinzugefügt, die die negativen Zahlen zum Vergleich richtig behandelt
Maik Semder