C# 從基礎到精通 — 第 10 講
將 C# 組織成可測試的遊戲結構
該部落格上撰寫的文章是出於教學目的而創建的。 另外,文章中的資訊可能不正確,請謹慎。
了解大量語法和創建易於更改的程式之間是有區別的。 最後,讓我們將遊戲規則、保存和螢幕分開,以創建一個可以在小單元中驗證的結構。
將規則放入純物件中
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 和非同步不是單獨的技術。 它是一種共同使用的工具,用於劃分可變部分並減少不正確的狀態。