← SEARCHABLE ARCHIVE
C# · 2026-08-12

C# 從基礎到精通 — 第 10 講

將 C# 組織成可測試的遊戲結構

INTERACTIVE READING MODE →

該部落格上撰寫的文章是出於教學目的而創建的。 另外,文章中的資訊可能不正確,請謹慎。

了解大量語法和創建易於更改的程式之間是有區別的。 最後,讓我們將遊戲規則、保存和螢幕分開,以創建一個可以在小單元中驗證的結構。

將規則放入純物件中

public sealed class CombatService
{
    public int CalculateDamage(int attack, int defense)
    {
        return Math.Max(1, attack - defense);
    }

    public void Attack(Character attacker, Character target)
    {
        int damage = CalculateDamage(attacker.Attack, target.Defense);
        target.TakeDamage(damage);
    }
}

如果您的戰鬥規則不直接引用 UI 或引擎對象,您可以在控制台和測試中使用相同的程式碼。 螢幕只負責顯示結果和傳輸輸入。

外部函數放在介面後面

public interface ISaveStore
{
    Task SaveAsync(SaveData data, CancellationToken token);
    Task<SaveData?> LoadAsync(CancellationToken token);
}

public sealed class GameSession
{
    private readonly ISaveStore saveStore;

    public GameSession(ISaveStore saveStore)
    {
        this.saveStore = saveStore;
    }
}

透過將檔案或伺服器實作放在介面後面,您可以放置​​一個假實作來進行測試,而不是真正的儲存庫。 這不是隱藏功能的層,而是可能以不同方式更改的分隔程式碼的邊界。

測試運行

[Fact]
public void DamageNeverDropsBelowOne()
{
    var combat = new CombatService();

    int damage = combat.CalculateDamage(5, 100);

    Assert.Equal(1, damage);
}

此測試檢查外部可見的規則,而不是簡單地遵循私有方法的實作。 然後,您可以看到即使組織了內部結構,是否仍保持相同的操作。

設定引擎邊界

在Unity中,一個MonoBehaviour可以接收輸入和生命週期,而一個常規的C#物件可以分成負責規則。 將所有類別設為 MonoBehaviour 可以更輕鬆地連接到編輯器,但建立和測試與引擎相關。

一個好的結構並不是擁有大量的文件。 狀態擁有者、非同步操作的生命週期、事件取消訂閱和外部資源邊界在程式碼中結構清晰。

C# 的類型、介面、泛型、事件、LINQ 和非同步不是單獨的技術。 它是一種共同使用的工具,用於劃分可變部分並減少不正確的狀態。