游戲引擎學習第270天:生成可行走的點

回顧并為今天的內容定下基調

今天的計劃雖然還不完全確定,可能會做一些內存分析,也有可能暫時不做,因為目前并沒有特別迫切的需求。最終我們會根據當下的狀態隨性決定,重點是持續推動項目的進展,無論是 memory 方面還是其他內容。

由于操作系統出現過問題,需要重啟機器,因此運行速度稍微有些遲緩,但目前一切已經恢復正常,游戲也在順利運行。調試界面中,我們可以通過展開查看更多詳細信息,也可以保持最小化狀態查看概覽。

今天的整體節奏比較自由,沒有特別硬性的任務安排。我們將按照當下的想法進行推進,靈活調整方向。這是一個開放創作的過程,我們有多種可選路徑可以探索,從不同角度推動游戲的設計和開發。

運行當前版本的游戲時,感覺是時候開始整理和優化當前的結構了。經過前面的設計梳理,我們現在已經比較清楚整個系統的構成和各部分的職責關系。是時候回頭把一些內容梳理得更清晰,進一步加強模塊的可控性與可維護性。

值得一提的是,盡管我們采用的是房間式攝像機設計,并未加入大量平滑滾動機制,這可能不太符合一些特別偏好“流暢滾動”的用戶期望,但目前的結構是為了實現我們更清晰、更可控的世界映射與交互而設計的。這是一種有意為之的設計取舍,服務于我們整體的系統規劃目標。

設計:基于房間的攝像機移動方式

游戲中不會加入任何形式的“鉆洞”機制,至少在設計層面完全排除了這種可能。如果真的想在屏幕之間做平滑滾動,那倒是可以接受,但游戲本身不會采用那種允許玩家處在兩個屏幕之間的體驗方式。整個游戲的核心理念是以“房間”為單位構建世界,即使玩家處在室外區域,也必須明確地處在某一個房間中,而不是漂浮在兩個房間之間。

所以,設計中不存在那種可以自由來回穿梭兩個區域的方式。舉個例子,如果玩家向上移動,不能像現在這樣從一個區域“滑過去”進入另一個區域,必須要等走到屏幕邊緣,觸發房間切換后,才能進入另一個房間。這是整個游戲世界規則的一部分,是不可協商的原則。

當然,如果其他游戲想要使用平滑滾動機制,這種做法是可以借鑒的,之前實現的滾動系統也可以作為參考。但在這個項目中,不會采用這種方案。

接下來的工作可能會集中在整理并清理世界相關的代碼部分。目前游戲運行時的“世界模式”模塊中,第一次進入時會做初始化工作。當前的代碼結構中已經具備基本的世界生成機制,當檢測到沒有初始化時就會進行初始化,并完成一系列設定。

我們曾經有過一個“AddWall”的函數,用于在世界中添加墻體元素,之前不太記得這個函數是如何被組織和管理的。現在再次查看,發現這部分功能被歸類在“PlayWorld”的流程中,也就是當進入“PlayWorld”狀態時,會進行世界的隨機生成并開始游戲流程。

從邏輯上看,這些初始化和生成流程已經被合理地抽離出來,整個流程是:進入游戲 → 檢測初始化狀態 → 執行世界生成 → 開始運行。整體結構清晰,模塊職責明確,說明前期的重構工作已經達成了目標,后續可以繼續優化和整理這些系統化的部分。

編輯 game_world_mode.cppgame_asset.cppgame.cpp:刪除地面區塊

首先要做的一件事是,徹底移除“ground chunks”(地面區塊)的概念。這個系統將不再被使用,因此需要從代碼中完全刪除相關內容。這項清理工作的重要性在于,它不僅可以簡化整體系統的復雜度,還能為將來的渲染系統優化掃清障礙。

過去為了支持 ground chunks,我們在渲染系統中做出了一些妥協,比如在后臺任務中執行渲染,而現在看來這些機制已經不再必要。既然不再計劃在后臺進行渲染,就可以把這些為支持 ground chunks 而引入的妥協全部移除,進而大幅簡化渲染相關的代碼。這對于未來的維護和擴展來說是非常有益的。

因此,我們著手移除所有與 ground chunks 有關的邏輯,包括:

  • 刪除所有 ground chunk 的數據結構和存儲代碼;
  • 清理 transient state(瞬態狀態)中保存 ground chunk 緩沖區的部分;
  • 去除重新加載 x 文件時重新計算 ground chunk 的邏輯;
  • 從相關的緩沖池、處理隊列中移除它們的使用痕跡;
  • 刪除相關變量如 groundBufferCount 及其更新邏輯。

不過,在這個過程中,需要注意保留低優先級任務隊列(low priority queue),因為這部分不僅用于 ground chunks,也用于其他資源的加載功能。雖然現在不再使用 ground chunks,但加載系統仍然需要這些隊列,所以不能一并刪除。

完成這些清理之后,程序可以正常運行,不再依賴 ground chunks,系統結構更加干凈。接下來一個可以進行的小任務是修復貼圖相關的一些問題,作為一個輕松的改動,也可能會比較有趣。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

運行游戲,發現畫面沒有變化

現在我們重新運行程序后,雖然視覺上看起來一切都和之前一樣,但實際上所有與 ground chunk(地面區塊)相關的系統已經被徹底移除,這是一個非常好的進展。

接下來我們開始整理整個 world wrap(世界封裝)相關的邏輯,目的是將當前的框架結構進一步收緊和優化,為后續正式的游戲代碼實現做好準備。當前的結構雖然基本可用,但還不夠清晰和整潔,適合進入實戰階段的開發。

這個整理工作不僅包括功能層面的調整,也涉及到代碼結構的重新梳理,比如明確模塊之間的職責劃分、精簡不再需要的接口,以及對可能存在的冗余或過度抽象進行精簡。此外,還會考慮未來游戲設計需求所需的擴展能力,確保這個世界系統可以穩定支撐整個游戲流程。

與此同時,加載了 mischief(調試與設計相關的可視化工具),這個加載過程稍顯緩慢,是因為當前環境中包含了大量的數據、元素或狀態配置。這些內容是之前調試或原型過程中積累下來的,也正說明目前系統已經積累了豐富的狀態,因此這次整理工作也變得尤為關鍵。

總體來看,現在的目標非常明確:在徹底移除舊機制后,進一步清理整理整個世界系統邏輯,為即將進入的游戲功能實現階段打好基礎。

黑板講解:基于圖塊(tile)或網格單元(cell)的移動與組合邏輯

我們現在的目標是正式建立“房間”這一概念。在設計上,每個房間都將由明確的網格單元構成,也就是說,整個世界會被劃分為清晰可見的網格格子,每個格子將成為基本單位。這種設計是有意為之的,后續游戲玩法將高度依賴于這個網格結構。

我們不會把“房間”概念固定到某個特定的數據結構里,也不強制統一每個房間的尺寸。由于攝像機支持縮放,我們可以靈活設置顯示范圍。不過,我們會定義一個“網格大小”(例如一個格子為 32 像素),并確保游戲中所有元素都在這個基礎上運行。這種網格系統會成為環境的首要結構,所有內容都需要嚴格對齊到這些格子上。

接下來我們會創建實際的網格實體,也就是說,每一個格子都會成為一個實體,保存關于該格子的信息。雖然這可能會帶來效率問題(比如同屏會有幾百個實體),但我們決定接受這種低效。因為我們想最大限度地實現游戲系統的組合性和交互性,希望讓游戲中所有的事物都能模擬、響應和互動,哪怕是地面本身也會作為一個實體參與模擬。

我們明確地做出這個設計選擇,并愿意為此承擔代價,比如更高的內存占用、更大的運行負擔、可能無法在性能較低的平臺上運行等問題。我們接受這個不完美,是為了實現更加豐富而復雜的游戲交互系統。

因此,我們將開始實施這一變化。雖然乍看之下并不是特別大的變動,其實這與我們最初開發貼圖系統時的方式類似,只是現在會以實體的形式來實現這些網格。未來我們甚至可能會探索不規則網格的可能性,但當前的重點是確保我們可以以明確的方式創建和管理這些網格。

我們首先會從 world mode 模塊入手,看之前用于創建墻體的 add_wall 函數。這個函數目前是以“屏幕”為單位創建房間,并通過參數定義房間的寬高(比如 17x9)。我們會把這個邏輯擴展為逐個格子創建實體,每個格子都清楚地表明自己的位置,并能承載各種交互信息。

此前我們還曾嘗試用一些“空間實體”等方式來模擬玩家的移動,但現在我們已經確定移動系統的設計方向:玩家只能處于某一個具體的格子內,不能停留在兩個格子之間。也就是說,玩家的每一步移動都必須是從一個格子到另一個格子,不能有“夾在中間”的狀態。

這將促使我們摒棄之前那種基于向量自由移動的方式,轉而使用更精確的格子移動系統。這也是接下來我們要重點構建和實現的部分。整體來看,我們現在進入了一個更成熟的階段,從試驗過渡到實際的系統構建。


示例:構建一個 5×3 的房間網格

我們希望構建一個房間,它是一個 5 格寬、3 格高的矩形,每個格子是一個實體。這些實體可以承載地形信息,比如是否可以通行、是否有物體等。

第一步:定義網格大小

我們定義每個格子為 32×32 像素:

const int tile_size = 32;
第二步:創建網格實體

我們創建一個循環,為每一個格子生成一個“網格實體”:

for (int y = 0; y < 3; ++y) {for (int x = 0; x < 5; ++x) {EntityID tile = CreateEntity();SetComponent(tile, Position, {.x = x * tile_size,.y = y * tile_size});SetComponent(tile, TileInfo, {.walkable = true,.type = TILE_FLOOR});}
}

上面代碼中,每個格子都是一個實體,擁有兩個組件:

  • Position:表明其在世界中的像素位置;
  • TileInfo:包含邏輯信息,比如是否可以通行、當前地形類型。

移動示例

假設玩家現在在 (2,1) 這個格子中,想要向右移動一格。系統邏輯會是這樣的:

int target_x = current_x + 1;
int target_y = current_y;// 查找對應的目標格子實體
EntityID target_tile = GetTileAt(target_x, target_y);if (GetComponent(target_tile, TileInfo).walkable) {MovePlayerTo(target_tile);
}

玩家只能從一個格子“跳躍”到另一個格子,而不會處于兩個格子之間。動畫上可以通過攝像機平滑處理,但邏輯上始終是離散的網格跳躍。


模擬效果

  • 每一個網格都可以有事件、地形、道具;
  • 比如 (4,2) 的格子可以是陷阱,只要角色踏入就觸發效果;
  • 后續也可以給每個格子綁定觸發器、環境變化等,使整個世界成為一個互動的模擬系統。

這個做法雖然增加了實體數量(5×3 就是 15 個實體),但也帶來了極大的擴展性,比如我們可以對每一塊地面進行獨立處理,讓游戲玩法更加復雜且有趣。

編輯 game_sim_region.hgame_world_mode.cpp:將 EntityType_Space 改為 EntityType_Floor,將世界構建方式從“挖空”轉變為“堆疊”

我們當前在重新整理游戲世界的底層表示方式,具體目標是構建一個以“格子(Grid)”為基礎的、離散化的空間邏輯系統。我們不再采用以前用“space”實體來代表可移動區域的方式,因為那種抽象已經不再符合我們的設計思路。以下是我們所做調整和設計意圖的詳細總結:


一、實體與地面概念重構

  • 以往我們使用一個叫做 space 的實體類型,表示可通行區域;
  • 現在我們將這種類型更名為 floor,它更準確地描述了“玩家可以站立的地方”;
  • 未來它可能會被替代為一個帶有 Traversable(可穿越)標志的普通實體類型,而不是單獨的實體類別;
  • floor 是我們世界中的基礎組成單元,每一個格子都會對應一個 floor 實體。

二、房間生成邏輯調整

  • 我們將通過嵌套循環來生成一個房間中的每一個格子;
  • 這些格子都是以 (x, y) 坐標為中心的位置進行偏移的,構建時會考慮居中布局;
  • 比如對于 17 × 9 的房間,會以 x = -8 到 8y = -4 到 4 的范圍來遍歷生成實體。
for (int y = -4; y <= 4; ++y) {for (int x = -8; x <= 8; ++x) {EntityID tile = CreateEntity();SetPosition(tile, (x * tile_size), (y * tile_size));SetFlag(tile, Traversable); // 設置為可站立}
}
  • 每一個實體都代表一個具體的地塊,具備明確的物理位置與邏輯功能。

三、碰撞系統精簡

  • 之前我們使用了 standard_room_collision,現在改為更通用的 standard_floor_collision
  • 實際上,我們不再需要對 floor 類型的格子進行實體間碰撞處理,因為它們是用于“站立”而非“阻擋”的;
  • 因此,這些實體不會設置碰撞盒,僅用于邏輯判斷和渲染。

四、渲染處理

  • 為了驗證生成的格子是否正確,我們為每個 floor 實體繪制一個描邊矩形,用于視覺調試;
  • 使用之前已有的 PushRectOutline 方法來顯示每一個 tile 的邊框;
  • 這個操作只用于開發期的可視化調試,后續可能會被真實地形資源所替換。

五、實體數量控制

  • 由于我們現在每一個格子都生成一個實體,這會大幅增加實體總數;
  • 暫時我們減少了初始化時的房間數量(screen count),只保留一個初始房間,避免超出實體上限;
  • 未來我們會將實體總數限制改為動態分配,而非硬編碼上限。

六、刪除舊系統

  • 舊的 ground chunk 系統已被完全移除;
  • 地面格子現在由 floor 實體逐個構建生成;
  • 這樣可以讓地面也成為游戲邏輯中的一部分,實現高度可組合的互動設計。

七、設計意圖總結

  • 我們選擇犧牲一定的性能與內存占用,換取極高的可擴展性與邏輯一致性;
  • 每一個可交互單位(無論是角色、物品還是地面)都被視為實體,均可被統一管理;
  • 這種“全實體”的方式能夠最大化游戲系統的組合潛力和未來擴展能力。

這種架構將使我們的游戲邏輯更清晰、行為更一致,并且為實現復雜地形、動態地圖和更多可交互元素打下了堅實的基礎。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

運行游戲,發現可以選中地板圖塊

我們現在已經成功創建了所有網格格子實體,并能夠在系統中選中它們。系統正常地將這些格子可視化顯示出來,一切運行良好。這意味著我們已經有效地將每個網格格子初始化,并且能夠通過當前的調試或顯示機制清楚地看到它們。

此外,我們可以利用已有的系統工具來顯示這些網格格子的位置,進一步驗證它們的生成是否準確。這一過程順利完成,視覺上也確認了格子的創建確實按預期發生。這種可視化方式對后續調試與開發非常有幫助,使我們可以直觀觀察整個網格布局的實際分布效果。

總的來說,這一步已經完成得非常順利,基礎的格子實體系統已經建立,并可以通過圖形方式清晰地呈現,具備了進一步開發與邏輯實現的基礎。

在這里插入圖片描述

編輯 game_world_mode.cpp:繪制圖塊

我們現在希望將這些地板格子更“永久性”地繪制出來,而不是僅在某些調試或特定模式下才可見。目前雖然已經成功生成并可以通過系統可視化地看到這些格子,但它們的繪制仍是臨時性的,屬于調試輔助性質,比如通過碰撞框(collision rectangles)來表示。

現在的目標是將這些格子始終繪制出來,使它們在游戲運行時也能一直可見。雖然還未使用正式的美術資源,但接下來的計劃是為這些地板格子配上真實的圖像資源,讓它們具備真正的視覺表現力。在此之前,先繼續用碰撞框的方式進行占位顯示,作為過渡階段。

這也意味著我們正在逐步從僅依靠調試工具的開發階段,邁向真正的游戲視覺和系統集成階段,為日后完整的地面渲染和交互鋪設好基礎。這個過程確認了格子的可見性、存在性與繪制邏輯的一致性,同時也準備好了后續替換美術資源的接口。
在這里插入圖片描述

在這里插入圖片描述

運行游戲,看到新的地板圖塊,開始思考如何使用它們

目前我們已經能夠清晰地看到所有地板格子了,這比之前有了明顯的改進,但還沒有完全實現我們真正想要的功能。接下來的目標是讓角色能夠基于這些格子進行明確的位置判斷和移動——也就是說,角色需要知道自己正處于哪一個格子上,或者是否已經離開了某個格子。

這就要求我們重新思考這些地板格子的用途和邏輯。以往我們使用“可通行(traversable)”這個概念,是基于空白空間的,那些可以在連續移動中穿過的區域。而現在的思路不同了:我們希望這些地板格子是角色明確“站在其上”的單位,是一個個具象的、離散的位置,而不是連續空間中的一部分。

基于這個新思路,我們需要對角色與這些格子的關系進行重新定義。例如,角色應當在邏輯上明確知道自己當前在哪個格子,或者是否從一個格子跳到了另一個格子。這不僅影響角色移動的判斷邏輯,也影響后續的交互系統、AI導航系統、甚至動畫和視覺表現。

此外,還要引入“高度”概念。也就是說,每個地板格子將具有一個“高度”值,代表其在空間中的立體位置。接下來的重點是思考這個高度值的意義:是否允許角色在格子之間因為高度差異而無法移動?例如,某個格子如果高出相鄰格子太多,角色是否就不能直接跳上去?這些問題都需要在整個邏輯流程中逐步明確。

因此,整個系統的重點正在從簡單的二維平面空間轉向一個具有高度和邏輯判斷的結構化格子系統。后續將逐步把這種格子的邏輯貫穿整個角色移動、交互和渲染管線,實現完整的空間感知與行為系統。

編輯 game_world_mode.cpp:根據圖塊類型有條件地設置 Traversable(可通行)標志

當前碰撞系統的邏輯是:每個物體都引用一個“碰撞體積”(collision volume),碰撞體積中包含了實體的物理范圍信息。在處理角色移動時,系統會通過檢測角色當前或即將移動到的位置是否存在碰撞體積,并依據這些體積來判斷是否可以移動。如果目標區域的碰撞體積被標記為可以通行,那么角色就可以自由移動過去。當前的角色移動方式正是基于這種機制完成的。

但是,現在我們正在考慮一種新的處理方式:不再默認所有格子都可以通行,而是根據具體位置決定哪些格子具有通行屬性。例如,在構建房間時,原本是將整個房間區域內所有地板格子統一標記為可以行走,但現在我們可以加入一些邏輯判斷,比如:

  • 如果某個格子的 offset_x 小于 -3 或大于某個值,那么就不給它添加“可通行”的標志;
  • 反之,如果滿足特定條件,則為其設置“可通行”標志。

這樣一來,就可以實現空間上的限制,構建出不可通行區域。角色在嘗試移動到這些區域時,碰撞系統會檢測到該區域沒有設置為“可通行”,于是阻止角色移動過去。這種方式可以更精細地控制角色的活動范圍,形成更復雜的場景結構,比如墻體、懸崖、障礙物等。

這種設計使得整個世界的構建更加靈活,并允許我們引入更多空間結構,比如平臺跳躍、梯度地形、多層地圖等。這也為未來實現高度差邏輯(例如角色不能直接跳上比自己高太多的格子)打下基礎。后續將進一步完善這種“格子可達性”的判定機制,使其不僅能表達通行與否,還能支持更復雜的移動條件和互動行為。
在這里插入圖片描述

在這里插入圖片描述

運行游戲,觀察可通行標志的效果

當前的移動和碰撞邏輯存在一些需要重新審視和改進的地方。

目前,在沒有物理障礙物阻擋的情況下,角色依然無法繼續向某些方向移動。表現為:角色在視覺上似乎可以前進,但實際上被“卡住”了,無法穿越某些區域。這種現象是由于地面格子沒有設置為“可通行”導致的。雖然表面上沒有障礙,但在邏輯層面,系統沒有將那些格子標記為可達,角色因此無法移動過去。

進一步分析發現,當前的格子系統沒有良好的選擇工具來處理“多個實體重疊”的情況,導致難以直觀判斷某個區域的實際狀態。尤其在存在多個實體疊加時,交互和調試都變得困難。對于這些問題,目前還沒有很好地解決辦法。

角色的移動邏輯當前是:檢測角色前方區域是否包含“可通行”的碰撞體積,如果有,就允許繼續移動。但是這種設計是假定空間是“連續”的,也就是角色是穿越一個開放的區域進行移動。然而,這樣的連續空間移動方式在當前設計下已經不再合適。

接下來的目標是:改變思路,不再依賴于“空白區域是否可以通行”來決定移動路徑,而是將地面格子視為“可落腳點”。也就是說:

  • 地面格子是一個個明確的“可停留點”;
  • 每次移動是從一個“落點”移動到另一個;
  • 判斷是否可以移動,不再是檢查空間是否空,而是看目標方向上是否存在另一個有效落點。

這種設計類似“跳格子”或“點對點導航”的邏輯,角色不會穿越空白區域,而是一步步“跳”到相鄰的落點上。這種方式也更容易支持高度差(Elevation)、平臺、障礙、路徑限制等復雜場景。

但要實現這一目標,必須對現有的移動系統進行重構:

  • 改變當前依賴空白空間檢測的移動判斷邏輯;
  • 為每個地面格子提供明確定義的“位置點”,并定義其與其他格子的鄰接關系;
  • 修改角色的移動控制,使其只能在這些“位置點”之間移動;
  • 保留原有的碰撞邏輯用于處理與其他實體或障礙物的交互。

最終,這種新的設計將使角色移動更具結構化,也更有助于構建復雜的地圖與機制,比如跳躍、單向通道、平臺升降等功能。而在當前階段,為了逐步實現這一目標,將開始在已有邏輯中添加必要的判斷和調整,逐步推進系統的重構過程。

編輯 game_sim_region.h:引入 sim_entity_traversable_point

當前的設計思路是將“可通行點”(traversable points)作為每個實體的一部分,進一步擴展現有的碰撞體積(collision volume)邏輯。碰撞體積組通常包含一系列的規則,用于描述物體如何與環境發生碰撞,而“可通行點”則是另一個概念,專門用來標記哪些位置是角色可以站立、移動到的區域。

思路是,除了碰撞體積外,每個實體可以有一組“可通行點”,這些點可以是具體的位置,角色如果想要移動到這些位置,需要確認目標位置是否是“可通行”的。也就是說,如果某個位置被標記為“可通行點”,角色就可以移動到該位置;如果不是,則無法移動過去。

這種設計的靈感來源于將每個實體的可移動性與具體的點關聯起來,而不是僅僅依賴于整個區域的“空曠”或“通行”的狀態。這意味著每個實體可以包含多個“可通行點”,而這些點是具體的、可以站立的地方,角色可以在這些點之間移動。例如,大多數情況下,這些“可通行點”會落在一個網格上,但系統也應該具備靈活性,以便在以后需要時可以支持非網格化的移動,允許角色在更多樣化的環境中移動。

這一設計的優點是更加結構化和靈活,能夠應對未來可能需要的復雜環境,比如平臺、階梯、斜坡等地形類型。每個實體可以在其內部定義多個“可通行點”,角色可以在這些點之間移動。為了實現這一目標,首先需要將“可通行點”與每個實體的碰撞體積組合起來,并確保每個位置的移動邏輯能正確識別這些點。

總之,目標是通過這種方式讓角色的移動更加明確,避免之前那種基于“空白區域”通行的模糊性,轉而使角色的移動依賴于具體的、定義明確的“可通行點”。這種方法也為將來擴展和創建更復雜的場景提供了靈活性。
在這里插入圖片描述

在這里插入圖片描述

黑板講解:蛇形路徑構造

在當前的設計中,為了讓角色能夠在不同的區域內進行移動,我們需要引入一個新的概念:可通行點(traversable points)。這些點并不僅僅是“可以穿過的區域”,而是明確的角色可以站立并移動到的地方。也就是說,這些可通行點是與實體關聯的,每個實體都可以有多個這樣的點,角色可以在這些點之間進行移動。

為了實現這一點,必須對現有的碰撞邏輯做一些調整。當前的碰撞處理方式是基于“碰撞體積”,即通過檢查是否能穿越某個區域來決定角色是否能移動。然而,新的想法是,不僅要檢測碰撞體積,還要增加“可通行點”的概念,只有在這些點存在的情況下,角色才能繼續移動。

舉個例子,如果我們想讓角色沿著一條“蛇形路徑”移動,這時就需要根據路徑的具體形狀來定義多個可通行點,而不是簡單地依賴于傳統的空曠區域。這種方式能讓角色的移動更加靈活和有趣,允許在復雜的路徑上進行更精細的控制。

為了實現這個目標,首先需要在每個實體中定義一個“可通行點”的集合。這些點代表了角色可以站立和移動的地方,而碰撞體積則用于標記那些不可穿越的區域。在代碼實現上,現有的碰撞邏輯會被調整,以便處理這些新的可通行點。

在進行碰撞處理時,當前的實現方式較為原始,并且存在很多可以改進的地方。然而,由于這是一個原型階段,現階段不會對這些代碼進行過多的修改,暫時會保持現有的狀態。當前的重點是確保引入“可通行點”的概念,并在角色移動時根據這些點來判斷是否能夠繼續前進。

簡而言之,通過將“可通行點”引入到每個實體中,可以讓角色在更復雜的地形和路徑上進行移動,而不是僅僅依賴于空曠區域的判斷。這種方法為未來的擴展和更復雜的場景提供了更多可能性,尤其是在角色路徑規劃和移動的精度方面。

考慮將實體的位置進行規范化

為了實現新的功能,需要對現有的代碼進行一些調整,特別是與碰撞體積和可通行單元相關的部分。主要目標是讓代碼更具語義性,并去除與“可通行單元”相關的處理。這意味著在移動邏輯中,將不再考慮傳統意義上的“可通行單元”,而是要專注于具體的站立點,這些站立點是角色可以在其上移動并停留的位置。

具體來說,當前的碰撞邏輯依賴于是否可以穿越某個區域,判斷是否可以進入某個單元格。然而,在新設計中,角色的移動是基于固定的“可通行點”,并且當角色決定從一個點移動到另一個點時,他們必須明確經過那個目標點。這些點是“已知”的、固定的,并且與地形的其他部分沒有直接的關系。因此,角色的每一步移動都嚴格遵循規則,不允許通過空白空間或無支持的區域。

這也意味著不再需要傳統的重力或自由下落的概念。角色的垂直位置(Z軸坐標)將始終和所站立的點一致,所以在游戲規則中不再有“自由下落”或“漂浮”的情況。實際上,可以刪除與重力和支持實體的相關代碼,因為這些概念已經不再適用。

總結來說,新的設計將移動邏輯轉變為基于可通行點的明確路徑,而不是傳統的穿越空曠區域。這種方法不僅簡化了物理引擎的復雜性,也讓游戲規則變得更加明確和可控,角色的每一步都會是一個明確的行動,確保了游戲的規則性和一致性。

編輯 game_sim_region.cpp.h:移除重力(Gravity)、Z軸支撐(ZSupported)和移動實體(MoveEntity)中的 Traversable 相關代碼

為了簡化碰撞處理和角色移動邏輯,現有的重力和支持性相關的概念將被去除。之前涉及的“重力”和“是否受支持”的標志將不再需要,因為角色的移動將不再受到這些因素的影響。角色的每次移動都是基于固定的“可通行點”,并且只有在沒有碰到障礙物的情況下才能移動到這些點上。這個過程不再涉及到重力或“自由下落”的情況。

具體來說,原本用于判斷是否可以通過空曠空間或移動的“可通行”標志也將被移除。現在,任何可以移動的空間,都可以通過計算碰撞體積與其他實體的碰撞來處理,只要沒有碰到障礙物,角色就可以前往該點。

在碰撞處理代碼中,之前為了計算角色能否繼續移動到一個區域,我們會使用一個“t_max”值來確定移動的最大距離。現在,所有的移動將假設最大距離是1,這樣就簡化了計算過程,避免了之前那種需要判斷移動到哪個空白區域的復雜性。

所以,碰撞處理代碼將變得更加簡單,只需要關注“是否能發生碰撞”這一點。當兩個實體發生碰撞時,代碼只需要檢測是否有障礙物阻擋角色的移動,而不再需要考慮“可通行區域”或“空白空間”的問題。

總的來說,這些改動的核心目標是讓角色的移動邏輯更加簡潔和明確,去除不必要的復雜性和冗余的計算。通過這種方式,角色的每次移動都是確定的,只要沒有碰到障礙物,角色就可以前往指定的目標位置。

在這里插入圖片描述

在這里插入圖片描述

編輯 game_world_mode.cpp:引入 MakeSimpleFloorCollision(構建簡單地板碰撞)

目前需要移除實體的“可通行”標志,因為新的設計將不再依賴該標志。現在,我們將通過修改碰撞檢測系統來實現角色在特定的“可站立點”之間移動。為此,首先需要在創建“地面碰撞”或“碰撞區域”時,將“可站立點”添加到這些區域內。

要實現這一點,首先需要調整現有的“簡單地面碰撞”函數,使其更加具體,加入“可站立點”的概念。這意味著在創建這些碰撞區域時,需要特別標記出“可站立”的位置。為此,我們可以擴展現有的功能,創建一個新的函數,命名為“make_simple_traversable”或類似的名稱,以便在處理地面碰撞時也考慮到這些可站立點。

接下來,在處理這些區域時,需要關注“可通行區域”中的位置。每個“可站立點”都會有一個相應的位置,并且這些點將影響角色的移動。具體來說,當創建一個“可站立區域”時,其總量將包括這些可站立點,而該點的位置可能會根據需要進行調整,比如稍微向下偏移。

目前,我們不需要對這些區域進行碰撞體積處理,因為這部分邏輯暫時不需要處理。雖然以后可能會希望為這些區域添加碰撞體積(例如某些區域可能高于其他區域),但目前可以暫時不考慮。

在繪制這些區域時,也需要顯示出這些“可站立點”。為了實現這一點,可以在繪制“碰撞體積”的時候,添加一個新的繪制函數,用于顯示這些“可站立點”。這樣,我們可以直觀地看到每個區域中可站立的具體位置。為此,繪制函數需要接受這些點的位置,并將其顯示在屏幕上,幫助我們理解和調試這一過程。

總的來說,當前的目標是將“可站立點”的概念整合到碰撞處理系統中,確保角色能夠在這些特定的點之間移動,而不再依賴原來復雜的“可通行”邏輯。這將簡化移動系統,明確每個角色移動的規則,并且為未來可能的擴展(例如非網格化的可站立點)做好準備。
在這里插入圖片描述

在這里插入圖片描述

運行游戲,角色持續向上移動,開始調查原因

目前的問題是在角色的垂直方向(Z軸)上不斷上升,而其他方向的運動是正常的。問題可能出在“地面點”的計算上。首先,需要確認如何計算角色的“地面點”。在代碼中,可以看到涉及到“玩家delta”和“地面測試”的部分,但目前不清楚具體是如何計算Z軸位移的。

一開始,可能存在一個“處理重疊”的概念,但目前我們并沒有使用這一部分,因此決定先移除它,以簡化問題的排查。接著,懷疑是某種“偏移”導致了角色垂直方向的運動。通過對代碼進行調試,可以看到,在角色的移動過程中,Z軸的位移值不應該發生變化。

進一步檢查時,發現“玩家delta”的Z分量可能正在無意間被修改,導致角色發生了上升運動。因此,決定暫時將“玩家delta”的Z分量設置為零,觀察是否能夠消除上升的運動。通過這種方式,發現問題的確是出在Z軸的加速度上,但目前并沒有明確的理由表明為何會出現這種現象。

繼續追蹤代碼,尤其是加速度的部分,檢查是否有意或無意地將加速度設置為正值,導致角色沿Z軸上升。通過進一步調試,確認Z軸的速度和加速度沒有被正確設置為零,導致角色仍然出現了向上的運動。

總之,問題的根本原因在于Z軸的加速度未被正確處理,造成了角色不應有的垂直運動。需要仔細審查加速度的設置過程,確保Z軸的運動被正確抑制,以避免出現不希望的上升運動。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

編輯 game_world_mode.cpp:取消空格鍵觸發跳躍

問題的根源在于按下空格鍵啟動游戲時,空格鍵觸發了跳躍代碼,從而導致了不希望出現的垂直運動。空格鍵的功能不僅僅是開始游戲,還意外觸發了角色的跳躍操作,導致了垂直方向的加速度。這就是我們看到角色不斷上升的原因。

目前的問題可以歸結為一個初始的跳躍動作,即按下空格鍵后,角色會進行一次跳躍,產生了垂直的速度。這段跳躍代碼在沒有被正確處理時,會在啟動時自動觸發。
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

運行游戲,角色保持在地板上

空格鍵的按下被錯誤地解釋為跳躍動作,這導致了游戲啟動時角色自動觸發了跳躍代碼,從而產生了不必要的垂直運動。這種情況看起來有些荒謬,但至少現在已經找到了原因。

問答環節

那么這種基于圖塊的移動方式是怎樣的?角色是否仍能在圖塊內自由移動,還是會“對齊”到圖塊?

關于基于“瓦片”的移動方式,問題是角色是否仍然能夠自由移動,或者是否會強制貼合到某個方塊上。最終的答案是,這兩種方式都不是實際的實現方式。

我們什么時候會解決坐標系統的問題?

計劃是首先清理路徑算法部分,然后整理存儲系統,假設坐標系統也會隨著整理一并得到完善和整理。

那樓梯井呢?以前樓梯的移動是無縫的,現在房間是鎖定的,樓梯該怎么處理?

樓梯的使用設計是,在樓梯之間移動時,玩家仍然能夠穿越它們,但不能在上下樓梯時改變方向。這種設計是刻意的,因為大多數情況下,一旦下樓后就無法再上去。因此,玩家在使用樓梯時需要做出選擇并堅持這一選擇。

有沒有打算換成 Visual Studio Code,而不是完整的 IDE?我記得現在完整的調試器已經包括在里面了

關于是否將代碼視頻化,首先不太清楚為什么需要這樣做。其次,提到Linux版本時,有人表示它并沒有展示所有功能,比如匯編部分等。所以不確定是否有更好的Windows版本需要驗證。如果沒有明顯的優越性,那么就沒有必要做出這樣的改動。

將每個圖塊都設為一個實體,有什么優點或權衡?

將每個地磚(tile)視為一個實體的原因在于,希望每個地磚能夠像其他任何實體一樣,參與所有的交互和變換。例如,地磚也應該能受到魔法、重力、變形等效果的影響。通過這種方式,無論是地面上的魔法浮空、改變重量或是其他效果,地磚都能按照相同的規則反應,從而讓游戲更具互動性和趣味性。

此外,使用每個地磚作為一個實體的好處是可以實現更靈活的關卡設計,避免了傳統網格限制,可以創建非規則的關卡布局。而如果強制使用網格,就無法實現這種靈活性。因此,選擇不強制使用網格,而是將每個地磚當作一個獨立的實體,可以更充分地利用現代計算機的處理能力,尤其是在2D游戲中,充分發揮處理器的計算能力去做有趣的模擬和交互,而不是僅僅進行3D渲染等資源密集型操作。

然而,這樣做的代價是,雖然可以讓游戲設計更加自由和豐富,但也放棄了使用網格帶來的優化。例如,網格化可以幫助我們壓縮存儲,減少內存帶寬和計算時間的浪費,使得游戲運行更加高效。但決定不使用網格,而將每個地磚當作獨立實體,意味著犧牲了一定的組織性和存儲效率。

你是把實體分配在堆上還是棧上?是堆上吧?那你能在棧上分配這么多東西嗎?

關于是否將實體分配在堆棧上或堆上,首先要澄清的是堆和棧的區別。棧是為函數調用分配的內存區域,而堆是用于動態內存分配的區域。在這種情況下,實體并沒有分配在函數調用的棧上,因為它們的生命周期與函數調用棧的生命周期不匹配。

實際上,實體并沒有真正分配在堆上。堆是虛擬內存系統中的一部分,通常使用虛擬內存分配(如malloc)來動態分配內存。但在這里,實際上沒有使用傳統意義上的堆,因為我們并沒有調用堆分配函數(如malloc)。

我們使用的是一個自定義的內存棧,它并不是與函數調用相關的棧,而是一個我們自己在內存中管理的棧。這意味著,實體分配的是一個內存塊,類似于棧的結構,并通過虛擬內存系統進行管理。所以,實體實際上是從我們手動分配的內存塊中按需分配的,而不是傳統的堆或棧。

你覺得這個新的網格地板系統在處理不同高度的可通行實體時,會不會和紋理貼圖(texture splats)發生沖突?

關于是否會因引入新的非網格實體系統和紋理切片而引發問題,特別是在希望實體具有可變高度的情況下:

不會存在這類問題。之前的紋理切片渲染方式已經被廢除,因此不會再使用那種渲染方法。這也是為何我們刪除了舊的“地塊”(ground trunk)系統。我們曾使用所謂的“ground chunks”(大塊區域)來組織地形和紋理映射,但由于該方式限制了可變高度和非網格結構的表達能力,現在已經放棄了。

新的系統將不再依賴固定的網格和切片紋理,而是采用更靈活的渲染與數據管理方式,使得每個實體都能擁有不同的尺寸、形狀甚至動態表現,進而實現更豐富的表現力與更自由的空間組織。因此,設計上的這些調整正是為了避免這類問題,而不是制造新問題。

既然現代 CPU 經常會重排序或合并寫操作,那它們是如何使用內存映射寄存器的?還能用來 bit-bang 串行協議嗎?

在現代CPU中,通常會對寫操作進行重排序或合并處理。然而,在面對內存映射寄存器(memory-mapped registers)時,情況變得更復雜和特殊。

CPU的內存管理單元(MMU)會識別不同類型的內存頁面,例如某些頁面會被標記為“寫合并”(write-combining),而另一些則不會。這意味著CPU在訪問這些內存時的行為取決于這些頁面的具體屬性,尤其是在頁面被映射用于硬件I/O而不是普通程序數據時。

在與硬件通信的場景中(比如實現串行協議或設備驅動),我們必須非常清楚當前操作的是哪類內存,并且明確CPU是否可以自由優化訪問順序。為了保證訪問順序正確,CPU提供了內存屏障(memory fence)和指令序列屏障(instruction barriers)等機制,可以明確指示:某些內存操作不得被重排序到某些屏障指令之前或之后。這些屏障命令通常會告訴CPU不要在重排序緩沖區中跨越某個屏障對讀寫操作進行優化。

因此,在實現依賴嚴格順序的通信協議或硬件控制時,需要開發者非常了解其所運行的CPU架構和內存子系統,合理使用內存屏障和同步機制來防止無意的指令重排序。

在早期的計算機架構中,情況要簡單得多,因為當時的CPU沒有緩存(cache)、也沒有亂序執行(out-of-order execution),所以內存訪問順序天然是嚴格遵循程序順序的。但在現代體系結構中,必須顯式處理這些細節,以確保正確的系統行為。

如果主角走到比自己高的地形后面,渲染時該怎么處理被遮擋的問題?

目前對于渲染方面的計劃還不確定,特別是當主角走到一個比他高的地形后面,被該地形遮擋的情況。目前還沒有明確的處理方式,也不確定是否會經常遇到這種場景。對這種遮擋關系要如何呈現,暫時沒有具體方案,也不太確定最終會采用哪種渲染方式。這部分渲染的邏輯可能需要根據后續實際需求和效果再做決定。總之,目前對于這類遮擋問題的渲染策略尚未定案。

這個系統是否能解決多個實體堆疊時碰撞區域卡住的問題,例如兩個主角加載在同一位置?

目前確實存在多個角色在同一位置加載時可能會發生卡住或重疊的問題,例如兩個角色被加載在同一個位置時可能會互相堆疊。但目前并沒有著手去改進碰撞檢測器的部分,當前的重點是讓游戲系統按最終設想的方式正常運行。也就是說,現在并不是在嘗試提高碰撞系統的質量,而是優先調整游戲結構和流程,使其朝向最終設計目標發展。等到系統框架穩定之后,才會對碰撞檢測等具體細節進行進一步完善和優化。最終會確保這些問題被正確處理,但目前這部分還不是重點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80895.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80895.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80895.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java反射詳細介紹

的反射&#xff08;Reflection&#xff09;是一種強大的機制&#xff0c;允許程序在運行時動態獲取類的信息、操作類的成員&#xff08;屬性、方法、構造器&#xff09;&#xff0c;甚至修改類的行為。它是框架開發&#xff08;如 Spring、MyBatis&#xff09;、單元測試工具&a…

c語言第一個小游戲:貪吃蛇小游戲05

貪吃蛇脫韁自動向右走&#xff1a;脫韁的野蛇 #include <curses.h> #include <stdlib.h> struct snake{ int hang; int lie; struct snake *next; }; struct snake *head; struct snake *tail; void initNcurse() { initscr(); keypad(stdscr,1); } int …

react-diff-viewer 如何實現語法高亮

前言 react-diff-viewer 是一個很好的 diff 展示庫&#xff0c;但是也有一些坑點和不完善的地方&#xff0c;本文旨在描述如何在這個庫中實現自定義語法高亮。 Syntax highlighting is a bit tricky when combined with diff. Here, React Diff Viewer provides a simple rend…

coco數據集mAP評估

0 coco數據集劃分說明 1 用yolo自帶的評估 from ultralytics import YOLOmodel YOLO("../spatial-perception/checkpoints/yolo11n.pt")metrics model.val(data"./coco.yaml", save_jsonTrue) ## save_json為True,可以把預測結果存成json文件&#xff…

sensitive-word-admin v2.0.0 全新 ui 版本發布!vue+前后端分離

前言 sensitive-word-admin 最初的定位是讓大家知道如何使用 sensitive-word&#xff0c;所以開始想做個簡單的例子。 不過秉持著把一個工具做好的原則&#xff0c;也收到很多小伙伴的建議。 v2.0.0 在 ruoyi-vue&#xff08;也非常感謝若依作者多年來的無私奉獻&#xff09…

好消息!PyCharm 社區版現已支持直接選擇 WSL 終端為默認終端

在過去&#xff0c;PyCharm 社區版雖然提供了鏈接 Windows 子系統 Linux&#xff08;WSL&#xff09;終端的能力&#xff0c;但用戶無法在設置中直接指定 WSL 為默認終端&#xff0c;這一功能僅限于專業版使用者。 而現在&#xff0c;在 PyCharm 2025.1.1 版本中&#xff0c;Je…

【Redis】string 字符串

文章目錄 string 字符串常用命令設置和獲取setgetmget & mset 計數操作incr & incrbydecr & decrbyincrbyfloat 字符串操作appendstrlengetrangesetrange 應用場景 string 字符串 關于 Redis 的字符串&#xff0c;有幾點需要注意 Redis 所有的 key 的類型都是字符…

本地部署firecrawl的兩種方式,自托管和源碼部署

網上資料很多 AI爬蟲黑科技 firecrawl本地部署-CSDN博客 源碼部署 前提條件本地安裝py&#xff0c;node.js環境,嫌棄麻煩直接使用第二種 使用git或下載壓縮包 git clone https://github.com/mendableai/firecrawl.git 設置環境參數 cd /firecrawl/apps/api 復制環境參數 …

(三)毛子整潔架構(Infrastructure層/DapperHelper/樂觀鎖)

文章目錄 項目地址一、Infrastructure Layer1.1 創建Application層需要的服務1. Clock服務2. Email 服務3. 注冊服務 1.2 數據庫服務1. 表配置Configurations2. Respository實現3. 數據庫鏈接Factory實現4. Dapper的DataOnly服務實現5. 所有數據庫服務注冊 1.3 基于RowVersion的…

uni-app微信小程序登錄流程詳解

文章目錄 uni-app微信小程序登錄流程實戰詳解微信小程序登錄流程概述1. 獲取登錄憑證&#xff08;code&#xff09;2. 發送登錄請求3. 保存登錄態4. 登錄狀態管理5. 應用登錄狀態請求攔截器中添加 token自動登錄頁面路由守衛 使用 Vuex 集中管理登錄狀態登錄組件示例登錄流程最…

GUC并發編程和SpringCloud,二者之間的關系

一.提問 我認為&#xff0c;Java開發中&#xff0c;如果項目的每一個小模塊需要不同人員并行開發時&#xff0c;就需要使用SpringCloud&#xff1b;如果要解決系統用戶激增&#xff0c;就是用GUC并發編程。 這個說法對么&#xff1f; 二.解答 你的理解部分正確&#xff0c;但不…

在 Vue 3 中使用 canvas-confetti 插件

&#x1f389; 在 Vue 3 中使用 canvas-confetti 插件 canvas-confetti 是一個輕量、無依賴的 JavaScript 動畫庫&#xff0c;用于在網頁上展示彩帶、慶祝動畫。非常適合用于抽獎、支付成功、活動慶祝等場景。 本教程將指導你如何在 Vue 3 項目中集成并使用該插件。 &#x1…

深入解析Spring Boot項目目錄結構:從新手到規范實踐

一、標準項目結構全景圖 典型的Spring Boot項目&#xff08;Maven構建&#xff09;目錄結構如下&#xff1a; my-spring-project/ ├── src/ │ ├── main/ │ │ ├── java/ # 核心代碼 │ │ │ └── com/ │ │ │ └── exa…

【C語言】宏經典練習題,交換奇偶位

交換奇偶位 寫一個宏&#xff0c;可以將一個整數的二進制位的奇數位和偶數位交換。 #define Swap(x) x(((x&0x55555555)<<1)((x&0xaaaaaaaa)>>1)) int main() {int a 10;Swap(a);printf("%d\n", a);return 0; } 寫宏的思路&#xff1a; 假設…

VSCode-插件:codegeex:ai coding assistant / 清華智普 AI 插件

一、官網 https://codegeex.cn/ 二、vscode 安裝插件 點擊安裝即可&#xff0c;無需復雜操作&#xff0c;國內軟件&#xff0c;無需科學上網&#xff0c;非常友好 三、智能注釋 輸入 // 或者 空格---后邊自動出現注釋信息&#xff0c;&#xff0c;按下 Tab 鍵&#xff0c;進…

FFmpeg 與 C++ 構建音視頻處理全鏈路實戰(三)—— FFmpeg 內存模型

經過前面文章的 FFmpeg 編程實踐&#xff0c;相信你已經對AVPacket和AVFrame這兩個核心結構體不再陌生。當我們編寫代碼時&#xff0c;頻繁調用unref系列 API 釋放內存的操作&#xff0c;或許讓你心生疑惑&#xff1a;這些函數究竟是如何實現內存釋放的&#xff1f;又該在何時準…

c 中的哈希表

哈希是一種可以接受各種類型、大小的輸入&#xff0c;輸出一個固定長度整數的過程。你可以將哈希理解成一種特殊的映射&#xff0c;哈希映射&#xff0c;將一個理論無限的集合A映射到有限整數集合B上。 哈希函數&#xff1a;哈希函數是哈希過程的核心&#xff0c;它決定了哈希映…

【一次成功!】Ubuntu22.04安裝cartographer

之前在ubuntu20.04上成功安裝cartographer&#xff0c;但是翻遍全網都沒找到官方的22.04安裝教程&#xff0c;然后找到小魚的&#xff0c;試了一下&#xff0c;一次成功&#xff0c;連接如下&#xff1a; gd2l-ros2/docs/humble/chapt10/get_started/2.Carto介紹及安裝.md at …

【WPF】Opacity 屬性的使用

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Opacity 屬性是定義一個元素透明度的屬性&#xff0c;其值范圍是從 0.0&#xff08;完全透明&#xff09;到 1.0&#xff08;完全不透明&#xff09;。由于 Opacity 是在 UIElement 類中定義的&…

8天Python從入門到精通【itheima】-6~10

目錄 7節-開發出第一個Python程序: 1.在cmd窗口寫下第一個最簡單的程序:Hello World!!! 9節: 1.如何卸載python: 2.報錯:不是可運行的程序 ?編輯 3.報錯:無法初始化設備PRN: 4.報錯:語法錯誤——非法的字符 10節-python解釋器: 1.python解釋器的原理: 2.解…