2016-11-15 96 views
0

問題:三角/浮點問題

當使用math.h中的三角函數用一個簡單的SDL 2.0.4應用程序(自頂向下移動/旋轉未遂),我發現有一些輕微的錯誤三角函數的計算,導致'玩家'不能完全按照所面臨的方向移動,這給我帶來了很多困擾。我搜索了爲什麼會出現這種情況,主要原因似乎是浮點運算。

我使用了一個叫做libfixmath的定點數學庫 - 並且問題解決了,但只是在某種程度上。以弧度返回的90度餘弦爲0.00775146,而不是0;然而,270度的餘弦確實返回了0弧度!我必須承認這個問題讓我陷入困境,我需要一些幫助(我的數學技能不是很好,這沒有幫助)。

在定點算術中使用的變量:

double direction = 0.0; 
double sinRadians = 0.0; 
double cosRadians = 1.0; // presuming player starts facing direction of 0 degrees! 

然後 '詮釋主(INT的argc,字符* argv的[])' 涉及這些變量的部分:

if (keyDownW == true) 
    { 
     Player.setXPos(Player.getXPos() + (10 * sinRadians)); 
     Player.setYPos(Player.getYPos() - (10 * cosRadians)); // +- = - 
     cout << "Cosine and Sine (in radians): " << cosRadians << ", " << sinRadians << endl; 
     cout << direction << " degrees \n" << endl; 
    } 
    else if (keyDownS == true) 
    { 
     Player.setXPos(Player.getXPos() - (6 * sinRadians)); 
     Player.setYPos(Player.getYPos() + (6 * cosRadians)); // -- = + 
    } 
    if (keyDownA == true) 
    { 
     if (direction <= 0) 
     { 
      direction = 345; 
     } 
     else 
     { 
      direction -= 15; 
     } 
     direction = direction * (3.14159/180); // convert to radians 
     cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction))); 
     sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction))); 
     direction = direction * (180/3.14159); // convert back to degrees 
    } 
    else if (keyDownD == true) 
    { 
     if (direction >= 345) 
     { 
      direction = 0; 
     } 
     else 
     { 
      direction += 15; 
     } 
     direction = direction * (3.14159/180); // convert to radians 
     cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction))); 
     sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction))); 
     direction = direction * (180/3.14159); // convert back to degrees 
    } 

當cosRadians和sinRadians被分配時,發生的是方向(已經從度數轉換爲弧度)被轉換爲一個定點數,然後用它來分別計算餘弦和正弦,然後從一個定點數字回到分配的雙重位置,所有與你se libfixmath庫。

這裏是目前的程序(編譯爲必要的.dll文件的.exe;我靜態鏈接libfixmath庫)所以你可以看到這個問題自己:https://mega.nz/#!iJggRbxY!ySbl-2X_oiJKFACyp_kLg9yuLcEsFM07lTRqLtKCsy4

任何想法,爲什麼發生這種情況?

+0

將定點轉換分解爲單獨的步驟並查看中間值。強烈懷疑這就是你會發現精度的降低。 – doug

+2

爲什麼在弧度轉換中這樣粗略地逼近pi?與pi最接近的兩倍是3.141592653589793 –

+0

如果你只能移動NSEW,爲什麼你要使用float? – stark

回答

1

一個問題是,您正在從度數來回轉換爲弧度並返回每一幀,並且每次都會失去精度。相反,請轉換爲臨時變量,如下所示:

double direction_radians = direction * (3.14159/180); // convert to radians 
    cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction_radians))); 
    sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction_radians))); 

這樣錯誤不會累積。

另外,是否有任何理由,你不能只使用math.h中的正常sin和cos函數而不是定點版本?如果這僅僅是每幀一次對玩家進行的,它不應該是速度臨界計算。

還有一件事:播放器的x和y位置是以整數還是雙精度來存儲的?如果它是一個整數,那麼你將無法沿着你想要的精確方向移動,因爲你必須移動每幀的整數量。爲了解決這個問題,您可以爲x,y位置保留2個雙變量,並將每個幀的運動矢量添加到它們。然後通過轉換爲int來設置x和y。這會阻止你每次都失去你職位的小數部分。

例如

playerX += (10 * sinRadians); 
playerY -= (10 * cosRadians); 
Player.setXPos((int)playerX); 
Player.setYPos((int)playerY); 
+0

謝謝,我沒有意識到錯誤積累!我選擇了定點版本,主要是針對浮點的小算術問題,看看它是否有助於解決問題;看起來確實有一點,但顯然這個問題並沒有完全消失。再次感謝您的答案!我明天將實施您的解決方案,因爲目前我已經很晚了。我會回來接受你的答案然後:) –

+0

@ T.Lane你將不得不接受一些舍入誤差,因爲像pi這樣的數字根本沒有確切的定點值。訣竅是限制錯誤的積累,特別是在同一方向上的錯誤。 –

+0

是的,玩家的x和y都是整數值。你對維持2次雙打的建議從來沒有發生過,非常感謝! –