在 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
草实例在我们的存储库中不存在,所以它被创建了,但是下次我们需要草时它已经存在,所以我们只返回它。