Menciptakan fisika gaya retro / NES dengan ketidaktepatan yang disengaja

16

Latar Belakang:

Saya mengalami masalah dalam mendapatkan kurva lompatan yang benar untuk proyek pembuatan ulang platformer retro milik saya. Gim asli untuk NES, dan kecepatan pemain disimpan dalam dua bagian terpisah: satu byte untuk seluruh angka dan satu lagi untuk bagian fraksional.

Gravity ditambahkan ke kecepatan Y pemain pada kecepatan 0,25 / frame.

Ketika pemain melompat, kecepatan Y-nya diatur ke -4.64453125. Sisa kurva lompat dibiarkan gravitasi.

Sebagai pemain naik kecepatan vertikalnya menyatu ke 0 pada tingkat 0,25 / frame. Ketika kecepatan pemain mencapai nilai kurang dari nol, bagaimanapun, kecepatan berubah mengikuti pola yang berbeda. Alih-alih menurun terus setiap 0,25 setiap frame, itu mengikuti pola ini:

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

Tampaknya ada hubungannya dengan integer overflow.

Data:

Ini adalah dump data dari aslinya. Ini adalah tabel kecepatannya.

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

Masalah:

Dalam permainan saya, saya belum bisa mencapai efek ini. Ketika kecepatan kurang dari nol, ia terus menurun secara teratur sebesar 0,25 daripada pola yang dijelaskan di atas. Alih-alih menyimpan keseluruhan dan bagian-bagian pecahan secara terpisah, saya menyimpannya bersama dalam satu pelampung.

Bagaimana efek ini bisa dicapai?

Zack Manusia
sumber
1
Sejujurnya saya hanya mengambil screenshot untuk mengetahui tinggi / panjang lompatan maksnya dalam piksel dan hanya mengubah fungsi Anda saat ini agar terlihat seperti mungkin. Anda mengatakan ketidaktepatan itu disengaja, jadi ini seharusnya tidak menyebabkan masalah?
Jonathan Connell
Saya pikir Anda perlu memposting bagian Anda mengubah kecepatan dan persis menggambarkan masalah dan kebutuhan Anda pada kode.
Ali1S232
2
@ Dapatkan apa? Dia benar-benar menggambarkan masalahnya.
Maik Semder
@maikSemder: Saya hanya ingin tahu tentang bagaimana ia mengimplementasikan mesin fisika untuk memberikan solusi berdasarkan kode-kodenya.
Ali1S232
Beri tahu saya jika Anda membutuhkan lebih banyak detail, saya tidak ingin menulis posting raksasa karena takut saya akan mendapat tanggapan dr.
Zack The Human

Jawaban:

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

Pada dasarnya Anda hanya perlu mengurangi 64 dari lowuntuk mengurangi 0,25, karena nilai 8 bit dapat memiliki nilai 256, jadi 256 * 0,25 = 64 Ketika ada underflow lowjuga kurangi 1 dari high.

Penafian: Kode ini sengaja salah ketika berbicara tentang angka negatif, ia seharusnya memodelkan anomali numerik yang dijelaskan dalam pertanyaan. Untuk alasan perbandingan, penerapan angka negatif yang tepat yang menangani kelas titik tetap dapat ditemukan di bagian bawah jawaban ini.

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 : Saya juga menambahkan konversi ke float dan dari float dan output

Output yang dihasilkan sama dengan di tabel Anda:

 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

Sebaliknya kelas titik tetap ini menangani angka negatif dengan benar:

#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
sumber
1
@ Zack ya pasti, lihat struktur posisi saya, saya menambahkan konversi ke fungsi float yang melakukan hal itu.
Maik Semder
1
@Zack juga menambahkan konversi fromFloat
Maik Semder
1
@Maik Anda pak, adalah pria terhormat. Terima kasih untuk bantuannya. Ini akan membuat saya kembali ke jalurnya.
Zack The Human
1
@Zack, sama-sama senang, membantu, terutama untuk pertanyaan yang menyenangkan :)
Maik Semder
1
@ Zack jika Anda tertarik, saya menambahkan kelas titik tetap yang menangani angka negatif dengan benar untuk perbandingan
Maik Semder