又到了每年 1~2 月這個時段,相比其他月份天數較少,期間過年家中又有得忙。
年節尾聲的時候老家那邊又傳出噩耗,大舅平常就有高血壓,早年有飲酒習慣,天候變化無常導致一個腦中風,拖了一段時間才被人發現,緊急送醫之後狀況就已經不樂觀了。過沒幾天後院方那邊就宣告不治,就算奇蹟般地救回來也會是植物人。
大舅享壽 60,無婚姻膝下亦無子。聽老家那邊的人說今年內就打算去辦理退休,但這次的突發意外所致,符合勞保遺屬資格的人都已經不在世了,阿姨也在哀嘆這下繳了數十年的勞保,結果勞退一毛都拿不到。
由於時間點的關係,和家人去送完大舅最後一程,隨後也得回歸平日的生活了,只能感嘆世事無常。
回到開發進度的部分。
上一篇在建立拋物線的架構,並透過它來預判角色跳躍路徑上是否會受阻的狀況。
經測試後,發現還是不太能夠應付特定的狀況,故又額外追加了幾種拋物線的算法類型,這邊稍微帶過:
● [倒鈎拋物線]
取起始點及目標點 Y 座標的拋物線,寬度則以預設乘算方式處理。
進行節點間路徑的碰撞體偵測時,當 X 座標與目標點相同,則將其固定。
該拋物線主要用於直接跳上單向平台地形。(或是與目標點水平座標相差較小時)
● [反向倒鈎拋物線]
將起始點視為拋物線之頂點,目標點為拋物線之終點。(僅其 Y 座標,X 座標會受拋物線寬度而延伸)
該拋物線專用於跳下單向平台地形時使用,即目標點 Y 座標應比跳下點還要來得低。
實際的應用結果為:使角色水平移動至目標處,隨後保持垂直落體。
● [反向拐杖拋物線]
將起始點視為拋物線之頂點(僅其 Y 座標,X 座標會受拋物線寬度而延伸),目標點為拋物線之終點。
該拋物線專用於跳下地形時使用,即目標點 Y 座標應比跳下點還要來得低。
實際的應用結果為:先垂直落體,當到達極限位置時,才使角色水平移動至目標處。
就過程上來說,可以一定程度上解決不規則牆面的狀況,因為目前連續地形的處理上並不會涉及垂直方向的牆體。
鑒於角色物理演算上的設置,跳躍的路徑在實質上並不是完全的水平對稱拋物線。
故在追加完拋物線的判定類型後,又反覆調整了角色實質跳躍力,以及受碰撞體寬度所影響的著地點,用以增加 AI 角色透過跳躍到達目的地的可行性。
另外,為了讓 AI 角色在跳躍的路徑選擇上更加靈活,它們也實裝了 Coyote Time 的機制。(可以參考
#8 有提及到)
也就是在騰空的瞬間,仍可以跳起以到達更高處的地形位置。
每種 AI 角色在一場景內,當觸發尋路事宜時,將會根據所有可能的地形組合,兩兩一組生成預設的拋物線判定預置。
∎ 當前地形兩邊界處
∎ 目標地形兩邊界處
∎ 距離上的最近節點
Δ 預設判定的示意圖
當有任一個拋物線判定成立,則這個地形組合就形成了節點通道,這會做為後續尋路過程的參考基準。
一般來說,在距離越近的座標之間進行跳躍,成功的機會也會越高,故在選擇上,AI 角色會盡可能往較近的有效節點之間來進行。
而經過拋物線所得到的跳躍資訊是儲存在一個特定的 Dictionary 之中的,而它的鍵值就包含了節點的座標資訊。
但遊戲過程中,AI 角色也只能給出自身的座標來做為參考基準,要如何在眾多的節點座標中挑選合適的目標,進而獲取儲存著的跳躍資訊呢?
Dictionary 的鍵值無論是採用節點座標 Vector2,又或是距離關係 float,都必須使用到符合的鍵值,才能從 Dictionary 中獲取相應的元素,這讓我們在搜索上是存有難處的。
也因此,這邊將另外實作一種,可以給出一個參考值,在 Dictionary 中尋找相對較為接近的鍵值,進而返回相應元素的特殊 Dictionary,這邊將其稱為 NearestKeyDictionary:
● [NearestKeyDictionary]
顧名思義即為取最近鍵值的字典。
其實就只是包覆了一個 Dictionary,一組會隨著 Dictionary 加入項目並排序的 List,以及做為可選欄位的 IComparer,用以自定義鍵值的比較規則。
使用時,先在 List 透過 BinarySearch 尋找離傳入值最為接近的鍵值,再將找到的鍵值配合 Dictionary,得到最終的目標項目。
這麼一來,就能解決前面提到 AI 角色給出自身座標,進而尋找最為接近跳躍資訊的需求了。
◆【節點與尋路】
場景內的所有地勢具連續性質的地形都會有特定的標記,被稱之為
連續地形,參考
#28。
同樣地,也會有一個控管這些連續地形的元件,讓 AI 角色進行尋路也是讓它來負責的。
初步的構思上,我們假設有五個連續地形 A, B, C, D, E。
每個連續地形之間的座標距離,先不影響最終的尋路結果,再假設:
∎ A 能夠到達 B 及 C
∎ B 能夠到達 E
∎ C 能夠到達 D
∎ D 能夠到達 E
則如果我們想要從 A 到達 E,則應該會有這兩種可行性:
∎ A→B→E
∎ A→C→D→E
僅根據經過的地形數量來看,第一條路徑會比第二條路徑來得短,故第一條路徑就是我們想要的結果。
由於這種在每個地形之間的移動成本相同的特性,故這邊將採用 BFS(
廣度優先搜尋,Breadth-First Search)來做為尋路的演算法。
每個連續地形將視為節點 (V),能讓 AI 角色透過跳躍到達的每個通道即為邊數 (E)。
實作的方法這邊就不再贅述,不過我們還能加上一些額外的功能,像是讓特定的節點暫時性地失效,能用來呈現特定的地形臨時消失,無法通過的樣子。
下面就直接用圖例來演示 AI 角色尋路的前後關係:
首先,當一種類型的角色首次在一場景中準備尋路時,先對所有可能存在的連續地形兩兩組成一組,進行我們前面提及過的預設拋物線判定處理。
動圖裡的拋物線方塊就是判定角色在跳躍路徑上,是否會受到地形阻礙的樣貌,正常遊戲期間當然不會有的。
就效能而言,比預想上的表現還要好很多,原本擔心同一時間一次性進行相對大量的碰撞偵測會不會產生體感上明顯的卡頓,看來是仍在可接受的範圍。
然後是給定起點及終點地形來進行尋路,如果得到了有效的結果,則將這個路徑之間的跳躍資訊,串接給 AI 角色本身來進行移動。
下圖的敵人角色並非經由手動進行操作的:
Δ 圖中的跳躍處理運用到了數種不同的拋物線類型
如此一來就能讓 AI 角色在兩個不同的連續地形之間穿梭了。
實裝這個尋路系統要調整的細項真的很多,雖然整體上來說沒有比 12 月那時算拋物線架構來得複雜,但取而代之的就是細瑣而繁亂。
現在要緊接著手的是針對動態地形在一定程度上的支援,搭上尋路演算法中針對同等長度路徑的優先次序,尋路期間的中斷行為,還有 AI 漫遊階段會隨機到達鄰近地形的可行性等等。
以上提及的部分完成後,最後則是實裝 AI 角色的無條件
直接傳送,即無視地形阻礙且不使用跳躍行為來到達目的地。這部分也得繪製相應的特效才行。

總而言之先這樣囉,不好意思這篇寫得有點趕,下兩個月內要把這個拋物線跟尋路的機制做一個收尾,好接續之後的地圖系統。
再次地,感謝閱覽~
