我們知道Unity3d是通過C#腳本語言的形式來實現游戲的邏輯代碼編寫,同樣SCOTT服務器也設置了通過C#腳本來實現游戲邏輯,但是本文並不是想真正分析解密他們的運行機制,只是想通過自己的一個需求,來探討總結下其中的原理。
下面來說下我自己的需求,比較簡單,由於經常在非開發環境部署一些小工具,做系統維護,但每次又懶得帶筆記本和編譯環境到現場,但系統數據又總是那麼奇葩,時常有bug出現,突發奇想是否是能把工具做成腳本,這樣現場就很容易進行調整(簡單修改腳本),現場搞定不用來回折騰,豈不是很Happy。
聲明:本文首發於蠻牛,次發博客園,本人原創。
http://www.manew.com/thread-96136-1-1.html
Unity3d使用的Mono這個地球人知道,Scott呢?我以前分析過其源碼,大概知道它需要兼容IconPyhton和C#兩種腳本,所以它做了一層封裝,但是看它引用的DLL lib有Mono的影子,所以估計也是Mono(不對請拍磚,其實它用什麼無所謂了)。實際原理很簡單了,也就是腳本動態編譯(有點像解釋執行,嚴格上應該不是,我覺得李總的熱更新的腳本應該不是動態編譯的因為很多平台根本不支持比如IOS),查了下資料目前c#動態編譯的一共三種方式CodeDom,Mono,Roslyn,這三種教程應該是一堆一堆的,但是Mono和Roslyn的都不是很多,畢竟這個需求比較小眾,而且Roslyn是最新出來的,例子都是“Hello world”級別的讓人很不爽,最後發現一個庫CS-Script,網址是https://csscriptsource.codeplex.com/,看了下文檔,瞟一眼代碼:
CSScript.EvaluatorConfig.Engine = EvaluatorEngine.Roslyn; //EvaluatorEngine.Mono; //EvaluatorEngine.CodeDom; var sqr = CSScript.Evaluator .CreateDelegate(@"int Sqr(int a) { return a * a; }"); var r = sqr(3);
看到1,2,3行代碼的時候,我會心的笑了,實際上CS-Script是對於三種方式的上層封裝,可以自定義選擇用那一種,這個方案對於我的已經夠用了。下面就是做個試驗,然後實施即可。
詳細的步驟教,我不想多說了CS-Script有自己詳細的教程。由於本人的需求是使用自己寫的代碼調用腳本,這裡就不講通過命令執行腳本的東東了。
第一步安裝,創建一個控制台項目,然後使用nuget進行CS-Script的安裝,在程序管理控制台執行
Install-Package CS-Script
Nuget會自動下載相關的DLL和Example腳本到項目中很簡單,結果如下
執行效果如下,具體可以看Test中的方法就是一些如何動態執行類,方法,靜態方法,接口等等簡單例子吧,對於我們的簡單需求已經夠用了(如果有復雜需求比如Host,上下文,高級動態編譯什麼的請看文檔,這裡估計也講不下)
至此簡單的測試環境已經有了
根據官方的例子稍微做一下改造,修改Main函數如下:
在bin下寫一個自己的Hello.cs腳本:
創建一個批處理執行,以便把文件名稱作為動態參數傳入,執行效果如下
這裡需要注意的是腳本的文件格式別忘記是utf-8的格式,不然中文會亂碼的。
上文中的腳本運行模板寫基本完成了,這樣只要將自己需執行的工具代碼,放到單獨的類中,然後啟動函數為Run即可,這樣只要在Dos批處理腳本中修改c#代碼文件的相應名稱就可以按照腳本執行了,下面是我的工具腳本代碼(是一個壓縮二級目錄的C#源碼):
滿心歡喜,可結果可恥的失敗了
分析其原因主要是我在Myscript腳本中使用了另一個c#類文件中的相關函數,也就是上圖中的紅色框標注出的代碼,實際上這裡是一個上下文缺失(或者是host問題),這裡的解決辦法一種是動態添加Assembly;一種是使用DLL。這裡為了簡單,我選擇第二種方法。這裡假設ZipUtil是一種公用類庫,我將其封裝到獨立的DLL中,作為類庫引用到工程中去。運行結果和預想的一樣執行成功了(So happy)
這裡也體現了CS-Script的強大,動態的就管理了第三方應用的DLL竟然不需要做任何處理,贊一個!
至此,小試牛刀使用CS-Script這個工具,很Easy就實現了C#腳本的動態調用和執行,也拋磚引玉引入了C#作為腳本動態編譯執行的原理和過程,當然復雜的應用還有很多,比如執行效率,熱插拔更新,高級動態編譯,上下文這裡留給大家自己研究吧。
對本人來說作為自己的需求已經達到,對於動態執行C#腳本也有了更深刻的認識,特別是通過排錯的過程,更加深刻的認識到使用腳本的前提和環境是什麼:腳本最好是一個單獨的C#文件即可(多了就失去了腳本的意義,也沒有必要);腳本的使用是在有完善的類庫的前提下,通過腳本來實現多變的邏輯,如果類庫不夠成熟穩定還是不要使用腳本了(這裡可以參考下Scott至少它提供了Framework級別的庫)
源碼下載
參考閱讀: