Skip to content

回歸到遊戲的開發

在場景上呈現Snake

July 03, 2017

Snake如預期的可以進行移動和改變方向,但只能看到Editor Console中的文字變化,沒有太大的感覺。因此,這次的重點就是讓它呈現在場景中。而為了呈現有很一些原先已撰寫好的函式要跟著做相對應的變動。

配合Unity中元件式的設計,不在原先的SnakeComponent裡做太多的功能,而是另行加入SnakeRenderComponent(SRC),一個專門處理呈現Snake的元件。但仍是將此元件視同為殼層,另有函式實現呈像的邏輯。

SRC元件單純的拿取一個Prefab,此Prefab決定Snake每一格的呈現,而此元件開始時則會預先從這個Prefab產生20個物件暫存到陣列(Array)中以供後續使用。雖然長度有可能超過20個,但可做動態增長或是導入物件池的概念使之更加完善,但目前暫時先不理會此潛在的問題,待之後有需求再做增修。

在SRC裡定義Render method,並在此method中直接呼叫一個Render function。主要的呈現邏輯則在此function中實現。每次都先將代表Snake每一格的物件關閉,而後依據Snake目前有多少Block來開啓並更新其位置。此處用Seq.Zip,它的特性是從A、B二個Collection裡依照順序將其中的元素取出後放置於Tuple中,若是二個Collection的長度不一的情況下,只取到最少元素的那個collection後便停止。

而zip出來的Tuple一定是二個元素,用Destructing方式直接拿取,讓代表Snake每一格的GameObject進行相對應的調整。

這就是在呈現端主要的製作。回到之前的SnakeComponent元件,將此原件視為主要元件,在此元件的Start裡拿取SRC元件,若是有拿取到,表示可以用SRC裡提供的Render進行,若是無法拿取到(元件沒有被加入到該物件上),稟持著在函式程式設計和遊戲設計共同的思維,不丟出異常(No exception thrown),加入預設的處理。沒有呈像元件的情況下,就自行用一個函式去接,並在此函式中做最基本的呈現-也就是將狀態導入到Editor Console中。

而因應的變動則是原先在Setup的function多加了一個參數,這個參數不是一般的型別,而是另一個function。因為Setup的參數中拿取了一個function,讓Setup有一個很特別的名稱,也就是Higher Order Function,簡單來說,就是拿取function當做參數的function。雖然名稱上很特別,但整個function的行為並沒有多大的不同。只是將每一次更新過的Snake直接導入到這個呈像用的function裡。

編譯後並將其更新過的Dll導入到Unity中,若是有SnakeComponent元件的物件有沒有加入SRC,則和之前的呈現方式一樣,只能有Editor Console裡看到文字變化。若是在此物件上加入了SRC元件,並放入一個Unity Cube當做Prefab,則會看到在XZ的平面上,有代表Snake的方格會依據時間和輸入的方向做變化。

Bingo!

看起來陽春未加修飾的Snake終於在場景上不停的移動著,按著AWSD看它改變方向,當一切都如預期般呈現時,突然,發覺有一個Bug,一個行為是所有其它的Snake遊戲都不會出現的。Snake在做方向移動時是可以直接往前進的反方向進行的,基本上是不被允許的,按照規則來看,Sanke的頭部只要不是碰到蘋果,任何其它的物件,就算是自己身體的一部份,也會直接讓遊戲結束。

而這個Bug源自於在輸入AWSD改變方向的當下並沒有任何的判定是否該方向可行,加入以下的判定後,則可解決這個問題。這個判定說穿了就是原先水平的移動只可改至垂直的方向,而原先垂直移動的只能改到水平的方向。快速的加入此判定後則可以在Unity裡看到正確的移動規則。

呈現部份有個大概後,就要開始進行其它的規則了。最優先的規則應該就是加入可讓Snake的長度增加的蘋果,和撞到非蘋果以外的物件時會導致遊戲結束的判定。這些規則的製作會進入下個篇幅中,但在此篇結束前仍有重要的事情要注意。

隨著元件的數量逐步增加,多檔案是不可避免的,但在F#的環境下,和C#不同的地方在於引用型別的順序。早年在開發Cpp時型別的引用順序很重要,所以會分出標頭檔(用副檔名h)和定義檔(用副檔名cpp、cxx等)在引用型別時都要注意型別是否已被宣告(不一定要被定義)。所以那年代會有宣告、定義不等同的可能,但隨著C#的出現,簡化這些引用的方式,在現今的C#中,宣告當下已定義,而在編譯器的規則制定下,只要是在同一個assembly裡,型別引用完全沒有順序上的問題。

但在F#中,雖然宣告也等同於定義,但型別的引用順序很重要,如果型別B要引用型別A,則型別A一定要先出現,而按照編譯器的規則,先出現代表若是此二個型別方別放入檔案B和檔案A中,則檔案A一定要在檔案B的上面(?)。此處上面是指在專案呈現時,雖然通用規則是按照字母做排序,但在Visual Studio裡,可以調動檔案的順序,而這樣的改動在C#專案沒有太大的意義,但在F#專案非常的重要,若是檔案順序不對,會導致引用上出現編譯錯誤。但有些IDE,如目前手中在用的Project Rider沒有提供此排序調整的選項,只能開啓fsproj檔,直接做xml的編修,調整引用的順序。

這份部就不再著墨,日後會另開一篇文章溝通F#引用的規則。只是在目前的多檔案中,SnakeComponent和SnakeRenderComponent有引用的順序問題,在製作上要特別的注意。


This is where ApprenticeGC goes.
Or whatever, you make the rules.