在 2D 橫向卷軸遊戲裡上下樓梯
在 2D 橫向卷軸動作類或冒險類遊戲中,其實“樓梯”並沒有想像中的常見,特別是強調平臺跳躍的 2D 遊戲中通常完全不會出現樓梯。其中一個重要原因,就是樓梯相關的人物動態實作起來並沒有想像中的簡單;但萬一遊戲規劃上或場景的合理性上需要大量使用樓梯系統呢?德國獨立遊戲團隊 DigiTales 的 Julian 前陣子分享了一篇這樣的文章,我們取得翻譯授權整理如下,供有需要的開發者參考。
文章原作:Julian Colbus 翻譯:IGDSHARE.ORG
原文連結:http://digitales.games/blog/tips/stairs-movement
遊戲連結:https://store.steampowered.com/app/1364100/Lacuna__A_SciFiNoirAdventure/
前提
上下樓梯功能長久以來對我來說猶如芒刺在背。我們在 2017 下半年時首度把這功能實作進當時的原型裡,而這些程式碼直到前一陣子都沒有什麼改變。就算以功能原型的角度來看,那個實作僅勉強堪用,當然也不該保留到遊戲上市版本之中。
不過呢,正因為它造成許多程式臭蟲並凸顯出了各種容易踩到的坑,我現在可以跟各位談談在設計自己的系統時,該注意哪些地方。我說“設計”是因為我會著重在遊戲設計的部份,關於程式怎麼寫只會大概提一下概念,因此就不把實際的程式碼拿出來講了。不然這篇本來就已經夠長的文章包準會一發不可收拾。
首先我要先快速說明我們移動系統的重點功能。如果你的需求完全不一樣,那接下來的解決方案可能對你不太適用。
- 遊戲本身是人物可以行走和衝刺的 2D 橫向卷軸,不能跳躍和奔跑,也就是說人物只能從底端和頂端兩處走進樓梯。
- 在樓梯移動時有專屬的動畫,不沿用平時的水平行走和衝刺。
- 樓梯有兩種:正向梯(直立)和側向梯(對角方向)。後者可再分為“左上至右下”與“左下至右上”兩種方向。
- 樓梯每一階的大小相同,以配合各種玩家角色動畫。樓梯長度不拘。
- 人物模組的碰撞區(與樓梯動作有關)在大腿附近。不過從概念上來說,這並不是什麼必要的條件。
- 沒有戰鬥和其他因素會對在樓梯上移動的人物施加外力。
- 設計目標是為了做出直觀、不易出錯又賞心悅目的樓梯動作。
瞭解所有前提之後,我們這就來開始疊代,把冒出來的問題一個個解決掉吧!首先從側向梯(對角方向)開始。各位之後會發現我們即將設下的原則大多也適用於正向梯(直立)。
第一步:走上樓梯
首要之務就是讓人物登上樓梯。我們先做出非常基本的第一代樓梯:當玩家即將通過樓梯時,會先觸發一個碰撞區,將人物切換為樓梯上的操控方式(我們稱之為“穿過檢查點”,穿過一詞與後面需要的功能相關,詳後述)。樓梯的另一端則有另一個檢查點切換回一般操控。
人物的碰撞區為綠色,穿過檢查點為淺藍色。
第一個要解決的問題乍看之下可能覺得很難發生,但實際上相當重要,需要及早考慮:人物碰撞區可能不會在正確的時機觸發檢查點,尤其在低影格率、而玩家移動速度又和影格率脫鉤的時候(當然,遊戲邏輯與影格率理應脫鉤)。當影格率比較低的時候,每張畫面之間的移動距離就會變大,而這個距離越大,玩家人物就越容易衝過檢查點。
在影格率高的情況下,人物會逐漸接近並在適當距離觸發檢查點:
然而在低影格率時,可能會造成人物碰撞區的尾巴去觸發檢查點:
這會導致行走動畫和樓梯影像不同調。因此我們在第二代樓梯中,必須要從接下來的兩種解法中擇一實作:
- 單純地在開始或結束樓梯上移動的瞬間,將玩家人物傳送到正確的“進入點”或“離開點”位置。因為傳送的距離只會在低影格率的情況下才比較大,當遊戲已經執行不太順暢的時候,這個傳送效果也不會太突兀。
- 讓物理運算與畫面繪製脫鉤,這樣自然就不會遇到影格率問題,因為碰撞的邏輯與運算與畫面更新切開了。如果你使用 Unity,FixedUpdate() 就可以處理這個問題,它每秒都會執行固定的次數,而不受影格率影響(預設為每秒 50 次,這個值可以調整)。
不論你使用兩者中哪個方法(第二個比較推薦),在本文中會保持使用“進入點”與“離開點”這兩個用詞,畢竟不論方法為何,它們在概念上仍然是相通的。
此處箭頭表示在兩連續影格之間的物體移動
現在讓我們來討論下一個比較明顯的問題。要是玩家想要能穿過樓梯呢?我們的設計到目前為止,人物一走到碰撞區就會走進樓梯。
第三代解決這個問題的做法,是在樓梯入口定義一個不同的碰撞區(稱為“進入範圍”)。只有在樓梯底端按住方向鍵上或在頂端按住方向鍵下,人物才會開始上下樓梯。左右移動只會讓人物穿過進入點,繼續一般的水平移動。
現在問題變成了一旦玩家在進入範圍內按住上或下,人物就會在當下所在位置直接開始進行上下樓梯的動作,而非從確切的進入點開始。就算他們先傳送到進入點(我們在第二代中做了這個功能),也會因為進入範圍太寬,而使傳送效果過於顯目:
為了解決這個問題,我們在第四代讓玩家於進入範圍內按住上或下時,人物會走到實際的進入點。例如當人物在樓梯底端入口的右側時,按住上會先讓人物走到進入點,然後才上樓梯。為了這個目的,我們需要增加一些只有“玩家人物在進入範圍內”條件成立時,才會有作用的額外操作方式。
圖內的箭頭顯示玩家目前按下的按鍵
當然,現在我們不再需要傳送了,但也正因為玩家可以穿過樓梯,就導致從另一方向接近樓梯進入點時,人物從一般走路動畫切換到上樓梯動畫不自然的問題。所以到了第五代,我們把進入範圍和穿過檢查點結合起來!檢查點需要更新成能視玩家人物接近的方向來調整位置,好讓人物不論從哪邊接近,都能在恰好的位置觸發檢查點。
總合運作起來如下:若玩家在進入範圍內按住上/下,人物會走向進入點,若在人物碰撞區碰到檢查點的時候,方向鍵仍是按住的,就會開始進入到在樓梯上移動的狀態。
注:穿過檢查點隨時都會依據玩家接近的方向,切換到與玩家相對的那一側,才能讓人物的碰撞區與檢查點在完全正確的時機接觸,並開始在樓梯上的移動。
現在玩家可以穿過每一個樓梯的進入點了。但要是上下樓梯是繼續向左或向右的唯一途徑呢?如果我們有時候就是要第一代的運作方式,強迫朝著樓梯按住左或右的玩家進入樓梯呢?依照你的關卡編排方式,某些樓梯可能是朝某方向前進的唯一通路,但其他樓梯僅是分支的通路。
因此,我們在第六代分出了兩種樓梯進入點(“可穿過”與“不可穿過”)。為此我們加入了一個叫做“可穿過”的布林值,供我們個別套用在每一道樓梯物件的兩端上。若某個進入點為不可穿過,那麼穿過檢查點就不會動態換邊,因為該進入點只能從一邊接近。
新增這項設計後,我們必須將玩家在意圖進入不同型別的樓梯時,可能會使用的按鍵和按鍵組合全都納入考量。在樓梯底端按上,和在樓梯頂端按下,至此已都有預期中的效果了。不過,當從左側走向不可穿過的樓梯時,“按住右”也會是玩家表達進入樓梯意圖的行為(反之亦然),因為這種樓梯不能穿過。現在當玩家觸發穿過檢查點的時候,只要按住下面個別狀況中的特定按鍵時,人物就會走上樓梯:
此例中的樓梯若是可穿過,玩家就必須按住上才會進入樓梯,或是按左/右穿過樓梯。
在可穿過的樓梯頂端也是如此,按住下進入,按住左 / 右是穿過。
如果是不可穿過,那按住右和上都會視為玩家有上樓梯的意圖。
在不可穿過的樓梯頂端同理,按住左和下都會走下樓梯。
這操作起來可能已經感覺不差了,但根據玩家操控的實作方式,如果讓玩家在走上樓梯時有好幾種按鍵組合可按,那這又會衍生出另一類的問題了。
為了要在第七代搞定這些事情,我們必須在不同型別的樓梯進入點處定義哪些按鍵會互相取消,還有哪些按鍵一起按不能把移動速度疊加上去。我們也必須一個個設定動畫,如果有兩個按鍵會互相取消(例如玩家同時按住左和右),那不該造成人物模型原地走動。為了解決所有可能的問題,關鍵就在於區分上行梯(左下到右上)和下行梯(左上到右下)。舉例來說吧,若玩家在下行梯的範圍內按住左(遠離樓梯)和下(在本例中視同走向樓梯),可能就會導致“原地踏步”的動畫。又或者他們會為了移動速度加倍而按住下(走向樓梯)和右(也是走向樓梯)。我們必須將所有型別樓梯可能會遇上的所有按鍵組合都想清楚,才不會捅出麻煩來。也許各位已經自行想出了能解決這個問題的精妙系統,不然就和我一樣,得在實際的移動邏輯之外手動定義一籮筐的布林組合。
在這情況中,按住左、按住下、還有按住左+下都應該要有同樣的結果。
下+右和左+右都彼此衝突。這兩種組合都會讓人物停止。
我呼籲各位千萬不要在這個步驟偷懶,一定要算到所有小細節。這不只能避免出錯,也能讓移動操控變得更直觀。
好啦。走上側向梯的部分搞定了。那正向梯的部分有什麼特殊規則嗎?
其實我們只需要加入一些小調整,就可以在第八代加入正向梯了,因為基本的邏輯都通用。在我們的遊戲中,做法是讓正向梯只有中間一小段寬度可以使用,而非整個樓梯面。因此,它們有兩個位置明確的進入點,就和側向梯一樣。這也代表玩家隨時可以穿過前梯,意即它們的頂端和底端都是可穿過的,因為樓梯影像的寬度必定比進入範圍還寬。當然了,在某些情況下玩家可能穿過正向梯後就立刻遇到牆壁而無法前進。其餘部分(走到樓梯上的行為)運作模式如出一轍。更棒的是,我們不需要去顧慮玩家按住左 / 右來嘗試走上正向梯的狀況。
在進入範圍內按住上會讓人物走向進入點並開始走上樓梯。
在進入範圍內按住下會讓人物走向進入點並開始走下樓梯。
第二步:樓梯上的走動恭喜你走到這一步了,值得掌聲鼓勵一下。我發誓,進入樓梯是這個系統最複雜的部分。現在我們就來定義在各類樓梯上的移動吧。
根據你遊戲的設定(例如你匯入寫實的物理和重力),以垂直方式或對角線方式向上移動可能會造成一些問題,比如說你的人物從上方一走進樓梯就會滑下去。因此到了第九代,我們必須強迫人物在樓梯上貼著一直線線段來移動,而不使用物理碰撞來決定人物的移動或站立的位置。事實上,我們需要在人物進入樓梯時取消所有作用中的力,同時忽略所有在樓梯上期間被施加的力。好在我們的遊戲不用考慮戰鬥,或其他會對人物在樓梯上移動時施加外力的因素。
但我們要怎麼確保玩家移動的角度一定能走到底端或頂端?我們可以計算頂端和底端之間的角度,不過既然為了配合人物動畫,樓梯的每一階都已經一樣大了,所以不管是長是短,我們的側向梯都會是同樣的角度,那麼我們只要寫死就行了。
角度顯示為藍色。它不是碰撞區,只存在於程式碼中。
好啦,那我們的移動操控要怎麼在樓梯上運作?就到第十代來設計一下吧。玩家會預期上、右和上+右都應該要能走上樓(下樓梯時則相反)。記得不能疊加速度。
再一次地,我們也得考慮會互消的按鍵組合。例如上樓的時候,左和下是一樣的,都應該要能取消視為同樣的上和右。
那正向梯上的操控方式呢?就靠第十一代來搞定!
各位可能覺得按上和下不就沒事了嗎。然而迫使玩家要先按住上或下走到樓梯最末端後,才能左右移動,不僅反直覺,也讓人感覺遊戲不精緻。玩家也可能會想,為什麼在離頂端或底端還差 1 畫素的地方按下左或右,卻是一點反應也沒有。因此我們必須分別在底部和頂部 1 公尺的地方定義“離開範圍”。玩家在離開範圍內按住左或右就會朝著樓梯的“離開點”移動。在抵達離開點後,他們就會走出樓梯並繼續朝著他們按住的方向走過去。也許各位注意到了,這就和進入範圍運作的方式完全一樣,讓玩家得以使用另外的按鍵走向進入點(也就是我們在很前面第三和第四代加入的功能)。還有,我們得考慮會互相取消或疊加速度的按鍵組合。
藍色為樓梯頂和底的離開範圍。各位可以用碰撞區來處理,不過我們是從人物和最近的進出點的距離來算。
加入離開範圍後感覺精緻了許多,但這樣做也另外產生了一些問題,必須使用第十二代來解決。舉例來說,如果前梯只有 2 公尺或是更短,那玩家同時處於頂端和底端的離開範圍怎麼辦?那就把離開範圍設為樓梯總長的 20%,不要寫死成 1 公尺。
樓梯短,離開範圍就短
這樣解決了樓梯短的問題,但樓梯很長的時候又出了新問題,因為 20% 的離開範圍實在太長了。所以我們更新了十二代的規則,在第十三代中限制兩端離開範圍最長各為 1 公尺。
第三步:離開樓梯
就快完成了!現在進入樓梯和沿著樓梯走都很順利,離開樓梯其實也滿簡單的。
至前述設計為止,我們的人物會一直延續樓梯上的移動,直到永遠:
在第十四代,我們設定了碰撞區讓玩家觸發的時候可以離開樓梯(也就是回到正常的行走操控,重新施加物理計算)。我們其實重複利用了頂端和底端的進出範圍碰撞區:
唯一的問題在於碰撞區過早觸發,在頂端的時候尤其嚴重,這是因為人物碰撞區高度的關係。我們把頂端的碰撞區上挪至足夠高度、也將底端碰撞區下移一點,讓人物上下樓梯到最末端的時候正好在碰撞區裡面。
可是,玩家在進入樓梯的過程中,人物還是在碰撞區裡,那他們可以決定不要上樓梯,在不再次觸發碰撞區的情況下穿過離開點嗎?當然可以。在第十五代中,我們用和進入樓梯同樣的方式來解決:我們替所有例子(側向下梯/側向上梯/正向梯)一個一個定義離開樓梯的意圖,如同第六代那樣。當玩家在離開範圍內按下任一個合理的按鍵組合,他們就會離開樓梯。
到了第十六代,我們再次考慮低影格率時,可能會導致玩家衝過離開點的情況。如果你已經使用第二代時提過的,與影格率脫鉤的物理系統,這就不會是問題。若否,我們將人物傳送到離開點,就如同傳送到進入點那樣:
大功告成了!只需少少的十六次疊代,我們就成功做出了直觀、不易出錯又賞心悅目的樓梯動作。
在影片錄製後,我們又稍微改善了這個系統。我們改成使用 FixedUpdate() 而非較容易被注意到的傳送方式,並新增了在正向梯上站著不動的閒置動畫,它會根據先前移動方向而有所變化(面向或背對玩家視點)。
當然我們也很清楚,和那些程式大佬以 AI 為基礎所開發的 3D 複合地形移動系統比起來,這種移動系統根本就是小巫見大巫。不過這套系統我們用起來很順,應該也能輕鬆在任何類似的 2D 橫向卷軸遊戲中實作出來。各位若是覺得它還能更簡化、更強化、不管什麼化之類的,都歡迎提出來和我們交流交流。我們很樂意改善我們的程式和這篇指南,為其他開發者提供助益。
希望這篇文章有寓教於樂的功用,或至少能點出一個道理:看似平淡無奇的機制實作起來可能相當棘手。
作者:Julian Colbus
譯者:IGDSHARE.ORG
來源:Indienova
地址:https://indienova.com/indie-game-development/tips-stairs-movement/
【編譯自http://digitales.games/blog/tips/stairs-movement】
相關文章
- 橫向卷軸探險遊戲《Animal Well》開發者分享創作故事遊戲
- 上樓梯演算法演算法
- 谷歌子公司推出新款機器人,可以爬樓梯擦樓梯谷歌機器人
- [演算法] 一、爬樓梯演算法
- leetcode 70 爬樓梯LeetCode
- [Python手撕]爬樓梯Python
- (39/60)DP基礎、斐波那契數、爬樓梯、用最小花費爬樓梯
- JavaScript原生實現樓梯外掛JavaScript
- 樓梯導航/線上諮詢特效特效
- 面試官在“逗”你係列:到底應該怎麼爬樓梯?!面試
- LeetCode 70題 爬樓梯 -- JavaScriptLeetCodeJavaScript
- [CareerCup] 9.1 Climbing Staircase 爬樓梯AI
- 746. 使用最小花費爬樓梯
- 橫板卷軸模式下的多人團戰競技? 這款騰訊專家看好的遊戲是這樣做的模式遊戲
- 別小看那些在破樓裡做遊戲的老哥遊戲
- 我用演算法學golang(爬樓梯)演算法Golang
- 爬樓梯(LintCode Climbing Stairs)AI
- John Harris:解析遊戲道具設計之藥劑和卷軸遊戲
- 對應用軟體:遊戲-《上古卷軸5》的點評遊戲
- 在遊戲裡拍照、造樓、飆演技,並不僅僅是一點個人愛好遊戲
- LeetCode每日一題:爬樓梯(No.70)LeetCode每日一題
- 使用 JavaScript 解決經典爬樓梯問題JavaScript
- C++遞迴演算法之爬樓梯C++遞迴演算法
- [Python手撕]使用最小花費爬樓梯Python
- LCR 088. 使用最小花費爬樓梯
- 程式碼隨想錄day32 || 509 斐波那契數列,70 爬樓梯,746 最小代價爬樓梯
- Climbing Stairs 爬樓梯問題,每次可以走1或2步,爬上n層樓梯總方法 (變相fibonacci)AI
- LeetCode-746 使用最小花費爬樓梯LeetCode
- excel怎麼橫向自動求和 excel表格橫向求和公式Excel公式
- Android自定義View-卷軸AndroidView
- 橫向Gridview使用View
- 縱向控制的橫向滾動
- 讓我們一起啃演算法----爬樓梯演算法
- 增補部落格 第十九篇 python 爬樓梯Python
- 字梯遊戲(C++)遊戲C++
- HTML橫向導航欄HTML
- 資料夾橫向排版
- Python演算法:如何解決樓梯臺階問題Python演算法