「模组:创建 SMAPI 模组」修訂間的差異
小 (更改使用指南的页面链接) |
1059 Studio(對話 | 貢獻) 小 (→如何反编译游戏代码?) |
||
(未顯示由 3 位使用者於中間所作的 34 次修訂) | |||
第1行: | 第1行: | ||
{{模组:制作指南/header}} | {{模组:制作指南/header}} | ||
− | + | '''如果您是第一次制作模组''' 请参阅 [[模组:目录#创建模组]] 来了解 C# 模组和内容包模组之间差异的简短描述。 | |
− | + | 您想使用 C# 创建 “SMAPI 模组 ”吗 ? ”本 指南 适合您! | |
==介绍== | ==介绍== | ||
===什么是 SMAPI 模组?=== | ===什么是 SMAPI 模组?=== | ||
− | SMAPI 模组 | + | SMAPI 模组使用 [https://smapi.io/ SMAPI] 编辑 接口来扩展游戏逻辑。 当 游戏中发生某些 事 情时(例如 当一个 对象 被 放置在世界中 时 ) ,该模组可以做出响应 ,定期运行代码(例如每个更新周期 运行 一次),更改游戏的资源和数据等。SMAPI 模组 是 使用 .NET 的 C# 编写 的 , 游戏 使用 MonoGame 运行 逻辑(绘制到屏幕 、 用户输入等) 。 |
+ | |||
+ | .NET 的另一种语言 Visual Basic 也可以用于编写模组,如果您了解该语言的对应语法即可使用它。得益于 .NET 新版本的特性,从游戏版本 1.5.5 开始使用该语言编写模组也可以直接跨平台运行,社区中已经存在使用该语言的模组并且运行正常,也许您正在运行它们。本文中会提及一些代码示例 。 | ||
===为什么模组使用 SMAPI?=== | ===为什么模组使用 SMAPI?=== | ||
SMAPI 可以做许多事情,例如: | SMAPI 可以做许多事情,例如: | ||
− | + | # 将 你的 模组加载到游戏中。 如果 没有 SMAPI 来 加载代码模组 ,则不可能实现它们 。 | |
− | # 将模组加载到游戏中。没有 SMAPI | + | # 提供接口和事件,以 原本无法实现的 方式与游戏 进行交 互。 例如 游戏资源 或 数据 更改、 玩家配置 、翻译、 反 射 等 的简化接口 。这些内容将在指南后面介绍。 |
− | # 提供接口和事件, | + | # 加载时重写模组以实现 跨平台兼容 性。 这 让您而 不必担心游戏在 Linux/Mac/Windows 版本之间的差异。 |
− | # | + | # 重 写 模组 来 更新它 。SMAPI 可 检测并修复 常见情况下因 游戏更新 而损 坏的模组代码。 |
− | # 重 | + | # 拦截错误。如果模组崩溃或 导致 错误,SMAPI 将拦截错误,在控制台窗口中显示错误详细信息,并在大多数情况下自动恢复游戏。这意味着 模组不会意外导致 游戏崩溃,并且可以更轻松地 排除 错误。 |
− | # 拦截错误。如果模组崩溃或 | + | # 提供更新检查。当 模组 有新版本可用时,SMAPI 会自动提醒玩家。 |
− | # 提供更新检查。当有新版本可用时,SMAPI 会自动提醒玩家。 | + | # 提供兼容性检查。SMAPI 会自动检测模组何时不兼容,并在导致问题之前将其禁用, 这样玩家就不会遇到损坏的 游戏。 |
− | # 提供兼容性检查。SMAPI 会自动检测模组何时不兼容,并在 | ||
===我能制作一个模组吗?=== | ===我能制作一个模组吗?=== | ||
是的!本指南将帮助你逐步创建一个简单的模组。然后你可以继续学习,让它做您想做的事即可。 | 是的!本指南将帮助你逐步创建一个简单的模组。然后你可以继续学习,让它做您想做的事即可。 | ||
+ | |||
如果你是编程新手,许多模组开发人员开始时几乎没有或完全没有编程经验。如果你下定决心,当然可以沿途学习,但是您应该为陡峭的学习曲线做好准备。刚开始时不要太过于自信,弄清楚它的时候,最好从一个小的模组开始。一开始很容易变得不知所措并放弃。模组社区非常热情,所以不要害怕问问题! | 如果你是编程新手,许多模组开发人员开始时几乎没有或完全没有编程经验。如果你下定决心,当然可以沿途学习,但是您应该为陡峭的学习曲线做好准备。刚开始时不要太过于自信,弄清楚它的时候,最好从一个小的模组开始。一开始很容易变得不知所措并放弃。模组社区非常热情,所以不要害怕问问题! | ||
第27行: | 第29行: | ||
===我可以不使用 SMAPI 来制作模组吗?=== | ===我可以不使用 SMAPI 来制作模组吗?=== | ||
− | 当然。许多的 SMAPI 模组支持 [[模组:内容包|内容包]],可以让你提供它们所使用的 JSON 文本文件、图像等。例如,你可以 [[模组:Content Patcher|使用 Content Patcher]] 来编辑游戏的贴图并且不需要任何编程技术。本指南的其余部分是关于创建新的 SMAPI 模组的。有关内容包,请参阅 [[模组:Content Patcher]] (或模组的文档(如果为其 | + | 当然。许多的 SMAPI 模组支持 [[模组:内容包|内容包]],可以让你提供它们所使用的 JSON 文本文件、图像等。例如,你可以 [[模组:Content Patcher|使用 Content Patcher]] 来编辑游戏的贴图并且不需要任何编程技术。本指南的其余部分是关于创建新的 SMAPI 模组的。有关内容包,请参阅 [[模组:Content Patcher]] (或模组的文档(如果为其 他模组 创建内容包)). |
− | === | + | === 我在哪里可以得到帮助?=== |
− | <span id="help"></span> | + | <span id="help"></span> 星露谷官方聊天室欢迎你的到来:[https://stardewvalleywiki.com/Modding:Community#Discord Stardew Valley Discord] |
− | == | + | == 开始== |
− | === | + | === 学习 C#=== |
− | + | 由于模组是用 C # 编写的,因此最好先熟悉它。无需记住所有内容,但是掌握基础知识(例如字段、方法、变量和类)将使其他所有内容都变得更加容易。 | |
− | + | 一些有用的资源: | |
− | * [https://docs.microsoft.com/en-us/dotnet/csharp/quick-starts/ ''C# | + | * [https://docs.microsoft.com/en-us/dotnet/csharp/quick-starts/ ''C# 快速开始''] 通过交互式学习 C# 基础知识。 |
− | * [https://mva.microsoft.com/en-us/training-courses/c-fundamentals-for-absolute-beginners-16169 ''C# Fundamentals for Absolute Beginners''] | + | * [https://mva.microsoft.com/en-us/training-courses/c-fundamentals-for-absolute-beginners-16169 ''C# Fundamentals for Absolute Beginners''] 是一个视频指南,将引导您完成 C# ,从基本概念到事件驱动编程(这是 SMAPI 模组最常用的)。 |
+ | * 已经知道如何编程? 请访问 [https://learnxinyminutes.com/docs/csharp/ LearnXinYMinutes] 快速了解 C# 语法和概念。 | ||
− | + | SMAPI 经常使用的几个概念 | |
− | + | * [https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics '' 泛型''] | |
− | + | * [https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/''基于事件的编程''] | |
− | + | * 对于序列化,SMAPI 通常使用 [https://www.newtonsoft.com/json ''NewtonSoft''] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ===要求=== | |
+ | 在你开始之前: | ||
+ | # 熟悉 [[模组:使用指南/入门|模组的使用]],本指南的其余部分假定你已经熟悉使用模组。 | ||
+ | # 安装游戏 | ||
+ | # 安装 [[模组:使用指南/入门#安装SMAPI|SMAPI]] | ||
+ | # 安装开发环境 | ||
+ | #* 在 Linux 上:安装 [http://www.monodevelop.com/ MonoDevelop] 或 [https://www.jetbrains.com/rider/ Rider] | ||
+ | #* 在 Mac 上:安装 [https://visualstudio.microsoft.com/vs/mac/ Visual Studio for Mac](这是一个重新命名的 MonoDevelop ) | ||
+ | #* 在 Windows 上:安装 [https://visualstudio.microsoft.com/vs/community/ Visual Studio Community],当安装程序询问工作负载时,选择“.NET 桌面开发” | ||
+ | # 安装 [https://dotnet.microsoft.com/en-us/download/dotnet/6.0 NET 6.0 SDK] (x64版本,也可以直接在 Visual Studio Community 单个组件中选择) | ||
− | == | + | 如果不熟悉 Visual Studio(Windows/Mac)或者 MonoDevelop(Linux),在 [[模组:IDE 参考]] 页面解释了如何完成本指南所需的重要工作 |
− | === | + | |
− | + | == 创建一个基本模组== | |
− | {{collapse| | + | === 快速启动=== |
− | # | + | 如果您有足够的经验可以跳过本教程,以下是本节的摘要: |
− | # | + | {{collapse| 展开以查看快速开始|content=  |
− | # | + | # 创建一个 C #类库 项目(注意不要选择成 .NET Framework 的那个,或者你可以使用另一种语言) |
− | # | + | # 目标框架选择 .NET 6.0 |
− | # | + | # 添加 [https://smapi.io/package/readme <samp>Pathoschild.Stardew.ModBuildConfig</samp>] NuGet 包 |
− | # | + | # 创建一个 <samp>ModEntry</samp> 类,将它继承自 <samp>StardewModdingAPI.Mod</samp> |
− | # | + | # 覆写 <samp>Entry</samp> 方法,并使用 [[#Mod APIs|SMAPI 事件和 API]] 编写代码 |
+ | # 创建一个 [[#Add your manifest|<samp>manifest.json</samp>]] 文件来描述你的模组 | ||
+ | # 创建 [[# 发布你的模组| 一个包含模组文件的zip压缩包]] 来发布 | ||
}} | }} | ||
− | === | + | === 创建解决方案=== |
− | |||
− | + | SMAPI 模组是一个动态链接库(DLL),具有由 SMAPI 调用的入口方法 | |
− | |||
− | |||
− | |||
− | |||
− | === | + | # 打开 Visual Studio 或 MonoDevelop |
− | + | # 使用“类库”项目创建解决方案(请参阅[[模组:IDE 参考#创建项目|如何创建项目]])(不要选择“类库(.NET Framework)”!那是在游戏 1.5.4 以及之前版本中所使用的) | |
+ | # 目标框架设置为 .NET 6(请参阅 [[模组:IDE 参考#选择目标框架|如何更改目标框架]])您可能需要 [https://dotnet.microsoft.com/en-us/download/dotnet/6.0 安装 SDK]。这是游戏安装和使用的版本 | ||
+ | # 添加 [https://www.nuget.org/packages/Pathoschild.Stardew.ModBuildConfig <samp>Pathoschild.Stardew.ModBuildConfig</samp> NuGet 包](请参阅 [[模组:IDE 参考#添加 NuGet 包| 如何添加 NuGet 包]]) | ||
+ | #* 如果报错“找不到类型或命名空间名称“StardewModdingAPI””,则可能没有检测到游戏路径,需要将 GamePath 属性设置为游戏的目录。这可以通过将“GamePath”属性添加到项目文件(“.csproj”或“.vbproj”)中的“PropertyGroup”节点下来完成 | ||
+ | # 之后重新启动 Visual Studio 或者 MonoDevelop | ||
+ | |||
+ | === 添加代码=== | ||
+ | 接下来添加代码 | ||
<ol> | <ol> | ||
− | <li> | + | <li> 删除 <samp>Class1.cs</samp> 或者 <samp>MyClass.cs</samp> 文件 ( 参阅 [[ 模组:IDE 参考# 删除文件| 如何删除文件]]).</li> |
− | <li> | + | <li> 在项目中添加一个 C# 类文件,取名为 <samp>ModEntry.cs</samp> ( 参阅 [[ 模组:IDE 参考# 添加文件| 如何添加文件]]).</li> |
− | <li> | + | <li> 在此文件中输入代码 ( 把 <samp>YourProjectName</samp> 换成你的解决方案的名字): |
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
using System; | using System; | ||
第91行: | 第102行: | ||
namespace YourProjectName | namespace YourProjectName | ||
{ | { | ||
− | /// <summary> | + | /// <summary> 模组入口点</summary> |
public class ModEntry : Mod | public class ModEntry : Mod | ||
{ | { | ||
/********* | /********* | ||
− | ** | + | ** 公共方法 |
*********/ | *********/ | ||
− | /// <summary> | + | /// <summary> 模组的入口点,在首次加载模组后自动调用</summary> |
− | /// <param name="helper"> | + | /// <param name="helper"> 对象 helper 提供用于编写模组的简化接口</param> |
public override void Entry(IModHelper helper) | public override void Entry(IModHelper helper) | ||
{ | { | ||
helper.Events.Input.ButtonPressed += this.OnButtonPressed; | helper.Events.Input.ButtonPressed += this.OnButtonPressed; | ||
+ | //意思是将 OnButtonPressed 方法绑定到 SMAPI 的 ButtonPressed 按钮按下事件 | ||
+ | //this 表示本对象,也就是当前的 ModEntry 类 | ||
} | } | ||
− | |||
/********* | /********* | ||
− | ** | + | ** 私有方法 |
*********/ | *********/ | ||
− | /// <summary> | + | /// <summary> 在玩家按下键盘、控制器或鼠标上的按钮后引发</summary> |
− | /// <param name="sender"> | + | /// <param name="sender"> 对象 sender 表示调用此方法的对象</param> |
− | /// <param name="e"> | + | /// <param name="e"> 对象 e 表示事件数据</param> |
private void OnButtonPressed(object sender, ButtonPressedEventArgs e) | private void OnButtonPressed(object sender, ButtonPressedEventArgs e) | ||
{ | { | ||
− | // | + | // 如果玩家还没有进入存档,则取消执行 |
if (!Context.IsWorldReady) | if (!Context.IsWorldReady) | ||
return; | return; | ||
− | // | + | // 向控制台输出按下了什么按钮 |
− | this.Monitor.Log($"{Game1.player.Name} | + | this.Monitor.Log($"{Game1.player.Name} 按下了 {e.Button}.", LogLevel.Debug); |
} | } | ||
} | } | ||
第125行: | 第137行: | ||
</ol> | </ol> | ||
− | + | 以下是该代码的功能细分: | |
− | # <code>using X | + | # <code>using X</code> ( 参阅 [https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive using directive]) 使该命名空间中的类在你的代码中可用 |
− | # <code>namespace YourProjectName</code> | + | # <code>namespace YourProjectName</code> (参阅 [https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/namespace namespace keyword] )定义命名空间,入门时不必担心,因为在新建类文件时 Visual Studio 或 MonoDevelop 会自动添加它 |
− | # <code>public class ModEntry : Mod</code> ( | + | # <code>public class ModEntry : Mod</code> ( 参阅 [https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/class class keyword]) 创建你的模组的主类文件,并继承 SMAPI 的 <samp>Mod</samp> 类。SMAPI 将自动检测你的 <samp>Mod</samp> 子类,而 <samp>Mod</samp> 对象使你可访问 SMAPI 的接口 |
− | # <code>public override void Entry(IModHelper helper)</code> | + | # <code>public override void Entry(IModHelper helper)</code> 是将模组加载到游戏中时 SMAPI 将调用的方法。这里的 <code>helper</code> 对象提供了对许多 SMAPI 接口的便捷访问 |
− | # <code>helper.Events.Input.ButtonPressed += this.OnButtonPressed | + | # <code>helper.Events.Input.ButtonPressed += this.OnButtonPressed</code> 添加了一个事件绑定到当按下按钮的事件发生时。换句话说,当一个按钮被按下 ( 也就是 <samp>helper.Events.Input.ButtonPressed</samp> 事件触发了), SMAPI 会调用你的 <samp>this.OnButtonPressed</samp> 方法。参阅 [[ 模组: 使用指南/APIs/ 事件|SMAPI 中的事件]] 获取更多信息 |
− | === | + | 如果您使用的是 Visual Basic 语言,以下是使用该语言的 Entry 示例,实现了和上面的 C# 代码相同的步骤和功能 |
− | + | <ol><syntaxhighlight lang="vb"> | |
+ | 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 | ||
+ | </syntaxhighlight></ol> | ||
+ | |||
+ | === 添加你的清单=== | ||
+ | 模组的清单为 SMAPI 提供模组的信息 | ||
<ol> | <ol> | ||
− | <li> | + | <li> 向你的解决方案添加一个名为 <samp>manifest.json</samp> 的文本文件</li> |
− | <li> | + | <li> 将这些代码复制到文件中:(注意要区分键值的大小写,避免出现意外情况) |
<syntaxhighlight lang="json"> | <syntaxhighlight lang="json"> | ||
{ | { | ||
− | "Name": "< | + | "Name": "< 模组的名字>", |
− | "Author": "< | + | "Author": "< 你的名字>", |
"Version": "1.0.0", | "Version": "1.0.0", | ||
− | "Description": "< | + | "Description": "< 一句话简单描述你的模组是干什么的>", //(可选) |
− | "UniqueID": "< | + | "UniqueID": "< 你的名字>.< 模组的名字>", |
− | "EntryDll": "< | + | "EntryDll": "< 你的解决方案的名字>.dll", |
− | "MinimumApiVersion": " | + | "MinimumApiVersion": "4.0.0", |
− | "UpdateKeys": [] | + | "UpdateKeys": [] //(可选)常用的更新键:"Nexus:???", "Gtihub:user/repository", "Moddrop:???" |
} | } | ||
</syntaxhighlight></li> | </syntaxhighlight></li> | ||
− | <li> | + | <li> 正确填写其中的信息,不要留着空信息</li> |
</ol> | </ol> | ||
− | + | 当游戏启动时,它将在控制台中输出。更多信息请参阅 [[ 模组: 使用指南/APIs/ 清单|清单文档]] | |
+ | |||
+ | ===试试你的模组=== | ||
+ | # 构建项目。<br /><small>如果正确执行了“[[#创建项目| 创建项目]] ”步骤,这会自动将模组添加到游戏的 <samp>Mods</ samp> 文件夹。</small> | ||
+ | # 通过 SMAPI 运行游戏。 | ||
+ | |||
+ | 到目前为止,只要您在游戏中按下某个键时该模组就会向控制台窗口发送一条消息。 | ||
+ | |||
+ | '''更改控制台文本颜色''' | ||
+ | |||
+ | 控制台中默认文本颜色的设置方式可能会导致它们不可读。 要更改文本颜色: | ||
+ | |||
+ | *'''Steam''':打开 Steam 并右键单击库中的游戏。单击“管理”下的“浏览本地文件”,然后打开“/Stardew Valley/smapi-internal/”文件夹中的 <samp>config.json</samp> 文件,搜索“ConsoleColors”,然后编辑“Trace”和“Debug”颜色,以便它们在控制台中可见。 | ||
+ | |||
+ | *'''Linux''':linux上的默认路径是 <samp>~/.local/share/Steam/steamapps/common/Stardew Valley/smapi-internal/config.json</samp>,除非你已经安装 您的游戏位于不同的驱动器或者标准 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 的文档在哪里?=== | |
− | + | 这只是“入门”教程。更多信息请参阅 [[ 模组: 制作指南/APIs|SMAPI 接口说明]] | |
− | |||
− | |||
− | |||
− | == | + | === 我可以看看其他模组的代码吗?=== |
− | + | 是的,近 70% 的 SMAPI 模组都是开源的。要找到他们的代码: | |
− | |||
− | === | + | # 打开 [https://smapi.io/mods/ 模组兼容性页面] |
− | SMAPI | + | # 单击搜索框下方的“show advanced info” |
+ | # 在“code”列中查找链接 | ||
+ | |||
+ | === 我如何使我的模组跨平台工作?=== | ||
+ | SMAPI 将自动调整模组使其可以在 Linux 、MacOS 和 Windows 上运行。但是你也应该采取一些措施来避免出现问题: | ||
<ol> | <ol> | ||
− | <li> | + | <li> 使用 [https://smapi.io/package/readme 跨平台构建配置] 包以自动设置您的项目引用,这使跨平台兼容性变得更容易,并使代码可以在任何平台上进行编译。(如果遵循上述指南,那么就没问题了)</li> |
− | <li> | + | <li> 使用 <samp>Path.Combine</samp> 方法来拼接路径,而不是使用斜杠或者除号,因为不同的操作系统所使用的分隔符是不一样的 |
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
− | // ✘ | + | // ✘ 不要这样做!这在 Linux 和 Mac 上不会起作用 |
string path = this.Helper.DirectoryPath + "\assets\image.png"; | string path = this.Helper.DirectoryPath + "\assets\image.png"; | ||
− | // ✓ | + | // ✓ 这样才是对的 |
string path = Path.Combine(this.Helper.DirectoryPath, "assets", "image.png"); | string path = Path.Combine(this.Helper.DirectoryPath, "assets", "image.png"); | ||
</syntaxhighlight></li> | </syntaxhighlight></li> | ||
− | <li> | + | <li> 使用 <samp>this.Helper.DirectoryPath</samp> 来获取当前模组文件夹路径,请勿尝试自行确定模组路径 |
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
− | // ✘ | + | // ✘ 不要这样做!如果 SMAPI 重写了程序集(例如对其进行了更新或跨平台),它将崩溃 |
string modFolder = Assembly.GetCallingAssembly().Location; | string modFolder = Assembly.GetCallingAssembly().Location; | ||
− | // ✓ | + | // ✓ 这样就没问题 |
string modFolder = this.Helper.DirectoryPath; | string modFolder = this.Helper.DirectoryPath; | ||
</syntaxhighlight></li> | </syntaxhighlight></li> | ||
+ | |||
+ | <li>“资源名称”标识可以通过 <code><nowiki>Game1.content.Load<T>("asset name")</nowiki></code> 等内容 API 加载的资产。这“不是”文件路径,并且资源名称并不总是与文件路径匹配。比较资源名称时,请确保使用 <code>PathUtilities.NormalizeAssetName("some/path")</code> 而不是 path helpers | ||
+ | |||
+ | <syntaxhighlight lang="c#"> | ||
+ | // ✘ 不要这样做! 它不适用于 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")); | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 请注意,无需标准化传递给 SMAPI API 的资源名称,SMAPI API 会自动标准化它们(尽管这样做也没什么坏处): | ||
+ | <syntaxhighlight lang="c#"> | ||
+ | // ✓ 这样就可以了 | ||
+ | helper.Content.Load<Texture2D>("Characters/Abigail"); | ||
+ | helper.Content.Load<Texture2D>(@"Characters\Abigail"); | ||
+ | </syntaxhighlight> | ||
+ | </li> | ||
</ol> | </ol> | ||
− | === | + | === 如何反编译游戏代码?=== |
− | + | 观察游戏代码的工作方式通常对开发很有用。游戏的代码都编译在 <samp>StardewValley.dll</samp> 文件中,可以对其进行反编译以获得原始代码的近似可读性。(由于反编译和生成优化的问题,可能无法准确还原逻辑,但已经足够看到它在做什么。) | |
+ | |||
+ | 要反编译游戏代码: | ||
+ | |||
+ | :# 首次步骤: | ||
+ | :## 在 Windows 上安装 {{github|icsharpcode/ILSpy/releases|ILSpy}}(在 Release 的 Assets 下获取“ILSpy_binaries”文件),或在 Linux 和 macOS 上安装 [https://github.com/icsharpcode/AvaloniaILSpy/releases Avalonia ILSpy] | ||
+ | :## 打开 ILSpy | ||
+ | :## 单击“View > Options”,滚动到底部的“Other”部分,然后启用“Always qualify member references” | ||
+ | :# 在 ILSpy 中打开 <samp>Stardew Valley.dll</samp> | ||
+ | :# 确保在语言下拉列表中选择“C#”(不是 IL、IL with C# 或 ReadyToRun) | ||
+ | :# 右键单击“Stardew Valley”并选择“Save Code”以创建可以在 Visual Studio 中打开的反编译项目 | ||
+ | :## 如果您使用 Avalonia ILSpy,请确保将 <samp>.csproj</samp> 文件扩展名添加到保存对话框中的文件名中,如下所示:<samp>Stardew-Valley.csproj</samp> (否则该项目将无法正确反编译) | ||
+ | |||
+ | 另外有一个很不错的反编译工具但已经不再维护了:{{github|dnSpy/dnSpy/releases|dnSpy}} | ||
− | + | 要解包 XNB 数据或图像文件,请参阅 [[模组:编辑 XNB 文件]] | |
− | + | ===“目标 .NET 6.0”是什么意思?=== | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 这里有多种不同的东西 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | * 目标版本:这是编译二进制文件所针对的 .NET 版本,最新版本的游戏必须以 .NET 6.0 为目标 | |
+ | * SDK 版本:这是安装的 .NET 版本,可以定位低于 SDK 版本的任何版本。如果在 VS 2022 中安装了 .NET 7.0,仍然可以使用 .NET 6.0 | ||
+ | * C# 版本:语言的版本与 .NET 版本分开(尽管存在对应关系),可以在项目中使用 <code><langversion></code> 属性来指定语言版本 | ||
[[Category:模组]] | [[Category:模组]] | ||
第231行: | 第305行: | ||
[[fr:Modding:Guide du Moddeur/Commencer]] | [[fr:Modding:Guide du Moddeur/Commencer]] | ||
[[pt:Modificações:Guia do Modder/Começando]] | [[pt:Modificações:Guia do Modder/Começando]] | ||
+ | [[ru:Модификации:Моддер гайд/Приступая к работе]] |
於 2024年4月3日 (三) 09:52 的修訂
← 模組:目錄
如果您是第一次製作模組 請參閱 模組:目錄#創建模組 來了解 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
- 單擊「View > Options」,滾動到底部的「Other」部分,然後啟用「Always qualify member references」
- 在 ILSpy 中打開 Stardew Valley.dll
- 確保在語言下拉列表中選擇「C#」(不是 IL、IL with C# 或 ReadyToRun)
- 右鍵單擊「Stardew Valley」並選擇「Save Code」以創建可以在 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>
屬性來指定語言版本