當前位置:首頁 >教程首頁 > 华体会hth体育app在线登录 > 遊戲UI設計師班 >程序員如何製作2D平台遊戲的控製器

程序員如何製作2D平台遊戲的控製器

發布時間:2018-11-17 19:57:24
  在“業餘程序員”係列,我想分享一下作為一名業餘程序員的經驗。我的分享有兩個主要目的:一是證明非專業的程序員也能製作出原型;二是向專業程序員展示設計師是怎麼做程序的,也許能得到一些反饋和建議吧!最後,本係列也是對我本人工作的一些反思。這次我先介紹我如何使用Unity為個人項目製作自定義操作器:

  為什麼使用自定義操作器,而不使用Unity默認的那個?

  你可能會問自己:“但為什麼不使用Unity自帶的角色控製器呢?”事實上,我試過了,至少在一開始的時候。但後來我遇到了一些問題:

  1、Unity的默認角色控製器是基於Unity的物理引擎的:如果你想製作一款物理平台遊戲(遊戲邦注:如《Little Big Planet》、《Trine》等),這也許是個不錯的解決方案。然而,我需要一種“敏銳”而準確的操作,像老式2D平台遊戲(《馬裏奧》、《索尼克》、《超級食肉男孩》等)那樣的,所以Unity的物理引擎太難調整了。

  2、Unity默認的角色操作器是基於膠囊狀碰撞器:所以,玩家能夠輕易地在斜坡上行走,但當你要做的是朝右移動的平台遊戲時,膠囊就會卡在邊緣上,這是不可行的:

2d Platformer-01

  3、最後,當我想到以後的AI尋徑,我決定自己做一個控製器:我認為在一般的平台遊戲中,使用簡單的功能如跑(速度)和跳(高度)可以更容易控製AI在路徑上的移動,並結合遊戲世界的限製條件(碰撞和重力)。

  基本原則

  沒什麼特別的,關於2D遊戲的碰撞有很多網上教程,我采用的方法不過是結合了我看過的東西和我自己的(不成功的)經驗。

  因為我的原型是一款貼圖平台遊戲,我本可以使用簡單的係統來檢查我想移動的貼圖是不是牆體。出於若幹原因,主要是因為我之前不懂得用這種方法處理斜坡,我選擇了更通用的係統,即使用光線投射來檢測碰撞。

  所以,基本的碰撞概念是非常簡單的:控製器以一定的速度值移動,除非這個速度導致它的碰撞器遇到另一個碰撞器。所以,我們隻需要檢測這些可能的碰撞!

  為了檢測這些碰撞,正如我前麵所說的,我使用光線投射:當有一個速度值時,就有光線會搜索這個速度的方向(X軸和Y軸)的碰撞。如果光線發現碰撞,命中點會確定這個控製器在這個軸上能夠達到的最大值。

  對於四個方向上的各個光線,偽代碼如下:

If speed on x axis > 0
If raycast to the right hits a collision on point (Px,Py)
Set xMaxLimit = Px

  取決於控製器碰撞器的大小和貼圖的大小,我必須在每一邊放兩束光線,即一個角一束。但如果你的兩束光比你的貼圖要大,那麼你使用兩束光就會遇到如圖所示的問題:

2d Platformer-02

  對一邊的光線不止一束的情況,我還必須比較它們的命中點P1和P2,並保留最接近的一個:

Set xMaxLimit = Minimum value between P1x and P2x

  最後,我檢測控製器的各邊是否被阻塞,如果是則速度設為0。如果沒有,則控製器將仍然被阻塞,但保持原來的速度值。所以,如果你突然想走另一條路,你必須等到相反的加速度彌補實際速度,最終將速度增加到另一個方向上:它使玩家感覺到“被卡住”。

  解決辦法相當簡單:

If controller position on x axis >= xMaxLimit – 1 (I added a 1 pixel buffer to prevent errors)
Set rightBlocked to true

If rightBlocked is true AND  speed on x axis > 0
Set speed on x axis = 0

  我想這是非常簡單和傳統的辦法吧……

  重力問題

  重力就是指,當控製器在空中下落時,每一幀,Y軸上的速度會在重力的作用下加上一個均勻加速度:

speed on y axis = speed on y axis + gravity acceleration * delta time

  (在這裏我就不談delta time(時間增量)了,因為程序員知道那是什麼東西,而非程序員可以很容易在網上找到解釋。)

  我現在不想在這裏詳細地解釋跳躍係統(因為我想另寫一文介紹)。這裏的重點是,當跳躍輸入被調用時,一個衝擊速度會在Y軸上被賦加給控製器,然後重力一步一步地抵銷高度,使控製器進入下落狀態。

  然後我使用相同的係統來確定移動限製和著陸標記:

If raycast to the bottom hits a collision on point (Px,Py)
Set yMinLimit = Px

If controller position on y axis <= yMinLimit + 1
Set grounded to true

If grounded is true AND is not jumping
Set speed on y axis = 0
Else
Apply gravity (see above)

  你可以看到上述代碼的一些變體:

  1、如果底部方向到光線上有速度,則不必檢測,因為總是有速度(由持續的重力加速度產生)。

  2、相同地,如果著陸,則不必檢測。

  3、如果控製器正在跳躍,則在設置Y軸上的速度為0以前需要檢測:否則,當跳躍衝力被賦到Y軸上的速度時,它直接回到0,因為控製器在下一幀裏可能仍然貼著地麵。

2d Platformer-03

  光源的重要性

  這不是一個大問題,我認為對專業的程序員來說是小菜一碟。但我自己花了一些時間,我真的想在這裏分享一下我是怎麼理解它的:

  一開始,我的光線是由控製器碰撞器的角產生的。問題是,控製器不能正確地處理右邊的碰撞。現在我知道它是一個代碼執行順序的問題,用下圖更容易解釋:

2d Platformer-04

  後來我發現了一個解決辦法:光線從更遠處投射,以產生“緩衝區”:

2d Platformer-05

  斜坡問題

  斜坡……當想到碰撞係統時,我總是很怕斜坡。我怕到不得不去尋找“無斜坡”的做法!但是,我最終克服了斜坡障礙!

  令人吃驚的是,在我整合基本的碰撞係統後,控製器居然能夠應付斜坡了。好吧,雖然處理得不是很漂亮,但至少不會卡在斜坡上了,這極大地鼓勵了我。事實上爬坡很好,因為X軸上的極限位置總是“推回”。然而,下坡很成問題,因為當玩家跑得非常快時,他會先在X軸上移動,然後受重力作用在斜坡上下落,這就產生了“彈跳”下坡現象!

  另外,在老式2D平台遊戲中,玩家碰到斜坡的底部中心。但當我的基本碰撞係統運作時,控製器碰到的是斜坡地麵的最接近底部角度的地方。

2d Platformer-06

  為了讓控製器固定在斜坡地麵上,我嚐試了多種解決辦法,從各種教程到自己購買方案,我最終決定使用比較簡單的一個。基本上就是:

  1、檢測控製器是否與斜坡接觸

  2、如果是,則直接設置Y軸關聯到斜坡碰撞的Y軸位置

  檢測是否與斜坡接觸

  首先,為了檢測控製器是否與斜坡接觸,我檢測它是否“在斜坡的上方”。為此,當一束底部光線發現碰撞時,我隻要檢測這個碰撞法向量和右邊的單位向量之間的差異:

If raycast to the bottom hits a collision on point (Px,Py)
If angle between collision hit normal vector and right unit vector differs from 90°
set slopeOnHitPoint to true

  當兩束底部光線之一不能確定控製器是否真的在斜坡的上方時,是因為發生了如下圖所示的情況:

2d Platformer-07

  所以為了確定是否在斜坡之上,以下條件之一必須為真:

  1、如果左光線和右光線均檢測到斜坡,則控製器在斜坡之上

  2、如果隻有一束光線檢測到斜坡,且作為命中點的斜坡被另一個命中點來得高

  然後,為了確定控製器是否在斜坡上,我必須確定它是否接觸斜坡或在斜坡的上方。然而,我發現我需要更大的“緩衝區”來防止當控製器在斜坡上時退出它的“著陸”狀態。所以,修改法的偽代碼是:

If aboveSlope
Set groundCheckValue to yMinLimit + 5
Else
Set groundCheckValue to yMinLimit + 1

If controller position on y axis <= groundCheckValue
Set grounded to true

If grounded is true And is not jumping
Set speed on y axis = 0
If aboveSlope
set onSlope to true
Else
Apply gravity
Set onSlope to false

  設置控製器Y位置

  既然我已經知道控製器是否在斜坡上了,那麼接下我隻要確定它的位置就行了。

  因為我希望控製器“呆在”斜坡的底部中心,從它的中心投射一束垂直向下的光線,且使用命中Y位置當作新的yMinLimit。

2d Platformer-08

  然後,為了避免控製器產生彈跳下坡的現象,我拋棄了Y軸速度,直接設置控製器Y位置為yMinLimit(X軸上的速度從未改變):

If onSlope
Set controller y position to yMinLimit
Else
Set controller y position to actual y position * y speed * deltaTime

Set x position to actual x position * x speed * deltaTime

  峰值問題

  當所有這些棘手的小問題都似乎解決了,我又遇到一個新問題:頂點!事實上,當達到頂點時,控製器就被認為位於斜坡上,它繼續用來自中心的光線確定yMinLimit。所以,隻要這個中心超過頂點,控製器就會產生碰撞,yMinLimit如下圖所示:

2d Platformer-09

  作為開發者,我必須承認當時我不知道我是否希望我的遊戲中出現這種碰撞……但我不想因為自己不能處理它們就回避它們!

  事實上,我沒有找到這個問題的清楚解決辦法,但我所選擇的做法似乎也蠻管用的……

  首先,我檢測控製器是否在斜坡的上方,也就是頂點在左邊:左光線是否檢測到斜坡,且介於左命中點與中心命中點之間的距離是否大於5(任意值,取決於對貼圖大小、控製器碰撞器的大小的測試)。

  最後,如果控製高於頂點,我就使用其他邊的光線命中點來確定yMinLimit。

2d Platformer-10

  這個辦法並不完美,因為中心光線和邊光線之間沒有過渡,不能很精準地確定yMinLimit,且它產生一個有點兒怪異的切換。但我仍然希望有一天能找到更穩妥的解決辦法。

  結論

  總之,在自己處理碰撞問題上,我確實遇到很嚴峻的考驗;但結果滿足了我的要求。如果某些程序員看到本文能給我一些反饋,我會很高興的。我還要問自己是否有更複雜的、處理其他類幾何形狀的碰撞係統也使用了類似方法?如果你知道,就請滿足我的好奇心吧!
华体会hth体育网 賞析
  • 2101期學員李思庭作品

    2101期學員李思庭作品

  • 2104期學員林雪茹作品

    2104期學員林雪茹作品

  • 2107期學員趙淩作品

    2107期學員趙淩作品

  • 2107期學員趙燃作品

    2107期學員趙燃作品

  • 2106期學員徐正浩作品

    2106期學員徐正浩作品

  • 2106期學員弓莉作品

    2106期學員弓莉作品

  • 2105期學員白羽新作品

    2105期學員白羽新作品

  • 2107期學員王佳蕊作品

    2107期學員王佳蕊作品

專業問題谘詢

你擔心的問題,火星幫你解答
×

同學您好!

您已成功報名0元試學活動,老師會在第一時間與您取得聯係,請保持電話暢通!
確定