歡迎來到我的都市天際線Mod開發系列教程第二期!首先讓我們看看這期主要開發的模組效果。
廣告牌不見了嗎?這就是一個能夠移除廣告牌的模組。讓我們再看一遍。這裡原本有廣告牌對吧?如果安裝了這個模組之後,就可以將這些廣告牌移除掉。這就是我們這個模組的主要功能。接下來我們要怎樣編寫這段代碼呢?讓我們來看看這個教程。
讓我們看一下第一步是什麼?實際上第一步就是建立這個項目,設置好項目。這是關鍵的第一步。此時,我們必須打開我們的開發工具——Visual Studio。然後點擊創建新項目。根據上期教程我們學到的內容,當我們創建新項目時,選擇C#,然後選擇Windows下的“類庫”,即Library。接著選擇輸出為DLL格式,用於創建C#的類庫。選擇DLL項目,即動態連接庫項目。然後點擊下一步,在配置新項目時,為項目命名。按照教程指導一步步進行。注意文檔中給出的名稱,複製粘貼過來,確保正確輸入。在選擇位置時,放在你的文檔底下,例如Future Studio或2019,如果是2017也可以。選擇文件夾後,點擊創建即可。
點擊創建按鈕,即可建立一個新項目。好,我們現在進入Visual Studio為我們創建的項目編輯器頁面。首先,我建議將"Class1"這部分改成一個更具描述性的名稱,比如"Mod"。這樣以後如果我們有很多C#文件時,就會更容易找到。我們找到這個"Mod",它是我們主要的起始文檔。在這個起始文檔裡,按照給出的代碼,複製粘貼即可。
然而,一旦我們複製粘貼,可能會遇到問題,顯示紅色波浪線。這表示找不到某個類型或命名空間。這時候我們需要將動態連接庫(DLL)提供的都市天際線鏈接起來。這在第一集中也有提及。我們需要添加引用,右鍵點擊項目,選擇“添加引用”,瀏覽找到都市天際線這個遊戲安裝目錄根目錄下的DLL文件,並加入應用程序中。建議大家將常用位置和指令寫在一個純文字文檔中,這樣每次選擇會更輕鬆。你可以先把這些內容寫在文檔中,然後在創建項目時,按照文檔內容進行復制粘貼操作,這樣能夠快速且準確地完成整個過程。
那麼最好的做法是這樣的,實際上,有時候我們會用到一些UI界面。所以更完整地按照步驟選擇確定。在這個階段,一個神奇的事情發生了。我之前紅色波浪線的報錯消失了。這表示我們已經成功引用了i city這個命名空間或類,也就是動態連接庫的內容已經連接成功,因此紅色錯誤部分消失了。接下來讓我們稍微解釋一下代碼的意思。Namespace表示命名空間,我們將這個模組命名為"tBeautiful",即一個讓城市更美化的模組。在其下一層級是Class,顯然Namespace的下一個就是Class。
Class代表類,我們將其命名為"tBeautifulMod",它繼承自"iUserMod"。而"iUserMod"是從哪裡來的呢?實際上"iUserMod"就是從"iCity"這個引用中提供的一個可以繼承的接口。接口通常用"I"來表示,表示接口。你可以點擊查看,它顯示這是一個接口。這個接口實際上來自都市天際線開發人員所提供的程序集,可以提到程序集"iCityVersion1000"等等。我們要繼承這個接口,然後開始進行開發。當繼承這個接口後,就需要去實現這個接口,也就是要去實際做這個接口定義的工作。在實現這個接口時,必須提供兩個信息:一個是名稱N,即你的模組名稱,另一個是Description,描述一下這個模組的用途。我們在上一期中介紹過這點,簡單來說這兩者是必須要提供的信息。在選擇模組時,這些信息會顯示出來,並在遊戲主菜單中顯示。因此,這些信息是必須要輸入的。
如果想要更仔細的話,我們可以在這裡輸入版本號,比如1.0版,這樣更容易識別。因為隨著時間推移,版本可能會慢慢更新。這個是開發每個模組都最基本的要求,就是對模組進行聲明,即瞭解模組的名稱和用途,並做一個簡單描述。讓我們繼續跟著教程往下走吧。
關於C#語言的語法,我就無法詳細說明了,因為限於篇幅。如果有不理解的地方,可以去搜索B站上許多關於Unity和C#入門教程,大家可以自行學習一下,由於篇幅問題,我無法深入講解,大家可以自己延伸學習一下。
好,第二步是什麼?
Loading hook。
Hook指的是使用我們自己的方式來複寫原有提供的方法,也可以翻譯成“鉤子”,就是將其勾住,然後用我們的代碼來改造它,或者說用我們的方式去修改原有內容。
關於城市模組的API,它包含了各種接口,可以繼
承這些接口,創建類來實現這些接口,從程序員的角度來看,我們通常將其翻譯為“接口”。
你可以創建類來改進這些接口,以便與遊戲進行連接。
其中最重要的接口是loading extension,加載擴展和擴展和類的改進。這個接口在加載過程開始、完成以及安全卸載時會收到通知,是最重要的接口。
當我們實現了這個接口後,在遊戲的載入過程中,我們的模組就會被載入,並可以顯示特定信息或者設置參數等。簡單來說,實現了這個接口後,我們可以在模組加載後執行我們在代碼塊中編寫的內容。例如,看一下代碼塊"on level loaded",意味著當遊戲載入時,也就是城市地圖被載入時,會執行什麼動作。這裡告訴我們要執行的動作是輸出一個面板,稱為"exception panel",有點像一個異常或警告面板,然後我們可以設置面板上顯示的消息,標題是"City Beautiful",內容則是"the level is loaded",表示城市地圖已經載入完成。
因此,每個模組在初始化時,首要做的事情就是通過代碼塊"on level loaded"來實現它的功能。讓我們先創建這段代碼,但根據所提到的,我們需要先創建一個名為"city beautiful loading.cs"的文檔。可以複製這個名稱,右鍵點擊添加新建項,選擇C#類,並按照剛剛複製的名稱粘貼為"CityBeautifulLoading.cs",然後添加即可成功創建這個文檔。
在我們的解決方案中,有一個包含默認內容的文檔,這是Visual Studio默認生成的內容。在這裡,它會幫助我們命名"NextSpace"命名空間,並將它寫入其中。為什呢?因為項目中存在兩個.cs文件,這兩個文件都屬於"CTBeautiful"命名空間,所以它會智能地將它們放在同一個命名空間下。這種自動化的處理可以幫助我們節省時間。
關於類的部分,它會根據你給出的答案名稱,比如"CityBeautifulLoading",自動為你設置類的名稱,這樣的設計非常方便,省去了手動輸入的步驟。這樣的處理十分貼心。
接著,我們可以逐步複製並粘貼這段代碼,覆蓋原來的內容。灰色部分代表什麼呢?灰色部分表示儘管它已經寫在代碼裡,但實際上沒有被引用,也就是說下面的代碼並沒有使用到這部分內容,因此這些using指令是不需要的。我們可以複製這部分內容並粘貼過來,這也是一個很好的操作。
在這種情況下,我們需要繼承ILoadingExtension接口。如果出現紅色報錯,那是因為它告訴我們必須要實現接口的成員。一旦接口定義好了,我們就必須實現這些接口成員,就像之前所說的那樣。複製並粘貼這些方法,當我們把所有方法都貼進去後,之前的紅色提示錯誤就不見了。這是因為根據接口的定義,我們必須實現這些方法,而我們已經將這些方法寫好了,所以不再報錯。
如果你刪除其中一些方法,會發現出現錯誤。例如,如果刪掉OnLevelUnloaded,系統會提示你未實現該方法,因為這些方法是必須實現的。如果遇到這種情況,程序將無法編譯。當嘗試生成解決方案時,會顯示生成失敗一次,成功零次,因為代碼存在錯誤,無法編譯成動態連接庫。確保提供接口所需的方法十分重要。
另外,在生成後事件命令行中,也需要將其複製和粘貼到項目屬性中。這樣可以確保在編譯時,能夠正確地將生成的動態連接庫放置在正確的地方。具體操作是右鍵點擊項目名稱,選擇屬性,找到生成事件,然後在生成後事件命令行處粘貼,並保存。
在編譯成功後,我們可以給出基本信息,並且可以進入遊戲中查看我們所寫的“level is loaded”這條消息是否成功顯示。首先,我們要啟動遊戲並加載我們編譯好的模組。在遊戲主菜單中,打開內容管理,關閉其他模組,只保留我們要測試的自己編寫的模組。然後加載遊戲,並選擇我們編寫的模組,如“CityCT”,然後關閉內容管理,加載一個小城市進行測試。
如果一切順利,遊戲加載後會立即顯示“the level is loaded”消息,表示我們的第一階段目標已經完成。成功學會了在模組加載後顯示消息和執行代碼。在確認成功後,我們可以繼續下一步的教程內容。
第三步是要研究建築物及其附屬道具、廣告牌等內容,包括桌子、裝飾品、樹木花草等。我們的目標是訪問並處理這些與建築物相連的各種元素,比如道具、廣告牌等。這些互相關聯的元素需要我們來存取和處理。
更具體地說,建築物的Prefab(預製)是什麼意思呢?其實,它類似於我們所說的藍圖。Instance(實例)是根據這個藍圖所創建的具體對象。提到建築物的Prefabs是建築物實例的模板。
換句話說,Prefab是建築物實例化的模板或樣板。因為Prefab就是藍圖。這意味著當一個建築物被創建時,它會使用Prefab中設置好的屬性,如紋理、模型以及其他mod中的內容。當我們修改建築物的藍圖時,也會影響建築物的實例化,即具體的建築物也會隨之改變。
這個原理是,當我們想要將建築物上的廣告牌、招牌或其他附屬物品與建築物藍圖關聯時,一旦修改了藍圖,具體實例也會相應改變。不僅限於建築物,還包括樹木、道具、車輛、交通設置和道路等,它們都有對應的Prefab,即藍圖。
關於Prefab Classes,它代表藍圖的類別,通常在遊戲中以“info”結尾,比如BuildingInfo、HarborInfo等。它們實際上都是預先設置好的藍圖,所有建築物的藍圖可以在這裡找到。接下來,我們將學習如何通過循環遍歷整個建築物集合,在場景中找到我們需要的元素,如廣告牌、桌子、椅子和裝飾品,並通過代碼進行操作。
當建築物消失、移動位置甚至改變顏色時,都有可能。他提到使用以下代碼來複寫或覆蓋"onLabelLoaded"方法。我們將這段代碼複製粘貼,然後重寫覆蓋這個方法,在重新編譯後,我們會輸出載入的建築物偏好的總數。
在複寫之後,遊戲加載後應該會顯示地圖中加載的建築物資產數量。接著,我們存檔(Ctrl + S),再進行編譯(Ctrl + Shift + B)。編譯成功後,進入遊戲查看是否能夠成功顯示。
在遊戲主界面中選擇加載遊戲,例如選擇"Test City",遊戲加載後會顯示與建築物相關的資產數量,比如有1945個。確認後關閉面板,即表示測試成功,之前所述的代碼運行正常。
通過循環遍歷所有建築物,使用for循環打印出它們的名稱。將這段代碼複製並保存,再次編譯,然後加載遊戲查看結果。你會看到關於建築物資產的內容,包括不同尺寸的建築物等,以及列出了所有建築相關資產的名稱,每個代表一個建築資產的模板或樣板。
代碼已經編譯並且測試完成,我們繼續看第四步驟。在第四步中,我們需要使用一個名為"Mod Tools"的工具,來查看每棟建築物內包含的詳細資產列表。如右側圖所示,它列出了建築物使用的所有資產,包括道具、裝飾等。因此,我們必須安裝"Mod Tools"這個工具。
可以點擊鏈接進入該工具的頁面並訂閱。在頁面底部,你可以看到該模組支持的版本,如1.13及以上。對於我們的遊戲版本而言,應該是兼容的。如果還沒有訂閱,可以點擊訂閱按鈕,系統會自動將其安裝到遊戲中。你也可以收藏該模組,以便在重新安裝遊戲後輕鬆找到它。
安裝好該模組後,我們回到教程中。現在,我們可以加載城市地圖,在建築物上點擊,並通過"EXPOWER"瀏覽器查看詳細資產列表。接下來,準備進入遊戲。在遊戲主菜單中,點擊"開始遊戲",確保啟用剛剛安裝的"Mod Tools"模組。然後進入內容管理,選擇模組,搜索並打開"Mod Tools",關閉內容管理面板,最後讀取遊戲,選擇有多個建築物的地圖。
進入遊戲後,關閉模組界面,選擇想要查看的建築物。選擇一個適合觀察的角度,使得能夠從不同視角查看建築物,如立體視圖,便於觀察和研究。
然後,我們左鍵點擊這棟建築物,就會發現一個彈出面板,這就是"Mod Tools"模組所提供的瀏覽器,能夠查看與該建築物相關的資產。在右下角有一個控制按鈕,點擊後可以拖動面板使其變大,以便更好地查看內容。接著,我們可以點擊"Building Info",根據教程中的提示,通常帶有"info"字樣表示為預設資產,類似於藍圖。
在"Building Info"面板中列出了許多關於該建築物的資產清單。重點查看廣告牌的位置。經過一番探索,根據教程指引,我們發現它位於"Unity Engine" -> "Material" -> "Building Info Plus Plus" 這個陣列中,裡面包含了該建築物所使用的道具,如露天陽臺的桌椅、花草等裝飾以及廣告招牌周圍的一系列物件,甚至停車場的標線也屬於其中。
點擊展開加號,顯示了相應道具的名稱,例如"Plant"可能代表植物,點擊"Preview",可以查看其模型網格預覽。關注點放在廣告招牌上,通常會帶有"BDBD"等字符,如"Billboard"。調節可見性為零時,就會隱藏起來,可以輕鬆定位到廣告招牌所在的位置,通常會在建築物的前方。進行適當調整後,即可找到並查看相關資產信息。
然後,第五個也是一個廣告招牌,我們同樣可以將其可見性調至零,這樣就會消失不見。通過這個例子,我們瞭解到當調整廣告招牌的"PABLIP"的可見度為零時,它就會隱藏起來,因此,將可見性調至零即可讓物體不可見。調至70的概率也較大能看見,在10時消失,而100則必定可見。
需要注意的是,我們在瀏覽器中進行的數值更改並不會寫入到遊戲存檔中,只是暫時性地讓我們看到這種變化。如果想在遊戲重新加載後移除該廣告招牌,我們需通過代碼實現。關於如何編寫這部分代碼,將在下個教程中詳細說明。
建議安裝並使用Mod Tools提供的場景瀏覽器,希望大家能在課後體驗一下,發現建築物內隱藏的豐富內容。城市:天際線這款遊戲之所以看起來如此逼真,實際上設計相當精妙,一個建築物內包含如此多元素,可以組合各種道具,呈現出不同的形態,增加了靈活性和創意。
通過這個過程,我有一個感覺,那就是當我們沒有使用這個Mod時,我們並不知道建築物內部竟然隱藏了這麼多可以查看的元素。因此,在玩這款遊戲時,如果要建立一個龐大的城市,我們的內存一定要足夠充足,否則可能會出現顯示問題或者程序崩潰等情況,因為實際上這些元素會消耗相當多的內存。
因此,對於這次關於場景瀏覽器的使用,建議大家親自體驗一下,這樣會更加有實感和體會。我們已經學會了如何消除廣告招牌,即找到廣告招牌的名稱,調整其屬性中的"PABLIP"將其可見性設置為零即可讓其消失。如果設置為100,則會顯示出來。通過這種方法來控制廣告招牌的顯示與隱藏。希望大家能理解這個步驟。
接下來,我們將回到Visual Studio開發工具來編寫代碼。首先,回顧教程,完成第五步,即使用編程方法來隱藏廣告招牌。我們需要將第三部分所編寫的面板顯示信息刪除,並替換為一個循環結構,遍歷所有道具資產。需要修改"OnLevelMod"方法,首先複製該方法,然後覆蓋修改。
在修改後的方法中,關鍵字"continue"用於控制循環,當遇到"continue"時,會忽略當前循環下面的內容,繼續進行下一個迭代。
當遇到關鍵字"continue"時,它將忽略下面的代碼並繼續執行下一個迭代,即查看下一個建築資產。因此,如果遇到"perfect"這個值等於"nor"或者空值,或者符合預設藍圖中"m_purpose"屬性為"mod"或"nor"的情況,都會跳過下面的代碼然後進行新的迭代。
相比使用雙等號來比較數值,我們可以使用"content"方法來檢測所有道具中包含特定名稱的道具,而不必等待篩選。我們可以隱藏所有道具中包含"BILIBER"這個詞的道具。通過將這些道具的"PABILITY"屬性設為零,即可將它們隱藏起來。如果編譯和執行成功,含有廣告招牌的建築物將被替換為沒有廣告招牌的建築物。我們可以嘗試一下。
在重新編譯後,我們發現建築物上的廣告招牌確實不見了。點擊查看該道具信息時,顯示"Building info problem"。這意味著成功隱藏了廣告招牌。這樣對於其他建築物也是同樣的操作,那些本來有廣告招牌的建築現在都看不到了,廣告招牌都已經清除掉了。
這個模組就是用來自動移除廣告招牌的。我們已經完成了編寫。希望大家覺得這個模組很有趣。通過這個模組開發教程,希望能讓大家對《都市天際線》內部運作邏輯有更深入的瞭解,慢慢學習細微的編程知識,然後按照自己的需求進行改造。本期的教程就到這裡結束了。