在遊戲中會經常遇到斜坡地形,比如衆所周知的魂鬥羅,角色可以在坡上移動和跳躍:
斜坡在2D遊戲中很常見,處理起來也較爲棘手。最初我打算用分離軸定律來實現,在建立了一個物理模型之後:
發現上坡時沒什麼問題,但下坡時有比較明顯的彈跳,這並不是預期的結果,因爲我想讓物體「粘」在坡上。產生這個問題的根本原因是物體的運動分爲兩個階段,
首先是水平方向的移動,最後是垂直方向的移動:
如果X軸和Y軸的速度沒有一定的方式關聯起來,就會產生跳躍問題。根據力的平行四邊形法則,物體的運行軌跡應該像這樣(以向右下坡爲例):
接下來要做的工作就是計算出xspeed
和yspeed
。
爲了簡單起見,先將斜坡統一設置成45°,即等邊直角三角形,此時只需要將Y軸速度設置爲與X軸速度相同即可。
由上圖可以發現,當x與y相等時,角色會貼着斜坡下降,而如果x軸的速度比y軸快,角色就會脫離斜坡,此時再加上y軸的重力加速度,將會出現上述跳躍的效果。
當然這裏不是說跳躍就是錯誤的,有些遊戲也會需要這種效果,只是在當前場景下讓角色「粘」在坡上更符合需求。
任意角度的斜坡速度處理
45°斜坡只是理想情況,實際上開發者可能要面對各種角度的斜坡,這裏的「各種角度」是指小於90°,因爲等於90°就相當於一面牆了。現在假設角色要在斜坡上移動的距離爲distance
(一般將其賦值爲角色的水平速度speedX),那麼其水平分量爲
vx = Math.cosθ * distance;
然後令
vy = vx;
即可得出在斜坡上速度的水平和垂直分量。由於現在的水平速度是平時速度的分量,所以在斜坡上移動的速度比在平地時要慢一些。
如果不記得三角函數,可以通過下圖複習一下:
上述方法都是在θ角已知的情況下展開討論的,如果不知道θ角也沒關係,它可以通過斜面的法線與垂直方向的夾角計算出來。
至此角色在下坡時跳躍的問題已經有了初步的解決方案。
現在還有一個關鍵問題需要解決,就是判斷角色何時處於斜坡上,在斜坡上的水平移動使角色有一段時間是脫離斜坡的,必須讓程序知道這個階段的角色仍處於斜坡之上而不是離開了。
//Pseudocode function checkCollision() { player.onSlope = false; if(checkPlayerOnSlope()) { player.onSlope = true; } if(player.onSlope) { vy = vx = Math.cosθ * distance; //計算角色在斜坡上的座標... } }
爲實現上面僞代碼中的checkPlayerOnSlope
這個方法,需要做一些額外的準備工作。
光線投射法
光線投射法(RayCasting)是一種遊戲中常見的碰撞檢測手段。可以想象成一個點朝某個方向發出光線,直到光線擊中或穿過待測目標。在光線投射過程中,如果記錄下其穿過的Tiles,就會得到一個結果集,用這個結果集來分析判斷角色所處的位置。現在假設某條光線穿過的區域如下圖所示:
藍色線段所佔的區域用紅色表示,但還不夠精確,預期得到的結果應該像這樣:
這裏需要用到Bresenham畫線算法(Bresenham's line algorithm)。計算機在畫一條直線時,是通過像素來表現的,當像素點密集後,肉眼就看不出來了。
(圖片來自wikipedia)
將該線段上所有點的座標計算出來,觀察其位於哪個Tiles內,最後就能得到線段經過的Tiles集合。
現在爲角色的腳部添加3條光線:
如果其中一條經過的Tiles有斜坡,那就說明角色位於斜坡上。
斜坡物理
當角色位於斜坡上與在平地時不一樣,應該忽略重力以及其它方向上的力的影響,否則會干擾其「粘」在斜坡上。角色位於斜坡上的y座標很容易根據下圖求出:
A點的y座標爲BA'。左右移動時,根據角色的x座標即可求出對應的y座標slopeY
,並一直令
player.y = slopeY;
直到離開斜坡。
在斜坡上忽略重力影響並不意味着角色不能進行跳躍,觸發跳躍時讓角色強制離地即可。
更新日誌
2017/04/09 更新角色跳躍
2017/04/21 更新角色衝刺
2017/05/01 更新角色狀態機
2017/05/16 更新角色攻擊動畫
2017/05/22 更新角色移動攻擊動畫
2017/05/24 更新角色跳躍攻擊動畫
2017/06/04 更新地圖繪製
2017/06/22 更新攝像機、長距離衝刺
2017/07/01 更新指令技
2017/07/06 更新蓄力技
2017/07/12 更新wall jump
2017/10/13 更新斜坡地形