C#直譯成F#比對差異
June 25, 2017
將Roll a Ball專案從Asset Store上拿取下來,它是Unity的範例教學,所以有相對應的教學影片,若是沒有留意有影片存在的開發者,想要了解這個專案怎麼一步步從頭產生的,可以花些時間觀看。但這裡主要會從修改使用的程式碼為主,不會再切步驟重覆講解整個專案怎麼完成,但此範例相當的單純,只有3個主要的檔案要做調整。整個遊戲藉由操控拿取值後用物理進行移動,如果撞到了方塊,則會獲取分數。
在選用的IDE中開新的sln並加入F#專案。由於要繼承MonoBehaviour和使用uGUI,故要將UnityEngine和UnityEngine.UI這二個Dll額外的引用至專案中。這二個Dll在Mac上和Windows上的位置並不同,可從此官方文件知曉。經歷了幾個版本UnityEngine.dll位置如文件中所示,但UnityEngine.UI.dll位置則有數度的調整,但主要是在和Managed目錄同層的UnityExtensions目錄的子目錄中。
直譯的過程相當的順利,不到十分鐘就完成了。將原先在專案中的那三個C#檔案全數砍掉,並將FSharp.Core.dll和建置出來的Dll檔放到Plugins目錄下。將場景中的Player、Camera和Pick Ups下所有的物件相對應的元件都置換成用F#撰寫的PlayerController、CameraController和Rotator。
替換完成後,就按下執行來玩玩看,整個表現和原先用C#所撰寫的功能應該是一樣的。Bravo,大功告成。
如果花些時間比對,會發現二邊的寫法結構上大至相同,由於是直譯,就算是這樣的寫法在F#裡不是很推薦的寫法,也會儘量維持原先的寫法。但在這三份精簡的程式碼裡除了mutable的用法或許有更好的方式表現,if的寫法或許可以換成match等,其它都是必要的轉換寫法。
但會先注意到的應該是沒有多餘的大括號和分號,F#裡利用縮排來達成述敍語句是否完成和其範疇(scope)的判定。所以省略了不必要的符號,但讓縮時排變得相當的重要,以往在C#裡若是行與行之間的縮排多或少空格,都沒有問題,但在F#裡則會出現語法的錯誤,除此之外,縮排用的空白字元不可以是Tab,在F#編譯時會產生錯誤。
回到和Unity的部份,為了要能夠成為合法的元件,所有的條件都要符合,像是型別名稱要和檔案名稱相同,要繼承MonoBehaviou型別等。不同於在C#中只用:符號,繼承的寫法要特別的用關鍵字inherit。
宣告型別用的函式要額外寫下member,但是否用this則沒有特別的規定,有些人會用self而有些人會省略成x,不論用何者,只要在該型別的範疇中保持一致性即可。
變數要在Unity Inspector裡被使用到,則必須是Unity認同的型別,且是公開的(public),若是宣告成私有的(private),則需要額外加上SerializeField屬性,如果要還原可以在Unity中對應的變數,則公開變數用val並加入mutable,但還要加入DefaultValue屬性,私有變數則用let mutable,不用再加入DefaultValue屬性。
等號在F#中除了用在let中當做初始值的設定外,其餘地方都會被視為判斷是否相同的比較,而不再是給值的功能。因此,在其它地方要給值,除了該變數是被宣告成mutable外,給值的符號變成了\<-。
而在整個直譯中,特地用了一個F#中特有寫法|>(Pipe),雖然並不是必要的,但展示了函式的參數數量為一個時,除了原先常用的寫法外,也可以利用Pipe的方式撰寫。而這樣的寫法在F#裡很常見,而且在串連多個函式時是不可避免的寫法。
拿取count時沒有用ToString()而是用了和String.Format很接近的方式,用sprintf的寫法較符合F#的使用,但ToString()是.Net中通用的寫法,F#也還是.Net的語言,所以怎麼寫其實都可以,故此處只是要帶入一些些多元的改寫方式。
在new Vector3這裡,會發現三個代表x、y和z的值特地加了f的後綴修飾,使編譯器解析成浮點數。浮點數在.Net中被分成Single和Double,而在F#中依順序則可寫成float32和float。雖然Single和Double的寫法可行,但不符合F#的撰寫方式。而一定要加入f是因為F#很嚴謹,不能會自行做轉換型別的動作。因此,若按照原先C#的版本只放入整數時,會被判別成錯誤的用法,因為Vector3的初始參數是浮點數而不是整數。
直譯中差異性和該注意的部份大致都提過了,這裡想要說的是雖然它現在是用F#的語法撰寫而成的,但它背離了F#的實際撰寫,也就是很少F#開發者會用這樣的寫法。但或許是在Unity的環境中,MonoBehaviour的元件有個既定的框架要遵循,不論是用C#或是F#,都必需以事先定義的物件方式撰寫其型別函式。就算它看起來有些怪怪的,不符合多數F#開發者的撰寫方式,但說實在的目前用F#撰寫Unity遊戲的開發者少之又少,或許接近直譯的撰寫才是正道,且不可避免!?
不論如何,在簡短的修改後,讓原先的C#語法變成了F#語法。從這精簡的專案中讓有興趣的開發者接觸到不一樣的一扇門。是否要跨進去則因人而異,但閒暇之餘或許也可以嘗試不同以往的開發方式。
++ 修改後專案並沒有放到GitHub上,故已無法還原整個修改後的專案 ++