模组:創建 SMAPI 模組
← 模組:目錄
如果您是第一次製作模組 請參閱 模組:目錄#創建模組 來了解 C# 模組和內容包模組之間差異的簡短描述。
您想使用 C# 創建「SMAPI 模組」嗎?」本指南適合您!
介紹
什麼是 SMAPI 模組?
SMAPI 模組使用 SMAPI 編輯接口來擴展遊戲邏輯。當遊戲中發生某些事情時(例如當一個對象被放置在世界中時),該模組可以做出響應,定期運行代碼(例如每個更新周期運行一次),更改遊戲的資源和數據等。SMAPI 模組是使用 .NET 的 C# 編寫的,遊戲使用 MonoGame 運行邏輯(繪製到屏幕、用戶輸入等)。
.NET 的另一種語言 Visual Basic 也可以用於編寫模組,如果您了解該語言的對應語法即可使用它。得益於 .NET 新版本的特性,從遊戲版本 1.5.5 開始使用該語言編寫模組也可以直接跨平台運行,社區中已經存在使用該語言的模組並且運行正常,也許您正在運行它們。本文中會提及一些代碼示例。
為什麼模組使用 SMAPI?
SMAPI 可以做許多事情,例如:
- 將你的模組加載到遊戲中。如果沒有 SMAPI 來加載代碼模組,則不可能實現它們。
- 提供接口和事件,以原本無法實現的方式與遊戲進行交互。例如遊戲資源或數據更改、玩家配置、翻譯、反射等的簡化接口。這些內容將在指南後面介紹。
- 加載時重寫模組以實現跨平台兼容性。這讓您而不必擔心遊戲在 Linux/Mac/Windows 版本之間的差異。
- 重寫模組來更新它。SMAPI 可檢測並修復常見情況下因遊戲更新而損壞的模組代碼。
- 攔截錯誤。如果模組崩潰或導致錯誤,SMAPI 將攔截錯誤,在控制台窗口中顯示錯誤詳細信息,並在大多數情況下自動恢復遊戲。這意味着模組不會意外導致遊戲崩潰,並且可以更輕鬆地排除錯誤。
- 提供更新檢查。當模組有新版本可用時,SMAPI 會自動提醒玩家。
- 提供兼容性檢查。SMAPI 會自動檢測模組何時不兼容,並在導致問題之前將其禁用,這樣玩家就不會遇到損壞的遊戲。
我能製作一個模組嗎?
是的!本指南將幫助你逐步創建一個簡單的模組。然後你可以繼續學習,讓它做您想做的事即可。
如果你是編程新手,許多模組開發人員開始時幾乎沒有或完全沒有編程經驗。如果你下定決心,當然可以沿途學習,但是您應該為陡峭的學習曲線做好準備。剛開始時不要太過於自信,弄清楚它的時候,最好從一個小的模組開始。一開始很容易變得不知所措並放棄。模組社區非常熱情,所以不要害怕問問題!
如果你已經具備編程經驗,那應該沒問題。具有 C# 或 Java 的編程經驗將使事情變得容易,但這並不重要。如果不熟悉 C#,則可以瀏覽下面的「學習C#」參考資料以填補所有空白。
我可以不使用 SMAPI 來製作模組嗎?
當然。許多的 SMAPI 模組支持 內容包,可以讓你提供它們所使用的 JSON 文本文件、圖像等。例如,你可以 使用 Content Patcher 來編輯遊戲的貼圖並且不需要任何編程技術。本指南的其餘部分是關於創建新的 SMAPI 模組的。有關內容包,請參閱 模組:Content Patcher (或模組的文檔(如果為其他模組創建內容包)).
我在哪裏可以得到幫助?
星露谷官方聊天室歡迎你的到來:Stardew Valley Discord
開始
學習 C#
由於模組是用 C# 編寫的,因此最好先熟悉它。無需記住所有內容,但是掌握基礎知識(例如字段、方法、變量和類)將使其他所有內容都變得更加容易。
一些有用的資源:
- C# 快速開始 通過交互式學習 C# 基礎知識。
- C# Fundamentals for Absolute Beginners 是一個視頻指南,將引導您完成 C#,從基本概念到事件驅動編程(這是 SMAPI 模組最常用的)。
- 已經知道如何編程? 請訪問 LearnXinYMinutes 快速了解 C# 語法和概念。
SMAPI 經常使用的幾個概念
- 泛型
- 基於事件的編程
- 對於序列化,SMAPI 通常使用 NewtonSoft
要求
在你開始之前:
- 熟悉 模組的使用,本指南的其餘部分假定你已經熟悉使用模組。
- 安裝遊戲
- 安裝 SMAPI
- 安裝開發環境
- 在 Linux 上:安裝 MonoDevelop 或 Rider
- 在 Mac 上:安裝 Visual Studio for Mac(這是一個重新命名的 MonoDevelop)
- 在 Windows 上:安裝 Visual Studio Community,當安裝程序詢問工作負載時,選擇「.NET 桌面開發」
- 安裝 NET 6.0 SDK(x64版本,也可以直接在 Visual Studio Community 單個組件中選擇)
如果不熟悉 Visual Studio(Windows/Mac)或者 MonoDevelop(Linux),在 模組:IDE 參考 頁面解釋了如何完成本指南所需的重要工作
創建一個基本模組
快速啟動
如果您有足夠的經驗可以跳過本教程,以下是本節的摘要:
展開以查看快速開始 |
---|
|
創建解決方案
SMAPI 模組是一個動態連結庫(DLL),具有由 SMAPI 調用的入口方法
- 打開 Visual Studio 或 MonoDevelop
- 使用「類庫」項目創建解決方案(請參閱如何創建項目)(不要選擇「類庫(.NET Framework)」!那是在遊戲 1.5.4 以及之前版本中所使用的)
- 目標框架設置為 .NET 6(請參閱 如何更改目標框架)您可能需要 安裝 SDK。這是遊戲安裝和使用的版本
- 添加 Pathoschild.Stardew.ModBuildConfig NuGet 包(請參閱 如何添加 NuGet 包)
- 如果報錯「找不到類型或命名空間名稱「StardewModdingAPI」」,則可能沒有檢測到遊戲路徑,需要將 GamePath 屬性設置為遊戲的目錄。這可以通過將「GamePath」屬性添加到項目文件(「.csproj」或「.vbproj」)中的「PropertyGroup」節點下來完成
- 之後重新啟動 Visual Studio 或者 MonoDevelop
添加代碼
接下來添加代碼
- 刪除 Class1.cs 或者 MyClass.cs 文件 (參閱 如何刪除文件).
- 在項目中添加一個 C# 類文件,取名為 ModEntry.cs (參閱 如何添加文件).
- 在此文件中輸入代碼 (把 YourProjectName 換成你的解決方案的名字):
using System; using Microsoft.Xna.Framework; using StardewModdingAPI; using StardewModdingAPI.Events; using StardewModdingAPI.Utilities; using StardewValley; namespace YourProjectName { /// <summary>模组入口点</summary> public class ModEntry : Mod { /********* ** 公共方法 *********/ /// <summary>模组的入口点,在首次加载模组后自动调用</summary> /// <param name="helper">对象 helper 提供用于编写模组的简化接口</param> public override void Entry(IModHelper helper) { helper.Events.Input.ButtonPressed += this.OnButtonPressed; //意思是将 OnButtonPressed 方法绑定到 SMAPI 的 ButtonPressed 按钮按下事件 //this 表示本对象,也就是当前的 ModEntry 类 } /********* ** 私有方法 *********/ /// <summary>在玩家按下键盘、控制器或鼠标上的按钮后引发</summary> /// <param name="sender">对象 sender 表示调用此方法的对象</param> /// <param name="e">对象 e 表示事件数据</param> private void OnButtonPressed(object sender, ButtonPressedEventArgs e) { // 如果玩家还没有进入存档,则取消执行 if (!Context.IsWorldReady) return; // 向控制台输出按下了什么按钮 this.Monitor.Log($"{Game1.player.Name} 按下了 {e.Button}.", LogLevel.Debug); } } }
以下是該代碼的功能細分:
using X
(參閱 using directive) 使該命名空間中的類在你的代碼中可用namespace YourProjectName
(參閱 namespace keyword)定義命名空間,入門時不必擔心,因為在新建類文件時 Visual Studio 或 MonoDevelop 會自動添加它public class ModEntry : Mod
(參閱 class keyword) 創建你的模組的主類文件,並繼承 SMAPI 的 Mod 類。SMAPI 將自動檢測你的 Mod 子類,而 Mod 對象使你可訪問 SMAPI 的接口public override void Entry(IModHelper helper)
是將模組加載到遊戲中時 SMAPI 將調用的方法。這裏的helper
對象提供了對許多 SMAPI 接口的便捷訪問helper.Events.Input.ButtonPressed += this.OnButtonPressed
添加了一個事件綁定到當按下按鈕的事件發生時。換句話說,當一個按鈕被按下 (也就是 helper.Events.Input.ButtonPressed 事件觸發了), SMAPI 會調用你的 this.OnButtonPressed 方法。參閱 SMAPI 中的事件 獲取更多信息
如果您使用的是 Visual Basic 語言,以下是使用該語言的 Entry 示例,實現了和上面的 C# 代碼相同的步驟和功能
Imports StardewModdingAPI
Imports StardewModdingAPI.Events
Imports StardewValley
Namespace YourProjectName
Public Class ModEntry
Inherits [Mod]
Public Overrides Sub Entry(helper As IModHelper)
AddHandler helper.Events.Input.ButtonPressed, AddressOf OnButtonPressed
End Sub
Public Sub OnButtonPressed(sender As Object, e As ButtonPressedEventArgs)
If Not Context.IsWorldReady Then Exit Sub
Monitor.Log($"{Game1.player.Name} 按下了 {e.Button}.", LogLevel.Debug)
End Sub
End Class
End Namespace
添加你的清單
模組的清單為 SMAPI 提供模組的信息
- 向你的解決方案添加一個名為 manifest.json 的文本文件
- 將這些代碼複製到文件中:(注意要區分鍵值的大小寫,避免出現意外情況)
{ "Name": "<模组的名字>", "Author": "<你的名字>", "Version": "1.0.0", "Description": "<一句话简单描述你的模组是干什么的>", //(可选) "UniqueID": "<你的名字>.<模组的名字>", "EntryDll": "<你的解决方案的名字>.dll", "MinimumApiVersion": "4.0.0", "UpdateKeys": [] //(可选)常用的更新键:"Nexus:???", "Gtihub:user/repository", "Moddrop:???" }
- 正確填寫其中的信息,不要留着空信息
當遊戲啟動時,它將在控制台中輸出。更多信息請參閱 清單文檔
試試你的模組
- 構建項目。
如果正確執行了「創建項目」步驟,這會自動將模組添加到遊戲的 Mods</ samp> 文件夾。 - 通過 SMAPI 運行遊戲。
到目前為止,只要您在遊戲中按下某個鍵時該模組就會向控制台窗口發送一條消息。
更改控制台文本顏色
控制台中默認文本顏色的設置方式可能會導致它們不可讀。 要更改文本顏色:
- Steam:打開 Steam 並右鍵單擊庫中的遊戲。單擊「管理」下的「瀏覽本地文件」,然後打開「/Stardew Valley/smapi-internal/」文件夾中的 config.json 文件,搜索「ConsoleColors」,然後編輯「Trace」和「Debug」顏色,以便它們在控制台中可見。
- Linux:linux上的默認路徑是 ~/.local/share/Steam/steamapps/common/Stardew Valley/smapi-internal/config.json,除非你已經安裝 您的遊戲位於不同的驅動器或者標準 Steam 庫文件夾之外。
- Windows:Windows 上的默認路徑應為「C:\Program files (x86)\Steam\steamapps\common\Stardew Valley\smapi-internal\config.json」,除非遊戲在不同的驅動器或 Steam 庫文件夾之外。
疑難解答
如果上述教程創建的模組不能正常運行:
- 重複查看以上步驟,以確保你沒有跳過任何內容
- 檢查是否有任何的錯誤消息,也許可以解釋為什麼它不起作用:
- 在 Visual Studio 中嘗試重新構建解決方案,查看 輸出 面板 或者 錯誤列表
- 在 MonoDevelop 中點擊 Build > Rebuild All 等待處理完畢。然後點擊 "Build: XX errors, XX warnings" 在頂部的條,點擊 XX Errors 和 Build Output 選項卡
- 參閱 疑難解答.
- 如果其他所有方法均失敗,請到 Stardew Valley Discord 尋求幫助 :)
常見問題
SMAPI 的文檔在哪裏?
這只是「入門」教程。更多信息請參閱 SMAPI 接口說明
我可以看看其他模組的代碼嗎?
是的,近 70% 的 SMAPI 模組都是開源的。要找到他們的代碼:
- 打開 模組兼容性頁面
- 單擊搜索框下方的「show advanced info」
- 在「code」列中查找連結
我如何使我的模組跨平台工作?
SMAPI 將自動調整模組使其可以在 Linux、MacOS 和 Windows 上運行。但是你也應該採取一些措施來避免出現問題:
- 使用 跨平台構建配置 包以自動設置您的項目引用,這使跨平台兼容性變得更容易,並使代碼可以在任何平台上進行編譯。(如果遵循上述指南,那麼就沒問題了)
- 使用 Path.Combine 方法來拼接路徑,而不是使用斜槓或者除號,因為不同的作業系統所使用的分隔符是不一樣的
// ✘ 不要这样做!这在 Linux 和 Mac 上不会起作用 string path = this.Helper.DirectoryPath + "\assets\image.png"; // ✓ 这样才是对的 string path = Path.Combine(this.Helper.DirectoryPath, "assets", "image.png");
- 使用 this.Helper.DirectoryPath 來獲取當前模組文件夾路徑,請勿嘗試自行確定模組路徑
// ✘ 不要这样做!如果 SMAPI 重写了程序集(例如对其进行了更新或跨平台),它将崩溃 string modFolder = Assembly.GetCallingAssembly().Location; // ✓ 这样就没问题 string modFolder = this.Helper.DirectoryPath;
- 「資源名稱」標識可以通過
Game1.content.Load<T>("asset name")
等內容 API 加載的資產。這「不是」文件路徑,並且資源名稱並不總是與文件路徑匹配。比較資源名稱時,請確保使用PathUtilities.NormalizeAssetName("some/path")
而不是 path helpers// ✘ 不要这样做! 它不适用于 Windows。 bool isAbigail = (asset.Name == Path.Combine("Characters", "Abigail")); bool isAbigail2 = (asset.Name == PathUtilities.NormalizePath("Characters", "Abigail")); // ✓ 这样就可以了 bool isAbigail = (asset.Name == PathUtilities.NormalizeAssetName("Characters", "Abigail"));
請注意,無需標準化傳遞給 SMAPI API 的資源名稱,SMAPI API 會自動標準化它們(儘管這樣做也沒什麼壞處):
// ✓ 这样就可以了 helper.Content.Load<Texture2D>("Characters/Abigail"); helper.Content.Load<Texture2D>(@"Characters\Abigail");
如何反編譯遊戲代碼?
觀察遊戲代碼的工作方式通常對開發很有用。遊戲的代碼都編譯在 StardewValley.dll 文件中,可以對其進行反編譯以獲得原始代碼的近似可讀性。(由於反編譯和生成優化的問題,可能無法準確還原邏輯,但已經足夠看到它在做什麼。)
要反編譯遊戲代碼:
- 首次步驟:
- 在 Windows 上安裝 ILSpy(在 Release 的 Assets 下獲取「ILSpy_binaries」文件),或在 Linux 和 macOS 上安裝 Avalonia ILSpy
- 打開 ILSpy
- 單擊「視圖 > 選項」,滾動到底部的「Other」部分,然後啟用「始終限定成員引用」
- 在 ILSpy 中打開 Stardew Valley.dll
- 確保在語言下拉列表中選擇「C#」(不是 IL、IL with C# 或 ReadyToRun)
- 右鍵單擊「Stardew Valley」並選擇「保存代碼」以創建可以在 Visual Studio 中打開的反編譯項目
- 如果您使用 Avalonia ILSpy,請確保將 .csproj 文件擴展名添加到保存對話框中的文件名中,如下所示:Stardew-Valley.csproj (否則該項目將無法正確反編譯)
- 首次步驟:
另外有一個很不錯的反編譯工具但已經不再維護了:dnSpy
要解包 XNB 數據或圖像文件,請參閱 模組:編輯 XNB 文件
「目標 .NET 6.0」是什麼意思?
這裏有多種不同的東西
- 目標版本:這是編譯二進制文件所針對的 .NET 版本,最新版本的遊戲必須以 .NET 6.0 為目標
- SDK 版本:這是安裝的 .NET 版本,可以定位低於 SDK 版本的任何版本。如果在 VS 2022 中安裝了 .NET 7.0,仍然可以使用 .NET 6.0
- C# 版本:語言的版本與 .NET 版本分開(儘管存在對應關係),可以在項目中使用
<langversion>
屬性來指定語言版本