從移動和跳躍開始,做好 2D 平台跳躍遊戲的角色控制 _BB001
2023-07-22 17:54:57
7.3k words / 25-minute read
目錄 Table of contents
  1. 前言
  2. 只是個 2D 遊戲的角色控制是能多複雜?
  3. 移動與跳躍?是能有多複雜?
  4. 1. 水平移動的速度參數
    1. 幾款經典橫向遊戲的參數
      1. Castlevania 惡魔城
      2. Super Meat Boy
      3. Super Mario Bros 超級瑪利歐兄弟
      4. LIMBO
      5. 轉向加速度(Turn Acceleration)
      6. 數值表格
    2. 使用更好的單位來表達 – 時間(秒數)
  5. 2. 跳躍速度、高度與重力(Gravity)
    1. 幾款經典橫向遊戲的參數
      1. Super Meat Boy
      2. Super Mario Bro
      3. LIMBO
      4. 數值表格
  6. 3. 可控制的跳躍高度(Variable Height)
    1. 放開時,上升速度直接歸零
    2. 按住時,持續給予向上的力
    3. 放開時,給予一個向下的力(Down Gravity)
  7. 4. Coyote Time / Edge Jump (邊緣跳躍)
  8. 5. 跳躍輸入緩衝 (Jump Buffering / Jump Cache)
  9. 6. 自適應 Collider / Hitbox Pinching / 階梯處理
    1. 應用案例
  10. 7. 邊緣偵測與落地校正
  11. 結語
  12. 參考資料和補充
  13. 致謝 Acknowledgements
  14. 後話 Next?

前言

長文、多 GIF 圖注意,建議用電腦版網頁瀏覽,網頁右側和文章開頭有目錄可以先看。

這是第一篇有關《Bionic Bay: 換影循跡》的開發日誌,想了一下還是從最基本的角色控制(Character Controller)開始介紹好了。

不過第一篇我還是想先介紹一下優秀 2D 平台跳躍遊戲的角色控制會用到的一些通用知識,主要都是整理自網路上的資源和我實際在 Bionic Bay 上的應用。

這篇文章純討論角色控制、角色物理,不會包含「角色動畫」、「攝影機」、「Game Feel」等等其他知識。
另外雖然這篇要講的是 2D 橫向遊戲,實際上這些知識在 3D 角色控制也通用(我實作上也是從 3D 角色控制那邊偷了不少)。

只是個 2D 遊戲的角色控制是能多複雜?

這邊先放一段 Bionic Bay 角色控制的測試影片:

在這個測試場景中只有角色有掛 scripts ,其他所有動態物件都只是 Rigidbody2D 和 Joint2D 的組合。
換句話說,這個角色控制器(Character Controller)並不仰賴於特定場景配置(唯一需要的是物件必須是由物理引擎驅動),隨時放進去任意場景都至少會有以下功能:

  • 能夠順利通過凹凸不平的地形。
  • 都能夠站穩在任何物體上,無論是否會移動(只要速度不要太快)。
  • 只要角度正確就能攀附邊角(Ledge Grab)、牆壁滑行(Wall Slide)、蹬牆跳躍(Wall Jump)。
  • 站在(或是掛在)物體之上時能夠正確地對該物體施力。
  • 離開移動中的物體時保有正確的慣性速度。
  • 當站立的物體速度過快時會被甩出去。

為了做到這件事情,請見下圖:

這是 Bionic Bay 的主角物理控制器的腳本(不含外觀、動畫和 Input 管理)在 Inspector 的樣子。
各個 Module 對應單一功能,有些 Module 雖然實作了不過目前沒用上(沒打勾)。

稍微摘要各個 Module 功能:

Module Description
Hero Movement 角色移動的核心模組(提供角色速度、旋轉、四方向的接觸狀態、移動函數、碰撞檢測)。
Horizontal Movement 控制角色水平方向(左右)的速度、加速度。
Gravity 控制角色承受的重力、垂直方向速度(不使用內建重力)。
Jump 控制普通跳躍的強度、高度。
Jump Holding 跳躍後如果未按住跳躍鍵會快速消耗目前上升速度。
Wall Slide 角色滑行於左右兩側的牆壁的功能。
Wall Jump 控制牆壁滑行狀態下的跳躍。
Edge Climber (未實裝)這是學 Ori 的實作,人物靠近牆壁與地面轉角的時候如果有按住跳躍鍵則會自動上升推進一小段。
Ground Connector 使角色得以站立於任何物件之上並隨之移動的系統,也涉及進入、離開動態地面的慣性運動。
Ledge Grab 角色可以用手攀附住符合角度的任意物體邊緣的系統。
Ledge Jump 攀附狀態的跳躍。
Crouch 蹲下功能,包含爬行功能。
Push 以動態調整的力道推動任意動態物件的系統(實際上遊戲沒用到)。
Slip 在地面上滑行的功能(當站在過於顛頗的地面時觸發)。
Slip Jump 控制地面滑行的跳躍。
Body Interpolator 自行實作 Rigidbody.interpolation 的功能(因為一些理由不允許使用內建的)。
Contact Colliders 儲存當前與角色有碰撞的所有 Collider 列表。
Dash 角色突進翻滾的功能。
Dash Holding (未實裝)若玩家沒有按住按鍵則會縮短翻滾距離的功能。
Controllable Dash 玩家在翻滾過程中可以直接透過方向鍵調整突進距離的功能。
Capsule Collider Resizer 調整角色 Collider 大小的功能,用於蹲下、移動縮小 Collider。
Preswap 遊戲中的「標記」(或稱攻擊)動作。
Ragdoll 就 Ragdoll 功能,之後開一篇來講。
Roll 這是從 Dash 中再切出來的滾動狀態,主要是供 Bounce ray 等特殊情況下的滾動狀態使用。

我相信有戰鬥系統的動作遊戲會更複雜,Bionic Bay 側重的點只有跑酷體驗所以只針對它強化。
不打算把所有功能都展開來聊,這第一篇文章會先以一個(我認為)最基本的橫向卷軸跳躍遊戲應該要處理好的系統開始介紹 – 也就是「移動」和「跳躍」。

這些模組、角色物理控制也是從開案初期(2019)直到最近幾個月都還有持續調整的一個系統,非常有可能在我介紹完以後又有功能調整。

移動與跳躍?是能有多複雜?

2D 橫向卷軸 / 平台跳躍遊戲是一個非常容易入門的類型,但非常可惜的是有太多案例是連最基本的這兩件事情都沒做好,不太確定到底是線上的教學參差不齊還是有什麼問題。因為要做好這件事情基本上也不會涉及什麼很難懂的概念或實作,都單純是「如果你沒有做,那只是因為你不知道可以這樣做」的知識而已。

以前一些 know-how 的資源比較分散,不過自從 GMTK 做了這個 Platformer Toolkit 之後我都直接推他。

https://gmtk.itch.io/platformer-toolkit
英文看的懂的話,強 烈 建 議 去 操 作 一 遍
(然後建議下載 Standalone 執行檔,因為 WebGL 的版本閹割了一些東西)

另外推薦一個很少看過有人分享的參考資料:You Say Jump, I Say How High?,是一篇 2015 年發表專門研究 2D 平台跳躍遊戲的跳躍和移動參數的論文,他本身就提及很多 know-how 了,然後嚴謹程度是從「定義參數」、「測量參數的方法」到「測量得出幾款經典遊戲的移動、跳躍參數」,都超爆幹詳細的那種,很值得讀。

接下來我就借用一下這個 Toolkit 和那篇論文來介紹幾個重要知識點。

1. 水平移動的速度參數

Platformer Toolkit 的速度設定畫面
最基本的東西,讓你的角色左右移動的時候會有比較順滑自然的感受,幾乎每一個橫向卷軸遊戲都會有自己一套參數組。

我常用的參數跟 GMTK 一致,分別有:

  • 最高速度(Max Speed):就可控制的上限速度。
  • 加速度(Acceleration): 在速度 0 到最高速度之間,方向鍵持續按住的情況下的加速度(每秒提升多少速度)。
  • 減速度(Deceleration): 在速度 0 到最高速度之間,放開方向鍵時的減速度(每秒降低多少速度)。
  • 轉向加速度(Turn Acceleration 或 Turn Speed):當目前角色移動方向與輸入方向不同的時候的加速度。

然後 GMTK 這裡在空中移動時的水平移動參數是用另一組,分別是 Air Acceleration 、 Air Control 、Air Brake。
這裡我不確定它們實際上的數值意義,我體感大致分別是「空中狀態的加速度」、「空中狀態的轉向加速度」、「空中狀態的減速度」。

但我不知道為什麼他要用三個新名詞,我 Bionic Bay 這邊的做法是「地面上」和「空中」狀態分別都有自己一組同名參數,如下圖。

Bionic Bay 主角的速度參數(先無視 Extra Max Speed,之後別篇再解釋)
上面這組速度參數的效果是要做成:

  • 地面上的左右移動反應比較即時(加減速度都相對高),如果只是方向鍵放開的情況會小小滑行,而急著需要轉方向的時候會很快地改變方向。
  • 在空中會比較慣性運動(減速度很低),加速度與地面相同代表你可以做到「先垂直起跳,然後往前移動」的超人動作,轉向速度比減速度高代表你有一定程度改變方向的能力(C 字跳)。

空中的水平移動還有一些變種參數,比如有些遊戲會引入空中摩擦力(Air Friction),讓水平速度降低,形成非對稱跳躍曲線,後面會提到。

幾款經典橫向遊戲的參數

這裡引用論文數據,圖中橘線表示按鍵按下去的時間點,綠線代表放開的時間點。

Castlevania 惡魔城


用 Platformer Toolkit 模擬 Castlevania 效果
典型的只有全速和零速度的設計,就是根本沒有實作加速度的機制,也可以理解成加速度、減速度都無限大。
早年重戰鬥的動作遊戲都這樣(沒記錯的話洛克人也是),好處是反應速度快,可控性高。

Super Meat Boy


用 Platformer Toolkit 模擬 Super Meat Boy 效果
Super Meat Boy 採用低加速度的設計,讓玩家有一個累積速度的過程。
特別的是他的減速度無限大,代表你手一放開就停止了,這個設計怪怪的比較像 bug ,晚點會提。

Super Mario Bros 超級瑪利歐兄弟


用 Platformer Toolkit 模擬 Mario 效果
瑪利歐就是典型的低加速低減速的超高慣性運動,會讓人有很滑很飄的控制感。

LIMBO


用 Platformer Toolkit 模擬 LIMBO 效果
LIMBO 跟瑪利歐設計一樣是加減速一致,但強度比較高所以更可控一點。
但它的數據比較特別的是最大速度和加速度都會受到輸入的值影響,而且不是線性成長。

轉向加速度(Turn Acceleration)

然後是它們各自的轉向加速度,因為 Castlevania 本來就可以瞬間轉向就不列了。
圖片中的橘線表示按下反方向輸入的時間點,綠線表示速度到達 0 的時間點。

看到這,有沒有感覺哪裡怪怪的?

Super Meat Boy 的轉向加速度(Turn Acceleration)竟然比它的減速度(Deceleration)還低!

這代表什麼?
「如果你在 Super Meat Boy 中想要緊急改變方向,那你應該直接放開移動鍵,讓角色瞬間停下來,之後再推動方向鍵去讓玩家加速。」

這個設計超級問號的,因為所有玩家想要緊急改變方向的直覺反應都是直接反向輸入,但你這樣做反而會滑行更久。所以我偏向認為只是開發者設定錯誤而已,減速度不應該是無限大才對。

放開移動輸入 往反方向輸入
放開移動輸入 往反方向輸入

數值表格

最後論文有幫我們整理成一張表格,直接看數字比較清楚:
各個經典 2D Platformer 的水平速度參數表格

嚴格來說水平速度參數組的選擇沒有對錯,舉這些經典遊戲的例子是希望幫助你了解數值為什麼這樣設計,它們能帶來什麼樣的效果。

使用更好的單位來表達 – 時間(秒數)

比起直接以「每秒增加多少速度(u/s)」作為單位,以設計師立場使用「加速幾秒後會到達最高速(s)」會更直觀方便。
用上面提供的數值表格為例就是以 Duration 來設計。
以前面說的速度參數組為例的話,改成以下這樣的參數形式會比較方便做數值設計:

  • 速度最大值(單位/秒)
  • 從速度 0 到達速度最大值要多久(秒)
  • 不做任何輸入的話,從速度最大值降到零要多久 (秒)
  • 往反方向輸入,從速度最大值降到零要多久(秒)

除非你要做什麼更精細的加減速變化又很難反向推導出以時間為參數的公式,否則這樣的設計比較好。

感謝 Ta David Yu 特別點出這件事情:D

2. 跳躍速度、高度與重力(Gravity)

這部分有以下這些數值要考慮:

  1. 起跳速度(Takeoff Velocity)
  2. 重力加速度 (g)
  3. 跳躍高度
  4. 跳躍上升到最高點要花多久時間(單位: 秒)

這裡列了 4 個數值,但實際上參數只會有 2 個,只要你決定了要設定其中任意兩個參數,剩下那兩個數值都能經由公式得出數值。

製作 Bionic Bay 的時候,我們採用了設定「跳躍高度」和「重力加速度」這兩個數值。
剩下的起跳速度、上升/下降時間都可以透過這兩個參數計算得出。

  • 以「跳躍高度」做數值設計很方便,尤其因為它跟關卡設計息息相關。
  • 「重力加速度」是相對比較沒那麼有利於數值設計的參數,一般會認為世界的重力加速度就是一個固定的加速度,對所有的物件都有一樣的效果
    ,在現實是 9.80665m/s29.80665 m/s^2,但在遊戲中就要看你的度量單位,以及你想要怎麼樣的效果(主要影響到上升到頂端的速度、下墜速度)。
  • 如果遊戲本身並不在乎重力一致性(所有物件、角色的重力不需要一樣)的話,或是整個遊戲你只在乎主角本身的跳躍感受,那可以改採用「跳躍上升到最高點要花多久時間」作為參數來推導出重力加速度,設計上會更加直覺。
  • 「起跳速度」我認為最不需要直接當成參數給設計師調整,但在程式實作上會以這個來推動物理。

另外值得提的是有些遊戲會有所謂 Terminal Velocity ,代表最高的下墜速度,就是說角色下墜速度絕對不會比這個速度更快。
Mario 和 Braid 就明顯有 Terminal Velocity 存在。

(Platformer Toolkit 把 Terminal Velocity 參數放在 Assists 類別)

而剩下也沒什麼其他參數了,Toolkit 裡面是多附贈了一個 Down Gravity 參數,是讓你墜落時所受到的重力加速度比上升中還要大,使得落地時間減少。
在 Platformer Toolkit 中你可以拉動 Down Gravity 拉桿使得角色墜落更快
我自己沒有很喜歡這種重力感就沒實作在 Bionic Bay 上。
例如 Celeste 就有用這招,不過他的本意是反過來 – 「讓起跳上升期間的重力減半」,而且效果很輕微,因為它本身的 Terminal Velocity 很低(很快就進入等速墜落)所以它用這個的代價也很低。

圖片出處:Celeste & Forgiveness

雖然直接使用 Down Gravity 的效果我不喜歡,但 Down Gravity 的概念在下面會提到的「可控制的跳躍高度」被發揚光大。

幾款經典橫向遊戲的參數

Super Meat Boy


這是非常典型的對稱跳躍曲線,沒有多做任何特別參數處理的結果。

Super Mario Bro

瑪利歐的起跳速度與當前水平速度掛勾
瑪利歐有個很妙的設計 – 你水平移動速度越快就能跳得越高,就是在鼓勵你全速移動、助跑跳高。

至於你可能會發現瑪利歐的曲線是非對稱的(最高點稍微往右偏),這代表它也有 Down Gravity 嗎?(在 Platformer Toolkit 的瑪利歐 preset 中是把它當成是有 Down Gravity 的設計)
先說結論,它並沒有 Down Gravity。真正的原因是下面會提到的「可控制跳躍高度」設計。

LIMBO

LIMBO 同瑪利歐的設計
LIMBO 也承襲了瑪利歐的助跑跳高設計,不過 LIMBO 加速過程也就 0.2 秒,所以單就這個設計的實際差異不大。
也因此有了以下一個新設計:
LIMBO 以一個固定距離為基準線,會區分成短跳和長跳
基於助跑距離是否有超出一個範圍,會區分成短跳和長跳,短跳的起跳速度較低,進而鼓勵玩家助跑跳躍。

LIMBO 還有個更特別的設計是它的起跳速度(Takeoff Velocity)是有額外的水平向前速度。
藍線是假設不包含向前速度的跳躍曲線,紅色是實際加上了向前速度的跳躍曲線
雖然幅度不大,但它可以讓你在前跳的時候獲得一定量向前的速度。

你可能又會發現 LIMBO 的跳躍曲線也是非對稱的,這是因為它有空氣摩擦力(Air Friction)設計,它會讓角色在空中的水平移動的速度持續衰減。

數值表格

各個經典 2D Platformer 的起跳速度與重力參數表格

3. 可控制的跳躍高度(Variable Height)

又可以稱「動態跳躍高度」的功能,概念是「按住跳躍輸入越久跳越高,按住時間越短跳越低」。
注意一下圖中的 SPACE 鍵代表跳躍的輸入,按住越久跳得越高,快速放開按鍵就只會小跳

不過光這個單一概念就可以有好幾種實作方法,這邊會列出三種做法。

放開時,上升速度直接歸零

讓按住跳躍鍵的結果等同於原始跳躍高度,但是一放開按鍵就會讓上升速度歸零。
這應該是很常見的做法,因為實作非常簡單,而做出來的效果會非常有立即性(因為放開的瞬間即跳躍的最高點)。
現有案例:Super Meat Boy。
Super Meat Boy 採用直接歸零的實作
這個做法讓玩家可控性最高,缺點是小跳的跳躍軌跡不自然,因為放開按鍵的瞬間角色會直接停止並開始墜落,少了「減速過程」。

GMTK Platformer Toolkit 採用這個實作。

按住時,持續給予向上的力

讓瞬按瞬放的跳躍結果等同於原始設定的跳躍高度,但你如果有按住的話會持續給予一個向上的力(直到消耗完為止)。
這是瑪利歐的做法,這個做法跟上一個做法可以說完全相反,我不確定有沒有其他遊戲也這樣做。
瑪利歐採用按住會持續獲得上升力的方式
這個做法的玩家可控性稍差一點(因為放開後還要一段時間才到頂端),優點是跳躍曲線比較好看、自然。
但缺點很明顯,大跳的上升軌跡不太自然、數值不好設計(因為你設定的起始跳躍高度是小跳高度)。

放開時,給予一個向下的力(Down Gravity)

讓按住跳躍鍵的結果等同於原始跳躍高度,但是一放開按鍵就會被添加一個額外的 Down Gravity 來把原始的起跳速度(Takeoff Velocity)抵銷掉為止。
這算是第一種方法的改良版,可以解決小跳的不自然感,缺點是可控性就降低了一點(因為放開後還要一段時間才到頂端)。
現有案例: LIMBOOri and the Blind ForestBionic Bay

我們 Bionic Bay 最終採用這個方案,我自己也覺得這是多數情況的最佳解。

實作上要稍微注意的點是,它的目的是「抵銷起跳速度」,並不是單純的讓下降的重力變大,當你的原始起跳速度已經被重力抵銷完了(即按住大跳的情況)就沒必要在加額外的 Down Gravity 了。

這裡用 Down Gravity 是為了對應文章前面提及的,論文中的用詞是 Release Drag。

4. Coyote Time / Edge Jump (邊緣跳躍)

這個專有名詞眾說紛紜,也有被稱為 Platforming Ledge Forgiveness
因為 Celeste 做太好了所以我就一律用他們使用的名詞 Coyote Time 來稱呼了。
這個「輔助功能」直接看 Celeste 的 GIF 圖就能懂了:
人物已經離開地面了才跳躍
圖片出處:Celeste & Forgiveness

硬要說明的話就是「當角色離地後 N 毫秒內仍可以進行跳躍動作」。

這個實作非常簡單,以 Unity-style C# 寫個範例碼,假設這是原本的角色跳躍寫法:

// 每幀更新呼叫此方法
void Update() { 
  bool isGrounded = CheckGround(); // 檢測角色目前是否站在地面上
  bool inputJump = GetInputJump(); // 是否按下跳躍鍵

  // 如果在地面上並且按下跳躍鍵,即可執行跳躍
  if (isGrounded && inputJump) {
    Jump();
  }
}

可跳躍判定isGrounded 換成 coyoteTime > 0 就好,如下:

const float TOLERANCE = 0.05f; // 離地後有 0.05 秒的時間容許跳躍
float m_CoyoteTime; // coyote time 的剩餘時間

// 每幀更新呼叫此方法
void Update() { 
  bool isGrounded = CheckGround(); // 檢測角色目前是否站在地面上
  bool inputJump = GetInputJump(); // 是否按下跳躍鍵
  
  if (isGrounded) {
    m_CoyoteTime = TOLERANCE; // 站在地面上就設定 coyote time
  } else {
    m_CoyoteTime -= Time.deltaTime; // 如果沒有站在地面上就開始倒數
  }

  // 如果 coyote time 還沒倒數至 0 並且按下跳躍鍵,即可執行跳躍
  if (m_CoyoteTime > 0 && inputJump) {
    Jump();
  }
}

我認為這真的是一個實作超級簡單,效果又超棒的功能。很多遊戲只要加入這個,平台之間的跳躍體驗就大幅上升。
不過雖然知識和實作都超簡單,也還是有大 IP 作品沒實作這件事情(對我就是在說 Metroid Dread),所以你有做至少就大概 PR70 以上了。

5. 跳躍輸入緩衝 (Jump Buffering / Jump Cache)

為你的跳躍輸入按鍵加入緩衝(buffer),就是
「輸入跳躍按鍵的當下若還無法觸發跳躍,在之後的 N 秒內只要有進入可跳躍的狀態,仍會自動觸發跳躍」
整句很繞口,看下面 GIF 圖比較好理解。
這是在 Bionic Bay 實作的效果,情境是在角色快落地的時候再次按下跳躍鍵(圖中的 SPACE 亮起代表按下)。

開啟輸入緩衝 關閉
快落地前按下跳躍能夠成功跳起來 快落地前按下跳躍但跳不起來

我從來沒想過為了遊戲教學寫的 Replay 系統可以在寫文章用上

需要這個輸入緩衝的原因是「當玩家預期要在角色落地的瞬間馬上再跳起來的話,通常都會早按」。如果你沒有做這個輸入緩衝的話會導致角色跳不起來,玩家就會怪罪遊戲說「我明明就按了啊」。

輸入緩衝真的超重要,而且不只跳躍輸入要緩衝,任何有這種需要抓準時機按下的按鍵都最好要有一定的緩衝時間(e.g. 攻擊、迴避、招架),否則玩家會一直怪罪遊戲判定不良。這和遊戲上常討論到的判定窗口(window)是類似概念但不同形式。

6. 自適應 Collider / Hitbox Pinching / 階梯處理

從這裡開始 GMTK Platformer Toolkit 就沒有實作了。

這裡引用 LevelHead 開發者在 GDC 2020 分享的 Forgiveness Mechanics: Reading Minds for Responsive Gameplay 來介紹這個輔助功能。

要解決的情境如下圖:
人物是橘色方塊要往右走,但前方有突起物
角色的前方有一個小突起物(階梯)擋住你的移動路徑,什麼都沒處理的話往前移動會被它卡住。

有一個 Naive 解是 – 「把你的角色 Collider 換成圓角形狀(Rounded Corners)」,比如說 Circle Collider 或 Capsule Collider ,這樣一來即便你直接撞上去也 有機會 讓物理引擎幫你解決。但這個方法的效果不夠穩定實用,而且弄不好甚至有機會在撞上去的瞬間讓你的人直接起飛。

而這篇要介紹的穩定實用方法是「當你移動的時候縮小碰撞範圍(Hitbox)」。
如下圖把碰撞範圍縮小至黃色區域:
縮小人物碰撞範圍(高度)藉以通過突起物

但是這並不是單純讓角色的 Collider 變矮然後就往下墜落,重點是「角色的碰撞範圍縮小後,依然保持與地面相同的距離」。
所以當角色成功站上突起物之後,也要讓它與新的地面高度保持原本的距離,也就是往上升。
可以直接看這個三步驟 GIF 示意圖:
踩上突起地面後要校正成原始的離地高度,後面兩步實際上是一次做完的

應用案例

一樣先用 LevelHead 提供的例子

沒有使用 Hitbox Pinching 使用 Hitbox Pinching

再來其實並不是只有水平移動可以用這招,垂直跳躍也能用
縮小 Hitbox 的軸向從原本的縮高度換成縮寬度,可以減少撞到頭的情況

Celeste 也有大量使用這招:



圖片出處:Celeste & Forgiveness

最後是在 Bionic Bay 的應用:
克服階梯問題

實際上關卡中不會刻意設計這種階梯,但這個功能可以大幅度地降低角色被場景各種縫隙、突起給卡住的情況

走在畸形物體表面上1 走在畸形物體表面上2

這個功能的實作依據角色移動的具體方法和地圖設計的不同而有不同的實作形式。

  • 比較傳統 Retro 類型的平台跳躍因為通常是 Tile-based 的地圖設計和更精準的角色控制方法(移動方式是基於程式碼直接對 position 進行控制),只考慮可操控角色本身的話會比較簡單實作。
  • Bionic Bay 因為是物理驅動(移動方式是設定 velocity 之後再交由物理引擎去跑)加上地形非常混亂不規則,採取的實作方式更接近通用 3D Character Controller 的形式,坑很多,實作有參考這兩個 Unity Assets:Kinematic Character ControllerCharacter Controller Pro,之後的文章會再補上具體是怎麼做的。

7. 邊緣偵測與落地校正

這邊引用 Dead Cells 在 TGDF 2018 的演講 使玩家感覺良好 – 《Dead Cells》開發回顧 的影片片段 – 「跳躍於平台之間」。

他們實作了所謂的「Cheating on arrival」,意思是「當角色在降落於「靠近平台邊緣」的時候,會自動校正落地點於平台之上」。
直接看比較圖:

Cheating on arrival None

這個概念是因為以玩家視角不會諒解所謂的「啊就剛好你的 Collider 就只差了一點點才能踩到地板,所以摔下去了」。
邏輯跟前面輸入緩衝(Buffering)的概念差不多,玩家的預期心理與實際上物理結果並不是一致的。

實作上你如果有做第六點的縮 Collider 輔助的話,其實就能達成類似的效果。因為在 collider 有縮的情況下,這樣的橫向跳躍的落地距離就可以有效增加(想像一下右圖無法踩到地是因為腳那區塊的 collider 撞到牆)。
但這裡講的落地校正是去主動偵測前方的落地點並讓整個角色座標位移。

不過我們取其精神即可,實作上不一定要學 Dead Cells 的落地校正

以我們 Bionic Bay 來說,邊角攀附(Ledge Grab)機制就能做到一樣的效果。

慢動作看:
綠色框框是抓取判定範圍,注意看人原本看起來已經掉下去了,但判定夠大可以抓回去

結語

以上是我想的到且我個人認為比較通用的「移動」和「跳躍」的知識點。
可能還有什麼通用招式是我不知道的,歡(拜)迎(託)跟我說一下。

實務上還會依據個別遊戲的不同去強化輔助不同的功能,不過核心理念都一樣,就是
「不要讓玩家被非刻意設計的小瑕疵卡住,讓玩家把時間花在開發者刻意設計的挑戰才是最重要的事」。

但也不是說把以上知識全部做到就能讓所有玩家滿意,實際上也是有玩家覺得 《Bionic Bay: 換影循跡》 的操作很滑很爛,這裡就直接引用我朋友說的:
「往好處想就是沒寫這堆功能就更爛」。
舉例來說光速度參數跳躍曲線的選擇就一定會分好幾派了,你不可能滿足所有人。除非你做一個可以自由調整參數的 meta 遊戲

參考資料和補充

礙於篇幅,我只整理了我覺得重要的知識點,有空的話可以直接看參考資料本身,他們都講得非常詳盡,一定對開發有幫助。

雖然文章內沒有引用,但我覺得還是值得一看的資料:

文章中 GIF 圖片的製作使用了:

  • ScreenToGif - 開源好物,可以很方便地錄製螢幕指定區域(可輸出 gif 或影片格式)。
  • vid2gif-ffmpeg-gifski - 本人寫的一個能夠快速地把影片檔轉換成高品質高壓縮率的 GIF 圖片的小工具(整合 ffmpeggifski 這兩套開源軟體的應用)。

致謝 Acknowledgements

感謝 DK LiaoKevin Lai、Mayavi、Ta David Yu 的校閱。


後話 Next?

回到開頭的 Bionic Bay 角色控制器展示,但整篇文完全沒有提到那些列出來的功能,這一篇的內容是屬於角色移動的核心模組、水平移動模組、跳躍模組的範疇內。而配合物理的各項機制因為並不算「通用」,應該也可以說是偏門,放在這一篇就不太適合。

下一篇預計會寫更特定主題、特定遊戲才適用的功能實作。
.
如果真有下一篇的話。
.
光這篇就耗費了整個週末邊聽 TGDF 邊寫,之後一個禮拜還持續在更改內容、補圖片 etc。深深感覺到自己寫爽的紀錄文和希望分享給別人看的文章差太多了,因為怕誤導讀者所以需要各種勘誤、檢視文字用法是一件超級花時間的事,真的敬佩所有願意寫文章、演講分享的人。
所以預防針一下如果真有下一篇那也只會是記錄用的自爽文,應該不會刻意在 SNS 分享。

另外這一篇之後還得花時間寫成英文版,希望能洗點國外開發者的好感度。