游戲引擎學習第222天

回顧昨天的過場動畫工作

我們正在制作一個游戲,目標是通過直播的方式完成整個游戲的開發。在昨天的工作中,我享受了制作過場動畫的過程,所以今天我決定繼續制作多個層次的過場動畫。

昨天我們已經開始了多層次過場動畫的基本制作,并且成功地做出了一個初步版本。這個版本看起來相當不錯,已經具備了一些深度效果,整體效果令人滿意。當然,仍然有一些細節可以進一步調整,使它看起來更加完美。

接下來,我的目標是繼續制作剩下的所有過場動畫,并確保它們的工作方式與目前已經完成的部分一致。這樣,我們就能將每個部分組裝成完整的過場動畫。每個部分都做好之后,接下來可以集中精力讓整體更加連貫和易于使用,甚至今天可能也會做一些這樣的調整。

總之,今天的工作主要是繼續制作和調整過場動畫的各個層次,確保它們都能順利運行,并準備好將它們整合成完整的過場動畫。

game_cutscene.cpp:引入 struct layered_scene 來保存所有的過場動畫數據

目前的工作內容主要是在設計和實現過場動畫的結構。現在的結構非常通用,實際上并沒有做任何特殊的處理,僅僅是把必要的過場動畫信息進行了整理。例如,主要的內容包括層數以及每一層的操作方式。我們可以像這樣簡單地定義一個“層數”變量,表示一共有多少層,比如“層數為8”,然后再在每個具體層中定義相應的操作。

這段代碼基本上已經包含了所需的所有數據結構,也就是過場動畫的全部信息。如果我們想要的話,可以將這部分內容獨立出來,實際上并沒有什么理由不這么做。例如,可以將這部分稱為“過場動畫”或者“分層過場動畫”。我們可以在這個結構中包含每個層的信息,如層數、鏡頭索引等數據。還可以通過查看文件格式和資產類型來進一步優化,比如為每個資產指定一個類型ID。

為了更清晰地組織這些數據,我們可以為每一層指定一個指針,指向包含所有信息的“層放置”數組。這樣,我們就能有一個非常基礎的過場動畫結構。雖然有些部分可能需要擴展,但對于目前的需求,這已經基本足夠了。

另外,我們還可以考慮增加一些其他字段,例如過場動畫的持續時間。可以簡單地為每個過場動畫設置一個持續時間字段,暫時將其設置為一個默認值(例如0到5秒之間),并在后續的調整中進行更改。這樣,我們就能進一步完善過場動畫的時長設置。
在這里插入圖片描述

game_cutscene.cpp:引入 RenderLayeredScene

現在如果我們想把這個系統轉換成真正可用的形式,也就是說實現對某個具體場景的渲染,我們可以定義一個函數,比如 RenderLayeredScene,它接收我們定義好的某個分層場景作為輸入。

只需要傳入當前的分層場景對象,再傳入一個歸一化的時間值(T 值),這個時間值表示當前動畫進度的位置,有點類似從 0 到 1 的比例。這些信息其實已經足夠進行渲染。

當然,還有一些額外的數據也可以一并傳入,比如攝像機的起始位置和結束位置,這些都能影響畫面顯示效果。所以我們會把相關的 CameraStartCameraEnd 變量也一并抽出來,用來提供視角的過渡控制。

我們從現有代碼中把這些相關邏輯提取出來,封裝成一個通用函數,這樣每次需要渲染一個分層場景時,就只需要調用這個函數并傳入所需的參數。接著,在調用位置,比如某一幀畫面中,我們只需要調用 RenderLayeredScene,并傳入當前所需的數據就可以完成渲染。

這個函數需要的參數包括:資源信息、渲染組、當前幀的歸一化時間等。原來用于處理 cutscene 的 T 值邏輯現在就可以直接拿來作為這個歸一化時間值的來源了。

這樣一來,整個 cutscene 系統就實現了高度通用性與模塊化,任何一個場景的渲染,只需要用標準結構傳入數據即可完成繪制,大大提高了代碼的復用性和維護性。
在這里插入圖片描述

game_cutscene.cpp:定義 layered_scene

我們現在所需要做的,就是正式定義一個分層場景。之前寫的這部分內容,正是用于定義這個分層場景的數據。我們可以直接把這段定義提取出來,單獨放到一個結構體中。之后,我們就能直接在程序中使用這個定義好的場景進行渲染。

我們將所有相關的信息封裝到這個結構體里,包括層數(layer count)是 8、鏡頭編號(shot index)是 1、資源類型(asset type)對應的是開場動畫。剩下的就是填充圖層定位(layer placement)數據,這些數據也已經準備好了,只需要設定一個指針指向這部分信息。

定義好場景之后,就可以在需要渲染的地方傳入這個場景結構體。接下來還需要補充兩個攝像機參數,分別是 CameraStartCameraEnd,這樣就能在場景切換中進行視角控制了。

將這兩個變量也加入定義中,然后設定對應的值,使得這個場景在播放時能完整運行。這樣,一個完整的分層動畫場景結構就構建完成,可以作為系統中任意 cutscene 的模板,后續其他鏡頭也能以此為基礎進行搭建與替換。

整體思路是把每個鏡頭都變得模塊化、數據驅動,使得系統對 cutscene 的加載和播放更加通用、高效、靈活。
在這里插入圖片描述

解決問題并從中概括,即面向壓縮的編程

我們一貫采用的編程方式非常直接明確。只要已經清楚接下來要完成的核心任務是什么,就會直接動手去寫實現代碼,這正是之前所做的。首先專注于完成具體功能,等完成后再回過頭來,把那些可以重復使用的部分抽離出來,整理成更通用的結構。

這種方式避免了大量前期冗余的設計討論,也不需要來回斟酌那些在實際問題中根本不重要的細節。只需先解決實際問題,然后再從已完成的解決方案中提煉出通用模式。與其一開始就進行過度設計,不如從具體做起,然后自然演化出更好的架構。

這個方法最適用于我們清楚如何完成某個具體目標的場景。例如,如果已經知道如何編寫一個完整的過場動畫流程,那就直接把這個過場動畫編碼出來。編碼完成后,所需的通用機制自然就會浮現出來,比如分層場景、鏡頭參數、動畫持續時間等。

如果一開始就不知道如何做某件事情,那確實不能采用這種方式。但當我們已經掌握其中一個案例的實現方法,就可以通過這個案例的構建過程,把其他共性部分提取出來,從而構建整個系統的通用結構。這種方式始終是最輕松、最高效的設計方法。
在這里插入圖片描述

game_cutscene.cpp:清理一些東西

現在已經完成了分層場景的基本結構設計,接下來就是將其整合到現有的渲染邏輯中。我們所需的內容幾乎保持不變,比如傳入的歸一化時間參數仍然照舊,只是現在像鏡頭索引、圖層數量和資源類型這些信息,全部從分層場景結構中提取出來,而不是單獨管理。

對于資源類型 ID,也直接從場景結構中讀取,這樣就實現了邏輯的集中和簡化。通過這種方式,每個場景變得更加獨立和自包含,使得渲染函數只需要接收一個完整的分層場景結構,而不再需要依賴外部零散的參數配置。

剩下需要處理的一些小問題,比如單位轉換相關的 meters 到 pixels 的操作,還有一些代碼細節需要清理。這些目前雖然還不是最理想的狀態,但已經能正常工作,只是后續可能需要讓這部分邏輯更加清晰和一致。

此外,注意到變量類型有些地方原來用的是 signed 類型,而實際上這里更適合用 uint32 這樣的無符號整數,這樣可以避免不必要的類型問題或隱式轉換引起的錯誤。

最后,圖層位置數組的引用方式也需要調整,現在應該從場景結構中讀取,而不是直接訪問之前的局部變量。整理完這些內容之后,基本上就完成了一個可復用的、結構清晰的分層場景渲染系統。
在這里插入圖片描述

game_cutscene.cpp:引入 scene_layer 和 scene_layer_flags

現在開始處理“距地距離”(distance above ground)的問題,這部分稍微有點棘手。因為在某些圖層中,例如角色固定不動、或具有特殊顯示位置的圖層,目前系統里并沒有一個明確的方法來表達這些“特別”的層。我們之前的方式是簡單地將其遠遠地移動到背景中去,但這種方式不夠明確和靈活。

為了解決這個問題,我們決定為每一層引入更強的表達能力,即每層擁有獨立的參數設置。具體來說,我們把之前混合在一起的隱式數據結構,改成顯式的數據字段。例如:

  • 明確設置圖層的位置 p
  • 明確設置圖層的高度 height
  • 為圖層增加一個“標志位”(flags)機制,讓我們能夠在每層上添加類似“處于無限遠”(at infinity)這樣的標記。

于是我們定義了 SceneLayerFlags,并在里面加上了一個 SCENE_LAYER_FLAG_AT_INFINITY 這個標記。這個標記的意義在于:如果某一層被標記為 at infinity,那么它的 Z 值不再是一個固定值,而是動態地加上 distance_above_ground,使其始終處于地面之上的某個相對高度。這樣我們就可以給它設置一個例如 -200 的基礎值,然后用 distance_above_ground 動態疊加,使其看起來始終處于“背景”或者“空中”的效果。

為了實現這個邏輯,我們將原先叫做 layer_placement 的數據結構重命名為更通用的 scene_layers,因為它現在不再只是描述“放置”,而是包含了更多的狀態信息,如位置、標志等。每一個圖層的數據現在會獨立處理,并從結構中讀取出對應的字段:

  • layer.p:圖層的位置;
  • layer.flags:圖層的標志位;
  • layer.height:圖層高度(如果有需要可以使用);

我們還使用了宏(macro)來輔助處理標志位的讀取,使代碼更簡潔,雖然這在功能上并不是必須的,只是覺得這樣更方便。

這樣一來,我們的系統就具備了更強的可擴展性和表達能力,可以根據圖層的具體需求設置不同的行為,比如:

  • 設置某些圖層始終相對地面浮動;
  • 將某些圖層標記為特殊狀態,便于后續邏輯處理;
  • 更容易管理不同層之間的顯示深度和邏輯區分。

目前結構已經基本清晰,接下來可以在渲染時直接根據這些參數進行調整,確保每個圖層根據自己的設定被正確渲染。這樣就為后續復雜場景的構建和動畫控制打下了堅實的基礎。
在這里插入圖片描述

在這里插入圖片描述

運行游戲并查看我們的場景

現在我們已經完成了基礎構建,接下來要做的就是驗證場景能否正常恢復顯示。最終場景如預期一樣渲染出來了,所有內容看起來都和之前一樣,這正是我們想要的效果。

這意味著現有的結構和功能已經成功實現了可復用的“圖層化場景”渲染邏輯。接下來就可以繼續開展后續工作了。

我們現在可以開始制作其他的場景了。只需要調用剛才完成的渲染函數,并傳入新的圖層場景數據即可渲染一個新的分鏡(cutscene)。這整個過程的重點是:

  • 代碼的可復用性已經達成:無需再次編寫一遍渲染邏輯,只需要提供新的數據;
  • 邏輯的通用性已經驗證:不論是哪個場景,結構一致的數據都可以通過同一個流程渲染出來;
  • 節省了大量重復工作:所有之前寫好的邏輯現在都能被重復使用,極大提升了開發效率。

接下來我們要做的,就是繼續定義其他新的圖層化場景,把它們像之前一樣組織好,然后一一調用同樣的接口進行渲染。這將讓整個劇情系統的分鏡管理變得更清晰、更高效,也更易于后續維護和擴展。

在這里插入圖片描述

在這里插入圖片描述

game_cutscene.h:將結構體從 game_cutscene.cpp 移到這里

現在我們要開始構建下一個分鏡場景,方法會和之前完全一樣。我們會按照相同的步驟來搭建。首先我們在代碼結構中,準備好一個新的位置來存放場景定義。

接著我們注意到項目中其實已經存在一個名為 game_cutscenes.h 的頭文件,雖然之前沒有特別注意到它的存在,但既然已經有了這樣一個文件,我們就決定把新的場景定義放到這里去。

這個文件可以很好地作為統一管理所有分鏡場景的地方,將不同的場景注冊和組織起來。這樣每次新增一個分鏡,都可以清晰地放在統一的結構里,后續查閱和修改也會更加方便。

最后,我們創建了一個新的場景,并將其放入結構中。整個過程令人滿意,現在又多了一個明確的、可組織的新場景定義點。

我們正式完成了下一步的準備工作,場景定義文件也變得更加健全,后續只需要繼續添加新的分鏡內容即可。整個系統正逐步走向更加模塊化和規范化的方向。

在這里插入圖片描述

game_cutscene.cpp:設置以合成更多的鏡頭

現在我們切換到了全屏模式,能夠完整地查看分鏡畫面,并開始像之前一樣進行分鏡的編輯。我們進入了 RenderCutscene 的流程中,接下來的目標是定義一個新的分鏡場景。

整個過程很基礎,沒有復雜的內容。我們復制了之前已有的一段分鏡代碼,作為新的模板,并開始構建一個新的分鏡,當前這個新分鏡被命名為 “Shot 2”。

我們首先進行了基礎的設置,比如用條件判斷語句把不同的鏡頭分開處理,避免變量或設置之間的沖突。雖然目前顯示的還是第一個分鏡的內容,但結構已經準備好,隨時可以替換。

然后我們檢查了是否存在標記為“無限遠”的圖層(此前用于背景固定層的特殊標記),發現當前這個分鏡并沒有這樣的需求,所有圖層都會隨場景一起移動。因此我們移除了所有與“無限遠”相關的設置。

我們暫時還沒有確定這個分鏡中相機的具體運動方式,所以相機移動部分被注釋掉或暫時保留為空。這意味著當前相機在場景中是靜止的,僅顯示分鏡圖層的內容,方便我們逐一查看每個圖層的效果。

接下來我們會繼續調整各個圖層的位置、內容,以及確定相機的運動路徑,從而完成這個新的分鏡場景的搭建。整個流程沿用了前一個分鏡的結構和方法,保持了代碼的可復用性和一致性。
在這里插入圖片描述

在這里插入圖片描述

game_cutscene.cpp:編碼 CameraStart 和 CameraEnd 中的 Z 軸運動

我們決定對當前場景的Z軸處理方式進行一次改進。之前我們在渲染過程中把 distance_above_ground(離地距離)直接硬編碼加到了 pz 值上,比如加了一個 10 - 5.5 這樣的偏移。但現在回頭來看,這樣做其實并不理想。

原因在于,我們希望Z軸的變化成為相機運動的一部分,而不是靜態地被加到某個變量里。硬編碼的Z偏移會限制我們對相機的控制,使得相機的運動不夠靈活且不透明。因此我們做了如下調整:


我們將原本硬編碼的Z軸偏移移除,改為讓相機在 camera_startcamera_end 中的Z值承擔這一變化。比如我們希望相機從高度10向下移動到高度5,那么就將相機的Z軸從 0.0 移動到 -5.0。這樣就可以:

  1. 把Z軸運動完整地納入相機軌跡中,增強了動畫可控性
  2. 不再需要額外處理 distance_above_ground 的加法邏輯
  3. 避免層疊數據結構之間的耦合,使得相機行為更直觀

同時我們考慮了一個更簡潔的方案:如果不再手動編碼 distance_above_ground,那么我們可以直接將它設為0,因為它的意義已經由相機的Z偏移代替了。也就是說,現在這個變量成了一個“純形式值”,在渲染邏輯中不會再被直接引用或參與計算。

最終效果是:

  • 所有與相機有關的高度變化現在都歸納進 camera_startcamera_endz 分量;
  • 圖層的渲染位置則獨立處理,不再混合進相機控制邏輯;
  • 渲染系統也變得更干凈、分工更明確;

我們恢復到了之前的渲染狀態,同時為今后每一個分鏡的相機運動留下了更大的自由度和表達能力。每一段Cutscene都可以通過這兩個變量來自由控制前后幀之間的鏡頭移動——這是我們更理想、更合理的做法。
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

game_cutscene.cpp:合成鏡頭 2

我們現在回到第二個鏡頭的制作,開始對其進行具體的圖層與相機運動調整。

首先我們注意到第一個圖層是靜止的,這很正常,因為當前相機還沒有任何移動。這個鏡頭計劃是一個緩慢的推進鏡頭(slow zoom),所以我們預設相機的Z軸從0推進到-5,形成一個比較輕微的推進感。如果感覺推進不夠明顯,也可以進一步加深推進程度,這取決于圖層之間的相對距離。

圖層的空間擺放也進行了初步的設定。第一個圖層,我們計劃將其放在相機前方大約-20米的位置,并設置較小的縮放比例。因為這個鏡頭是一個近景鏡頭,圖層不多,整體畫面也比較緊湊,推進的效果會更加顯著。為了控制推進的節奏,我們決定將相機的移動距離設為0.5米,模擬一個非常微妙的拉近。

時間上的控制暫時沒有加入,我們計劃在后續調整鏡頭節奏時一并處理。

接下來,我們添加了第二個圖層,它需要位于孩子的前方。初始擺放完成后,進一步微調了位置,使之與整體構圖更協調。隨后檢查是否還有其他圖層,確認還有一層是冰柱(icicles)。

冰柱圖層被放置在畫面頂部,并設定為離相機更近的位置,例如-8米,以便在推進鏡頭時呈現更強的視差效果(parallax)。這個圖層也上移了一些,以確保它在正確的屏幕位置,增強畫面層次。

檢查后確認該鏡頭僅由三個圖層構成:

  1. 背景層(遠處)
  2. 中景層(孩子)
  3. 近景層(冰柱)

我們接下來開始微調視差效果。觀察中景層的窗戶部分后,決定將其進一步靠近,以增加透視感和空間感。同樣也微調了孩子圖層的位置,讓他更居中地位于窗戶中間,同時略微下調其Y軸,使其構圖更自然。

調整完所有圖層的空間位置后,我們回到相機設置,再次確保相機推進的終點對準孩子的位置,形成一個具有明確視覺焦點的推進鏡頭。

最終這一組調整達到了我們預期的效果:

  • 鏡頭推進自然;
  • 圖層間深度關系合理;
  • 透視與視差增強了空間感;
  • 畫面焦點清晰,對準孩子;
  • 各圖層構圖協調。

這一版的鏡頭感覺良好,基本達到了視覺與運動的雙重目標。
在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

(近 → 遠) :

  1. Icicles(前景,最靠近相機,用于增強空間感和運動感)
  2. Wall and window(中景或前中景,構圖元素)
  3. Hero and tree(中景或背景主角圖層,是視覺焦點)

缺少雪花…α

目前我們已經完成了一個剪輯,整體看起來效果不錯。這個剪輯場景中只包含了三個圖層,分別是:

  1. 主角和樹:這個圖層展示的是角色本身以及身邊的一棵樹,我們將這個角色稱為“主角”。
  2. 墻與窗:第二個圖層中包含了一面墻和一個窗戶,形成了房屋外部的背景元素。
  3. 冰柱:第三個圖層是一些冰柱(比如從屋檐垂下來的冰錐),被放置在畫面的最前景,以增強縱深感和畫面的層次。

原本計劃中還打算加入一些飄落的雪花,用來進一步豐富場景的動態效果,但我們意識到很可能在資源包中忘記包含雪花圖像,這是一個疏漏,后續可能會單獨補充雪花相關的素材。

不過目前這些靜態圖層的組合已經滿足該場景的需求。剪輯整體結構和過渡都達到了預期,特別是鏡頭緩慢推進到主角身上的動態設計也已經調整得比較滿意。冰柱等元素的位置也經過微調,以增強前景的視覺運動感,畫面層次感更清晰。

總的來說,這個剪輯就是圍繞“主角與樹”“墻與窗”和“冰柱”這三類圖層構建的,整體感覺已經相當不錯。接下來我們將繼續制作下一個剪輯。

game_cutscene.cpp:合成鏡頭 3

我們現在開始制作第三個剪輯,在索引中新增一個場景,重新開始配置。這一幕的背景是室外場景。

這個剪輯相對有趣,但也存在一定挑戰。在設想這個鏡頭時,我們并不確定所采用的透視是否能奏效。這個場景的鏡頭運動方式不是垂直方向,而是橫向移動,這是比較少見的一種處理方式。我們有一段是垂直移動的,還有這一段是水平橫移的。這種處理方式在這種剪輯技術中相對少見,所以我們也不確定是否能很好實現。但如果最后發現無法達到預期效果,那也沒關系,藝術制作中本來就經常要進行反復調整和修改。

我們開始搭建這個剪輯,首先加載天空圖層。天空圖層在理論上是應該靜止的,因為它是實際的天空背景,不應該隨著鏡頭運動。但由于這段鏡頭模擬的是一個“俯視下來的移動鏡頭”(Over-the-top camera move),所以也可能需要讓天空稍微移動來配合這種透視效果,因此我們暫時保留它是否靜止的判斷,等后續調整再看。

接下來我們加載第二層,也就是森林背景。這是位于中景的戶外環境。這時我們發現這類運動可能需要一種“反向運動”機制——也就是說,為了模擬攝像機“俯視移動”的感覺,部分圖層可能要進行反方向的視覺移動來制造真實感。所以我們考慮引入一個新機制,比如設定一個“反向移動標志”,讓某些圖層在剪輯中反向運動,以達到正確的視覺模擬效果。

我們繼續添加更多圖層。當前我們已經加載了一個新圖層,這是我們要模擬的“頂部視角”的一部分。這個視角的設想是——攝像機從上方向下拍攝,然后緩緩移動,使觀眾有一種鏡頭越過人物頭頂的感覺。

現在我們把主角(孩子)的圖層也加入進來。這個角色圖層我們希望它隨著攝像機運動一起移動,而背景圖層可能不動或反向移動,形成視覺上的“鏡頭繞著角色移動”的效果。這意味著角色圖層應該與攝像機保持一致的運動,而背景圖層要模擬出相對運動差。

這個設計思路要求我們后續仔細調試每一層的運動方式,尤其是鏡頭運動過程中,不同圖層產生的視差與節奏必須協調,才能形成理想的透視與動感。整個場景的邏輯是構建一個俯視鏡頭從背景穿越至主角上方,通過合理的圖層組合與運動設計,實現動態的過渡效果。我們將在繼續調整位置和動畫方式的基礎上,進一步測試視覺結果是否符合預期。
在這里插入圖片描述

在這里插入圖片描述

game_cutscene.h:將 CounterCameraX 和 CounterCameraY 添加到 scene_layer_flags

我們繼續添加功能,在這里我們引入了一個新的標志變量,用來實現“反向相機運動”。我們先切換回常規視圖,然后開始添加這個新標志。這個標志可以命名為 counter_y 或更明確的 counter_camera,具體來說我們最終定義為 counter_camera_xcounter_camera_y 等。

這個機制的作用是在某些圖層中啟用相機運動的反向偏移,即當相機向一個方向移動時,這些特定圖層會以相反的方向做相應的位移,從而在視覺上看起來像是保持了相對位置不變。這種效果在模擬“鏡頭繞物體運動”時非常重要,尤其是在構建俯視鏡頭(top-down shot)或水平掃動鏡頭時格外有用。

在具體實現中,我們修改了之前統一使用的相機偏移方式。原來我們是通過一個整體的 camera_offset 直接影響位置計算的,現在我們將其拆分為 pxpy 兩個維度分別處理:

  • 對于 px,我們先減去 camera_offset.x,但根據是否設置了 counter_camera_x,決定是減去還是加上這個偏移值。
  • 對于 py,同樣判斷是否設置了 counter_camera_y,來選擇加減偏移量。

這種方式允許我們對每個圖層單獨控制它是否要應用反向相機運動,而不是所有圖層一視同仁。這樣一來,我們就能很靈活地決定哪些圖層應該隨著鏡頭運動而移動,哪些圖層應該產生相對靜止或反向漂移的效果,從而更精確地模擬真實鏡頭中的透視、空間感與層次深度。

總結:

  • 添加了一個新機制 counter_camera_x/y 來支持圖層根據需求執行反向相機運動;
  • 將位置偏移計算拆分為 xy 兩個方向進行;
  • 每個方向都支持獨立控制是否進行反向偏移;
  • 為接下來的復雜鏡頭設計打下基礎,尤其適合處理非線性運動、模擬旋轉或環繞等鏡頭。

在這里插入圖片描述

在這里插入圖片描述

game_cutscene.cpp:處理 Z 情況

我們意識到之前的代碼邏輯中忽略了對 Z 軸(深度)方向 的處理,這是一處遺漏。在進行相機偏移的處理時,只考慮了 X 和 Y 方向的位置偏移,忘記對 Z 方向進行處理。

Z 軸的處理非常重要,特別是在模擬景深、遠近視差(Parallax)或推進鏡頭等情況下,Z 方向的位移直接影響圖層在空間上的表現。

于是我們補上了 Z 軸的偏移處理邏輯,即:

RenderGroup->Transform.OffsetP.z  = pz - camera_offset.z

這段代碼的意思是將圖層在 Z 方向上的位置也根據相機的 Z 偏移進行相應調整,從而保持統一的相對空間位置。因為當相機在 Z 方向上有運動時,如果不對圖層的 Z 值做補償,畫面中就會出現錯位或不一致的視覺效果。

現在 X、Y、Z 三個方向的偏移都已經正確處理,場景中所有圖層在相機移動時應該能夠保持準確的空間關系,視差效果和鏡頭運動也能按照預期表現。這是構建多圖層鏡頭運動系統中非常關鍵的一步。
在這里插入圖片描述

game_cutscene.cpp:繼續合成鏡頭 3

我們正在調整一組鏡頭,實現的是一個具有縱深感和前后視差效果的場景過渡,試圖讓背景圖層與相機運動形成反向關系,從而制造出空間感和動態效果。

首先,我們設置背景圖層與相機形成「反向運動」,也就是相機往一個方向移動時,背景圖層以相反方向位移。我們不確定這樣設置是否會完全起作用,但打算嘗試一下看看效果。

接著我們開始微調角色初始位置,讓角色一開始處在畫面較低的位置,并在鏡頭移動過程中逐漸脫離畫面,以此營造出鏡頭拉遠的感覺。同時我們也延長了鏡頭時長,原本是5秒,現在改為10秒甚至20秒,以便讓整個動作更自然、更緩慢,尤其是與后續配音匹配時能留出更多節奏空間。

為了適配鏡頭的拉遠效果,我們調整了背景尺寸,使其大幅擴大,能夠覆蓋整個窗口,避免在鏡頭移動過程中看到邊緣。我們還調整了圖層的深度順序和縮放比例,比如讓某些遠景圖層處于“無限遠”,保證它們不會因為相機前進而發生明顯偏移,從而維持遠景穩定。

隨后我們對畫面進行逐步測試和觀察,發現角色的運動速度過快,無法表現出“拉近”效果中應該有的那種頭部逐漸變大的視覺印象。于是我們給相機加上了一點Z軸(向前)的運動,營造出景別變化的感覺,這樣畫面看起來更有動感,也更接近我們想要的效果。

我們也嘗試通過移動其他圖層位置來配合整體的透視變化,使“男孩頭部變大”的效果更加明顯。為此還對遠景背景位置進行后移,使其更加符合透視關系。由于這個鏡頭不是簡單的網絡插值,而是帶有透視投影效果,所以我們必須精細控制每個圖層的位置和比例。

最終,通過不斷試驗,我們找到了合適的鏡頭運動節奏、背景比例、角色位置和景深配置,達到了較為理想的畫面效果。雖然這個鏡頭仍然有一定難度和不確定性,但整體上已呈現出預期的空間感和動勢,為后續的過渡鏡頭和敘事鋪墊打下基礎。

在這里插入圖片描述

game_cutscene.cpp:合成鏡頭 4

接下來,我們開始著手處理下一個鏡頭。首先清理了一些標記和設置,讓場景回到一個更合理的狀態。接著,檢查了鏡頭設置,準備進行一個緩慢的縮放。這個鏡頭沒有特殊的相機動作,只是一個簡單的鏡頭縮放,看起來就像是逐漸拉近的效果。

為了調整這個縮放效果,決定讓鏡頭略微靠近一點,并且調整鏡頭的運動速度,確保場景過渡的流暢性。完成這些調整后,繼續添加新的圖層,確保每個圖層都設置正確。

接下來,發現場景中有一個小小的動畫效果,決定在系統中添加一個新功能來支持這個動畫。這個動畫的效果比較輕微,但仍然需要系統支持才能完成。為了調整這個動畫,需要做一些新的功能擴展,但這不會太復雜。

在此過程中,還注意到場景中的人物數量不太對,原本以為會有更多的小孩出現在畫面中,但目前只看到一個小女孩在前景中。這可能是資源包中出現了問題,導致人物缺失,需要稍后檢查和更新資源包。

此外,還注意到場景中有一些閃亮的裝飾物(例如亮片),這些也需要確保正確顯示。

最終,確認了這個鏡頭的設置,并且完成了對動畫的處理,保證了整個場景的效果能夠如預期般運行。這一切都做好后,準備繼續進行后續調整和測試,確保每個鏡頭都能夠順利呈現。

反正就是挨著調鏡頭

在這里插入圖片描述

缺少孩子β

首先,對于場景中的一些問題,決定暫時假設它們是正確的。雖然有感覺應該有更多的小孩出現在畫面中,但目前沒有找到他們的蹤影,可能是資源導出時出現了問題。對不起,小孩們,似乎有些小孩丟失了,但其他部分基本上看起來是對的。

接下來,重點是調整場景中的一些元素,特別是裝飾品(如亮片)。亮片的尺寸需要調整,變得更合適一些,應該離攝像機更近一些。調整后,亮片應該不會顯得那么搶眼,避免遮擋其他重要元素,應該調整為一個更加合適的比例。

此外,孩子和圣誕老人應該稍微向下調整位置,看起來應該像是稍微低一些的樣子。調整這些元素的位置,使得整體效果更加自然。

最后,回顧了一下文件的導出,發現確實有一些小孩缺失,計劃稍后重新導出并更新資源包。因為這些小孩應該位于畫面的角落,但目前找不到他們的位置,可能是在保存文件時丟失了。

討論錯位的孩子γ

在處理場景中的一些元素時,發現自己可能錯位了很多孩子角色。這并非有意為之,完全是個失誤。始終嘗試盡量保證所有的角色都在場景中,但偶爾一些角色會因為太煩人而決定“失蹤”,這時就不小心把他們錯放了。其實在游戲中也鼓勵這種做法——有時實在是讓角色消失更為方便,尤其是當他們真的非常擋路時。

接下來,調整了一些角色的位置,特別是一個小女孩的位置。原先的位置稍微遮擋了另外一個小女孩,所以需要稍微縮小她的大小,讓她低一些,這樣看起來更加合適。調整后,感覺效果不錯,看起來就對了。

接著,又調整了圣誕老人和孩子的位置,原先他們的相對位置有點不對,圣誕老人似乎壓住了地面的一些元素,所以稍微向一邊移了下,調整后看起來好多了。對于這些細微的調整,感覺每一處的改變都非常細小,但卻非常關鍵。每一個細微的調整,給場景帶來的效果可以是巨大的,所以這些小動作和細節處理都非常重要。

game_cutscene.h:將 MinTime 和 MaxTime 添加到 scene_layer,將 Transient 添加到 scene_layer_flags

為了讓這個余弦動畫按預期執行,我們需要確保兩個元素在不同時刻播放。可以通過一個簡單的方式來實現這一點,即添加一些“排除”機制。具體來說,我們可以設置一個時間范圍,定義某些場景層在特定的時間段內出現或消失。

實現的方式是設置一個類似“瞬態層”的標記,也就是一個僅在特定時間范圍內激活的層。這樣,我們就能夠在時間上控制某些層的顯示,確保它們不會同時存在于畫面中。

具體做法是,我們可以給這個瞬態層加一個標記,讓它只在指定的時間段內存在。通過這種方式,可以輕松地控制動畫中各個層的顯示時機,確保它們在正確的時刻開始和結束。

在這里插入圖片描述

在這里插入圖片描述

game_cutscene.cpp:設置 Transient 層

我們為了解決兩個圖層不能同時播放的問題,可以采用“瞬態層(transient layer)”的概念,將這兩個圖層都標記為瞬態層,然后通過設置它們的播放時間范圍,使它們分別出現在鏡頭的前半段和后半段。

實現上,我們可以添加一個參數,例如 thought_change_time,設定為一個值,比如 10。這個值將作為時間分界點,控制兩個瞬態圖層的出現時機。例如,第一個圖層在前半段(時間小于 10)出現,第二個圖層在后半段(時間大于等于 10)出現。

接下來在渲染場景時,遍歷每一層時可以加一個 active 標志來判斷該層是否應該被渲染。默認情況下,activetrue,但如果當前圖層被標記為瞬態層,那么就需要判斷當前歸一化時間 t_normal 是否在其指定的時間范圍 [min_time, max_time) 之間。如果在這個范圍內,該層就被激活并參與渲染,否則就被跳過。

特別地,在判斷時間區間時,使用了“左閉右開”的區間判斷方式,即:

  • t_normal >= min_time:表示當前時間已經到達或超過該層的起始時間。
  • t_normal < max_time:表示當前時間還沒有超過該層的結束時間。

這樣可以實現兩個圖層在某個關鍵時刻(比如 0.5)進行無縫切換,保證它們不會在同一個時間點重疊。這個時間判斷的精度設計是刻意的,確保不同圖層之間不會發生時間上的沖突,從而實現清晰準確的視覺切換效果。
在這里插入圖片描述

觀看動畫版本

這是當前動畫版本的呈現效果,可以清楚地看到其中的動作變化,比如角色將帽子戴到另一個角色頭上的小動作。整個片段中,角色間的交互已經具備了一定的動態表現力。

在此基礎上,也可以考慮是否加入一些過渡效果,例如淡入淡出(fade)的方式,讓兩個圖層之間的切換更加平滑自然。這種做法的可行性需要根據整體畫面的風格判斷是否合適。如果畫面風格比較干凈利落,淡化切換可能會讓視覺語言顯得不夠利索;但如果想加入一些更具表現力或情緒性的元素,漸變過渡可能是一種值得嘗試的方式。

實現這種漸變的方式可以使用漸變函數(ramp)來控制兩個圖層之間的透明度權重。例如,從前一個圖層逐漸降低透明度,同時下一個圖層逐步增強透明度,這樣在一段時間內兩個圖層都會存在于畫面中,但視覺上只感受到一種柔和的過渡感。

雖然目前還未決定是否要使用這樣的漸變方式,但技術實現層面是可行的,關鍵是是否與整體視覺風格協調一致。現在的版本已經能夠展現基本動作邏輯,是否進一步美化轉場效果可以作為下一個優化方向來考慮。

game_cutscene.cpp:合成鏡頭 5

現在繼續處理鏡頭五的部分。在剩下的五分鐘內,計劃再推進一下整體進度。到目前為止,已經完成了大約一半的鏡頭,進度良好。

開始查看鏡頭五的內容。首先看到的是背景圖層的導入,屬于一個標準的背景畫面。觀察之后認為這個背景可以稍微往后拉一些,使得整個構圖更合理。

繼續查看接下來的圖層安排,初步判斷鏡頭五仍是一個結構比較常規的畫面,沒有特別復雜的動態或分層變化。畫面中各元素的排列方式和前面處理的鏡頭類似,繼續按部就班地進行就可以。

這一部分的關鍵在于確認背景位置和后續圖層的相對關系是否協調,確保畫面空間感與整體節奏的一致性。下一步將繼續處理鏡頭五中的具體圖層內容,并根據需要微調位置與層級。

在這里插入圖片描述

最精彩的鏡頭δ

這是整個場景中非常重要的鏡頭——“關鍵鏡頭”,一切戲劇性的展開都從這里開始。在這一鏡頭中,我們首先設置了背景圖層,并將其拉得相對較遠,以此營造出一個緩慢移動的效果。同時為了保持視覺沖擊力,將其整體比例調大,使得它在構圖中更具存在感。

接著計劃為鏡頭添加一個更具戲劇性的相機移動,以提升整體的動態張力。然后開始處理畫面中的門的圖層,將它們添加進場景,并且要確保這些門能夠填滿整個畫面。這一部分的圖層也會使用 transient(臨時)標志,意味著它們會在畫面中短暫出現,然后被后續的圖層取代。

設定這些門的顯示時間為鏡頭切換時間點(shot change time),例如 1.0,確保門在特定時間點消失,讓下一個重要角色順利登場。

隨著門的爆開,格蘭帕斯(Krampus)終于登場——這個眾所周知的假日惡魔角色突然出現在孤兒院的大門口。為了讓他的出現更具沖擊力,將其圖層略微縮小,使其比例更合適,并調整其位置以獲得更理想的構圖。同時,他的圖層設置得比前景更接近鏡頭,使得他在視覺上更突出,并帶有明顯的動感。

另外還加入了雪的圖層,進一步增強了節日氛圍和畫面層次感。通過合理設置雪的顯示方式,使整個畫面更加生動豐富。整個鏡頭完成后,效果非常令人滿意,畫面節奏緊湊,角色登場充滿張力,是目前為止非常喜歡的一個鏡頭。

格蘭帕斯(Krampus)”是歐洲民間傳說中的一個神秘角色,特別流行于奧地利、德國、匈牙利、捷克等地區的圣誕節文化中。他是一個反面節日角色,可以被理解為圣誕老人(Santa Claus)的“邪惡搭檔”

簡單來說,Krampus 是誰?

  • 外貌:通常被描繪成一個高大、全身長毛、有角、舌頭很長的惡魔形象,有時還拖著鐵鏈或手持柳條。
  • 職責:他不像圣誕老人那樣獎勵乖孩子,而是懲罰不聽話的孩子
  • 故事背景
    • 圣尼古拉斯(Santa Claus)會獎勵乖孩子;
    • Krampus 則會懲罰壞孩子,甚至把他們裝進麻袋里帶走。

Krampus 的“節日”

在一些地區,每年的 12 月 5 日 是“Krampus Night(克蘭帕斯之夜)”,年輕人會裝扮成 Krampus,在街上游行,制造氣氛,也是一種傳統慶典。


在創作/影視中

Krampus 經常被用作節日恐怖題材的角色,象征節日文化中的陰暗面。許多動畫、電影或游戲會以他為靈感,塑造出一種在喜慶氣氛下潛伏的“威脅”角色。


“這就是電子游戲”ε

我們完成了整個鏡頭的布置,整體效果非常不錯,令人滿意。這個鏡頭的構成和動畫都體現出一種非常典型的視頻游戲式風格,也確實是在做“電子游戲”的感覺。

我們已經把所有需要的元素都加入到了畫面中。為了讓鏡頭更有動感,我們打算對攝像機的移動軌跡做一些微調,加入一點向上的平移。這樣可以更好地呈現出角色格蘭帕斯的氣勢——因為他像是一個山羊形態的角色,所以鏡頭緩緩地向上移動,有種仰視的感覺,更具張力。這種鏡頭運動可以更好地引導觀眾的視線,把注意力集中到格蘭帕斯身上,氣氛會更有壓迫感。

我們還考慮了發音上的差異——如果按照中歐大陸風格的發音,他的名字可能更像是“克蘭帕斯”或“克拉姆普斯”(Krampus / Krampus),無論如何,這種差異在不同地區的表現中都是常見的現象,也增添了一點文化趣味。

這個鏡頭整體已經讓我們感到很滿意,最終的構圖和節奏也非常到位。接下來可能還會有人對這個部分提出一些問題,比如關于鏡頭語言、角色氛圍或動態表現的細節,但就目前來看,整體已經進入了一個比較穩定和完善的階段。

game_cutscene.cpp:合成鏡頭 6

我們繼續進行下一個鏡頭的制作,這是第六鏡頭。因為場景制作非常有趣,而且能看到它們實際出現在游戲中,非常有成就感,所以決定再做一個。

在這個鏡頭中,格蘭帕斯已經完全登場。按照設定,他自然是要開始追逐孩子們了,這是他的“職責”。他出現的目的就是專門對付那些淘氣的孩子,這正是他的“行事風格”,可以說是他存在的意義。

這次的鏡頭設置依然從兩層結構開始,并重新整理了圖層數量。我們估算了一下格蘭帕斯與前景的距離,大概是八米左右,于是將他放置在適當的位置,使他在畫面中的比例和位置更具視覺沖擊力。

對格蘭帕斯的位置進行了微調,使其更加居中并且具有威脅感,同時保持和前一個鏡頭的連貫性。從目前的效果來看,這個鏡頭的構圖已經相當不錯了,格蘭帕斯的存在感強烈,畫面張力十足。

整體氣氛符合預期的敘事發展節奏,也為接下來的動作鋪墊了很好的基礎。鏡頭設定簡潔但有力,讓格蘭帕斯的壓迫感直接傳達給觀眾,進一步營造出緊張氛圍,令人期待后續的發展。
在這里插入圖片描述

當克蘭普斯出現時ζ

我們繼續制作了第六個鏡頭,這一段是格蘭帕斯真正開始追逐孩子的場景——通常來說,格蘭帕斯一登場,事情就開始“變得有意思”了。他的出現基本意味著故事節奏的加快,也是游戲中最令人期待的時刻之一。

在這一鏡頭中,沒有使用任何“transient”臨時圖層,而是采用了標準圖層結構,場景構建比較常規。我們依舊從設置圖層數量開始,并加載了雪的圖層,這一圖層為整個畫面增加了深度和空間感。雪花是分離圖層導出的,這樣每個圖像都更具專屬的空間層次,也便于后續動態表現的調整。

接著我們加載了被格蘭帕斯追逐的孩子的圖層。這個孩子的位置進行了微調,包括左右和上下的輕微移動,讓其在構圖中更合理。他應該略低一些,以增強被追逐感。

相機的移動也進行了調整,決定采用略微向上的鏡頭運動,模擬相機從地面稍微上仰的效果,以增強格蘭帕斯的壓迫感和畫面的戲劇性。

隨后添加了代表孩子眼淚的圖層。這個圖層進行了大小調整,并通過鎖定時間軸的位置精確對齊到孩子臉部。我們確保眼淚是從眼角流出而不是鼻子,這種微調雖然細小但非常重要。考慮到眼淚是和角色面部動作綁定的,所以它不會作為獨立運動圖層,而是緊貼孩子本身。

然后添加了第五圖層,也就是另一個小孩。這一圖層的位置和比例依舊需要對照前景角色進行合理推算。如果這個小孩位于主要角色稍前方,那么她就需要稍微放大,同時位置稍高一點以匹配透視。

接著加入了第六圖層,包含花環等場景道具。這一部分的擺放相對較難確定,因為道具的比例和位置既不能干擾主角的動態,又要保持裝飾作用。對這一層進行了多次嘗試,包括縮放和垂直位置調整,最終選定了一個比較理想的位置。

最后我們重新審視雪的圖層,認為它原本的位置過于前景,因此將其調整為更偏向中景的深度層次,并適當減小雪的顆粒尺寸,使其更自然地融入整體畫面。

整體來說,這一鏡頭構圖已經趨于完整,層次分明、動作明確,同時保持良好的故事節奏和視覺沖擊力。畫面中的雪、人物、裝飾和動作結合得自然流暢,鏡頭運動也增強了畫面的動感,整個場景效果令人滿意。

在這里插入圖片描述

為什么不傳遞每個過場動畫的時長,而不是讓所有的過場動畫時長相同?

目前我們在制作過場動畫時,確實還沒有區分每個鏡頭的具體時長,所有鏡頭默認采用相同的持續時間。但這是暫時的安排,并不是最終的實現方式。

我們之后是會為每個鏡頭分別傳遞并設定其各自的時長的,這是已經在計劃內的事情。只不過在目前這個階段,我們的目標是先完成鏡頭的布局和場景的搭建,還不需要同步到音樂節奏或者做精細的時間調控,所以先使用統一時長作為過渡。

一旦進入到更精細的階段,比如過場動畫需要配合背景音樂、對白或節奏點進行展示,我們就會為每個鏡頭指定具體的持續時間,確保整個敘事的節奏與氛圍更加精準貼合。

總結來說,現在是為了方便快速制作內容,暫時統一了鏡頭時長,等之后節奏需要更精細化時,我們會對每個鏡頭的時間單獨處理。

對于奇怪的攝像機運動場景,你是否考慮過使用曲線插值,而不是線性插值?

關于鏡頭的旋轉和曲線運動,我覺得使用三拍形式反而能產生一種不同的效果,而不是線性運動。三拍式的運動感覺更有動態感,這樣的設計能夠在場景中加入一些有趣的變化。

如果是旋轉的鏡頭,可能就需要更柔和的曲線運動來平滑過渡,畢竟這種旋轉動作本身是帶有曲線感的。但從這個場景來看,我對它最終的效果并不是特別有信心。可能很難做到完全合適的視覺效果,所以我并不確定這會不會真的看起來很好。

至于其他的提問,關于Yankee的工作,她確實是游戲的插畫師,負責了大量的藝術設計和視覺工作,具有很強的插畫能力。

楊天的藝術真棒!

游戲的藝術風格看起來非常棒,我很喜歡。雖然我自己不太參與藝術設計的細節,我通常會讓藝術家去做這部分的工作。我基本上只是給出一些簡單的想法,比如“這里應該有一個格蘭帕斯從門口出現”,然后我可能會畫個簡單的草圖,標明場景的大概布局,比如格蘭帕斯在這兒,其他角色在那兒,接著藝術家就會基于這些簡單的描述,展開創作。

藝術家負責了所有角色的設計,包括格蘭帕斯的外形、其他人物的形象以及整個游戲的視覺風格。所有這些視覺內容都是藝術家發揮創造力后設計出來的。

請做更多的工作η

大家似乎都希望看到更多的過場動畫,那就讓我們做更多的吧,感覺這樣很有意義。反正如果有問題,明天還可以繼續提問,每個工作日都可以隨時向我們詢問。總的來說,這個過程非常有趣,我可以一直做下去,真的很喜歡做這些事情,感覺每天都能做得很開心。

game_cutscene.cpp:合成鏡頭 7

在第七鏡頭中,場景設定相對簡單,只有兩個層次。一個是格蘭帕斯,另一個是孩子,二者在相對運動中。格蘭帕斯向左滑動,孩子向右滑動。這一鏡頭的特點是平移鏡頭,鏡頭不會變焦,只是水平移動。鏡頭的動作是左右平移,展示兩者之間的運動,最終使得格蘭帕斯顯現出來。

此場景使用了“臨時標志”(transient flag)和“反向攝像機動作”標志,用來控制鏡頭和角色的移動。具體來說,格蘭帕斯和孩子的相對運動可以通過調整兩者的位置來實現,保證鏡頭拍攝效果和動作的自然過渡。由于這個場景較為簡單,調整的元素較少,主要是格蘭帕斯和孩子的位置調整,確保兩者在鏡頭中合適的顯示。

在場景的后續調整中,重點是移動鏡頭,以確保格蘭帕斯和孩子之間的互動清晰可見。格蘭帕斯和孩子的運動要協調,以表現他們之間的對抗和互動。

接下來進入第八鏡頭,這一鏡頭不需要做太多的時間變化調整,主要是控制層次的變化和角色的相對移動,確保過場動畫的流暢性和視覺效果。

game_cutscene.cpp:合成鏡頭 8

在第七鏡頭的設置中,首先檢查了鏡頭和角色的層次,確保每一層都正確地展示了角色和物體的位置。首先是格蘭帕斯的設定,鏡頭需要調整,確保畫面清晰,方便查看每一層的元素。鏡頭的運動采用了放大縮小的方式,具體表現為鏡頭對格蘭帕斯進行一定的放大,保證他在鏡頭中的位置和動態更清晰。

在具體操作上,鏡頭和層次的調整需要確保每個元素都居中并覆蓋整個畫面。這是為了保證視覺效果的平衡,讓每個元素都有足夠的展示空間,特別是像手套這樣的道具。手套作為一個重要的物品,它的大小和位置需要精確調整,保證它看起來與角色的比例匹配。

同時,其他層次也被逐一加入,包括一些孩子的角色和吊飾等,這些元素需要保持協調,避免畫面過于擁擠。在這一過程中,注意到了鏡頭的運動軌跡,特別是對于放大縮小的處理,確保鏡頭的移動更加自然。

隨著鏡頭的推進,故事情節也有所展開。格蘭帕斯從最初的追逐孩子轉變為幫助孩子,表現出一種積極向上的形象,與傳統的圣誕老人角色形成對比。這個轉變讓角色更加立體,也為后續的劇情發展奠定了基礎。

最后,調整了畫面中的裝飾元素,如吊飾等,確保它們的位置和大小符合畫面整體的視覺效果。為了讓鏡頭更加有層次感,決定讓鏡頭稍微向下傾斜一些,這樣可以更好地聚焦于手套等物品,同時突出格蘭帕斯和孩子之間的互動。

這個鏡頭的設置基本完成,主要聚焦在細節調整上,確保每個元素都能在鏡頭中正確展現,達到預期的視覺效果。

在這里插入圖片描述

game_cutscene.h:將 v2 參數添加到 scene_layer

在這段過程中,討論了代碼實現中的一些細節,主要是關于參數的設定和代碼優化。首先提到,雖然不想過于沉溺于自己設定的細節中,但依然決定嘗試做一些修改,優化代碼的方式。提出了設定一個“間隔”參數,甚至考慮了設定一個“最大值”或“最小值”的區間參數,像是“32”和“bob”之類的變量名,用來控制某些功能的范圍。

接著,介紹了如何使用一個通用的向量作為參數傳遞,并提到將在這些對象中進行打包處理,目的是為了管理多個數據的組織方式。盡管遇到了一個小問題,即沒有收到預期的錯誤提示,可能是因為代碼在實現時沒有按照預期執行,產生了不完全的錯誤提示。盡管如此,對于這些問題并不感到特別煩惱,認為這并不是非常關鍵的地方,且認為這只是一個技術上的小偏差。

整體來看,這段討論的重點是對代碼中某些參數的調整和優化,以便使其更加符合需求,雖然有些問題出現了,但并沒有太大影響,依然在進行相關操作以完成整體工作。

game_cutscene.cpp:合成鏡頭 9

在這段內容中,討論了關于第九個鏡頭的設置。這個鏡頭主要是一個簡單的鏡頭拉近(zoom),聚焦到一個小孩上。他正回到自己的房間,鏡頭會從遠處拉近到他的身上,確保鏡頭的移動方向與場景相符,調整了鏡頭的角度,使得畫面更具表現力。

接下來,討論了需要的幾個層級。第一個層級是鏡頭拉近至孩子身上的效果,第二個層級則是一些細節的處理,例如房間的天花板上的物品。這個層級中的物品相對較小,且會很快被鏡頭移動過去,因此放置這些物品時也需要考慮它們的大小與位置。

具體來說,鏡頭中的燈具、裝飾物和其他元素都需要調整大小和位置,使得它們與鏡頭的運動相適應,避免遮擋重要的畫面內容。燈具和裝飾物的大小被做了適當的調整,有些元素如燈具還需要稍微移動,確保它們出現在畫面中的合適位置。

在完成這些調整之后,鏡頭的整體效果感覺非常順暢,所有層級都正確排列,鏡頭的移動和物體的擺放都達到了預期的效果。最終,確認鏡頭的布局完成,剩下的任務是繼續處理后續的鏡頭,完成所有的鏡頭序列。

此外,還提到,剩下的工作是將這些鏡頭按照順序連接起來,完成開場的動畫。雖然目前還沒有聲音和配音,但整體的畫面和動畫布局已經準備好了。剩余的工作將在第二天繼續完成,只剩下最后的兩個鏡頭。

在這里插入圖片描述

你喜歡聽到鏈接器的哀嚎和它因無法寫入 .exe 而在西西弗式的痛苦中扭動嗎?

在這段內容中,討論了對Microsoft Linker的負面看法,認為它是一個非常糟糕的工具。對于Linker在遇到問題時的痛苦和掙扎,表達了一種輕微的娛樂感,認為它在遇到困難時的表現是可以接受的,甚至某種程度上令人愉快。對于這種情況的態度反映出對這個工具的不滿,并且對其在處理問題時的無力感感到某種程度的解脫或滿足。

你們有安排配音演員了嗎?

在這段內容中,提到由于還沒有找到合適的人選來進行開場序列的旁白配音,因此配音并沒有包含在當前的包中。等找到合適的配音演員之后,會把旁白部分加入到包中。

第一個鏡頭中的手套是左手手套,接下來的鏡頭是右手手套

在這段內容中,提到了第一個場景中的手套是左手手套,而下一個場景中的手套卻是右手手套。對于這種情況,可能需要修正,因為不應該出現不同的手套方向。解決方法是可以將左手手套翻轉過來,變成右手手套。這樣處理是比較簡單的,而且手套本身發光,因此沒有特別的方向要求,翻轉手套應該是個合理的選擇。

第一場景中的門是開的,但在克蘭普斯進入之前關了

在這段內容中,提到第一個場景中的門是開著的,但在角色進入之前門卻是關著的。對于這種情況,應該做一些修正,并且記錄下需要修正的地方,特別是一些藝術方面的修訂筆記。可能存在一個場景中門是關著的,然而具體情況還不清楚,需要進一步確認和詢問,可能會找到相關的素材。

在鏡頭 2 中,我注意到窗戶頂部有個縫隙。那還在嗎?

在這段內容中,提到在窗口的頂部似乎有一個空隙,需要確認是否仍然存在。回顧了鏡頭二和鏡頭三,提到可能是鏡頭二中的問題,確實看起來窗戶頂部的部分有偏差,似乎過于下移。針對這一點,考慮進行調整,以解決這個問題。盡管這可能是一個缺少視差效果的鏡頭,但仍需進一步修正。

我可以收養其中一個孩子嗎?

提到一個問題,是否可以收養其中一個孩子。對于這個問題,表示不確定,因為不清楚該孤兒院的具體政策。

試試社區中的配音演員?我們中的不少人愿意參與/有很好的麥克風

提到可以嘗試在社區中尋找配音演員,表示有不少人愿意并且有一定的基礎,但需要找到一個特別合適的人選,特別是能夠提供高質量配音的人選。

這個游戲會支持左撇子嗎?

關于是否支持左撇子玩家,認為其實玩起來差別不大,不論是左手還是右手,都只是四個方向的操作。因此,不管是左撇子還是右撇子,游戲的玩法基本上是一樣的。

你能解釋一下動畫中的漸變效果嗎?

在討論動畫中的漸變效果時,需要明確具體是指哪種漸變效果。漸變可能指的是圖像、元素或者場景的淡入淡出效果,或者是一些特定動作、過渡的視覺效果。所以,具體是哪種類型的漸變效果需要進一步確認。

你應該在那個地方加一點雪,這樣能增加深度感

需要在畫面上加一點雪花效果,以增加深度感。為了記住這些修改,可能需要更新并記錄一些事項,包括手套的朝向等細節。考慮到沒有筆,決定將這些修改內容寫在系統里,以便以后能夠查看和提醒自己。

我們仍然需要自己的三角函數,這樣就不需要每次都調用 C 標準庫中的 sin() 函數了

需要使用一些三角函數來停止當前的簽名操作,通過使用C語言的標準庫來實現。標準庫提供了一些函數,例如sprintfsignfloorceilround等。對于這些函數中的一些,可能不需要花費太多時間來實現,因為它們比較簡單,如truncate。而像floorceil這樣的函數實現起來可能需要更多工作。對于signround函數,雖然不容易實現,但使用現有的工具可以幫助完成這些工作。

你之前提到過我們可以通過漸變讓動畫序列更流暢。例如,當圣誕老人給英雄戴上帽子時。我想知道那會是什么樣子

提到的這種漸變效果可以通過在時間范圍內選擇特定部分,改變兩個層的透明度來實現。例如,可以讓一個層的透明度從1漸變到0,而另一個層的透明度則從0漸變到1。通過這種方式,就能讓一個物體(例如角色)漸漸出現或消失,達到逐步顯現或隱去的效果。這樣的方法非常直觀,只需調整時間范圍內透明度的變化即可。

我注意到過場動畫的位圖沒有隨著窗口大小縮放。這是為什么?

問題出在窗口的大小和渲染的分辨率之間不匹配。當前,游戲在一個固定的分辨率下渲染,因此它不會根據窗口的大小來調整。如果游戲能夠請求一個較小的窗口,并根據窗口大小渲染,那么就可以解決這個問題。目前的問題是,盡管我們可以改變分辨率,但渲染并沒有根據實際窗口大小進行調整,因此導致了窗口和渲染內容的不一致。

win32_game.cpp:暫時切換到使用更小的后臺緩沖區

目前,渲染的問題與窗口大小和分辨率之間的匹配有關。游戲引擎支持分辨率獨立性,可以請求引擎生成較小的圖像,只需要調整后端緩沖區的大小即可。然而,在當前的測試過程中,窗口的大小是固定的,并沒有進行窗口尺寸的動態調整。因此,盡管引擎可以處理更小的窗口和分辨率,但為了測試游戲實際運行時的分辨率(例如 1920x1080),目前并沒有讓窗口大小與分辨率調整匹配。游戲會有一個窗口,通常是全屏模式,且不會允許用戶調整游戲窗口的大小。這種裁剪行為是有意為之的,并且是為了確保游戲在最終發布時在正確的分辨率下運行。
在這里插入圖片描述

floor 和 ceil 很難嗎?

實現 floorceil 函數并不像 signtan 函數那樣困難,但仍然具有一定的挑戰性。要正確處理這些函數,必須使用一些“魔法常量”并執行一些復雜的操作。這些操作的實現涉及到不少細節,需要一些精心設計的代碼。例如,如果使用的是 sfc(某種庫),這些函數的實現非常簡單,直接可以調用 floorceil 函數。但如果使用的是其他庫(如 sse2),則需要自己動手實現,并且這并不簡單,必須細致地思考并完成一些特定的計算。雖然這項工作并不算特別難,但為了做到正確,確實需要仔細考慮和執行,大約需要四到五條指令。

為什么我們要重做 sin() 函數?

減少使用 sign 函數的原因主要是出于教育目的。雖然在實際應用中,減少使用 sign 函數可能有性能上的考慮,但這里的重點是通過實現這些低級功能來學習底層編程。如果只是調用現有的庫函數,我們就無法真正了解這些功能是如何實現的。

sign 函數本身是一個相對復雜的函數,如果只是簡單地調用它而不理解它的實現,我們就無法掌握如何使用如冪級數近似、泰勒級數近似等方法。因此,學習如何手動實現這些功能非常重要,尤其是當你需要模擬一個沒有現成庫函數的功能時。

這種做法的核心理念是,避免依賴庫函數,而是通過自己實現,掌握如何做這些低級操作。雖然在實際應用中,可以使用 C 運行時庫來簡化開發,但出于教育目的,希望盡量減少依賴庫函數,確保每個人都能理解如何實現 C 運行時庫中的所有功能。最終的目的是通過這種方式,學習和掌握更多的編程技巧,而不是簡單地依賴別人已經做好的東西。

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

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

相關文章

Leedcode刷題 | Day31_貪心算法05

一、學習任務 56. 合并區間代碼隨想錄738. 單調遞增的數字968. 監控二叉樹 二、具體題目 1.56合并區間56. 合并區間 - 力扣&#xff08;LeetCode&#xff09; 給出一個區間的集合&#xff0c;請合并所有重疊的區間。 示例 1: 輸入: intervals [[1,3],[2,6],[8,10],[15,1…

app逆向專題五:新快報app數據采集

app逆向專題五:新快報app數據采集 一、抓包尋找數據接口二、編寫代碼三、完整代碼一、抓包尋找數據接口 打開charles,并在手機端打開新快報app,點擊“廣州”或者“經濟”等選項卡,抓包,尋找數據接口,如圖所示: 二、編寫代碼 這里介紹一種簡便的代碼編寫方法,在數據…

Java面試黃金寶典45

1. 非對稱加密 RSA 定義:RSA 是一種廣泛使用的非對稱加密算法,其安全性基于大整數分解的困難性。它使用一對密鑰,即公鑰和私鑰。公鑰可公開用于加密消息,而私鑰必須保密,用于解密由相應公鑰加密的消息。要點: 公鑰公開,私鑰保密,二者成對出現。加密和解密使用不同的密鑰…

提權實戰!

就是提升權限&#xff0c;當我們拿到一個shell權限較低&#xff0c;當滿足MySQL提權的要求時&#xff0c;就可以進行這個提權。 MySQL數據庫提權&#xff08;Privilege Escalation&#xff09;是指攻擊者通過技術手段&#xff0c;從低權限的數據庫用戶提升到更高權限&#xff…

在虛擬機上修改saprk的版本

之前安裝的spark版本是3.4&#xff0c;現在實驗需要的版本是2.4。現在需要更改spark的版本。 方法很簡單&#xff1a; 直接將原有的spark3.4的文件刪除&#xff0c;再安裝2.4版本。 安裝過程之后再寫。Spark2.1.0入門&#xff1a;Spark的安裝和使用_廈大數據庫實驗室博客

文獻分享: DESSERT基于LSH的多向量檢索(Part3.2.外部聚合的聯合界)

原論文 文章目錄 1. \textbf{1. } 1. 定理 4.2 \textbf{4.2} 4.2的內容 1.1. \textbf{1.1. } 1.1. 一些符號 1.2. \textbf{1.2. } 1.2. 定理內容 3. \textbf{3. } 3. 聯合界限 Ps. \textbf{Ps. } Ps. 運行時間分析 1. \textbf{1. } 1. 定理 4.2 \textbf{4.2} 4.2的內容 1.1. \t…

MIPI協議介紹

MIPI協議介紹 mipi 協議分為 CSI 和DSI,兩者的區別在于 CSI用于接收sensor數據流 DSI用于連接顯示屏 csi分類 csi 分為 csi2 和 csi3 csi2根據物理層分為 c-phy 和 d-phy, csi-3采用的是m-phy 一般采用csi2 c-phy 和 d-phy的區別 d-phy的時鐘線和數據線是分開的,2根線一對…

【中間件】nginx反向代理實操

一、說明 nginx用于做反向代理&#xff0c;其目標是將瀏覽器中的請求進行轉發&#xff0c;應用場景如下&#xff1a; 說明&#xff1a; 1、用戶在瀏覽器中發送請求 2、nginx監聽到瀏覽器中的請求時&#xff0c;將該請求轉發到網關 3、網關再將請求轉發至對應服務 二、具體操作…

在3ds Max中視口顯示為黑色或深灰色

在3ds Max中視口顯示為黑色或深灰色 Autodesk Support 2023年10月8日 涵蓋的產品和版本 問題&#xff1a; 在3ds Max中&#xff0c;使用“深”UI方案時視口顯示為完全黑色&#xff0c;使用“淺”UI方案時視口顯示為深灰色。 原因&#xff1a; 已為用戶界面禁用Gamma校正。…

Vue.js 中 v-if 的使用及其原理

在 Vue.js 的開發過程中&#xff0c;條件渲染是一項極為常見的需求。v-if指令作為 Vue.js 實現條件渲染的關鍵手段&#xff0c;能夠根據表達式的真假來決定是否渲染某一塊 DOM 元素。它在優化頁面展示邏輯、提升用戶體驗等方面發揮著重要作用。接下來&#xff0c;我們就深入探討…

Verilog:LED呼吸燈

模塊接口說明 信號方向描述clk輸入系統時鐘&#xff08;100MHz&#xff0c;周期10ns&#xff09;rst_n輸入低電平有效的異步復位信號led_en輸入總使能信號&#xff08;1開啟呼吸燈&#xff0c;0關閉&#xff09;speed_en輸入呼吸速度調節使能信號speed[2:0]輸入呼吸速度分級&a…

我的計算機網絡(總覽篇)

總覽--網絡協議的角度 在一個龐大的網絡中&#xff0c;該從哪里去了解呢&#xff1f;我先細細的講一下我們訪問一個網站的全部流程&#xff0c;當我們的電腦連上網絡的時候&#xff0c;就會啟動DHCP協議&#xff0c;來進行IP地址&#xff0c;MAC地址&#xff0c;DNS地址的分配…

開源的PMPI庫實現及示例代碼

開源的PMPI庫實現及示例代碼 PMPI (Profiling MPI) 是MPI標準中定義的接口&#xff0c;允許開發者通過攔截MPI調用進行性能測量和調試。以下是幾個常用的開源PMPI庫實現&#xff1a; 1. MPICH的PMPI接口 MPICH本身提供了PMPI接口&#xff0c;可以直接使用。 2. OpenMPI的PM…

Unity 基于navMesh的怪物追蹤慣性系統

今天做項目適合 策劃想要實現一個在現有的怪物追蹤系統上實現怪物擁有慣性功能 以下是解決方案分享&#xff1a; 怪物基類代碼&#xff1a; ? using UnityEngine; using UnityEngine.AI;[RequireComponent(typeof(NavMeshAgent))] [RequireComponent(typeof(AudioSource))] …

PyTorch進階學習筆記[長期更新]

第一章 PyTorch簡介和安裝 PyTorch是一個很強大的深度學習庫&#xff0c;在學術中使用占比很大。 我這里是Mac系統的安裝&#xff0c;相比起教程中的win/linux安裝感覺還是簡單不少&#xff08;之前就已經安好啦&#xff09;&#xff0c;有需要指導的小伙伴可以評論。 第二章…

【區塊鏈安全 | 第三十八篇】合約審計之獲取私有數據(二)

文章目錄 前言漏洞代碼代碼審計攻擊步驟修復/開發建議審計思路前言 在【區塊鏈安全 | 第三十七篇】合約審計之獲取私有數據(一)中,介紹了私有數據、訪問私有數據實例、Solidity 中的數據存儲方式等知識,本文通過分析具體合約代碼進行案例分析。 漏洞代碼 // SPDX-Licens…

《微服務與事件驅動架構》讀書分享

《微服務與事件驅動架構》讀書分享 Building Event-Driver Microservices 英文原版由 OReilly Media, Inc. 出版&#xff0c;2020 作者&#xff1a;[加] 亞當 ? 貝勒馬爾 譯者&#xff1a;溫正東 作者簡介&#xff1a; 這本書由亞當貝勒馬爾&#xff08;Adam Bellemare…

小剛說C語言刷題——第22講 二維數組

昨天我們講了一維數組&#xff0c;今天我們來講二維數組。 1.定義 二維數組是指在數組名后跟兩個方括號的數組。 2.語法格式 數據類型 數組名[下標][下標] 例如&#xff1a;int a[5][9];//表示5行9列的數組 3.訪問二維數組元素 格式&#xff1a;數組名[行坐標][列坐標]…

Vue 大文件分片上傳組件實現解析

Vue 大文件分片上傳組件實現解析 一、功能概述 1.1本組件基于 Vue Element UI 實現&#xff0c;主要功能特點&#xff1a; 大文件分片上傳&#xff1a;支持 2MB 分片切割上傳實時進度顯示&#xff1a;可視化展示每個文件上傳進度智能格式校驗&#xff1a;支持文件類型、大小…

「邏輯推理」AtCoder AT_abc401_d D - Logical Filling

前言 這次的 D 題出得很好&#xff0c;不僅融合了數學邏輯推理的知識&#xff0c;還有很多細節值得反復思考。雖然通過人數遠高于 E&#xff0c;但是通過率甚至不到 60%&#xff0c;可見這些細節正是出題人的側重點。 題目大意 給定一個長度為 N N N 的字符串 S S S&#…