game_sim_region.cpp:在BeginSim中移除EntityOverlapsRectangle調用
現在我們接近一個關鍵點,雖然還沒完全結束,但我們已經把所有東西遷移到了一個新概念上——即那些臨時創建的控制器結構,稱為“腦”(brains),這些腦的運行效果還算不錯。
接下來我們還有一些關于實體劃分的工作要做。上一期結束時,我們開始拆分實體的渲染邏輯,這次我們準備繼續推動這個進程,看看接下來要重點解決什么大問題。
在開播前,有人提醒了一個潛在的bug,這個bug現在還沒引發問題,因為我們目前還沒觸發相關的操作,但以后肯定會出問題。這個bug是因為最近對開始模擬(BeginSim)和結束模擬(EndSim)流程的處理沒有重新檢查導致的。
具體來說,現在我們用的系統是先加載區塊(chunks),將所有實體從區塊中提取出來,模擬它們,然后再寫回區塊。這里面會把區塊拆掉,因為我們不打算再把數據打包回去,只是簡單地把實體從區塊中取出。
但是代碼里還保留了“EntityOverlapsRectangle”這個調用,用來修剪邊界。結果導致一些實體因為沒通過這個檢測,沒被加載進來,從而無聲無息地“消失”了。它們被從區塊拉出,但沒有被記錄保存,所以丟失了。
這個問題的修復其實很簡單——直接刪掉這段代碼,因為它是不必要的。這樣做以后就不會再有邊緣實體莫名消失的問題。
這次發現這個bug真的幫我們節省了很多時間,否則以后可能要費很大功夫去找原因。
目前來看,這個修復不會影響現有功能,只是防止未來出問題。
另外,我們還需要確認“AddEntity”函數本身是否總是正確添加實體。回顧 BeginSim 流程發現,我們會遍歷所有實體,除非達到最大實體數限制(那是無效路徑,必須避免),否則都會添加實體。
即使實體不在模擬矩形范圍內,也會被保留,只是標記為不更新(Unupdate),雖然不確定以后是否還保持這種設計,但至少這樣能確保實體被加載,這才是重點。
以上就是這次主要修復和流程確認的內容。
運行游戲,準備將所有實體遷移到新路徑
我們需要回到之前的工作方向。昨天開始,我們嘗試讓實體的渲染變得更加程序化,或者說更偏向于數據驅動,而不是通過硬編碼的函數來決定它們如何渲染。目前,只有樹這種實體走的是新的渲染路徑,其他實體依然沿用舊的渲染方式。
現在,我們的目標是逐步把所有其他實體都遷移到新的渲染路徑上,直到沒有任何實體仍然使用舊的渲染流程為止。這么做是為了讓渲染體系更加統一和靈活。
接下來,我們會著手分析并實現這個遷移過程,看看如何才能完成所有實體渲染路徑的切換,從而徹底擺脫舊的硬編碼渲染方式。
之前我先檢查一下代碼感覺磚塊之間跳動的很奇怪
修復一下
game_brain.cpp:讓ExecuteBrain使用ddP2而不是ddP
我們需要順帶修復另一個在論壇上提到的 bug,這個問題出現在實體邏輯的“brain”模塊中。雖然目前這段代碼還沒有真正發揮作用,但仍然需要修正。問題在于我們原本使用了一個二維向量(ddp),但在這里其實需要的是一個三維向量(ddp2)。由于讀取方式錯誤,可能會越界讀取,導致結果完全錯誤,甚至會返回一些毫無意義的數據。因此我們已經將其替換為正確的三維向量,以避免后續潛在問題。
現在,除了樹木之外的其他實體仍然使用舊的渲染路徑進行渲染。我們的目標是將它們全部遷移到新路徑上,使所有實體統一走數據驅動的渲染流程。
我們先觀察現有的新渲染路徑的工作方式。整體機制是這樣的:遍歷所有需要繪制的渲染片段(piece),對于每一個片段,我們提取出對應的貼圖(bitmap),然后將其推入渲染隊列中。這種方式類似于之前處理樹木時的邏輯,只不過現在我們需要讓更多類型的實體也遵循這種流程。
當前代碼中還有一些舊的渲染代碼是死代碼,準備被淘汰。我們可以將諸如繪制生命值(draw hit points)這類內容整合進新的渲染路徑邏輯中,這樣結構會更加合理。
接下來我們準備處理其他實體的遷移,比如“monstar”實體。它的渲染方式很簡單,僅包含一個陰影和一個軀干貼圖,這種結構很容易適配新的分片渲染系統。我們只需給“monstar”配置對應的渲染片段即可。
同理,“hero head”(英雄頭部)也可以遷移過去,我們只需要給它增加相應的偏移值,就能順利完成整合。
總之,接下來的任務是把所有實體按照新的方式定義它們的渲染片段,并統一走新架構流程,逐步廢除舊的硬編碼渲染邏輯,從而實現一個更加模塊化、數據驅動的渲染系統。
game_world_mode.cpp:讓AddPiece接收一個Offset參數
我們現在要為渲染片段(piece)添加一個偏移量(offset)。這是渲染系統向更復雜動畫系統演進的一步。當前我們先僅支持一個靜態的偏移量,不急于做過度設計,因為后續可能會隨著動畫需求的增加而變得更復雜。
這個偏移量之后可能不僅僅是一個固定值。例如,如果我們希望某個身體部位有動畫效果,比如頭部搖動或者手臂擺動,就需要用到動態偏移,甚至與動畫系統聯動。但是目前階段我們先實現一個基礎版本,等處理主角(hero)的動畫時再獲得更多設計方面的啟發,到時再決定是否擴展。
完成對偏移量字段的定義之后,我們接著在 add_piece
函數中加入對這個偏移值的設置邏輯。我們將偏移值賦給渲染片段對象,確保它能夠參與渲染計算。
隨后,我們預期在編譯時會遇到一個報錯,因為之前調用 add_piece
時并沒有提供這個新的偏移參數。為了解決這個問題,我們統一在舊的調用點中將偏移量設為 (0, 0)
,表示默認無偏移,從而確保現有邏輯不受影響。
接下來,在進行測試時,我們特別注意到數據結構布局發生了變化。由于我們沒有實現結構數據的熱重載,因此每次變更數據結構時都需要重新啟動游戲程序。代碼本身是支持熱重載的,但只限于代碼邏輯的變更,不包括結構定義和數據布局的變更。
目前為止,一切都運行正常,我們確認偏移值添加成功,并沒有破壞已有功能。這為后續將所有實體遷移到新渲染流程打下了基礎,也為引入動畫支持提供了接口。我們將繼續擴展和完善這部分內容。
PushBitmap第五個參數就是offset
game_world_mode.cpp:讓AddMonstar調用AddPiece
我們現在已經可以正確繪制樹木了,接下來要把其他實體(例如怪物 monstar)也遷移到新的渲染路徑中來。我們通過調用 add_piece
函數為 monstar 添加它的渲染片段。具體來說,我們進入了舊有的 monstar 渲染代碼中,找到它原本使用的 push_bitmap
調用,將其改為新的 add_piece
方式。
monstar 的渲染涉及兩個部分,一個是影子(shadow),另一個是身體(torso)。我們知道這兩個圖像資源分別對應 Asset_Shadow
和 Asset_Torso
。因為舊代碼中沒有給 monstar 添加偏移量,所以我們在 add_piece
調用中偏移量設為默認值 (0,0)
,不進行任何調整。
關于 ShadowAlpha
的處理,在現階段我們沒有特別處理這個參數,而是暫時將其設置為 0.5,表示一個固定半透明度的影子。不過我們意識到這個做法只是臨時措施,因為影子的渲染最終將需要特殊處理。
具體而言,影子的渲染不應該和普通圖像一樣處理。未來的渲染器需要支持單獨的一輪影子渲染處理流程,因為影子之間的疊加規則并不總是線性的。例如,兩個影子重疊不代表應該更暗;可能需要根據特定邏輯決定最終表現效果。因此,我們在代碼中添加了一個 TODO 注釋,標注影子渲染需要單獨處理,并考慮標記某些片段為“影子”,以便渲染器在后期能夠區分并單獨渲染。
此外我們注意到影子當前的高度也設置為 4.5,這和其他實體一致,暫時保留這一設定。是否合理還有待進一步觀察和調整。
最后一步是為 add_piece
的調用補全參數。它需要傳入五個參數:實體引用、圖像類型、渲染高度、偏移量、顏色。我們設置了顏色為默認值,完成之后就可以預期看到 monstar 被正確繪制出來,進入新的統一渲染流程中。
這一步為系統中更多類型的實體遷移到新渲染體系奠定了模板,同時也為未來影子系統的完善打下了基礎。我們后續會繼續處理主角及其他對象的遷移。
運行游戲,看到Monstar出現
我們現在已經成功渲染出怪物 monstar,它已經按照新的渲染路徑被繪制出來了,可以清楚地看到其顯示在屏幕上。
不過,目前我們尚未開啟碰撞系統,之前相關的邏輯被關閉了,因此現在我們仍然可以隨意穿過 monstar 所在的位置,也就是說,玩家可以直接移動到與怪物重疊的位置。這顯然是不正確的行為。
因此,接下來的一個關鍵工作,就是將 monstar 的移動行為也遷移到新的“控制器”系統中。這個控制器系統是我們新引入的實體控制方式,用于管理實體的位置、狀態以及與其他實體的空間關系。如果怪物使用了這個新的控制系統,它就能夠在邏輯上占據它所在的方格(Tile/Square),這樣其他實體就無法隨意進入同一個位置,從而實現基本的碰撞檢測和空間占用邏輯。
目前的狀態下,怪物雖然顯示出來了,但并沒有參與到實體間的空間占用判斷中。所以我們需要盡快讓它進入這個控制器體系,使得它可以成為一個在空間中有實際存在意義的對象,而不僅僅是一個圖像。
這也為我們后續實現更復雜的行為,比如怪物的自動移動、AI邏輯、攻擊觸發等等,打下了必要的基礎。這個步驟不僅能恢復基本的碰撞機制,還能夠讓整個模擬系統更加一致、健壯。
game_world_mode.cpp:讓AddPlayer調用AddPiece
我們繼續沿著清理和重構的路線推進,現在已經完成了 monstar(怪物)的渲染系統遷移,接下來我們將 hero head(英雄頭部)也遷移到新的渲染方式中。
具體做法如下:
我們在創建玩家實體時(add player 的時候),給它添加一段用于構建渲染數據的邏輯,也就是為玩家添加渲染所需的“頭部”組件。這個“頭部”使用的是 hero head 的圖像資源(也稱為 asset hero head,或簡稱 head)。
我們還使用了一個叫做 hero size c 的尺寸數據,這個尺寸值并不太明確我們最初為什么設定成這樣,但目前看是為了在渲染時進行方便縮放。所以暫時還是保留它,并將它移動到靠近渲染構建的位置,方便后續統一清理。
同時我們也添加了一個臨時的顏色值,雖然目前顏色并沒有被特殊處理,但它是接口中必須提供的參數之一,因此先填入一個占位顏色。
最后將這一切集成后,我們重新運行程序。可以看到,英雄的頭部已經成功渲染出來,并且會根據方向發生變化,說明方向切換與渲染系統集成良好,頭部方向能正確響應移動輸入。這表明英雄頭部的渲染遷移已經成功,邏輯上也已經接入新的系統。
這一改動完成后,意味著又有一類實體(玩家頭部)脫離了舊的渲染路徑,成功進入到新的、數據驅動的渲染方式中。隨著越來越多的實體完成這一步,我們將徹底擺脫原本硬編碼的渲染流程,轉而使用更加靈活、可擴展的系統,從而為后續動畫系統、角色自定義、狀態切換等復雜機制提供基礎。
game_world_mode.cpp:考慮如何處理HeroBody和Familiar
我們正在持續、有條不紊地逐步清除舊的實體渲染路徑,目前已經將大多數實體遷移到新的系統中。現在只剩下兩個比較復雜的實體:familiar(跟隨物)和hero body(英雄身體)。這兩個實體與之前不同,它們包含了動畫行為,因此不能像之前那些靜態實體那樣直接遷移。
具體來說:
- familiar 有一個上下浮動的動作,表現為不斷“跳動”;
- hero body 則具備某種“彎曲”的動態效果。
這些動作特性意味著它們的渲染不僅依賴于靜態的偏移或貼圖信息,還需要考慮實時變化的數據,也就是說它們的渲染組件必須能響應實體當前的運動狀態,而不是像普通實體那樣只依賴于構造時的固定數據。
因此,我們需要構建一種新的基礎架構,來描述這種更復雜的運動與渲染關聯:
-
需要一個“骨架系統”(Armature) —— 它不是傳統意義上的 3D 骨骼,而是用于記錄每個實體在運動過程中產生的動態參數,比如上下浮動的幅度、角度偏移等;
-
渲染部件需要與“骨架”掛鉤(Latch on) —— 即渲染片段(如貼圖、頭部、身體等)應該知道自己應該如何響應骨架數據進行變換,比如隨浮動高度變化、按角度扭曲等;
這種機制的概念其實就是一種“骨骼動畫系統”的原型。雖然我們現在還沒有建立完整的動畫系統框架,但實際上我們已經在手動處理類似的邏輯,例如英雄頭部和身體在行走時會有不同的擺動,這種效果本質上和骨骼動畫非常接近。
接下來我們的目標就是:
- 把這種動畫行為系統化,統一管理動畫相關的數據;
- 把靜態和動態的渲染部分融合到一套統一的數據驅動框架中;
- 通過構造一套“動態控制 + 渲染輸出”的接口機制,讓渲染組件能夠感知并響應實體狀態的變化。
這是朝向更加模塊化、可擴展、支持高級動畫表現的渲染系統邁出的關鍵一步。我們正在逐漸揭示并建立真正需要的系統,而不再是零散處理每個實體的特殊行為。這個過程雖慢,但非常重要,也是整個系統進化的核心所在。
game_world_mode.cpp:讓AddPlayer為Shadow、Torso和Cape調用AddPiece,暫時不包括動畫部分
我們開始著手整理并重構關于英雄身體的渲染邏輯。原本的 heroBitmaps
變量(包括頭部、披風、軀干等)現在已經不再必要,因為我們已經通過新的 add_piece
接口以更清晰的方式表示這些構成部分,因此可以將其清除或置為 0。
接下來我們采用逐步構建的方式,先生成不含動畫的靜態版本,確保基本顯示邏輯沒有問題,然后再計劃如何恢復動態效果。當前階段的目標是把英雄的軀干和披風這類有動畫的組件,先以靜態圖像形式顯示出來。
具體操作如下:
- 在英雄的渲染配置中,除了頭部,現在加入了身體的三個部分:影子(shadow)、軀干(torso)、披風(cape);
- 對于影子部分,本身就沒有動畫,處理起來簡單,直接加入;
- 軀干和披風這兩個部位在原始實現中具備動畫效果 —— 包括
xAxis
和yAxis
的動態參數用于擺動等; - 目前這部分動畫參數暫時移除,僅以靜態狀態添加進渲染流程,以后再逐步恢復;
- 顏色參數也臨時使用了當前已有的配置,比如使用
(1,1,1)
或直接復用之前已有的顏色邏輯; - 此外,像
T-Bob
這種用于處理高度偏移或特殊跳動行為的參數,在現階段也無法正確反映,因此暫時也未處理; - 為了日后方便恢復動畫邏輯,將
ShadowAlpha
用宏定義的方式提取了出來,作為未來重構處理陰影層時的參考。
最終,雖然我們暫時失去了部分動態表現(如擺動和跳動),但英雄的主要可見部分已經可以通過新路徑被正確渲染。我們接下來的任務就是圍繞這些靜態基礎構件,重新構建動態系統的掛鉤方式,以便恢復完整的動畫行為。這個階段是“先拆分靜態組件、再統一動畫邏輯”的關鍵過渡步驟。
運行游戲,看到除Familiar外的所有角色
現在場景中除了 familiar(跟隨物)之外,其它所有實體已經成功被渲染出來了。它們都出現在應有的位置,并且渲染邏輯本身運作正常,沒有出現錯誤或崩潰現象。
目前的渲染效果缺少了先前我們為測試目的所添加的跳躍動畫(hop animation),因此整體視覺表現看起來呆板、笨拙許多。這種顯著的動畫缺失導致角色顯得不再生動,這當然是不理想的,但這是我們預期中的現象。
我們已經進入了一個重要的階段 —— 把所有實體組件轉換為可通過統一系統管理的靜態形式,并確認這些基礎渲染在無動畫狀態下能夠正常工作。雖然臨時犧牲了動畫效果,但這是為后續更合理的動畫系統架構做準備的必經之路。
當前缺失的動畫效果和視覺表現將在后續階段中被逐步恢復,我們將設計和引入更系統化的機制來支持角色的動態表現,例如跳躍、擺動、跟隨等行為,并使其與當前的新組件渲染系統兼容統一。整體工作仍在持續推進中,方向和狀態都符合預期。
game_world_mode.cpp:讓AddFamiliar調用AddPiece
現在進入了最后一個渲染實體的處理階段:familiar(跟隨物)。
我們參考之前的處理方法,對 familiar 實體進行了與其他實體類似的處理流程。具體操作如下:
-
替換渲染調用:
將原先使用的push_bitmap
等直接繪制函數替換為統一的add_piece
調用方式。
familiar 的兩個渲染組件(影子和頭部)分別被轉化為兩個add_piece
調用。 -
設置參數:
- 傳入參數中,使用當前的實體指針作為識別目標;
- 對于影子部分,直接指定為 shadow 類型,沒有額外邏輯;
- 對于頭部部分,同樣創建了對應 piece,但缺乏動畫信息(例如上下跳動的 bob 動作),暫時被省略;
- Bob 動作涉及的
BobSign
變量失效,目前還未恢復; - 有部分舊代碼中的變量如
b
,被確認僅是頭部位圖,不影響結構統一化;
-
臨時放棄動畫邏輯:
由于現階段目標是完成所有實體的靜態結構整合,暫時不處理 familiar 的動態 bob 動作,只保證其渲染結構存在,后續會通過新的動畫系統來恢復動態表現。 -
完成整合:
至此,所有游戲中的實體(包括 hero、monstar、trees 和 familiar)都完成了從舊式渲染方法向add_piece
系統的遷移。
總結來說,現在所有實體的渲染都已經整合到了統一的組件系統中,盡管臨時犧牲了部分動畫表現(如跳動、擺動等動態效果),但靜態結構完整,渲染穩定,為后續構建更靈活的動畫系統奠定了良好基礎。整個過程正在按預期逐步推進。
運行游戲,看到所有角色出現
我們已經成功重建了所有實體的靜態渲染結構,并徹底清理掉了原先基于硬編碼的老舊渲染邏輯。現在所有實體的渲染都通過統一的、結構化的 add_piece
系統進行生成,這使得我們可以靈活地組合任意渲染片段,從而極大地提升了渲染系統的可維護性與擴展性。
接下來的目標是將之前臨時移除的動態動畫部分重新整合回系統中。計劃如下:
-
識別缺失內容:
明確目前尚未回歸的功能,主要包括:- hero 身體部件的擺動動畫;
- familiar 的上下浮動動畫;
- cape 等部件的動態彎曲;
- 與 x 軸、y 軸相關的動態變換;
- 部件間相對位置的實時調整。
-
優先采用最簡單方式重建動畫行為:
首先將動畫以最基礎的方式回歸系統,例如:- 使用 sin 波函數恢復上下浮動;
- 靜態賦值補回部分動畫偏移;
- 手動硬編碼動畫參數以快速驗證系統結構完整性;
后續再根據經驗逐步泛化邏輯和數據結構。
-
構思下一階段動畫系統架構:
為了支持更復雜和靈活的動畫需求,初步計劃如下:- 設計一個“骨架系統”用于描述動畫控制結構(即每個 piece 的動畫參數綁定);
- 將動畫參數獨立成可配置模塊,例如 Bob 值、傾斜角、偏移方向等;
- 構建動畫驅動的參數綁定邏輯,使得 piece 可按動畫狀態動態生成;
- 建立 piece 與其動畫狀態的對應關系,形成數據驅動的動畫機制。
-
目標是構建一個可擴展的部件動畫系統:
通過上述步驟,不僅恢復現有動畫,還為將來支持更多角色、更多動畫狀態(如攻擊、跳躍、受傷等)預留出接口和數據結構。
當前的成果奠定了系統統一化和模塊化的基礎,下一步將通過增量改進方式,將動態動畫邏輯一點點整合進新的渲染系統中,逐步過渡到一個具有“動畫骨架驅動”的靈活可控的角色動畫框架。
game_entity.h:引入entity_visible_piece_movement,包含AxesDeform和BobOffset
我們在游戲實體(game entity)內部增加了一個非常基礎的組件,叫做“移動相關的渲染片段”(movement piece)。這個組件用來描述實體的可見部分如何隨著運動產生變化。具體設計了幾個關鍵點:
-
運動類型標記(movement flags):
通過標記的方式來定義渲染片段的運動屬性,例如:- None 或 Rigid(剛性):表示這個部件直接附著在實體的骨架或框架上,沒有任何額外變形。
- 軸向變形(Axis Deform):當實體的軸(例如身體方向或姿態軸)發生變形時,這個部件也跟隨發生相應的形變。
- Bob 偏移(Bob Offset):用于表現部件的上下擺動或浮動效果。
-
使用 Flags 組合:
這些運動特性用 Flags 來組合,方便給不同的渲染片段指定不同的運動模式,可以靈活組合多種動畫效果。 -
整合之前代碼邏輯:
通過提取已有的運動處理代碼,把它轉化成這個運動片段的屬性體系,從而更系統化地管理實體各部分的動畫行為。 -
目的:
這種方式為后續實現更復雜的動態動畫(比如角色的擺動、搖晃、彈跳等)奠定了基礎,能夠讓渲染片段根據實體狀態和運動參數靈活調整形態和位置。
總體來說,這是在實體系統里添加了一種基礎的運動屬性描述機制,用來驅動渲染部件的動態表現,從而逐步構建一個更完整、更靈活的動畫控制框架。
game_world_mode.cpp:讓Familiar設置BobOffset標記,Hero的Cape設置AxesDeform標記
我們為實體的動畫系統增加了對“運動標志(movement flags)”的支持,并根據之前的動畫邏輯為各個角色部件分配了合適的標志。主要工作內容如下:
-
標志分配策略:
-
Familiar 的頭部:被設置為具備 bobbing(上下浮動) 動效,所以給它添加了 Bob 偏移標志。
-
Hero(主角)角色:
- 身體(Torso):需要 bobbing 動效。
- 披風(Cape):需要 軸向變形(axis deform),因為披風會根據角色動作變形,同時也有 bobbing 動效。
- 其他部件:如頭部等并不需要特殊變形或浮動。
-
-
添加標志的實現方式:
- 在
add_piece
函數中增加了一個用于接收運動標志的參數。 - 這些標志在添加片段時一并傳入,并賦值保存。
- 當前階段只是完成了標志的設置部分,尚未在實際的渲染或動畫邏輯中使用這些標志。
- 在
-
狀態說明:
- 雖然這些運動標志目前“不會做任何事情”,因為還沒連接到具體的動畫變換公式上,但它們已經被正確傳入并存儲了。
- 接下來只需要根據這些標志,在渲染更新或實體變換過程中執行相應的動畫計算(如偏移或變形),就可以恢復原有動畫功能。
-
后續目標:
- 下一步將基于這些標志恢復動畫邏輯:例如,如果一個片段被標記為 bob offset,就使用之前的浮動公式調整其垂直位置;若標記為 axis deform,就使用實體的當前姿態軸進行變換。
- 這樣就能讓目前靜態的各個片段恢復動態表現,重新擁有原來的動畫效果。
當前我們完成了動畫系統“骨架”的搭建,已經能夠支持多種動畫類型的擴展,后續只需補全響應機制即可逐步恢復并完善整個角色動畫系統。
game_world_mode.cpp:讓HeroBody執行AxesDeform
我們開始將動畫片段的繪制恢復為完整形式,逐步實現之前缺失的動畫效果。下面是本階段詳細的處理流程與排查過程:
一、重構繪制調用,恢復動畫細節
-
恢復完整參數調用:
-
在遍歷每個 piece 時,不再只使用簡化的
push_bitmap
調用,而是用上之前完整版本的調用,包括:- 實體的變換信息(transform)
- 位移偏移(offset)
- Y方向的 bobbing 浮動值(T-Bob)
- X軸與Y軸變換矩陣(用于軸向變形)
-
-
初始化處理:
-
默認初始化 X 軸與 Y 軸矩陣為單位矩陣,表示沒有變形。
-
默認初始化 T-Bob 和 offset 為零。
-
然后根據每個 piece 的 flags 判斷是否需要實際填充動畫信息:
- 如果有
AxisDeform
標志,就使用實體的 X 和 Y 軸替代默認值。 - 如果有
BobOffset
標志,就加入浮動偏移。
- 如果有
-
-
命名調整:
- 將之前用于表示運動方式的
movementFlags
更名為更通用的flags
,統一所有功能性標志的位置。
- 將之前用于表示運動方式的
二、動畫效果結果與問題排查
-
效果回歸情況:
- Hero 的跳躍 bobbing 動作已經恢復。
- 然而 Torso(軀干)沒有出現軸向變形,仍然保持垂直,不符合預期。
- Cape(披風)表面看起來動了,但實際可能也沒做正確的變形,只是位置偏移。
-
可能問題點分析:
- 代碼層面可能的問題:flags 判斷邏輯或矩陣賦值過程出現了問題。
- 數據設置層面的問題:piece 本身的 flags 可能沒有正確設置或被清除。
- 狀態傳遞問題:實體的 X/Y 軸數據沒有在邏輯中傳入或初始化丟失。
三、深入排查步驟
-
查看 piece 的標志設置:
- 驗證了 Hero 的 Torso 和 Cape 的 piece 都正確地被設置為含有
AxisDeform
和BobOffset
。 - 所以理論上,它們應該都能觸發軸向變形與浮動偏移。
- 驗證了 Hero 的 Torso 和 Cape 的 piece 都正確地被設置為含有
-
查看動畫邏輯引用位置:
- 查找使用到
entity.x_axis
和entity.y_axis
的代碼,確認其是否在繪制過程中被正確引用。 - 最初懷疑是否代碼在某次整理中被誤刪。
- 查找使用到
-
通過版本控制工具回溯:
- 打開舊版本試圖查看是否代碼確實被刪。
- 發現其實并未刪除,而是因為搜索關鍵詞不準確(搜索了“x axis”但實際代碼中寫的是
x_axis
)導致誤判。
四、結論與下一步工作
-
實際上動畫邏輯依然存在,但目前效果未生效,原因尚未確定。
-
下一步計劃:
- 檢查渲染函數是否真的使用了修改后的 X/Y 軸信息。
- 檢查是否傳入的是正確的實體引用或矩陣值。
- 確保每個 piece 在繪制調用中都使用了實際的 flags,并據此選擇正確的矩陣與偏移。
- 驗證最終繪制是否應用了這些矩陣變換。
通過這次調整,我們已建立了一個更模塊化、靈活的動畫處理系統框架,能夠按需控制各個片段的變形與浮動效果,后續只需完善對應的渲染邏輯調用即可完整恢復之前的動態表現。
game_world_mode.cpp:讓BeginLowEntity正確設置軸向
我們發現當前動畫系統中存在一個關鍵的 bug,并在檢查代碼過程中對問題進行了深入分析和澄清,最終提出了修復方案。以下是這一部分的詳細中文總結:
一、對代碼位置的誤判與澄清
- 起初以為某段邏輯(關于 headDelta 的計算)必須放在“head”的代碼塊內部,否則屬于 bug。
- 但隨后確認實際代碼中已使用
if
語句包裹該邏輯,因此它并不總是執行,其位置并沒有問題。 - 所以這并非真正的 bug,只是對代碼結構的誤解。
二、真正的問題:重復覆蓋實體的 Y 軸方向向量
- 經過進一步檢查,真正的問題是每幀執行時都會無條件地覆蓋實體的 Y 軸方向向量。
- 即便該向量之前已經根據動畫狀態計算得到正確值,仍然被無腦重置為默認狀態,導致動畫變形失效。
- 例如 torso 和 cape 這些需要基于軸向量進行變形的部分,表現異常或沒有動畫效果。
三、解決方法:在構造時正確初始化軸向量
-
在
begin_low_entity
之后,會對整個實體結構體執行清零操作(zero struct),這個操作會把所有字段歸零。 -
必須在這之后顯式設置實體的軸向量,例如:
entity->x_axis = V2(1, 0); // 或其他默認值 entity->y_axis = V2(0, 1); // 通常代表垂直方向
-
這樣能確保即使后續沒有顯式設置,它們也有合理的初始值,供動畫系統使用。
四、冗余代碼清理
- 當前代碼中仍然存在
entity_low
相關的老舊邏輯。 - 鑒于這一部分系統已經重構,不再依賴
entity_low
,因此這些相關代碼應當被刪除。 - 清理冗余邏輯有助于減少混淆,提升代碼可讀性和維護性。
五、預期效果
- 修復后,每個實體在初始化時即擁有合適的軸向量。
- 動畫繪制函數在檢測到啟用了
AxisDeform
標志的 piece 時,即可正確讀取并應用這些軸向量,實現部位變形。 - 動畫恢復正常,邏輯清晰統一,系統健壯性提升。
這一步完成了關鍵的動畫基礎數據修正,打通了從數據標志到動畫表現之間的通路,為后續動畫系統的擴展與優化打下了堅實基礎。
game_world_mode.cpp:讓BeginLowEntity正確設置軸向
我們在檢查動畫系統時發現一個關鍵性問題,并對問題的來源和修復方式進行了梳理與總結,具體內容如下:
一、關于邏輯位置的初步誤判與澄清
- 一開始認為某段用于計算
headDelta
的代碼應該放在“head”部分內部,否則就構成 bug。 - 但在進一步檢查后發現該段代碼實際上已經包含在
if
條件判斷中,因此不會無條件執行,邏輯是合理的。 - 所以初步判斷是錯誤的,這部分沒有問題,不需要修改。
二、真正的 bug:軸向量每幀被無條件覆蓋
- 代碼中存在一個真正的問題,即在每一幀更新時,都會直接覆蓋實體的 Y 軸方向向量(
y_axis
)。 - 即便之前已經根據動畫或變形邏輯正確設置了向量,這個覆蓋操作會讓變形失效。
- 舉例來說,如果實體需要扭曲(如 cape 或 torso),這種直接覆蓋會導致這些部分完全無法按照預期進行動畫變形。
- 因此關鍵 bug 是:每幀都不加判斷地重新設置了實體的軸向量,破壞了動畫邏輯。
三、解決方案:在構造函數中正確初始化軸向量
-
在調用
begin_low_entity
創建實體后,會使用zero struct
把整個實體結構清零。 -
如果不隨后手動設置軸向量,它們就會保持為(0,0),變形系統中使用這些向量將導致圖形無法正確顯示。
-
正確做法是:
entity->x_axis = V2(1, 0); // 設置默認水平軸 entity->y_axis = V2(0, 1); // 設置默認垂直軸
-
這樣,即使在后續幀中沒有顯式覆蓋這些向量,也能保證圖形系統有可用的合法值用于計算動畫與變形。
四、關于遺留系統的冗余邏輯
- 當前系統中仍保留著
entity_low
的相關代碼。 - 但在之前的架構更新中,我們已經廢棄了
entity_low
,它不再參與任何邏輯計算。 - 這一部分已經失去了存在意義,應當從系統中徹底移除,以減少干擾和混淆,保持系統的清潔性與一致性。
五、預期修復效果
- 修復后,每個實體在初始化時即帶有合法的軸向量,后續動畫系統可以正確讀取這些向量。
- 當動畫 piece 啟用了變形標志(如
AxisDeform
)時,即能基于這些軸向量執行正確的視覺變形。 - 系統整體邏輯更加一致,動畫表現恢復正常,不再被每幀錯誤覆蓋的向量干擾。
- 同時,冗余的
entity_low
系統被移除,邏輯簡化,維護更容易。
這部分的修復是動畫系統基礎邏輯的關鍵一環,確保了動畫部件可以根據配置與標志做出正確的變形和運動,是支撐后續動畫拓展的重要前提。
運行游戲,確認變形效果正確
我們現在成功實現了基于可變形軸的正確變形效果,這對動畫表現來說是非常重要的改進。同時,我們徹底剔除了渲染代碼中所有針對實體某個部件(piece)特定處理的邏輯,使得渲染代碼不再依賴于實體類型。這樣一來,渲染部分的代碼更加簡潔和通用,提升了系統的模塊化程度和可維護性。接下來,我們需要思考的重點是如何進一步優化和組織這些模塊,確保動畫與渲染系統能夠更加靈活和高效地協同工作。
game_entity.h:移除entity中的entity_type,查看依賴情況
我們現在成功實現了基于可變形軸的正確變形效果,這對動畫表現來說是非常重要的改進。同時,我們徹底剔除了渲染代碼中所有針對實體某個部件(piece)特定處理的邏輯,使得渲染代碼不再依賴于實體類型。這樣一來,渲染部分的代碼更加簡潔和通用,提升了系統的模塊化程度和可維護性。接下來,我們需要思考的重點是如何進一步優化和組織這些模塊,確保動畫與渲染系統能夠更加靈活和高效地協同工作。
運行游戲,驗證去掉entity_type后效果無異常
我們現在運行的代碼完全沒有實體類型字段,這是我們想要的一個重要進展。之前那些“類型”信息已經被解耦,不再把實體作為一個整體的“類型X”來看待,而是將各種特性拆分成多個小的類型,比如碰撞類型、身體形狀類型、腦類型等,這些都可以自由組合搭配,不需要將實體看作單一整體。這種設計更靈活,也更符合需求。
在這種基礎上,我們對“腦”(brain)的系統感到滿意,認為它基本實現了預期的功能,雖然以后可能還會有一些改進,但整體已經是我們想要的樣子了。實體結構中,唯一還保留并經過深思熟慮的字段是實體ID,因為實體ID的存在是必需的,并且需要支持更新。
接著我們回顧了當前的世界模式(world mode)代碼。大致流程是:
- 遍歷所有控制器,檢測是否有人開始玩游戲。
- 遍歷所有腦代碼,讓每個腦執行思考并指示其控制的實體做出動作。
- 遍歷所有實體,做調試操作,檢查實體是否可更新(updatable),執行一些全局透明度(Global Alpha)處理。
- 執行移動模式(movement mode)和物理更新,這部分代碼還需要進一步拆分和優化。
- 移動實體的代碼是當前最薄弱的環節,依舊是舊的標準物理代碼,需要重構和改進。
- 渲染代碼變得更合理,不再受實體類型的限制。它只是簡單地遍歷需要渲染的對象列表,渲染它們和疊加顯示生命值、調試信息等。
總體來說,系統結構逐漸清晰,代碼變得更簡潔,不再被實體類型所束縛。我們還想讓物理部分單獨成塊,渲染部分也清晰分開。
關于“實體是否可更新”的問題還存在疑惑,不確定為什么一個實體必須是“可更新”的才能被渲染,感覺有些奇怪,但目前先接受這個邏輯。
接下來計劃對腦類型做進一步整理。腦類型現在已經有了基本定義,比如“monstar腦”會使用動作規格(movement spec),利用跳躍等行為。腦有自己的ID、槽位、類型等定義。未來腦系統會更加細化,功能也會豐富。
總的來說,系統架構逐步走向模塊化和解耦,結構更加靈活,具備良好的擴展性和維護性。
game_brain.h:引入brain_hero、brain_monstar和brain_familiar
我們現在定義了幾種腦(brain)類型,包括“brain hero”、“brain monstar”和“brain familiar”。目前腦的分類還不是最終的類型,也不一定是未來實際會用到的腦類型。腦的設計會比較靈活,未來可能會更加多變和動態。
現在的做法是先硬編碼這些腦的基本功能,先讓它們能做想讓它們做的事情,再觀察哪些地方可以拆分、優化,避免提前過度設計。這是一種偏向壓縮和簡化的設計思路,認為這樣更實用。
目前“familiar”腦只包含一個頭部部分,而“monstar”腦只包含一個身體部分。其他細節暫時還不復雜,先保持簡單。
接下來會重新啟用部分代碼,進行測試和調試,看看實際效果如何。
game_world_mode.cpp:添加Brain_Familiar分支
我們現在將配置設置為“AI跟隨英雄”為真,目的是讓這個功能默認能夠正常運行。定義了“brain familiar”,它只有一個頭部部分,代碼中遍歷時需要做空間查詢,但目前暫時不用關心這個細節。我們的目標是在場景中找到最近的擁有“brain hero”類型的實體。
在代碼實現上,所有涉及實體的地方都替換為對頭部的引用。運行時,預計不會有任何行為發生,因為當前沒有任何實體安裝了“brain familiar”這個腦。為此,在添加“familiar”實體時,需要執行一個新增腦的操作,為該實體綁定“brain familiar”的腦ID。
目前遇到的問題是,“familiar”沒有跟隨我們預期的行為,可能是因為沒有設置它的移動模式,這一點需要后續確認。
有一個設計上的想法:將腦的類型(brain type)和腦的結構體(struct)綁定為同一個實體,比如“brain hero”、“brain monster”、“brain familiar”等,這樣可以簡化代碼結構,提高安全性,減少因手動賦值產生的錯誤。
因為在目前的代碼設計中,腦類型和腦槽(brain slot)是分開的,但實際上腦類型賦值時一定會對應一個腦槽,考慮將腦類型和腦槽合并成一個整體,比如“brain type and slot”,這樣更符合邏輯,也更便于維護。
總之,這種設計調整能幫助克服C++語言中需要用宏定義來避免錯誤的缺陷,增強代碼的健壯性和可維護性。
運行程序Familier沒反應呢
仍然沒反應
game_brain.h:使BrainSlotFor_函數接受Type參數
腦類型(brain type)實際上并沒有單獨出現,而腦槽(brain slot)4 是一個能夠同時返回腦類型和槽位的實體。我們通過在腦類型前面加上“type_”前綴來獲取對應的枚舉值,這樣在使用腦槽時,可以同時傳入腦類型和需要打包的值。
為了保證安全性和規范性,腦槽的值限定為U6類型(無符號6位整數),因為預期槽位數不會超過這個范圍。這樣設計后,腦槽就成為一個統一且安全的組合體,能夠避免因拼寫錯誤或手誤導致的類型和槽位混用問題,這種錯誤在之前很容易出現。
這套方法不增加代碼的復雜性,反而是一個零成本但非常有效的錯誤防護措施。因此決定采納這種設計,借此減少后續開發中的潛在錯誤。
當前只剩下大約五分鐘時間,將利用這段時間把這個功能完成,其他像“familiars”相關的功能可以留到明天繼續開發。
總結就是,腦槽和腦類型綁定為一體,既方便又安全,避免了類型槽位錯用的問題,并且對現有代碼的侵入性極低,是一種極具優勢的設計改進。
game_entity.h:移除entity中的BrainType,并修復編譯錯誤
我們將腦類型(brain type)徹底移除,轉而使用結合了腦類型與槽位信息的腦槽(brain slot)結構,簡化并統一了管理方式。接下來需要處理由此改動引發的連鎖反應(Fallout),以完成代碼調整。
在重構過程中,編譯器提示不能將 brain type
轉換為 U6
,存在潛在的數據丟失問題。盡管理論上這些枚舉值不會超出 U6 表示范圍,但為了避免編譯器報錯,我們不再深究這一點,而是進行適當調整以確保通過編譯。
隨后,我們調整了添加大腦(add brain)功能中的邏輯,原本使用的是單獨的 brain type
,現在全部替換為新的 brain slot
結構,因此需要在相關位置將參數替換為 type_brain_XXX
形式,例如:
type_brain_hero
type_brain_snake
type_brain_familiar
type_brain_floaty_thing
type_brain_monster
這類替換操作保證了代碼中使用統一的枚舉前綴規則,并且通過這種方式可以進一步增強類型系統的穩健性與一致性。
這一改動屬于代碼層面的系統化重構,目標是提升類型安全,減少邏輯混亂的風險,使得未來添加或修改腦類型時更加規范和不易出錯。在重構完成后,系統整體行為應當保持不變,也就是“功能不變,結構更優”。這意味著,我們當前所做的工作已經接近完成,接下來只需驗證一切正常運行即可。
有一個報錯宏不接收一個參數
展開之后
運行游戲,發現功能無變化
我們目前的結構性重構已基本完成,現階段整體邏輯應與原本保持一致,沒有引入功能性變化。也就是說,雖然做了大量代碼調整,但程序行為在現有范圍內應不受影響。
當前還有一項未完成的工作是“familiar”的大腦邏輯尚未完善和啟用,但這部分計劃留到后續再處理。
通過這次重構,我們完成了以下幾項關鍵工作:
- 移除了原有的腦類型(brain type)字段,不再單獨使用,而是整合到新的結構中。
- 引入了新的 brain slot 結構,將腦類型與槽位綁定成一體,從而提升了類型安全性,防止由于拼寫或邏輯錯誤導致的類型槽位錯配。
- 統一了腦類型的枚舉命名格式,采用
type_brain_xxx
形式,便于查找、識別和擴展。 - 更新了所有相關邏輯中對大腦類型和槽位的引用,保證整個系統在使用層面也統一采用了新的約定。
- 在添加腦(add brain)操作時也進行了對應適配,確保在賦值腦槽位時不會出現類型錯配的風險。
此外,也預留了后續演進空間。例如,目前雖然各種腦(如 hero、monster、familiar 等)仍為硬編碼類型,但我們預期后期會進一步解耦,使大腦系統更具擴展性與組合彈性。在此之前,當前的方式先滿足需求、快速推進,同時也已在結構設計上做了鋪墊。
總體而言,我們已經完成了本輪重構的核心目標,接下來只需繼續完善 familiar 的行為邏輯,并在后續對整套大腦系統進行進一步提煉與模塊化。
問答環節
如果你廢除實體類型的概念但仍把一切緊綁在腦中,這樣做有什么實際好處?這不只是把責任轉移到腦了嗎?
我們目前已經徹底摒棄了傳統的“實體類型(Entity Type)”概念,但并非將所有職責都集中到“腦(Brain)”中。實際上,我們通過這一系列變更獲得了以下幾個顯著的好處:
系統徹底解耦:邏輯與表現的分離
之前,實體的所有行為、渲染方式、控制器歸屬、移動邏輯、速度等信息都緊耦合在一個“實體類型”中。這種方式雖然簡單直觀,但導致各個功能模塊無法獨立變化,也不利于組合和復用。
現在,我們做到了以下幾點:
- 腦(Brain)與實體解綁:可以為任意組合的實體分配任意腦邏輯。
- 運動行為與腦解耦:不同的腦可以使用相同的運動方式,或為同一個腦指定不同的運動邏輯。
- 渲染表現與控制邏輯分離:可以給不同外觀的角色綁定同一個腦邏輯,或者給相同外觀的角色指定不同的腦。
換言之,我們實現了高度模塊化設計,所有行為不再依賴單一“實體類型”的定義。
組合自由:實現“拼裝式”實體系統
新的設計允許我們實現任意組合,例如:
- 將英雄的腦賦給一個外觀奇怪的實體,實現“奇裝異服的主角”。
- 給看起來像英雄的角色分配一個敵方大腦,實現偽裝敵人。
- 將多個子實體組成的系統(例如頭和身體)交由同一個腦統一控制,也可以靈活拆分或重新組合控制方式。
以前這種“跨實體協調行為”的需求無法實現,因為所有邏輯都綁定在一個類型上,現在腦成為了行為調度的中心,使得這些高級功能成為可能。
構建復雜多實體系統成為可能
例如英雄的“頭”和“身體”作為兩個實體存在,以前無法讓它們受一個輸入控制器協調控制。現在,通過腦的統一控制:
- 可以同時控制多個實體;
- 可以在游戲中動態拆分/組合控制邏輯,比如實現“頭部脫離身體”后的獨立控制;
- 將來甚至可以為腦引入參數化系統,進一步細化行為特征而無需創建新類型。
錯誤率降低 & 代碼可維護性增強
因為我們也進行了如“Brain Slot 與 Brain Type 綁定”的改造,提升了類型安全性,避免因錯誤分配導致的運行時問題。
總結
我們通過摒棄傳統“實體類型”的設計,將行為邏輯外置到腦模塊中,成功實現了:
- 行為、控制、渲染、運動的完全解耦;
- 支持跨實體控制的復雜邏輯;
- 組合自由和復用能力的顯著提升;
- 后續可擴展性和錯誤防范能力增強。
這是一項從結構上提升整個系統靈活性和表達力的重大進展。
經歷了這么長時間,你終于習慣實時編程了嗎?這已經變成舒服的習慣了嗎?
我們現在雖然已經習慣了直播編程這種模式,但并不覺得這是一種很舒適的習慣。相比于正常、不直播的編程狀態,直播時的編程要困難得多,主要原因有以下幾點:
一邊編程一邊說話的負擔很重
編程時需要高度集中注意力處理復雜的邏輯問題,而同時還要持續進行講解和表達思路,這對認知負荷是雙重打擊:
- 思考和語言會互相打斷,影響效率;
- 很多時候只能分心處理較簡單的事情;
- 無法深入進入“心流”狀態,思維被外部打斷頻繁。
頻繁切換不同項目/模塊,打斷思路
當前經常并行處理多個項目或系統模塊,這會進一步拉高復雜度:
- 上下文切換頻繁,每個模塊都有獨立的狀態與結構,切換時要重新加載思維模型;
- 很多時候為了展示或推進進度,必須跳過細節,導致心中留下“未完成”的負擔;
- 也無法在一個問題上長時間沉浸式深入,降低了思考深度。
分段開發的節奏感不自然
直播過程中為了讓觀眾跟上進度,往往需要將一個系統拆成小片段逐步推進:
- 這雖然有利于展示和解釋,但對我們而言是一種干擾;
- 很多邏輯原本可以順暢地一氣呵成,但為了分段講解而打斷,會導致設計被“人為分解”,思路受限;
- 小片段不容易看到整體結構,也會加劇編寫時的猶豫和回溯。
總結
我們可以說——
直播編程絕不是一種更輕松的工作方式,相反它讓一切變得更困難。
具體體現在:
- 注意力被分散;
- 心流難以維持;
- 模塊切換頻繁;
- 節奏受限,思路難以連貫。
雖然我們仍堅持以這種形式推進工作,但每一次都是在比正常更艱難的條件下完成系統的搭建與邏輯實現。
你能再解釋一下剛才對brain slot的處理嗎?我有點跟不上了
我們在處理腦(brain)系統的“槽位”(slot)機制時,做了一些結構上的改進和防錯處理。下面是這部分工作的詳細總結:
當前的腦系統結構
我們目前的“brain”系統是這樣設計的:
- 每個腦(brain)擁有若干“槽位”(slot),可以把子實體(如頭、身體等組件)放進去;
- 每個腦都有一個類型(例如:hero、monstar、familiar等);
- 每種類型的腦都有不同的布局(slot 的結構和含義),比如 hero 可能有 head 和 body,而 familiar 目前只有 head。
舊實現存在的問題
原先的實現中:
brain type
和brain slot
是兩個完全分離的變量;- 在給一個實體賦予腦的時候,需要手動指定這兩個值;
- 這樣就會有一個嚴重的隱患:可以不小心把 hero 類型的 slot 用在 monstar 類型的 brain 上,例如:
brain.type = BrainType::familiar;
brain.slot = BrainHeroSlot::head; // ? 錯誤但代碼不報錯
這類錯誤可能會導致運行時行為錯亂,甚至是難以追蹤的 bug。
結構改進:綁定類型與槽位
為了解決這個問題,我們進行了如下改進:
定義一個結構 brain_slot
,把 type
和 slot_index
綁在一起
- 每種腦類型(hero、familiar 等)都定義一個宏或結構;
- 每當我們調用時,用這個結構生成一個同時包含類型與槽位編號的對象;
- 這樣,類型和槽位是強綁定的,不會意外混用。
例如:
#define BrainSlot(type, index) { .type = type_##type, .index = index }
使用方式變為:
BrainSlot(familiar, 0) // 明確指定了 familiar 類型的第 0 號槽
- 通過宏統一處理,保證類型與槽的匹配一致;
- 也避免了開發時的手誤或 copy-paste 錯誤;
- 增加類型安全性,防止邏輯層的錯配。
重命名與類型同步
為了配合這套機制,我們做了以下命名約定:
-
每個 brain type 的枚舉名稱與結構體前綴保持一致,如:
type_brain_hero
type_brain_monstar
type_brain_familiar
-
這樣我們在宏中可以直接通過字符串拼接自動構造類型名,從而避免手動重復輸入或出錯。
可擴展性考慮
雖然目前常見的腦類型只有兩三個槽位(比如頭和身體),但我們也為未來可能出現的復雜結構(比如:
- 多節身體的蛇形結構、
- 多個肢體的蜘蛛、
- 能自由組合拆分的實體系統,
做好了結構上的預備。slot 系統的泛化設計,可以支持任意數量和排列組合的部件。
總結收益
這套改進方案雖然看似只是包裝了原有邏輯,但帶來了以下顯著好處:
- 提高類型安全性,避免手動錯配 type/slot;
- 統一接口風格,調用更清晰明確;
- 方便實驗與擴展,支持更復雜的實體組合形式;
- 減少運行時出錯的可能,預防 bug 源頭;
- 沒有增加使用復雜度,幾乎是“零成本”的增強。
這使得我們可以在設計實體系統時更自由地混合搭配不同的渲染外形、行為邏輯、控制方式,而不會被低級錯誤拖慢節奏。未來哪怕要引入更多腦類型或槽位配置,也能非常順暢地支持下去。
你會如何處理類似控制其他實體意識,或者意識交換到不同結構身體這種情況?
我們正在探討如何實現將意識控制其他實體,或將我們的意識轉移到結構不同的身體中的機制。目前我們認為,實現這一點并不復雜,關鍵是將目標實體的“腦ID”設置為我們的ID,然后拋棄我們原本的身體。這樣就完成了意識的轉移或控制。
我們強調系統中的各個組件應該是解耦的。這種解耦設計非常重要,雖然現在可能顯得有些抽象,是因為設計思路都集中在我們腦中,而他人可能還沒完全理解。但我們認為完全不需要類似“玩家控制”這樣的標志位。舉例來說,如果我們要實現意識轉移的功能,就不必使用傳統意義上的“玩家控制”標志。
具體操作中,例如我們設定按下空格鍵(也就是“開始”按鈕)來觸發某個事件,我們會通過判斷是否按下了空格鍵來執行意識轉移的邏輯。在檢測按鍵狀態時,我們不檢查是否持續按下,而是判斷是否被按下過一次,確保不會因為進入檢測邏輯后一直讀取不到新輸入。
在這樣的設計中,我們只需要在按下空格鍵時,把目標實體的“腦ID”設置為我們自己的,然后清除原有的實體,就實現了意識的遷移或控制。我們所依賴的系統結構允許這種行為而無需額外設置冗余控制標志,從而保持系統的簡潔性與靈活性。
game_brain.cpp:在ExecuteBrain中實現意識控制
我們計劃實現一個“心智控制”的功能,具體是讓當前實體控制附近的其他具有大腦的實體。我們的設想是,在某個按鍵(例如空格鍵)被按下后,系統會執行控制轉移操作:將我們當前的意識(腦ID)轉移到最近的有腦實體上,并拋棄我們原本的頭部(大腦),從而實現控制權的轉移。
為此,我們會在按鍵觸發時執行如下流程:
-
搜索當前實體附近的其他實體,尋找一個最近的目標。
-
判斷目標是否擁有有效的大腦(例如大腦槽位非零),以確保我們不會控制沒有思維能力的對象,比如樹。
-
如果找到了合適的目標,我們就執行以下操作:
- 清除我們當前的頭部(即原本的大腦或意識容器)。
- 將我們的大腦槽位(例如 brainSlot4)賦予那個最近的目標實體,也就是將我們自身的意識“安裝”到那個實體上。
- 這個過程類似于把我們的“頭”安到了另一個身體上,從而實現控制該實體。
此外,為了防止出現找不到可控制目標的情況,我們也加了邏輯檢查,避免最終導致我們變成無頭狀態(即失去大腦后沒有新目標接管)。
這一機制的核心是通過大腦槽位的重新綁定來實現意識的轉移,不依賴于傳統的“玩家控制”標志。我們以結構化的方式處理大腦插槽和頭部替換,使得“控制權轉移”變得直接且高效。整體設計強調組件解耦與功能明確,讓系統既具靈活性又易于擴展。
game_brain.h:引入NoBrain概念
我們進一步完善了“心智控制”機制的實現。在設定中,為了將我們的意識從當前實體轉移到另一個實體,我們需要處理的是“brain ID”(大腦標識符),而不是整個“brain slot”(大腦槽位)。通過對“brain ID”的賦值和管理,我們可以簡化控制邏輯。
我們的操作流程如下:
- 設計一個“無腦”的標識,用于判斷一個實體是否可以被接管。這個“無腦”狀態其實只影響其“brain ID”,所以我們只需返回一個為零的ID即可表示該實體沒有大腦。
- 在尋找可控制目標時,我們會從附近的實體中篩選出“brain ID”不為0的對象。因為brain ID為0意味著它沒有可用的大腦,不適合作為控制目標。
- 然后,我們將我們的“brain ID”賦值給那個目標實體,實現意識的遷移。
- 接著,我們放棄原有的頭部,以完成從舊身體到新身體的轉換。
- 在邏輯中加入了額外判斷,確保不會選擇我們自己作為控制目標。因為我們總是離自己最近,容易誤選為目標。如果不排除自己,就可能出現意識又回到自己的情況。
這個過程雖然現在還處于早期測試階段,但邏輯已經比較清晰。我們已經構建了必要的判定機制、狀態轉移方法,并簡化了數據結構的依賴,重點放在“brain ID”的處理上。下一步是在場景中嘗試拾取熟悉的頭部,以驗證控制權是否真的可以轉移成功。整個機制的設計以最小耦合為目標,確保功能模塊清晰、行為可控、邏輯可擴展。
運行游戲,控制Familiar
我們在測試心智控制機制時遇到了一些問題,尤其是在嘗試將意識從一個實體轉移回之前的實體時,控制權無法成功切換回去。本應可以實現雙向的意識轉移,但在實踐中發現返回原頭部的操作似乎未生效。
分析過程中,我們意識到以下幾個關鍵點:
-
成功控制其他實體的頭部:我們已經成功將意識從自身轉移到了另一個實體的頭部,說明基本的意識遷移機制是可行的。
-
嘗試返回控制原本的頭部失敗:在嘗試將意識再度從目標實體返回到我們原來的頭部時未能成功,這顯然與邏輯判斷有關。
-
添加斷點進行調試:為了找出問題,我們設置了斷點,重點檢查控制權轉移時的判斷條件是否正確執行。
-
條件檢查的問題:
- 當前判斷邏輯是:“找不到一個brain ID值不等于當前頭部brain ID的實體”。這意味著,如果目標實體與當前頭部的brain ID一致,則不予處理。
- 但此邏輯在嘗試回到原實體時會失敗,因為返回時目標實體可能正好與當前brain ID一致。
- 此外,當前邏輯默認我們始終擁有一個“頭部”,但實際上在某些情況下我們可能是“無頭”的,因此缺少頭部時的判斷會造成邏輯中斷。
-
改進建議:
- 在執行腦ID比較時,加入對當前是否擁有頭部的檢查,防止因為“無頭”狀態而跳過關鍵邏輯。
- 在選擇目標實體時,不僅要判斷brain ID不等于自身,還要排除原始實體已經被釋放或狀態不可用的情況。
總結來說,當前的意識轉移邏輯在從自身轉向他人時是可行的,但在嘗試返回時缺乏足夠的狀態判斷,尤其在“是否仍有頭部”以及“目標是否是自身”等方面判斷不足。下一步應修復這些判斷條件,增強系統的回環能力,讓意識控制實現真正的雙向流動。
控制了Familier Familier不能移動看一下什么原因
奇怪為什么會發生碰撞
先不讓發生碰撞讓被控制著走動
game_brain.cpp:讓ExecuteBrain交換BrainID和BrainSlot
我們進一步分析并調整了意識轉移的核心邏輯,主要解決的問題是:當我們將意識從當前實體轉移到另一個目標實體時,原本的身體(或頭部)不應被置為空殼狀態,而應保留其可控性,以便未來可以再度切換回來。為此,我們決定采用**“互換式”意識遷移**,而不是單向的替換。
具體改進和設計如下:
-
問題根源
原邏輯在轉移意識時,僅將當前brain ID賦給目標實體,而沒有處理原有身體的狀態。這會造成原本的身體變為“無腦體”,變得無法再次被使用或控制。 -
引入互換機制
為了解決這個問題,我們引入了互換式分配:- 當前實體的brain ID和brain slot被暫存為舊值。
- 將目標實體的brain ID和slot賦值給當前實體,實現控制權接管。
- 同時將原實體(頭部)的brain ID和slot改為目標原有的值,實現狀態交換。
-
判斷前提
這項互換操作前提是當前實體必須擁有頭部,也就是說,必須存在一個“正在控制”的身體。 -
設計目的
通過這種互換邏輯,我們可以實現雙向的意識切換,使得原本被我們“奪取”的身體依然保持可用狀態,而不是變成永遠不可控制的“無意識體”。這也避免了邏輯中出現大量無效實體的問題。 -
邊緣問題識別與優化
在測試中發現碰撞系統存在問題,會影響實體之間的相互識別與靠近行為,因此我們也打算臨時調整碰撞設置,以便專注調試意識轉移機制。碰撞系統被視為當前最大的潛在問題,需要在后續專門修復。
總結來說,我們的設計目標已經從“單向控制”轉變為“實體間的對稱交換”,這使得系統更完整、邏輯更穩定,未來也能擴展出更多可逆的操控行為,如循環控制鏈、意識投射、多實體快速切換等高級功能。
game_world_mode.cpp:讓PlayWorld將所有碰撞設置為NullCollision
我們目前并不認為碰撞系統是“房間里的大象”(即最大的問題),但確實是一個尚未徹底解決的部分。雖然此前在一定程度上禁用了碰撞功能,但還沒有真正完成對其邏輯的修復與驗證。
為了專注測試當前的其他關鍵系統(如心智控制、實體互換等),我們臨時決定將部分實體設為無碰撞狀態。這樣做的目的是為了減少調試時的干擾,使我們能夠在測試控制邏輯的過程中,不被實體之間的物理接觸與阻擋所影響。
具體調整操作如下:
- 將實體設置為無碰撞:通過修改相關屬性,暫時讓這些實體之間互不產生物理碰撞或干擾。
- 簡化測試環境:這樣我們可以在不受阻擋的情況下,自由在實體之間移動和切換控制權,避免因碰撞卡住或阻擋導致邏輯無法正常運行。
- 暫不處理碰撞邏輯的正確性:當前重點是確保心智交換等功能的實現,碰撞系統將留作后續專門修復的部分。
這項調整雖然只是臨時措施,但對于提高調試效率、聚焦核心邏輯的實現具有重要意義。等到控制系統穩定后,將會重新啟用碰撞系統并進行全面修復與優化。現在,我們可以順利地在不同實體之間切換與“駕駛”,進一步驗證意識互換機制的可靠性與靈活性。
運行游戲,在Familiar和原始Hero間切換控制
我們現在已經能夠在多個實體之間順利進行心智交換操作,實現了“交換控制權”和“返回原始實體”等功能。通過多次測試,實體之間的意識互換表現穩定,支持來回切換,如從實體A切換到實體B,再返回實體A。
在進行進一步測試時,我們嘗試與一個看起來較近的實體進行切換,但發現系統并未識別它為可控目標,而是選中了一個距離更遠的實體作為切換對象。這說明當前識別邏輯中存在對某些實體的不兼容問題。
經過分析,初步判斷是:該近距離實體可能沒有大腦(brain),因此在控制邏輯中被排除。系統只允許向具有有效 brain ID 的實體進行控制切換,而當前這個實體(推測為“monstar”)可能未被賦予brain ID,所以不能作為控制目標。
為了解決這個問題,我們決定進行如下調整:
-
檢查并賦予brain:
- 明確判斷該實體(如monstar)是否已具有brain。
- 如果沒有,為其賦予一個有效的brain ID,使其能夠參與控制邏輯。
-
優化最近實體識別邏輯:
- 當前邏輯可能只簡單地遍歷距離,并忽略了目標是否有效。
- 需優化篩選機制,先過濾出有效、有brain的目標,再進行距離判斷,以確保選中的總是“最近的可控實體”。
-
測試多個目標場景:
- 驗證多個不同類型的實體能否成為控制目標,包括monstar、hero、NPC等。
- 測試實體的brain賦值對控制權轉移是否起到決定性影響。
目前系統已具備基本的控制切換能力,重點在于完善目標篩選機制和實體初始化狀態,確保所有合理目標都可作為心智控制的對象。同時需保證遠近優先級與可控性判斷不沖突,提升體驗與穩定性。
game_world_mode.cpp:讓AddMonstar設置腦(brain)
經過確認,monstar 這一類實體默認是沒有 brain 的,因此之前在進行心智交換時,系統無法識別其為有效目標。這并不是嚴重問題,我們可以直接為 monstar 添加一個 brain,從而使其具備被控制的可能性。
具體處理步驟如下:
-
為 monstar 創建并綁定 brain:
創建一個新的 brain 實例,并將其與 monstar 的身體進行綁定。這樣 monstar 就正式具備了接受意識轉移的能力。 -
確認綁定成功:
一旦 brain 與 monstar 的實體正確關聯,系統就可以在進行心智切換操作時將其識別為有效目標。 -
測試功能:
- 成功將當前控制的意識轉移至 monstar 身上。
- 驗證 monstar 是否可以被正常控制(移動、操作等)。
- 再次從 monstar 切換回其他實體,確認雙向切換是否穩定。
通過這一操作,現在可以將 monstar 納入完整的心智控制系統。所有具備 brain 的實體都可以參與意識互換流程,無論其原本是否是“玩家角色”或其他類別。系統變得更加通用、靈活,并支持動態添加新可控目標的能力。
運行游戲,心靈控制Monstar和Familiar
我們在測試過程中,成功將一個非典型的身體部位(例如 torso)錯誤地拾取作為“頭部”來控制,雖然這種行為在邏輯上并不合理,但從技術角度是可行的。這說明當前系統在身體結構識別方面還存在一定的容錯性或判斷不嚴密的情況。
盡管控制了 torso,我們仍然可以將意識切換回之前的實體,說明心智交換機制本身是健壯的,不受具體身體部位類型的影響,只要實體具備有效的 brain 和可識別的 slot,即可完成控制權的遷移與返回。
目前我們識別出的幾點關鍵工作與現狀如下:
-
控制了非頭部結構的實體:
系統允許將 torso 拾取作為頭部來執行心智控制操作。雖然這在實際邏輯中不符合常理,但機制上并未設限,反而提供了靈活性。 -
意識可在非典型實體之間來回切換:
即使選中的目標并非理想的控制對象,也能完成交換與回退。這為后續處理特殊角色(如無頭角色、合成單位等)提供可能性。 -
需要修復動作控制代碼:
當前控制邏輯中,移動代碼尚未恢復至標準版本,導致某些實體在被接管時移動行為異常。計劃將其還原為之前穩定版本,確保行為一致。 -
系統邏輯已初步成型:
整個心智交換系統已進入可操作階段,支持多實體、異常結構體、返回原控制權等多個關鍵路徑。
通過這些測試結果,我們驗證了系統的彈性,并明確了下一步需要優化的方向,尤其是在身體部件識別、控制權歸屬規則、移動邏輯恢復等方面。這些調整完成后,系統將更加穩定且具備更高的泛化能力。
也是發生碰撞
我們什么時候實現僵尸腦?
我們計劃較早實現“僵尸腦”(Zombie Brain)這一邏輯模塊,原因是目前系統中的許多怪物本質上都具有類似僵尸的行為模式,因此實現“僵尸腦”不僅具有通用性,而且優先級較高。
目前的判斷與規劃如下:
-
怪物行為原型大多為僵尸類:
很多現有或預期中的敵方單位都是具備簡單攻擊/跟蹤/追擊行為的,這些正好與僵尸行為模型一致。因此,僵尸腦將作為早期標準 AI 模型進行推廣應用。 -
僵尸腦將作為通用基礎 AI:
這個腦模型可以處理最基本的目標識別、路徑移動、靠近攻擊等,適用于不需要復雜邏輯的敵人單位,是非常好的起點。 -
實現時間優先:
鑒于它的廣泛適用性和對整體戰斗體驗的重要影響,我們會在早期就著手開發它,并將其作為測試平臺用于完善腦系統與行為接口的聯動機制。 -
后續可拓展其他腦類型:
僵尸腦將作為模板或基類存在,之后可以衍生出更復雜的腦結構,比如巡邏型腦、逃避型腦、協作型腦等。