สร้างฟิสิกส์สไตล์เรโทร / NES ขึ้นใหม่ด้วยความไม่แม่นยำโดยเจตนา


16

พื้นหลัง:

ฉันมีปัญหาในการทำให้การกระโดดโค้งถูกต้องสำหรับโปรเจ็กต์ retro platformer ของฉัน เกมดั้งเดิมมีไว้สำหรับ NES และความเร็วของผู้เล่นจะถูกเก็บไว้ในสองส่วน: หนึ่งไบต์สำหรับจำนวนเต็มและอีกส่วนสำหรับเศษส่วน

Gravity ถูกเพิ่มเข้ากับความเร็ว Y ของผู้เล่นในอัตรา 0.25 / เฟรม

เมื่อผู้เล่นกระโดดความเร็ว Y ของเขาถูกตั้งไว้ที่ -4.64453125 ส่วนที่เหลือของเส้นโค้งการกระโดดจะถูกปล่อยให้อยู่กับแรงโน้มถ่วง

ในขณะที่ผู้เล่นเพิ่มความเร็วแนวตั้งของเขาให้เป็น 0 ในอัตรา 0.25 / เฟรม เมื่อความเร็วของผู้เล่นถึงค่าน้อยกว่าศูนย์อย่างไรก็ตามความเร็วจะเปลี่ยนไปตามรูปแบบที่แตกต่างกัน แทนที่จะลดลงเรื่อย ๆ 0.25 ทุกเฟรมมันจะเป็นไปตามรูปแบบนี้:

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

ดูเหมือนว่าจะมีบางอย่างเกี่ยวข้องกับจำนวนเต็มล้น

ข้อมูล:

นี่คือดัมพ์ของข้อมูลจากต้นฉบับ มันเป็นตารางความเร็ว

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

ปัญหา:

ในเกมของฉันฉันไม่สามารถบรรลุผลนี้ เมื่อความเร็วน้อยกว่าศูนย์มันก็จะลดลงเรื่อย ๆ 0.25 แทนที่จะเป็นรูปแบบที่อธิบายไว้ข้างต้น แทนที่จะเก็บชิ้นส่วนทั้งหมดและเศษส่วนแยกกันฉันกำลังเก็บพวกมันไว้ด้วยกันในทุ่นเดียว

ผลกระทบนี้จะเกิดขึ้นได้อย่างไร?


1
ความจริงแล้วฉันแค่ถ่ายภาพหน้าจอเพื่อคำนวณความสูง / ความยาวกระโดดสูงสุดของเขาในหน่วยพิกเซลและเพียงแค่ปรับแต่งฟังก์ชั่นปัจจุบันของคุณ คุณบอกว่าความไม่แน่ชัดนั้นมีเจตนาดังนั้นสิ่งนี้ไม่ควรทำให้เกิดปัญหา?
Jonathan Connell

ฉันคิดว่าคุณต้องโพสต์ส่วนที่คุณกำลังเปลี่ยนความเร็วและอธิบายถึงปัญหาและความต้องการของคุณในรหัส
Ali1S232

2
@Gajet อะไรนะ? เขาอธิบายปัญหาอย่างแน่นอน
Maik Semder

@ maikSemder: ฉันแค่อยากรู้เกี่ยวกับวิธีที่เขาใช้เครื่องมือฟิสิกส์เพื่อให้แก้ปัญหาตามรหัสของเขา
Ali1S232

แจ้งให้เราทราบหากคุณต้องการรายละเอียดเพิ่มเติมฉันไม่ต้องการเขียนโพสต์ยักษ์เพราะกลัวฉันจะได้รับ tl; dr การตอบสนอง
Zack The Human

คำตอบ:


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

โดยทั่วไปคุณเพียงแค่ต้องลบ 64 จากlowเพื่อลบ 0.25 เพราะค่า 8 บิตสามารถมี 256 ค่าได้ดังนั้น 256 * 0.25 = 64 เมื่อมีอันเดอร์โฟล์ในการlowลบ 1 highด้วย

Disclaimer:รหัสนี้เป็นความผิดโดยเจตนาเมื่อมันมาถึงจำนวนลบมันควรจะเป็นแบบจำลองความผิดปกติของตัวเลขที่อธิบายไว้ในคำถาม สำหรับเหตุผลในการเปรียบเทียบการนำตัวเลขลบที่เหมาะสมไปใช้ในการจัดการคลาสพ้อยท์คงที่สามารถพบได้ที่ด้านล่างของคำตอบนี้

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

แก้ไข : ฉันยังเพิ่มการแปลงเพื่อลอยและจากการลอยและการส่งออก

ผลลัพธ์ที่สร้างขึ้นเหมือนกับในตารางของคุณ:

 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

ในทางตรงกันข้ามคลาสพ้อยท์คงที่จะจัดการกับจำนวนลบอย่างเหมาะสม:

#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
@ แซคใช่แน่นอนดูโครงสร้างตำแหน่งของฉันฉันเพิ่มการแปลงลงในโฟลว์ฟังก์ชั่นซึ่งทำเช่นนั้น
Maik Semder

1
@Zack ยังได้เพิ่มการแปลงจาก Float
Maik Semder

1
@ Maik คุณเป็นสุภาพบุรุษ ขอบคุณสำหรับความช่วยเหลือ สิ่งนี้จะพาฉันกลับมาติดตาม
Zack The Human

1
@Zack คุณมากยินดีต้อนรับยินดีที่จะช่วยเหลือโดยเฉพาะอย่างยิ่งสำหรับคำถามที่ดีเช่น :)
Maik Semder

1
@Zack ในกรณีที่คุณมีความสนใจฉันเพิ่มระดับจุดคงที่จับตัวเลขที่ติดลบอย่างถูกต้องสำหรับการเปรียบเทียบ
Maik Semder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.