ระดับกลาง40 นาทีบทที่ 4

สร้าง 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)
สารบัญ

ในบทนี้เราจะสร้างมอดที่เขียนข้อความลงใน console ของ SMAPI เมื่อโหลดมอด และเมื่อผู้เล่นกดปุ่ม เป็นโครงสร้างพื้นฐานที่มอด C# ทุกตัวใช้

ภาพรวมของโปรเจกต์

MyFirstMod/
  • 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);
        }
    }
}

อธิบายทีละส่วน

  1. ModEntry : Mod SMAPI หา class ที่สืบทอดจาก Mod ใน DLL ของเรา แล้วสร้าง object ขึ้นมาเอง
  2. Entry(IModHelper helper) เมธอดแรกที่ทำงาน helper เป็นทางเข้าสู่ API ของ SMAPI เช่น helper.Events, helper.ReadConfig และ helper.ModContent
  3. this.Monitor.Log(...) เขียนข้อความลง console และไฟล์ log ระดับ (LogLevel) มี Trace, Debug, Info, Warn, Error และ Alert
  4. ButtonPressed += ... สมัครรับ event การกดปุ่ม
  5. 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 แล้ว พร้อมอัปโหลดให้คนอื่นใช้ อย่านำโฟลเดอร์ซอร์สโค้ดไปแจกแทน เพราะผู้เล่นใช้งานซอร์สโค้ดโดยตรงไม่ได้

เอกสารอ้างอิง

ค้นหาใน SVMT

กรองตามประเภท

พิมพ์คำที่ต้องการค้นหา (ภาษาไทยหรืออังกฤษ)