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