在 RPG 遊戲中實現地圖
Flyweight 是結構設計模式之一。它用於通過與類似物件共享儘可能多的資料來減少已用記憶體量。本文件將教你如何正確使用 Flyweight DP。
讓我通過一個簡單的例子向你解釋它的想法。想象一下,你正在開發 RPG 遊戲,你需要載入包含一些角色的大檔案。例如:
#
是草。你可以走在上面。$
是起點@
是搖滾樂。你不能走路。%
是寶箱
地圖樣本:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@############@@@@@######@#$@@@
@#############@@@######@###@@@
@#######%######@###########@@@
@############################@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
由於這些物件具有類似的特徵,因此你無需為每個地圖欄位建立單獨的物件。我將告訴你如何使用 flyweight。
讓我們定義一個我們的欄位將實現的介面:
public interface IField
{
string Name { get; }
char Mark { get; }
bool CanWalk { get; }
FieldType Type { get; }
}
現在我們可以建立代表我們欄位的類。我們還必須以某種方式識別它們(我使用了列舉):
public enum FieldType
{
GRASS,
ROCK,
START,
CHEST
}
public class Grass : IField
{
public string Name { get { return "Grass"; } }
public char Mark { get { return '#'; } }
public bool CanWalk { get { return true; } }
public FieldType Type { get { return FieldType.GRASS; } }
}
public class StartingPoint : IField
{
public string Name { get { return "Starting Point"; } }
public char Mark { get { return '$'; } }
public bool CanWalk { get { return true; } }
public FieldType Type { get { return FieldType.START; } }
}
public class Rock : IField
{
public string Name { get { return "Rock"; } }
public char Mark { get { return '@'; } }
public bool CanWalk { get { return false; } }
public FieldType Type { get { return FieldType.ROCK; } }
}
public class TreasureChest : IField
{
public string Name { get { return "Treasure Chest"; } }
public char Mark { get { return '%'; } }
public bool CanWalk { get { return true; } } // you can approach it
public FieldType Type { get { return FieldType.CHEST; } }
}
就像我說的,我們不需要為每個欄位建立單獨的例項。我們必須建立一個欄位儲存庫。Flyweight DP 的本質是我們只在需要它時動態建立一個物件,它在我們的倉庫中不存在,或者如果它已經存在則返回它。讓我們編寫一個簡單的類來為我們處理這個:
public class FieldRepository
{
private List<IField> lstFields = new List<IField>();
private IField AddField(FieldType type)
{
IField f;
switch(type)
{
case FieldType.GRASS: f = new Grass(); break;
case FieldType.ROCK: f = new Rock(); break;
case FieldType.START: f = new StartingPoint(); break;
case FieldType.CHEST:
default: f = new TreasureChest(); break;
}
lstFields.Add(f); //add it to repository
Console.WriteLine("Created new instance of {0}", f.Name);
return f;
}
public IField GetField(FieldType type)
{
IField f = lstFields.Find(x => x.Type == type);
if (f != null) return f;
else return AddField(type);
}
}
大! 現在我們可以測試我們的程式碼:
public class Program
{
public static void Main(string[] args)
{
FieldRepository f = new FieldRepository();
IField grass = f.GetField(FieldType.GRASS);
grass = f.GetField(FieldType.ROCK);
grass = f.GetField(FieldType.GRASS);
}
}
控制檯中的結果應該是:
建立了一個新的 Grass 例項
建立了一個新的 Rock 例項
但是,如果我們想要兩次吃草,為什麼草只出現一次呢?那是因為我們第一次呼叫 GetField
草例項在我們的儲存庫中不存在,所以它被建立了,但是下次我們需要草時它已經存在,所以我們只返回它。