日本風(fēng)格網(wǎng)站seo快速優(yōu)化
倉庫: https://gitee.com/mrxiao_com/2d_game
回顧昨天的 bug
今天我們繼續(xù)開發(fā)進(jìn)度,進(jìn)行調(diào)試昨天代碼的問題,主要是關(guān)于如何跟蹤玩家和敵人在世界中的高度位置。雖然我們做的是一款 2D 游戲,但我們希望能夠處理多層的房間,玩家可以上下走動(dòng),這需要處理與三維位置相關(guān)的問題。
昨天我們開始調(diào)整實(shí)體的 Z 坐標(biāo),確保它們有完整的高度信息,并且應(yīng)該從地面開始,位置會(huì)從地面向上延伸。然而,在調(diào)試時(shí),發(fā)現(xiàn)玩家的角色似乎比預(yù)期低,應(yīng)該碰到樹木的位置時(shí),角色卻沒有停下來,顯得像是角色下沉了。我們也觀察到角色在樓梯處的碰撞沒有按預(yù)期進(jìn)行,可能是因?yàn)榻巧?Z 坐標(biāo)沒有正確計(jì)算其高度。
因此,有人猜測(cè)問題出在沒有修正站在地面上的代碼,忘記考慮實(shí)體的高度。也就是說,雖然角色的底部位置被設(shè)置在地面上,但角色的高度應(yīng)該從該位置向上浮動(dòng)。因此,調(diào)整這個(gè)問題,確保角色位置正確是接下來的重點(diǎn)。
黑板:實(shí)體的基點(diǎn)與地面位置之間的差異
有人提出了一個(gè)很好的猜測(cè),問題可能出在實(shí)體的位置計(jì)算上。我們來看一下實(shí)體的高度問題。假設(shè)實(shí)體是這樣的形狀(側(cè)面圖),它的 Z 維度代表它的高度。如果我們把實(shí)體的高度表示為 Z 維度,那么它的中心位置應(yīng)該在地面上方半個(gè) Z 維度的高度。也就是說,實(shí)體的實(shí)際地面接觸點(diǎn)應(yīng)該是它的中心位置減去它的一半高度。
因此,假設(shè)地面是某個(gè)高度,那么實(shí)體應(yīng)該從地面加上它的高度一半來確定其實(shí)際的位置。這就意味著,我們不能簡(jiǎn)單地把實(shí)體的位置設(shè)置為地面,而是要加上它的一半高度,才能確保它的底部位置正確。換句話說,地面實(shí)際上應(yīng)該在中心點(diǎn)下方半個(gè) Z 維度的位置。
這個(gè)建議看起來很有道理,可能正是導(dǎo)致問題的根本原因。
查看當(dāng)前如何指定這些概念
在處理這個(gè)問題時(shí),首先發(fā)現(xiàn)了實(shí)體的位置計(jì)算沒有考慮到實(shí)體的尺寸,尤其是高度(Z 維度)。具體來說,地面高度的計(jì)算沒有考慮到實(shí)體的高度,這導(dǎo)致了實(shí)體的位置不正確。
我們?cè)谔幚碇丿B(overlap)時(shí),地面高度是通過一個(gè)插值(lerp)計(jì)算得出的。為了修復(fù)這個(gè)問題,計(jì)劃在進(jìn)行重疊測(cè)試后,根據(jù)實(shí)體的尺寸調(diào)整地面高度。也就是說,在計(jì)算出重疊后的地面高度后,需要再調(diào)整它,使其考慮到實(shí)體的實(shí)際尺寸,特別是其高度(Z 維度)。這應(yīng)該能解決當(dāng)前問題,確保實(shí)體的底部能正確地與地面對(duì)齊。
game_sim_region.cpp: 將 0.5 * Entity->Dim.Z 加到地面位置
要解決這個(gè)問題,計(jì)劃將地面高度調(diào)整為原本的地面高度加上實(shí)體高度的一半。也就是說,在計(jì)算地面高度時(shí),需要將原本的地面位置上移,移動(dòng)的距離是實(shí)體高度的一半。這就是在之前圖示中提到的做法,通過這種方式確保實(shí)體的位置正確。
運(yùn)行游戲,發(fā)現(xiàn)問題還沒完全解決
調(diào)試過程發(fā)現(xiàn)當(dāng)前的碰撞行為有些不合理,看起來可能是已經(jīng)到了頂部樓層,導(dǎo)致上下碰撞變得異常。此時(shí)需要進(jìn)一步調(diào)試碰撞檢測(cè)代碼,尤其是在繪制方面。為了更清楚地理解問題,首先可能需要添加一些調(diào)試代碼來檢查當(dāng)前的碰撞情況,確保問題出在哪里。
調(diào)試:進(jìn)入計(jì)算地面位置的代碼
通過調(diào)試代碼,發(fā)現(xiàn)碰撞系統(tǒng)中的地面計(jì)算已經(jīng)修復(fù)了之前的錯(cuò)誤,角色的位置現(xiàn)在正確地懸浮在地面上,符合預(yù)期的高度(0.62的實(shí)體高度的一半)。這解決了之前在預(yù)演階段提到的bug。不過,問題可能還在于繪制部分的代碼沒有完全處理好,因此需要繼續(xù)修復(fù)繪制代碼。
game.cpp: 查看當(dāng)前的繪制代碼
繪制代碼當(dāng)前使用了一個(gè)占位符方法,導(dǎo)致調(diào)試變得有些困難。具體來說,在繪制位圖時(shí),傳入的是位圖的左上角位置,而不是位圖的中心。因此,繪制代碼中的 DrawBitMap
調(diào)用使用了最小角落位置,而這與預(yù)期的中心對(duì)齊有所不同。代碼實(shí)際上對(duì)位圖的位置進(jìn)行了偏移,但并未考慮位圖的實(shí)際大小,導(dǎo)致繪制時(shí)的對(duì)齊問題。雖然目前的代碼中已經(jīng)通過偏移量修正了這個(gè)問題,但這種方式比較粗糙,存在不一致的問題。最終需要決定是修復(fù)這個(gè)對(duì)齊bug,還是重新整理代碼來改善渲染系統(tǒng)。在不確定時(shí),決定暫時(shí)不對(duì)渲染系統(tǒng)做大幅改動(dòng),保持現(xiàn)有代碼結(jié)構(gòu)。
引入 EntityBaseP
為了正確對(duì)齊實(shí)體的繪制位置,應(yīng)該將實(shí)體的位置調(diào)整為基于地面對(duì)齊,而不是基于其中心位置。建議將實(shí)體的當(dāng)前位置重新命名為 EntityBaseP
,其位置應(yīng)為當(dāng)前實(shí)體位置加上實(shí)體在 Z 軸上的一半尺寸。這樣,繪制時(shí)就能夠與地面對(duì)齊,因?yàn)楫?dāng)前地面位置的對(duì)齊是基于這個(gè)偏移量的。雖然目前這種方式對(duì)大多數(shù)實(shí)體來說是合理的,可能對(duì)于飛行類實(shí)體不適用,但這可以作為初步解決方案,未來如果需要,可以進(jìn)一步引入自定義對(duì)齊方式。
此外,繪制影像時(shí)的透明度計(jì)算(ShadowAlpha)也應(yīng)與此對(duì)齊,以確保繪制的一致性,即影像的透明度應(yīng)基于調(diào)整后的 EntityBaseP
來計(jì)算。
運(yùn)行游戲,發(fā)現(xiàn)問題似乎已經(jīng)解決
目前已經(jīng)解決了繪制和碰撞檢測(cè)中的一些問題,特別是在位置計(jì)算和停止點(diǎn)的處理上,似乎達(dá)到了預(yù)期的效果。玩家可以正確地在環(huán)境中移動(dòng),碰撞和停止位置的邏輯也符合預(yù)期。
然而,仍然存在一個(gè)問題,就是階梯的碰撞檢測(cè)。階梯不會(huì)正確與玩家發(fā)生碰撞,因?yàn)殡A梯的高度只達(dá)到了地面上方的高度,并未覆蓋到玩家可能需要攀爬的區(qū)域。因此,需要解決階梯的碰撞問題,使得玩家能夠正確地與階梯交互。
下一步計(jì)劃是在階梯上添加兩個(gè)矩形,一個(gè)位于階梯的底部,一個(gè)位于頂部。這樣做是為了能夠更好地控制階梯的碰撞范圍,并確保玩家只會(huì)在特定的位置與階梯發(fā)生碰撞,而不是輕易穿越階梯的側(cè)面。這個(gè)問題的解決需要對(duì)當(dāng)前的碰撞代碼進(jìn)行一些修改和調(diào)試。
game.cpp: 給樓梯添加兩個(gè)矩形
計(jì)劃在階梯上添加兩個(gè)矩形,一個(gè)位于階梯底部,另一個(gè)位于頂部,以便更好地處理階梯的繪制和碰撞。在繪制時(shí),需要對(duì)這兩個(gè)矩形的位置進(jìn)行偏移,確保它們準(zhǔn)確地定位到階梯的上下部分。為了實(shí)現(xiàn)這一點(diǎn),需要利用已經(jīng)實(shí)現(xiàn)的偏移機(jī)制。
在現(xiàn)有的 PushRect
函數(shù)中,已經(jīng)支持對(duì)矩形進(jìn)行偏移,但是遇到了一些問題。偏移量應(yīng)該應(yīng)用于第二個(gè)矩形,這樣可以確保它與第一個(gè)矩形不同,并且根據(jù) Z 軸的高度進(jìn)行相應(yīng)的調(diào)整。為了便于辨識(shí),底部的矩形會(huì)用不同的顏色顯示。
然而,當(dāng)前遇到的問題是,矩形的偏移似乎沒有正確應(yīng)用,導(dǎo)致它們沒有按預(yù)期位置移動(dòng)。這可能是因?yàn)?Z 軸偏移的處理沒有在繪制時(shí)被正確使用,雖然代碼上看起來已經(jīng)允許了偏移。
下一步需要檢查 Z 偏移的計(jì)算是否正確,并確保在繪制時(shí)正確應(yīng)用這些偏移。這可能需要對(duì)現(xiàn)有代碼進(jìn)行一些調(diào)試和修改,確保偏移量能正確影響繪制過程。
處理 ZFudge
當(dāng)前存在的問題是,ZFudge
只用于計(jì)算地面點(diǎn),但沒有真正將其應(yīng)用到 X 和 Y 坐標(biāo)的偏移上,這導(dǎo)致物體在 Z 軸上的位置被抬高,但 X 和 Y 坐標(biāo)沒有受到影響。為了更好地處理階梯和其他物體的繪制,想要嘗試將 Z 偏移直接應(yīng)用于 X 和 Y 坐標(biāo)。
盡管代碼的結(jié)構(gòu)比較混亂,但希望通過一些調(diào)整,能在現(xiàn)有代碼的基礎(chǔ)上獲得更多的靈活性,而不必深入修改整個(gè)系統(tǒng)。為此,考慮將 Z
偏移與 EntityBaseP
結(jié)合使用,使得偏移能直接影響物體的繪制。
實(shí)驗(yàn)步驟包括去除現(xiàn)有代碼中的某些部分,并在需要的位置手動(dòng)添加 Z 偏移的影響。這可能會(huì)導(dǎo)致一些問題,尤其是當(dāng) Z 偏移被直接應(yīng)用時(shí),可能會(huì)引入額外的復(fù)雜性,因此需要進(jìn)一步評(píng)估這種方法是否合適,并決定是否繼續(xù)使用該方案。
檢查對(duì) Familiar 的影響
當(dāng)前遇到的問題是,黃色物體的顯示位置不對(duì),導(dǎo)致它似乎消失了。這并不是預(yù)期的行為,因此需要調(diào)試和找出原因。盡管嘗試了多種方案,但仍然沒有找到問題的根源,正在進(jìn)一步檢查并嘗試解決這個(gè)問題。
MetersToPixels 被 OffsetZ 預(yù)乘
當(dāng)前問題的原因在于坐標(biāo)轉(zhuǎn)換中的錯(cuò)誤,特別是“米到像素”的轉(zhuǎn)換方式。問題出在“偏移Z”值與“米到像素”轉(zhuǎn)換的順序,導(dǎo)致了值被重復(fù)乘以“米到像素”,這影響了圖形的渲染。為了避免這種重復(fù)乘法,應(yīng)該在處理 Z 偏移時(shí)將“米到像素”的轉(zhuǎn)換推遲,確保在正確的位置進(jìn)行乘法操作。這是當(dāng)前代碼的一大問題,進(jìn)一步的解決方案可能需要重構(gòu)這部分渲染代碼,避免繼續(xù)積累更多的“臨時(shí)修補(bǔ)”方式。
運(yùn)行游戲,看到效果更接近正確
當(dāng)前的問題是在樓梯代碼的實(shí)現(xiàn)上,特別是在使用地面位置計(jì)算時(shí),樓梯的位置仍然不正確。盡管圖形渲染已經(jīng)接近預(yù)期,但樓梯的渲染和碰撞檢測(cè)依然存在問題,導(dǎo)致其顯示位置不準(zhǔn)確,應(yīng)該位于玩家角色上方,并且在屏幕中心對(duì)齊。需要進(jìn)一步調(diào)試樓梯的代碼,并確保其正確使用地面位置的計(jì)算結(jié)果。這個(gè)問題可能涉及到樓梯的碰撞檢測(cè)邏輯以及如何正確應(yīng)用地面坐標(biāo)的偏移。
game.cpp: 不再添加 OffsetZ
在嘗試去除偏移量并直接使用 Z 偏移時(shí),圖形的顯示效果變得更加正確。不過,這種方法可能會(huì)在長(zhǎng)期的繪制過程中引發(fā)一些問題,特別是在處理等距藝術(shù)風(fēng)格時(shí),因?yàn)檫@要求所有的繪制計(jì)算必須適應(yīng)等距視角。盡管目前去除偏移后的結(jié)果看起來比較合理,但不確定這種方式是否能長(zhǎng)期保持穩(wěn)定,需要進(jìn)一步驗(yàn)證和調(diào)試。
黑板:ZFudge 的作用
Z 偏移量的作用是根據(jù)物體在 Z 軸上的位置對(duì)其進(jìn)行縮放,目的是讓物體在 Z 軸上越高時(shí),離攝像機(jī)的距離越遠(yuǎn),從而產(chǎn)生一種透視拉伸效果。這個(gè)縮放效果有助于使遠(yuǎn)離攝像機(jī)的物體看起來更遠(yuǎn),但也使原本近距離的物體被放大。由于游戲采用等距視角,這種處理方式與實(shí)際的 Z 軸深度有所沖突,因?yàn)樵诘染嘁暯窍?#xff0c;物體并不直接在 Z 軸上繪制,而是以一定角度呈現(xiàn),這就導(dǎo)致了視角上的不匹配。
盡管這種視角匹配存在一定的問題,但它是實(shí)現(xiàn)特定藝術(shù)風(fēng)格的必要條件,并且是 2D 游戲中常見的挑戰(zhàn)之一。當(dāng)前,角色可以正常地跳躍到不同的 Z 軸高度,且整體效果不錯(cuò)。接下來的工作是繼續(xù)完善樓梯的碰撞檢測(cè),確保它能夠正確延伸到天花板以上,以便玩家可以在樓梯上上下行。這項(xiàng)任務(wù)是可實(shí)現(xiàn)的,且應(yīng)是當(dāng)前工作重點(diǎn)。
game_sim_region.cpp: 查看 SpeculativeCollide
為了確保角色只能從樓梯的底部或頂部進(jìn)入,而不能從樓梯的側(cè)面直接進(jìn)入并跳到任何位置,使用了一個(gè)名為 SpeculativeCollide 的機(jī)制。這個(gè)機(jī)制的作用是限制角色只能在特定條件下與樓梯發(fā)生碰撞,確保角色只能沿著樓梯的上下方向行進(jìn)。
在碰撞檢測(cè)過程中,結(jié)合了 Ground tight 信息,這有助于判斷角色是否接觸到樓梯的有效部分。這樣可以避免角色從樓梯的側(cè)面進(jìn)入,確保樓梯的碰撞處理僅在角色位于樓梯的底部或頂部時(shí)才生效。
引入 GetEntityGroundPoint
為了簡(jiǎn)化代碼并避免重復(fù)計(jì)算,決定引入一個(gè)名為 GetEntityGroundPoint
的調(diào)用函數(shù)。這個(gè)函數(shù)將返回一個(gè)實(shí)體的地面位置,并且通過將實(shí)體的Z維度偏移一半來調(diào)整該位置,確保地面位置正確。這樣一來,所有需要計(jì)算實(shí)體地面位置的地方都可以統(tǒng)一調(diào)用這個(gè)函數(shù),避免在多個(gè)地方重復(fù)計(jì)算,提升代碼的可維護(hù)性。
例如,在需要獲取某個(gè)實(shí)體的Z位置時(shí),直接調(diào)用 GetEntityGroundPoint
,這個(gè)函數(shù)會(huì)自動(dòng)處理位置的偏移。通過這種方式,可以更方便地修改和管理地面位置的計(jì)算方式,而不需要在代碼中逐一修改所有相關(guān)部分。
使用 GetEntityGroundPoint 設(shè)置 EntityBaseP 和地面位置
當(dāng)前正在考慮如何使用 GetEntityGroundPoint
來計(jì)算實(shí)體的偏移量,并調(diào)整實(shí)體位置與地面位置的關(guān)系。具體思路是通過獲取實(shí)體的位置,然后根據(jù)其 Z 值計(jì)算地面位置,再調(diào)整以實(shí)現(xiàn)正確的偏移。
黑板:計(jì)算地面位移
這段內(nèi)容的核心思想是,通過將實(shí)體的位置與地面位置進(jìn)行比較,計(jì)算出它們之間的偏移量,從而確保地面正確對(duì)齊實(shí)體的位置。具體步驟如下:
-
實(shí)體位置與地面位置的差值:為了避免在不同地方重復(fù)計(jì)算地面位置,決定使用
GetEntityGroundPoint
函數(shù)來獲取每個(gè)實(shí)體的地面位置。然后,通過計(jì)算實(shí)體位置與地面位置的差值(即實(shí)體位置減去地面位置)來得到偏移量。 -
計(jì)算偏移量:這個(gè)偏移量是一個(gè)向量,表示從當(dāng)前地面位置到實(shí)體位置的距離。通過這個(gè)偏移量,地面高度可以被正確調(diào)整。
-
通過向量運(yùn)算調(diào)整位置:使用向量減法來計(jì)算從地面到實(shí)體位置的偏移量,確保實(shí)體與地面正確對(duì)齊。
總結(jié)來說,目的是通過集中計(jì)算地面位置,簡(jiǎn)化代碼中的多個(gè)計(jì)算,并確保以后修改時(shí)能一致地應(yīng)用。
運(yùn)行游戲,發(fā)現(xiàn)現(xiàn)在可以上樓了
現(xiàn)在,修復(fù)了上樓的代碼,但下樓的問題依然存在。這是因?yàn)榕鲎矙z測(cè)部分已經(jīng)正確工作,或者至少已經(jīng)調(diào)試了盡可能多的已知問題。修復(fù)后,碰撞檢測(cè)不再與那些最大值位于地面上的物體發(fā)生碰撞,因?yàn)樗鼈儗?shí)際上處于我們下方的地面層。這意味著在地面層上方的物體不會(huì)再被誤判為碰撞對(duì)象。
game.cpp: 使樓梯延伸到地面之上
目前,樓梯的碰撞檢測(cè)已經(jīng)正常工作,但存在一個(gè)問題:我們無法從上方進(jìn)入樓梯。問題可能出在樓梯的高度設(shè)置上,樓梯被設(shè)置得太高,導(dǎo)致我們無法順利進(jìn)入。為了改進(jìn)這一點(diǎn),計(jì)劃是讓樓梯稍微超出地面層的一定高度,這樣可以確保我們?cè)谝苿?dòng)過程中能夠與樓梯發(fā)生碰撞,避免無法進(jìn)入的情況。
此外,之前提到的“步高”問題也需要處理,考慮到這是影響樓梯交互的一個(gè)因素?,F(xiàn)在樓梯的頂部雖然可以與玩家正確碰撞,但仍然存在一些小的高度差,可能導(dǎo)致玩家不能順利進(jìn)入。這些細(xì)節(jié)需要進(jìn)一步調(diào)整。
黑板:理解 StepHeight
目前,樓梯的碰撞檢測(cè)存在一些問題,特別是在上下樓梯時(shí)的交互表現(xiàn)?,F(xiàn)有代碼的邏輯是,如果玩家在接近樓梯時(shí),步伐與樓梯的高度差大于一定值(例如0.1米),就不能直接跳上樓梯,而必須從合適的高度進(jìn)入。然而,這個(gè)高度差和玩家與樓梯的交互方式存在一些問題,尤其是當(dāng)玩家嘗試從樓梯側(cè)面接近時(shí),碰撞和過渡效果并不理想,造成了一些“跳躍”或不平滑的過渡。
為了解決這個(gè)問題,計(jì)劃對(duì)現(xiàn)有的樓梯模型進(jìn)行調(diào)整。當(dāng)前的樓梯實(shí)際上是以坡道形式實(shí)現(xiàn)的,而不是傳統(tǒng)的階梯。這意味著樓梯的碰撞矩形應(yīng)該具有更大的范圍,以覆蓋整個(gè)坡道區(qū)域,從而避免因碰撞區(qū)域不夠大而導(dǎo)致的跳躍或穿透現(xiàn)象。然而,計(jì)算地面位置時(shí),并不應(yīng)該使用整個(gè)樓梯的碰撞矩形,而應(yīng)僅使用一個(gè)較小的參考值,例如樓梯的單層高度。這樣就能確保玩家能夠更加平滑地與樓梯交互,避免過高或過低的觸發(fā)問題。
這種方式能夠讓樓梯的碰撞檢測(cè)更具靈活性,也能為未來的調(diào)整提供更多可能性。
game_sim_region.h: 在 sim_entity 中添加 WalkableHeight
為了更好地處理樓梯的交互,考慮了根據(jù)不同的樓梯高度變化來調(diào)整碰撞檢測(cè)。當(dāng)前的方案是通過調(diào)整最小Z值來確定“可行走高度”,這意味著在不同樓梯區(qū)域,玩家與樓梯的交互將根據(jù)樓梯的高度變化來確定是否可以通過。這種方法的目標(biāo)是確保玩家能夠在不同的樓梯高度之間平滑過渡,而不受不必要的碰撞限制。
為了避免在不同樓梯區(qū)域之間有太多重復(fù)計(jì)算,建議引入一個(gè)“可行走高度”的概念,這個(gè)高度可以通過結(jié)合當(dāng)前樓梯區(qū)域的最小Z值與樓梯的實(shí)際高度來計(jì)算。通過這樣的方式,樓梯的碰撞檢查就變得更加高效和一致。同時(shí),在實(shí)際開發(fā)中,這一機(jī)制也便于日后的調(diào)整,因?yàn)橹恍枰谀承﹨^(qū)域?qū)Α翱尚凶吒叨取边M(jìn)行修改即可,無需重新編寫碰撞邏輯。
這種方法能使得樓梯的交互更加靈活且符合實(shí)際需求,尤其是在多層樓梯或不規(guī)則樓梯的情況下,可以避免由于碰撞體積設(shè)置不當(dāng)導(dǎo)致的玩家行為問題。
game.cpp: 在 SpeculativeCollide 中使用 WalkableHeight
我們決定在添加樓梯時(shí),能夠指定其上升的高度,這樣可以確保樓梯的碰撞區(qū)域與可行走區(qū)域分離。具體來說,我們會(huì)將 entity_sim
結(jié)構(gòu)中的 walkable_height
設(shè)置為游戲狀態(tài)中世界瓦片的深度(以米為單位)。這樣,樓梯的碰撞矩形高度就與可行走區(qū)域的高度分開了,這有助于在處理樓梯時(shí)更好地控制玩家的行為。
運(yùn)行游戲,嘗試樓梯
在進(jìn)行樓梯調(diào)試時(shí),雖然樓梯的上升部分正常工作,但在嘗試下樓時(shí)出現(xiàn)了問題。具體來說,存在一個(gè)明顯的臺(tái)階突起,導(dǎo)致無法順利下樓。盡管其他部分的碰撞檢測(cè)已經(jīng)正常,但在樓梯的最底部仍然有些問題。需要進(jìn)一步檢查并修復(fù)這個(gè) bug,以確保下樓時(shí)不再遇到這種障礙。
game_entity.h: 引入 GetStairGround
在進(jìn)行代碼調(diào)試時(shí),發(fā)現(xiàn)了兩個(gè)不同的地面計(jì)算方式,這可能是導(dǎo)致問題的根源。需要?jiǎng)?chuàng)建一個(gè)函數(shù)來統(tǒng)一計(jì)算地面高度。通過改進(jìn)現(xiàn)有代碼,可以避免不一致的計(jì)算,減少潛在的 bug。
進(jìn)一步的計(jì)劃是完善實(shí)體系統(tǒng),逐步增加更多的實(shí)體特定數(shù)據(jù),這樣在后續(xù)改進(jìn)和維護(hù)時(shí)能夠更容易地處理各種不同類型的實(shí)體。盡管目前的實(shí)體系統(tǒng)尚未完全構(gòu)建,但現(xiàn)有的基礎(chǔ)已經(jīng)具備擴(kuò)展的潛力。為了避免重復(fù)的錯(cuò)誤,代碼中的一些假設(shè)需要被固化,保證系統(tǒng)的一致性和穩(wěn)定性。
同時(shí),涉及地面點(diǎn)計(jì)算的問題也得到了一些澄清,發(fā)現(xiàn)并不是 bug,而是代碼在某些情況下由于假設(shè)不一致導(dǎo)致的暫時(shí)性錯(cuò)誤。在重構(gòu)時(shí),需要將 GetEntityGroundPoint
和相關(guān)計(jì)算步驟整合,確保所有計(jì)算使用一致的地面點(diǎn)。通過這些改進(jìn),整體的實(shí)體系統(tǒng)和地面計(jì)算方法將更加健壯,后續(xù)的開發(fā)和調(diào)試也會(huì)變得更加高效。
game_sim_region.cpp: 將 HandleOverlap 和 SpeculativeCollide 改為使用 GetStairGround
在這段內(nèi)容中,代碼的目標(biāo)是將現(xiàn)有的重疊處理邏輯簡(jiǎn)化為調(diào)用 GetStairGround
函數(shù)來獲取樓梯的地面高度,并保留原有的 GetEntityGroundPoint
函數(shù)調(diào)用作為備份,以防未來需要對(duì) X 和 Y 方向進(jìn)行調(diào)整。以下是詳細(xì)總結(jié):
-
簡(jiǎn)化代碼:原本有多個(gè)處理重疊的代碼段,現(xiàn)在決定使用
GetStairGround
來計(jì)算樓梯的地面高度。這樣可以避免重復(fù)代碼,并集中處理樓梯地面的計(jì)算。 -
保留備份:盡管不太可能,仍然保留對(duì)
GetEntityGroundPoint
函數(shù)的調(diào)用,以防未來需要在 X 和 Y 方向進(jìn)行位置調(diào)整。這樣做可以增加代碼的可擴(kuò)展性和容錯(cuò)性。 -
移除冗余代碼:在樓梯的重疊處理部分,移除了不再需要的代碼,特別是與重疊處理無關(guān)的內(nèi)容,如檢查兩個(gè)條件的代碼。這些檢查似乎是多余的,因?yàn)樘幚順翘莸奈ㄒ粯?biāo)準(zhǔn)應(yīng)該是“需要跨越的高度”。
-
確認(rèn)類型:為了避免發(fā)生不必要的錯(cuò)誤,增加了類型檢查,確保調(diào)用
GetStairGround
時(shí),傳入的實(shí)體確實(shí)是樓梯類型。這樣可以防止錯(cuò)誤調(diào)用其他類型的實(shí)體并導(dǎo)致程序崩潰。 -
待處理問題:代碼中提到還存在一些可能沒有完全實(shí)現(xiàn)的部分,并標(biāo)注為需要進(jìn)一步工作的地方。這可能是為了后續(xù)的優(yōu)化和功能擴(kuò)展做準(zhǔn)備。
整體來說,目的是通過精簡(jiǎn)和重構(gòu)現(xiàn)有的重疊處理邏輯,讓代碼更加簡(jiǎn)潔、清晰,并提高其可維護(hù)性,同時(shí)為將來可能出現(xiàn)的需求留有余地。
運(yùn)行游戲,發(fā)現(xiàn)問題得到解決
目前的情況看起來進(jìn)展不錯(cuò),已經(jīng)有很多功能能夠正常工作,雖然仍有一些不完美的地方。地面高度的計(jì)算是一個(gè)主要問題,但整體效果已經(jīng)有了顯著的改善。不過,仍然出現(xiàn)了一些異常,尤其是某些地方的表現(xiàn)并不符合預(yù)期,可能是存在bug。比如在角色下樓梯時(shí),某些動(dòng)作沒有完全按照預(yù)期進(jìn)行,需要仔細(xì)檢查并修復(fù)。
game_sim_region.cpp: SpeculativeCollide 需要知道是上樓還是下樓
當(dāng)前遇到的問題是關(guān)于處理樓梯的碰撞和上下樓梯的地面類型。需要解決的核心問題是如何讓碰撞系統(tǒng)能夠正確處理從樓梯上移開或走上樓梯的情況,確保能夠識(shí)別樓梯之外的地面類型。這涉及到對(duì)地面高度的規(guī)范化,并且在碰撞循環(huán)中考慮高度變化,避免不合適的高度變化導(dǎo)致物體無法正確移動(dòng)。
接下來計(jì)劃解決的問題是:在碰撞系統(tǒng)中加入處理地面層級(jí)的概念,使得碰撞可以正確處理跨越樓梯等地形的情況。同時(shí),還需要進(jìn)一步確保不同的地面高度在不同的區(qū)域能被正確處理,確保碰撞系統(tǒng)能準(zhǔn)確判斷和限制物體的移動(dòng)范圍。
總的來說,今天完成了預(yù)期的一些任務(wù),但仍有一些細(xì)節(jié)需要進(jìn)一步完善,尤其是在碰撞和地面處理方面。計(jì)劃在明天繼續(xù)深入解決這些問題,并對(duì)地面層級(jí)進(jìn)行更細(xì)致的設(shè)計(jì),以確保系統(tǒng)能夠正確處理不同高度的過渡。
問答環(huán)節(jié)
問:你在 Linux 上使用 Clang 嗎?
在Linux系統(tǒng)上使用clang編譯器,在Windows上則使用Visual Studio的編譯器。同時(shí),雖然他們?cè)?jīng)在Mac上進(jìn)行開發(fā),但目前已經(jīng)不再頻繁使用Mac進(jìn)行項(xiàng)目發(fā)布,因此對(duì)Mac的開發(fā)環(huán)境不再熟悉。
問:從零開始自學(xué) C++ 的最佳方法是什么?
關(guān)于如何從零開始自學(xué)C++,程序員建議最好的方法是通過大量的編程實(shí)踐來學(xué)習(xí)。首先,可以從一些基礎(chǔ)的教材入手,比如《C程序設(shè)計(jì)語言》這本書(K&R),這本書對(duì)于理解C語言及其思想非常有幫助。除了閱讀書籍,觀看相關(guān)的編程直播,觀察別人是如何編程的,嘗試模仿和理解這些操作也是一種很好的學(xué)習(xí)方式。
程序員提到,自己學(xué)習(xí)C++的經(jīng)歷已經(jīng)過去很多年,因此不太記得具體的學(xué)習(xí)材料,且也不清楚現(xiàn)在是否有特別推薦的入門教程。不過,持續(xù)編寫代碼和解決問題是自學(xué)C++的核心。
問:你知道 Voxpel 編程嗎?
對(duì)于Vox pel編程,程序員表示自己不太清楚這個(gè)術(shù)語的含義,推測(cè)它可能與Voxel(體素)有關(guān)。雖然不熟悉Vox pel引擎的編程,但他知道如何編寫體素引擎。
問:你打算把家里的樓梯換成坡道嗎?
我們?cè)谟螒蛟O(shè)計(jì)中加入了一個(gè)能夠平滑上下來回滑動(dòng)的樓梯,感覺非常好,甚至比傳統(tǒng)游戲中的樓梯更順暢。最初并沒有打算加入坡道,但由于這一滑動(dòng)體驗(yàn)太令人滿意,我們決定保留這種設(shè)計(jì),并在未來進(jìn)一步完善。我們也注意到游戲中常見的2D樓梯處理方式,通常角色會(huì)觸發(fā)固定動(dòng)畫并轉(zhuǎn)屏切換,而我們的設(shè)計(jì)則沒有這種切換感,而是通過平滑的滑動(dòng)讓玩家能夠自如上下樓層,帶來更好的體驗(yàn)。盡管現(xiàn)實(shí)生活中不會(huì)實(shí)現(xiàn)類似坡道,但我們決定在游戲中繼續(xù)沿用這一創(chuàng)新設(shè)計(jì)。
問:你是怎么讓 Visual Studio 調(diào)試視圖的右側(cè)垂直分割的?
如何在 Visual Studio 調(diào)試器中將窗口分割成垂直布局。方法很簡(jiǎn)單,只需將窗口(如模塊窗口)拖動(dòng)到屏幕上。當(dāng)拖動(dòng)窗口時(shí),會(huì)看到一個(gè)類似指南針的指示符,若將窗口放置在其中一個(gè)區(qū)域,窗口會(huì)自動(dòng)對(duì)齊。如果放置在其他區(qū)域,則會(huì)分割窗口。這樣可以根據(jù)需要自由調(diào)整窗口布局,例如創(chuàng)建多個(gè)窗格,或者將窗口分為上下或左右不同的布局。
然而,在舊版本的 Visual Studio(如 2008 版)中,存在一個(gè)嚴(yán)重的 bug:嘗試對(duì)窗口進(jìn)行??繒r(shí)會(huì)導(dǎo)致程序崩潰,而微軟在其知識(shí)庫中明確表示該問題不會(huì)修復(fù),建議用戶避免使用這一功能。但在較新的版本(如 2013 版)中,這個(gè)問題已經(jīng)解決,不會(huì)再發(fā)生崩潰。
問:為什么你在代碼中使用這么多魔法數(shù)字?
這里的“魔法數(shù)字”指的是代碼中未經(jīng)過清晰命名的常量值,這些值往往在代碼中沒有解釋其含義,導(dǎo)致代碼不易理解和維護(hù)。
問:你怎么看待友元類?
在開發(fā)中,認(rèn)為使用 friend
類是多余的,覺得會(huì)浪費(fèi)打字。由于不使用 private
,因此也沒有必要使用 friend
。
問:這是不是變成了反向 Doom?幾乎是 2D 渲染 3D,而不是 3D 渲染 2D?
在討論游戲渲染時(shí),提到了一種“2D渲染在3D中”的設(shè)計(jì)方式,類似于經(jīng)典的《DOOM》中的“2.5D”視角,但方式有所不同。《DOOM》采用了近似的3D效果來表現(xiàn)墻面,實(shí)際上仍然是2D渲染,而我們則是通過精靈分割來實(shí)現(xiàn),精靈本身沒有傾斜,而是采用了3D定位系統(tǒng)。這種設(shè)計(jì)可以理解為“反向DOOM”或“2D渲染在3D中”。
我們認(rèn)為,確保3D基礎(chǔ)結(jié)構(gòu)的穩(wěn)固非常重要,因?yàn)檫@可以避免開發(fā)過程中出現(xiàn)意外問題,減少臨時(shí)處理的需求。雖然2D渲染方式可能有點(diǎn)“hacky”,但考慮到實(shí)現(xiàn)成本相對(duì)較低,這種方法仍然是值得嘗試的。通過提前實(shí)現(xiàn)扎實(shí)的3D基礎(chǔ)結(jié)構(gòu),能夠確保后續(xù)開發(fā)中不會(huì)頻繁遭遇問題,避免不斷進(jìn)行臨時(shí)的解決方案。
問:如果你上樓進(jìn)入一個(gè)封閉的房間,最上層會(huì)不會(huì)漸變顯示?
在討論游戲中的樓梯和房間的渲染時(shí),提出了一個(gè)問題:當(dāng)玩家從樓梯上走到一個(gè)封閉的房間時(shí),最上層是否應(yīng)該漸變顯示。對(duì)此,猜測(cè)可能會(huì)采用漸變效果來解決這個(gè)問題,但也表示目前還不確定具體的實(shí)現(xiàn)方式。由于這個(gè)問題還需要進(jìn)一步的實(shí)驗(yàn)和調(diào)整,因此暫時(shí)沒有最終的解決方案,仍然需要一些嘗試和探索。
問:你計(jì)劃什么時(shí)候?qū)`進(jìn)行 Z 排序?
在討論精靈的Z排序時(shí),表示計(jì)劃在渲染器實(shí)現(xiàn)時(shí)進(jìn)行,但預(yù)計(jì)這一部分的工作不會(huì)在短期內(nèi)完成,可能還需要一段時(shí)間,幾個(gè)月后才會(huì)開始著手。
問:你為什么在代碼中使用這么多魔法數(shù)字?為什么有這么多 #define?
在討論代碼中的“魔法數(shù)字”時(shí),解釋了為什么有些數(shù)字沒有使用宏定義(#define
)。如果某個(gè)數(shù)字只在局部使用,且沒有在其他地方引用,那么就沒有必要將其定義為宏,否則會(huì)污染全局命名空間。如果某個(gè)數(shù)字在多個(gè)地方使用,那可能就是一個(gè)需要改進(jìn)的地方,可以提出指出,看看是遺漏了還是有其他合理的原因。
問:你打算使用高級(jí)著色器技術(shù)嗎?如果是,你打算使用哪些?
在討論是否使用高級(jí)著色器技術(shù)時(shí),表示計(jì)劃使用一些高級(jí)著色器技術(shù),但目前還為時(shí)過早,具體細(xì)節(jié)將在未來的開發(fā)中逐步實(shí)施。
問:看起來我錯(cuò)過了一集,你在其中實(shí)現(xiàn)了從高層看到低層。你記得那是在哪集嗎?將來地板會(huì)保持透明嗎?
在討論渲染器時(shí),提到地板不會(huì)保持透明,盡管目前地板是透明的,這樣做比較方便。未來會(huì)添加地面,可能會(huì)首先加入一個(gè)占位符地面,而不是保持透明。同時(shí),還需要處理如從上層下降到下層時(shí),如何漸變處理上層的顯示效果等問題。這部分的工作計(jì)劃在接下來的開發(fā)中進(jìn)行,但具體細(xì)節(jié)仍在考慮中。對(duì)于何時(shí)實(shí)現(xiàn)這個(gè)功能,也沒有明確的記憶或時(shí)間點(diǎn)。
問:如果我沒記錯(cuò)的話,早期我們開始使用角色底部中心位置進(jìn)行碰撞檢測(cè),然后改為中心位置,并且在 X 和 Y 方向上有所偏移。現(xiàn)在似乎又回到了使用中心 X 和 Y,并繼續(xù)計(jì)算底部中心位置。你認(rèn)為是否需要將這一點(diǎn)整合起來?
在討論碰撞檢測(cè)時(shí),提到最初使用的是角色底部中心位置來進(jìn)行碰撞檢測(cè),后來調(diào)整為中心位置,偏移了x和y坐標(biāo)?,F(xiàn)在,似乎又回到了使用中心x和y坐標(biāo)的方式,同時(shí)保留了底部中心的位置。關(guān)于是否需要將這些方法合并,認(rèn)為目前的做法更合理,因?yàn)榻y(tǒng)一處理x、y和z坐標(biāo)更加簡(jiǎn)潔,避免了不同坐標(biāo)軸上采用不同的處理方式,這樣可以簡(jiǎn)化數(shù)學(xué)運(yùn)算,避免在處理復(fù)雜的碰撞檢測(cè)時(shí)出現(xiàn)混亂。如果讓碰撞檢測(cè)的不同部分(如Minkowski和干涉測(cè)試)在x、y和z上使用不同的方式,可能會(huì)增加數(shù)學(xué)復(fù)雜度,影響效率。因此,統(tǒng)一處理所有坐標(biāo)軸并單獨(dú)處理與地面相關(guān)的偏移,似乎是更明智的選擇。盡管如此,這個(gè)決定仍然有可能在未來被重新評(píng)估。
問:森林中間漂浮的頭是怎么回事
森林中飄浮的頭部是一個(gè)“伙伴”,它會(huì)跟隨玩家四處移動(dòng)。
問:能解釋一下 game.h 中的這一行嗎?ControlledHeroes[ArrayCount(((game_input *)0)->Controllers)];,看起來你在強(qiáng)制轉(zhuǎn)換一個(gè)空指針并引用它
無法直接獲取數(shù)組成員的大小,因此需要通過一些技巧來繞過這個(gè)限制。目標(biāo)是計(jì)算數(shù)組的大小,但C語言不允許直接對(duì)類型或成員使用sizeof
,必須使用實(shí)際的變量或值。因此,采取了一個(gè)技巧性的方法,使用一個(gè)空指針并將其強(qiáng)制轉(zhuǎn)換為正確的類型,模擬對(duì)該數(shù)組的引用,從而間接計(jì)算數(shù)組的大小。這種做法雖然看似不太優(yōu)雅,但由于C語言的限制,這是目前的解決方法。
問:為了繞過 Mischief 縮放問題,你能先找到想要的縮放級(jí)別,然后在四個(gè)角上畫四個(gè)點(diǎn),接著再回來對(duì)齊這些點(diǎn)到角落嗎?
關(guān)于Mischief縮放問題的解決方案。提出了一種方法,即找到需要的縮放級(jí)別后,在每個(gè)角落繪制四個(gè)點(diǎn),然后回來時(shí)通過對(duì)齊這些點(diǎn)來恢復(fù)視圖。但這種方法被認(rèn)為不太方便,因?yàn)槊看味夹枰业竭@些點(diǎn)。目標(biāo)是找到一種可以直接設(shè)置縮放的方式,而不需要不斷地定位這些點(diǎn)。
你可以使用 C++ 的 decltype 或 typeof 來找出 ArrayCount
在討論C++中使用decltype
和typeid
等特性時(shí),提到盡管這些功能可以幫助獲取類型信息,但由于它們傳統(tǒng)上并不總是得到很好的支持,因此盡量避免使用這些特性,尤其是對(duì)1990年代的C語言風(fēng)格更為偏好。雖然decltype
可能在某些情況下有用,但對(duì)于目前的需求,仍然更傾向于使用C語言的子集。對(duì)于如何準(zhǔn)確獲取類型信息,提出了一個(gè)假設(shè)的做法,可能通過decltype
獲取控制器數(shù)組的大小,但也承認(rèn)這部分實(shí)現(xiàn)可能還存在一些未知的問題,決定將其作為讀者的練習(xí)留待解決。
問:為什么你計(jì)劃使用 OpenGL 或 DirectX 進(jìn)行硬件加速?實(shí)現(xiàn)這一點(diǎn)需要了解實(shí)際的 GPU 架構(gòu)嗎?
在討論為何選擇使用OpenGL或DirectX時(shí),指出這兩個(gè)是唯一能夠有效訪問3D硬件的API?,F(xiàn)代計(jì)算機(jī)的CPU無法高效地進(jìn)行圖形渲染,因此必須使用GPU來渲染游戲。雖然自己編寫渲染器作為教育項(xiàng)目,但最終不會(huì)將其作為游戲的主要渲染方式,因?yàn)镃PU渲染無法與GPU渲染的速度相比。
問:你怎么看待 Khronos 的 Vulkan?
對(duì)于Vulkan的看法,目前尚未收到其規(guī)格說明,因此無法做出具體評(píng)價(jià)。理想情況下,希望硬件接口盡可能簡(jiǎn)化,理想的狀態(tài)是通過環(huán)形緩沖區(qū)與硬件直接交互,而不需要驅(qū)動(dòng)程序。如果Vulkan比現(xiàn)代OpenGL更接近這一目標(biāo),那么它會(huì)是一個(gè)積極的進(jìn)展。具體好處取決于Vulkan的設(shè)計(jì)質(zhì)量,如果設(shè)計(jì)得當(dāng),將是一個(gè)顯著的提升;如果設(shè)計(jì)不理想,則可能仍然是一個(gè)改進(jìn),但不那么顯著。
問:當(dāng)你知道如何編程但沒有藝術(shù)技能時(shí),做游戲的最佳方式是什么?找個(gè)藝術(shù)家來為你做嗎?這是我的軟肋,但我想自己做個(gè)游戲
如果有編程能力但缺乏藝術(shù)技能,最好的方法是雇傭一位藝術(shù)家來完成視覺設(shè)計(jì)。但如果預(yù)算不足,或者沒有藝術(shù)技能,可以考慮制作程序生成的游戲。很多成功的程序員都通過簡(jiǎn)單的藝術(shù)風(fēng)格獲得了受歡迎的游戲。可以嘗試制作3D游戲,利用光照來彌補(bǔ)藝術(shù)設(shè)計(jì)的不足,或者通過簡(jiǎn)化圖形定義來減少對(duì)復(fù)雜藝術(shù)內(nèi)容的需求。
例如,可以通過幾何形狀來創(chuàng)建游戲視覺,或者借用現(xiàn)有的視覺設(shè)計(jì),比如《迷你地鐵》這款游戲,它的視覺設(shè)計(jì)非常簡(jiǎn)潔,主要是基于地鐵線路圖。因此,即使沒有高端的藝術(shù)制作能力,也可以通過借鑒已有的簡(jiǎn)單設(shè)計(jì)來構(gòu)建游戲,避免被藝術(shù)內(nèi)容所限制??傊?#xff0c;如果無法承擔(dān)藝術(shù)制作,最好定義一個(gè)能夠不依賴復(fù)雜藝術(shù)而依然能吸引玩家的游戲設(shè)計(jì)理念。