พื้นฐาน C# ที่จำเป็นสำหรับการเขียนมอด Stardew Valley
สรุปพื้นฐานภาษา C# ที่ต้องใช้เขียนมอด SMAPI ทั้งตัวแปร เงื่อนไข เมธอด class, namespace, inheritance และ event พร้อมตัวอย่างที่เชื่อมกับโค้ดมอดจริง
เขียนโดย Pokpong Limpaphan · อัปเดต
สถานะการตรวจสอบ: ตรวจกับเอกสารทางการแล้ว
เนื้อหาตรวจสอบกับเอกสารทางการแล้ว แต่ยังไม่ได้ทดสอบในเกมจริงทุกขั้นตอน (ตรวจล่าสุด )
ตรวจกับ: เอกสารภาษา C# ของ Microsoft
ตัวอย่างโค้ดเป็น C# มาตรฐาน ยังไม่ได้คอมไพล์ในระบบที่ใช้เขียนบทความ แนะนำให้ลองพิมพ์และรันด้วยตัวเอง
สิ่งที่จะได้เรียนรู้
- อ่านและเข้าใจโครงสร้างของไฟล์ ModEntry.cs
- ใช้ตัวแปร ชนิดข้อมูล if และ method ได้
- เข้าใจ class, inheritance (override) และการสมัคร event ด้วย +=
- จัดการค่า null อย่างปลอดภัย
สิ่งที่ต้องมีก่อนเริ่ม
- อ่านบท "Stardew Valley Modding คืออะไร" แล้ว
- ติดตั้ง .NET SDK ตามบท "ติดตั้งเครื่องมือบน PC" (ถ้าต้องการลองรันโค้ด)
สารบัญ
บทนี้ไม่ได้สอน C# ทั้งภาษา แต่เลือกเฉพาะเรื่องที่เจอแน่นอนเมื่อเขียนมอด SMAPI แต่ละหัวข้อมีตัวอย่างที่โยงกลับไปยังโค้ดมอดจริง เพื่อให้เห็นว่าความรู้นั้นใช้ตรงไหน
1. ตัวแปรและชนิดข้อมูล
ตัวแปร (variable) คือกล่องเก็บค่า C# เป็นภาษาที่ต้องรู้ชนิดของค่าเสมอ
int gold = 500; // จำนวนเต็ม
double speed = 1.5; // ทศนิยม
bool isRaining = true; // จริง/เท็จ
string farmerName = "Somchai"; // ข้อความ
var dayOfMonth = 12; // ให้ C# เดาชนิดเอง (ในที่นี้คือ int)
ในมอด เราอ่านค่าจากเกมมาเก็บในตัวแปรบ่อยมาก เช่นเงินของผู้เล่นหรือวันที่ในเกม
2. ต่อข้อความด้วย string interpolation
ใส่ $ หน้าเครื่องหมายคำพูด แล้วใส่ตัวแปรใน { } ได้เลย
string message = $"{farmerName} มีเงิน {gold}g";
รูปแบบนี้เจอบ่อยมากเวลาเขียน log เช่น this.Monitor.Log($"Pressed {e.Button}.", LogLevel.Debug);
3. เงื่อนไข if และ return ก่อนกำหนด
if (gold >= 1000)
{
// ทำงานเมื่อเงื่อนไขเป็นจริง
}
else
{
// ทำงานเมื่อเงื่อนไขเป็นเท็จ
}
ในมอดนิยมเขียนแบบ “ตรวจแล้วออกก่อน” (early return) เพื่อลดการซ้อน if หลายชั้น
if (!Context.IsWorldReady)
return; // ยังไม่ได้โหลดเซฟ ไม่ต้องทำอะไรต่อ
! แปลว่า “ไม่” ดังนั้นบรรทัดนี้อ่านว่า “ถ้าโลกของเกมยังไม่พร้อม ให้ออกจากเมธอด”
4. เมธอด (method)
เมธอดคือชุดคำสั่งที่ตั้งชื่อไว้ เรียกใช้ซ้ำได้
int AddBonus(int baseGold, int bonus)
{
return baseGold + bonus;
}
int total = AddBonus(500, 50); // total = 550
ส่วนประกอบของเมธอดคือ ชนิดของค่าที่ส่งกลับ (int) ชื่อ (AddBonus) และพารามิเตอร์ในวงเล็บ ถ้าไม่ส่งค่ากลับให้ใช้ void
5. class, object และ namespace
class คือแบบพิมพ์เขียวของข้อมูลและเมธอด object คือสิ่งที่สร้างจาก class นั้น
namespace YourName.MyFirstMod
{
internal class Crop
{
public string Name;
public int DaysToGrow;
public Crop(string name, int daysToGrow)
{
this.Name = name;
this.DaysToGrow = daysToGrow;
}
}
}
namespaceใช้จัดกลุ่มโค้ดและกันชื่อซ้ำกับโค้ดของคนอื่น ในมอดนิยมตั้งตามรูปแบบชื่อผู้สร้าง.ชื่อมอดthisหมายถึง object ปัจจุบันpublicให้โค้ดอื่นเข้าถึงได้ ส่วนprivateและinternalจำกัดการเข้าถึง
ด้านบนสุดของไฟล์มักมีบรรทัด using ซึ่งบอกว่าจะใช้ namespace ไหนบ้าง เช่น
using StardewModdingAPI; // API หลักของ SMAPI
using StardewModdingAPI.Events; // event ต่างประเภท
using StardewValley; // โค้ดของตัวเกม
6. Inheritance และ override
ทุกมอด SMAPI มี class ที่ สืบทอด (inherit) จาก class Mod ของ SMAPI
internal sealed class ModEntry : Mod
{
public override void Entry(IModHelper helper)
{
// SMAPI เรียกเมธอดนี้ตอนโหลดมอด
}
}
: Modแปลว่าModEntryได้ความสามารถทั้งหมดของModมา เช่นthis.Monitorสำหรับเขียน log และthis.Helperoverrideแปลว่าเรากำหนดการทำงานของเมธอดEntryเอง แทนของเดิมใน class แม่sealedแปลว่าไม่มี class อื่นสืบทอดจากModEntryต่อได้
classDiagram
class Mod {
+IModHelper Helper
+IMonitor Monitor
+IManifest ModManifest
+Entry(IModHelper helper)*
}
class ModEntry {
+Entry(IModHelper helper)
-OnButtonPressed(sender, e)
}
Mod <|-- ModEntry : สืบทอด
7. Event และ +=
เกมเกิดเหตุการณ์ (event) ตลอดเวลา เช่น เริ่มวันใหม่ ผู้เล่นกดปุ่ม หรือโหลดเซฟ SMAPI ให้เรา “สมัครรับ” event ได้ด้วย +=
public override void Entry(IModHelper helper)
{
helper.Events.GameLoop.DayStarted += this.OnDayStarted;
}
private void OnDayStarted(object? sender, DayStartedEventArgs e)
{
this.Monitor.Log("A new day started!", LogLevel.Info);
}
อ่านบรรทัดแรกได้ว่า “เมื่อเกิด event DayStarted ให้เรียกเมธอด OnDayStarted” ส่วนเมธอดที่ถูกเรียก (event handler) ต้องรับพารามิเตอร์สองตัวตามที่ SMAPI กำหนด
senderคือผู้ส่ง event (ปกติไม่ได้ใช้)eคือข้อมูลของ event นั้น เช่นButtonPressedEventArgsมีe.Buttonบอกว่ากดปุ่มอะไร
ดูรายชื่อ event ทั้งหมดได้ที่ SMAPI Events
8. null และเครื่องหมาย ?
null แปลว่า “ไม่มีค่า” ถ้าเรียกใช้ค่าที่เป็น null จะเกิด NullReferenceException ซึ่งเป็น error ที่พบบ่อยที่สุดในมอด
string? itemName = null; // ? หลังชนิดข้อมูล แปลว่าค่านี้อาจเป็น null ได้
int length = itemName?.Length ?? 0;
// ?. ถ้า itemName เป็น null จะไม่เรียก .Length และให้ผลเป็น null แทน
// ?? ถ้าค่าทางซ้ายเป็น null ให้ใช้ค่าทางขวา (0)
ในเกม ค่าหลายอย่างเป็น null ได้ในบางสถานการณ์ เช่น ไอเท็มที่ผู้เล่นถืออยู่ (Game1.player.CurrentItem) จะเป็น null เมื่อมือว่าง จึงควรตรวจก่อนใช้เสมอ
if (Game1.player.CurrentItem is null)
return;
9. List และ foreach
var crops = new List<string> { "Parsnip", "Potato", "Cauliflower" };
foreach (string crop in crops)
{
Console.WriteLine(crop);
}
List<string> คือรายการของข้อความ ใช้ using System.Collections.Generic; (ในโปรเจกต์ console ของ .NET รุ่นใหม่ บรรทัดนี้ถูกเพิ่มให้อัตโนมัติผ่าน implicit usings)
ลองอ่านโค้ดมอดจริง
ลองอ่านโค้ดนี้แล้วอธิบายแต่ละบรรทัดด้วยความรู้จากบทนี้
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
namespace YourName.MyFirstMod
{
internal sealed class ModEntry : Mod
{
public override void Entry(IModHelper helper)
{
helper.Events.Input.ButtonPressed += this.OnButtonPressed;
}
private void OnButtonPressed(object? sender, ButtonPressedEventArgs e)
{
if (!Context.IsWorldReady)
return;
this.Monitor.Log($"{Game1.player.Name} pressed {e.Button}.", LogLevel.Debug);
}
}
}
คำตอบโดยย่อ: มอดนี้สมัครรับ event การกดปุ่ม เมื่อผู้เล่นกดปุ่มหลังจากโหลดเซฟแล้ว จะเขียนชื่อผู้เล่นและปุ่มที่กดลงใน console ของ SMAPI
แก้ปัญหาที่พบบ่อย
| ข้อความ error | สาเหตุที่พบบ่อย |
|---|---|
CS0246: The type or namespace name 'X' could not be found |
ลืม using หรือยังไม่ได้ติดตั้งแพ็กเกจ ModBuildConfig |
CS1002: ; expected |
ลืมเครื่องหมาย ; ท้ายคำสั่ง |
CS0103: The name 'X' does not exist in the current context |
สะกดชื่อตัวแปรผิด หรือประกาศไว้นอกขอบเขต { } |
NullReferenceException (ตอนเล่นเกม) |
ใช้ค่าที่เป็น null โดยไม่ได้ตรวจ |
ขั้นต่อไป
พร้อมแล้วไปสร้างมอดจริงตัวแรกใน SMAPI Hello World หรือสร้างโปรเจกต์ด้วย Mod Generator แล้วเปิด ModEntry.cs อ่านประกอบ
เอกสารอ้างอิง
- C# documentation (Microsoft Learn)(เอกสารทางการ)
- A tour of the C# language (Microsoft Learn)(เอกสารทางการ)
- Modder Guide - Get Started(เอกสารทางการ)