สร้าง SMAPI Hello World Mod ตัวแรกด้วย C#
สอนสร้างมอด SMAPI ตัวแรกด้วย C# ทีละขั้น ตั้งแต่สร้างโปรเจกต์ net6.0 ติดตั้ง ModBuildConfig เขียน manifest.json และ ModEntry.cs ไปจนถึง build และทดสอบในเกม
เขียนโดย Pokpong Limpaphan · อัปเดต
สถานะการตรวจสอบ: ตรวจกับเอกสารทางการแล้ว
เนื้อหาตรวจสอบกับเอกสารทางการแล้ว แต่ยังไม่ได้ทดสอบในเกมจริงทุกขั้นตอน (ตรวจล่าสุด )
ตรวจกับ: SMAPI 4.5.2 (เอกสารและ release notes), Pathoschild.Stardew.ModBuildConfig 4.4.0, ตัวอย่าง ModEntry จาก Modder Guide ทางการ
โค้ดตรงกับตัวอย่างในเอกสารทางการ แต่ยังไม่ได้ build กับเกมจริงในระบบที่ใช้เขียนบทความ (ต้องมีไฟล์เกมจึงจะ build ได้) โปรดทดสอบและแจ้งผลกับเรา
สิ่งที่จะได้เรียนรู้
- สร้างโปรเจกต์มอด C# ที่ตั้งค่าถูกต้องสำหรับ SMAPI 4
- เขียน manifest.json และ ModEntry.cs ได้เอง
- build มอดและเห็นข้อความของมอดใน SMAPI console
- ใช้ event ButtonPressed ตอบสนองการกดปุ่มของผู้เล่น
สิ่งที่ต้องมีก่อนเริ่ม
- ติดตั้ง SMAPI, .NET SDK และ IDE ตามบท "ติดตั้งเครื่องมือบน PC"
- เข้าใจพื้นฐาน C# (class, method, event)
สารบัญ
- ภาพรวมของโปรเจกต์
- ขั้นที่ 1: สร้างโปรเจกต์ class library
- ขั้นที่ 2: ตั้งค่าไฟล์ .csproj
- ขั้นที่ 3: เขียน manifest.json
- ขั้นที่ 4: เขียน ModEntry.cs
- ขั้นที่ 5: build และทดสอบ
- ลองต่อยอด
- แก้ปัญหาที่พบบ่อย
- build ไม่ผ่าน: หาเกมไม่เจอ
- SMAPI ข้ามมอด: its DLL doesn’t exist
- SMAPI ข้ามมอด: manifest ไม่ถูกต้อง
- ไม่เห็นข้อความ pressed
- เกิด error ตอนเล่น
- แจกจ่ายมอด
ในบทนี้เราจะสร้างมอดที่เขียนข้อความลงใน console ของ SMAPI เมื่อโหลดมอด และเมื่อผู้เล่นกดปุ่ม เป็นโครงสร้างพื้นฐานที่มอด C# ทุกตัวใช้
ภาพรวมของโปรเจกต์
- MyFirstMod/
- manifest.json
- ModEntry.cs
- MyFirstMod.csproj
ขั้นที่ 1: สร้างโปรเจกต์ class library
มอด SMAPI เป็น class library (โปรเจกต์ที่ build ออกมาเป็น .dll) ไม่ใช่โปรแกรมที่รันเองได้
dotnet new classlib -n MyFirstMod
cd MyFirstMod
คำสั่งนี้สร้างไฟล์ Class1.cs มาให้ ลบทิ้งได้เลย เพราะเราจะสร้าง ModEntry.cs แทน
ใน Visual Studio ให้เลือกเทมเพลต Class Library ที่เป็น C# แล้วตั้งชื่อ MyFirstMod
ขั้นที่ 2: ตั้งค่าไฟล์ .csproj
เปิด MyFirstMod.csproj แล้วแก้ให้เป็นแบบนี้
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>MyFirstMod</AssemblyName>
<RootNamespace>YourName.MyFirstMod</RootNamespace>
<Version>1.0.0</Version>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Pathoschild.Stardew.ModBuildConfig" Version="4.4.0" />
</ItemGroup>
</Project>
จุดสำคัญ
TargetFrameworkต้องเป็นnet6.0ให้ตรงกับ runtime ของเกม (ดูเหตุผลในบท ติดตั้งเครื่องมือ)Pathoschild.Stardew.ModBuildConfigคือแพ็กเกจทางการที่ช่วยอ้างอิง DLL ของเกม คัดลอกมอดไปโฟลเดอร์Modsและสร้าง zip ให้อัตโนมัติ- เพิ่มแพ็กเกจด้วยคำสั่งก็ได้:
dotnet add package Pathoschild.Stardew.ModBuildConfig
ขั้นที่ 3: เขียน manifest.json
สร้างไฟล์ manifest.json ไว้ข้างไฟล์ .csproj
{
"Name": "My First Mod",
"Author": "YourName",
"Version": "1.0.0",
"Description": "My first SMAPI mod.",
"UniqueID": "YourName.MyFirstMod",
"EntryDll": "MyFirstMod.dll",
"MinimumApiVersion": "4.0.0",
"UpdateKeys": []
}
| Field | ความหมาย |
|---|---|
Name |
ชื่อที่แสดงให้ผู้เล่นเห็น (จำเป็น) |
Author |
ชื่อผู้สร้าง |
Version |
เวอร์ชันแบบ semantic version เช่น 1.0.0 (จำเป็น) |
UniqueID |
รหัสไม่ซ้ำ ใช้ได้เฉพาะ A-Z, 0-9, _, ., - (จำเป็น) |
EntryDll |
ชื่อไฟล์ .dll ที่ได้จากการ build ต้องตรงกับ AssemblyName |
MinimumApiVersion |
SMAPI เวอร์ชันต่ำสุดที่มอดต้องการ |
UpdateKeys |
ใช้ให้ SMAPI ตรวจหาเวอร์ชันใหม่ เช่น "Nexus:12345" ใส่ได้หลังอัปโหลดมอด |
ตรวจความถูกต้องได้ทันทีด้วย Manifest Validator
ขั้นที่ 4: เขียน ModEntry.cs
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
namespace YourName.MyFirstMod
{
/// <summary>จุดเริ่มต้นของมอด</summary>
internal sealed class ModEntry : Mod
{
/// <summary>SMAPI เรียกเมธอดนี้หนึ่งครั้งหลังโหลดมอด</summary>
public override void Entry(IModHelper helper)
{
this.Monitor.Log($"{this.ModManifest.Name} {this.ModManifest.Version} loaded!", LogLevel.Info);
helper.Events.Input.ButtonPressed += this.OnButtonPressed;
}
/// <summary>ทำงานเมื่อผู้เล่นกดปุ่มใดก็ได้</summary>
private void OnButtonPressed(object? sender, ButtonPressedEventArgs e)
{
// ข้ามถ้ายังไม่ได้โหลดเซฟ
if (!Context.IsWorldReady)
return;
this.Monitor.Log($"{Game1.player.Name} pressed {e.Button}.", LogLevel.Debug);
}
}
}
อธิบายทีละส่วน
ModEntry : ModSMAPI หา class ที่สืบทอดจากModใน DLL ของเรา แล้วสร้าง object ขึ้นมาเองEntry(IModHelper helper)เมธอดแรกที่ทำงานhelperเป็นทางเข้าสู่ API ของ SMAPI เช่นhelper.Events,helper.ReadConfigและhelper.ModContentthis.Monitor.Log(...)เขียนข้อความลง console และไฟล์ log ระดับ (LogLevel) มีTrace,Debug,Info,Warn,ErrorและAlertButtonPressed += ...สมัครรับ event การกดปุ่มContext.IsWorldReadyเป็นtrueเมื่อโหลดเซฟเสร็จแล้ว ถ้าเรียกGame1.playerก่อนหน้านั้นอาจเกิด error
ลำดับการทำงานตั้งแต่ SMAPI โหลดมอดจนถึงตอนผู้เล่นกดปุ่ม
sequenceDiagram
participant SMAPI
participant Mod as ModEntry
participant Game as เกม
SMAPI->>Mod: สร้าง ModEntry และเรียก Entry(helper)
Mod->>SMAPI: Monitor.Log("... loaded!")
Mod->>SMAPI: สมัครรับ Input.ButtonPressed
Game->>SMAPI: ผู้เล่นกดปุ่ม
SMAPI->>Mod: OnButtonPressed(sender, e)
alt โหลดเซฟแล้ว
Mod->>SMAPI: Monitor.Log("... pressed ...")
else ยังอยู่หน้าเมนู
Mod-->>Mod: return ไม่ทำอะไร
end
ขั้นที่ 5: build และทดสอบ
dotnet build
ถ้า build สำเร็จ ModBuildConfig จะคัดลอกไฟล์ไปที่ Stardew Valley/Mods/MyFirstMod/ ให้เอง จากนั้นเปิดเกมผ่าน SMAPI ควรเห็นข้อความแบบนี้ใน console
[12:00:05 INFO SMAPI] Loaded 1 mods:
[12:00:05 INFO SMAPI] My First Mod 1.0.0 by YourName | My first SMAPI mod.
...
[12:00:06 INFO My First Mod] My First Mod 1.0.0 loaded!
โหลดเซฟแล้วกดปุ่มอะไรก็ได้ จะเห็นข้อความ pressed ตามมา (ระดับ Debug แสดงใน console ตามการตั้งค่าเริ่มต้น และถูกบันทึกในไฟล์ log เสมอ)
ลองต่อยอด
- เปลี่ยน event เป็น
helper.Events.GameLoop.DayStartedเพื่อเขียน log ทุกเช้า - กรองเฉพาะปุ่มที่ต้องการ เช่น
if (e.Button != SButton.F5) return; - อ่านรายชื่อ event ทั้งหมดใน SMAPI Events
แก้ปัญหาที่พบบ่อย
build ไม่ผ่าน: หาเกมไม่เจอ
เพิ่ม <GamePath> ใน .csproj ตามที่อธิบายใน บทติดตั้งเครื่องมือ
SMAPI ข้ามมอด: its DLL doesn’t exist
ค่า EntryDll ใน manifest ไม่ตรงกับชื่อไฟล์ .dll ที่ build ได้ ให้ตรวจว่าตรงกับ AssemblyName ในไฟล์ .csproj
SMAPI ข้ามมอด: manifest ไม่ถูกต้อง
ตรวจ manifest.json ด้วย Manifest Validator ปัญหาที่พบบ่อยคือลืมเครื่องหมายจุลภาค หรือ UniqueID มีช่องว่าง
ไม่เห็นข้อความ pressed
ตรวจว่าโหลดเซฟแล้ว (ที่หน้าเมนูหลัก Context.IsWorldReady เป็น false) และเปิดไฟล์ SMAPI-latest.txt ดู เพราะข้อความระดับ Debug จะอยู่ในไฟล์เสมอ
เกิด error ตอนเล่น
นำไฟล์ log ไปตรวจใน Log Analyzer และอ่านบท Debug ด้วย SMAPI log
แจกจ่ายมอด
ไฟล์ zip ที่ ModBuildConfig สร้างไว้ในโฟลเดอร์ bin เป็นมอดที่ build แล้ว พร้อมอัปโหลดให้คนอื่นใช้ อย่านำโฟลเดอร์ซอร์สโค้ดไปแจกแทน เพราะผู้เล่นใช้งานซอร์สโค้ดโดยตรงไม่ได้
เอกสารอ้างอิง
- Modder Guide - Get Started(เอกสารทางการ)
- Manifest(เอกสารทางการ)
- Events(เอกสารทางการ)
- Logging(เอกสารทางการ)
- ModBuildConfig package(เอกสารทางการ)