玩家在玩游戏的过程中,会获得或者消耗游戏内的资源,比如经验,金币,钻石,装备等各种游戏内定义的相关数据。并且任何模块都可能会触发获得或者消耗,比如领取宝箱,副本战斗消耗体力并且获得经验等。
如果各个地方都自己处理资源的消耗和获得,将是非常吃力不讨好的一件事,因此,一个统一的游戏资源处理器用来处理玩家对游戏资源的获得和消耗是很有必要的
游戏资源处理器
游戏中,资源相关的有如下操作:
1.判断玩家资源是否足够
2.玩家消耗指定资源
3.玩家获得指定资源
4.资源变动需要记录引起变动的模块
要操作资源,第一步是定义资源的结构,
游戏内资源可以按照资源类型分类,比如经验,金币,装备等属于不同类型的资源。而同一种相同的资源也存在不同的模型,比如装备中,可以有鞋子,头盔等
资源在数值表中到处都可能配置,因此需要约定一个字符串表示形式,比如约定如下格式:
资源类型1,模型id1,数量1||资源类型2,模型id2,数量2||资源类型3,模型id3,数量3
因此定义一个简单的资源结构如下:
public class GameResourceObject
{
/// <summary>
/// 游戏资源类型枚举
/// </summary>
public ResourceTypeSubEnum ResourceTypeSub{get;set;}
/// <summary>
/// 资源模型Id
/// </summary>
public Int32 ModelId{get;set;}
/// <summary>
/// 资源数量
/// </summary>
public Int32 Count{get;set;}
/// <summary>
/// 创建资源
/// </summary>
/// <param name="subType">类型</param>
/// <param name="count">数量</param>
public GameResourceObject(ResourceTypeSubEnum subType, Int32 count)
: this(subType, 0, count)
{
}
/// <summary>
/// 创建资源
/// </summary>
/// <param name="subType">类型</param>
/// <param name="modelId">模型id</param>
/// <param name="count">数量</param>
public GameResourceObject(ResourceTypeSubEnum subType, Int32 modelId, Int32 count)
{
this.ResourceTypeSub = subType;
this.ModelId = modelId;
this.Count = count;
}
}
资源类型定义:
public enum ResourceTypeSubEnum
{
/// <summary>
/// 经验
/// </summary>
Exp = 1,
/// <summary>
/// 金币
/// </summary>
Gold = 2,
}
根据上面的操作,我们可以抽象出如下接口:
interface IGameResource
{
/// <summary>
/// 资源类型
/// </summary>
ResourceTypeSubEnum ResourceType { get; }
/// <summary>
/// 检查资源是否足够
/// </summary>
/// <param name="player">玩家</param>
/// <param name="resource">资源</param>
/// <returns>状态码</returns>
ResultStatus IsEnough(Player player, GameResourceObject resource);
/// <summary>
/// 添加玩家资源
/// </summary>
/// <param name="player">玩家</param>
/// <param name="subModuleId">模块Id</param>
/// <param name="resources">添加的资源集合</param>
/// <returns>处理之后的资源集合</returns>
List<GameResourceObject> GetResources(Player player, Int32 subModuleId, List<GameResourceObject> resources);
/// <summary>
/// 消耗玩家资源
/// </summary>
/// <param name="player">玩家</param>
/// <param name="subModuleId">模块Id</param>
/// <param name="resources">消耗的资源集合</param>
void ConsumeResources(Player player, Int32 subModuleId, List<GameResourceObject> resources);
}
实现经验和金币的资源接口:
public class GoldImpl : IGameResource
{
public ResourceTypeSubEnum ResourceType => ResourceTypeSubEnum.Gold;
public void ConsumeResources(Player player, List<GameResourceObject> resources)
{
int sum = resources.Sum(s => s.Count);
player.Gold -= sum;
}
public void GetResources(Player player, List<GameResourceObject> resources)
{
int sum = resources.Sum(s => s.Count);
player.Gold += sum;
}
public ResultStatus IsEnough(Player player, GameResourceObject resource)
{
if (player.Gold < resource.Count)
{
return ResultStatus.PlayerGoldNoEnough;
}
return ResultStatus.Success;
}
}
public class ExpImpl : IGameResource
{
public ResourceTypeSubEnum ResourceType => ResourceTypeSubEnum.Exp;
public void ConsumeResources(Player player, List<GameResourceObject> resources)
{
int sum = resources.Sum(s => s.Count);
player.Exp -= sum;
}
public void GetResources(Player player, List<GameResourceObject> resources)
{
int sum = resources.Sum(s => s.Count);
player.Exp += sum;
}
public ResultStatus IsEnough(Player player, GameResourceObject resource)
{
return ResultStatus.ResourceTypeSubEnumNotUse;
}
}
准备工作做完,现在可以实现资源处理类,处理类有如下需要实现的:
1.加载实现的资源处理接口
2.提供判断资源是否足够的接口
3.消耗或者获得资源
实现如下:
public class GameResourceHandler
{
#region 字段
//类名称,用于错误标识
private const String mClassName = "GameResourceBLL";
private static Dictionary<ResourceTypeSubEnum, IGameResource> mResourceTypeImpl = new Dictionary<ResourceTypeSubEnum, IGameResource>();//资源处理集合
public const String StitchingSymbol = "||"; //拼接符号
#endregion
#region 初始化
static GameResourceHandler()
{
mResourceTypeImpl.Clear();
var types = Assembly.GetExecutingAssembly().GetTypes();
if (types == null || types.Count() == 0)
{
throw new Exception("没有任何资源实现。");
}
var itype = typeof(IGameResource);
foreach (var type in types)
{
Type[] allInterfaces = type.GetInterfaces();
if (allInterfaces.Any(a => a == itype))
{
var tempImpl = type.Assembly.CreateInstance(type.FullName) as IGameResource;
if (mResourceTypeImpl.ContainsKey(tempImpl.ResourceType))
{
throw new Exception(String.Format("ResourceType={0}的资源已经存在实现。", tempImpl.ResourceType.ToString()));
}
mResourceTypeImpl[tempImpl.ResourceType] = tempImpl;
}
}
}
#endregion
#region 公共方法
/// <summary>
/// 将资源配置字符串转化为游戏资源列表,格式为:
/// (GameResourceTypeSub,ModelID,Count||GameResourceTypeSub,ModelID,Count||GameResourceTypeSub,ModelID,Count)
/// </summary>
/// <param name="strGameResource">资源配置字符串</param>
/// <param name="isLock">是否锁定创建的资源</param>
/// <param name="multiple">倍数</param>
/// <returns>游戏资源列表</returns>
public static List<GameResourceObject> ConvertToGameResourceObject(String strGameResource)
{
List<GameResourceObject> result = new List<GameResourceObject>();
// 检查字符串不为空
if (String.IsNullOrEmpty(strGameResource))
{
throw new Exception("参数为空");
}
//将字符串切割
String[] strArray = StringUtil.Split(strGameResource, new String[] { ",", "||" });
if (strArray.Length % 3 != 0)
{
// 判断数量是否足够
throw new Exception(String.Format("参数{0}的格式不正确", strGameResource));
}
GameResourceObject gameResourceObject;
// 处理切割后的字符串
for (Int32 i = 0; i < strArray.Length; i += 3)
{
// 取出对应的值
Int32 resourceTypeSub = 0;
Int32 modelId = 0;
Int32 count = 0;
if (!ConvertUtil.TryParseToInt32(strArray[i], out resourceTypeSub)
|| !ConvertUtil.TryParseToInt32(strArray[i + 1], out modelId)
|| !ConvertUtil.TryParseToInt32(strArray[i + 2], out count))
{
throw new Exception(String.Format("参数{0}的格式不正确", strGameResource));
}
// 判断数量是否正确
if (count < 1)
{
throw new Exception(String.Format("数量:{0}不正确", count));
}
gameResourceObject = new GameResourceObject((ResourceTypeSubEnum)resourceTypeSub, modelId, count);
result.Add(gameResourceObject);
}
return result;
}
/// <summary>
/// 将资源集合转换为字符串表示形式
/// </summary>
/// <param name="resourceList">资源集合</param>
/// <param name="ifTriggerException">是否触发异常</param>
/// <returns>表示资源的字符串</returns>
public static String ConvertToString(ICollection<GameResourceObject> resourceList, Boolean ifTriggerException = true)
{
String sourceString = String.Empty;
if (resourceList == null || resourceList.Count == 0)
{
if (ifTriggerException)
{
throw new Exception("参与转换的资源集合不能为null或数量为0。");
}
return String.Empty;
}
else
{
foreach (var item in resourceList)
{
sourceString += String.Format("{0},{1},{2}||", (Int32)item.ResourceTypeSub, item.ModelId, item.Count);
}
}
return sourceString.TrimEnd('|');
}
/// <summary>
/// 判断资源是否足够
/// </summary>
/// <param name="player">玩家对象</param>
/// <param name="gameResourceList">游戏资源列表</param>
/// <returns>资源是否足够</returns>
public static ResultStatus CheckIfGameResourceEnough(Player player, IEnumerable<GameResourceObject> gameResourceList)
{
ResultStatus result = ResultStatus.Success;
//判断空数据
if (gameResourceList == null || gameResourceList.Count() == 0)
{
return result;
};
foreach (var item in gameResourceList)
{
result = GetResourceImpl(item).IsEnough(player, item);
if (result != ResultStatus.Success)
{
return result;
}
}
return result;
}
/// <summary>
/// 处理游戏资源的获取和消耗
/// </summary>
/// <param name="player">玩家对象</param>
/// <param name="getGameResourceList">获取的游戏资源列表</param>
/// <param name="consumeGameResourceList">消耗的游戏资源列表</param>
/// <param name="funcAfterResource">在处理完资源后需要执行的方法</param>
/// <returns>获得的资源</returns>
public static void HandleGameResource(Player player, IEnumerable<GameResourceObject> getGameResourceList, IEnumerable<GameResourceObject> consumeGameResourceList,
Action funcAfterResource)
{
//判断玩家资源是否足够
if (consumeGameResourceList != null && consumeGameResourceList.Count() > 0)
{
var result = CheckIfGameResourceEnough(player, consumeGameResourceList);
if (result != ResultStatus.Success)
{
throw new Exception(String.Format("玩家资源不足。ResultStatus={0}", result.ToString()));
}
}
//处理资源消耗
if (consumeGameResourceList != null && consumeGameResourceList.Count() > 0)
{
var typeDict = GetTypeDict(consumeGameResourceList);
foreach (var item in typeDict)
{
GetResourceImpl(item.Key).ConsumeResources(player, item.Value);
}
}
//处理资源获得
if (getGameResourceList != null && getGameResourceList.Count() > 0)
{
var typeDict = GetTypeDict(getGameResourceList);
foreach (var item in typeDict)
{
GetResourceImpl(item.Key).GetResources(player, item.Value);
}
}
funcAfterResource();
}
#endregion
#region private methods
/// <summary>
/// 根据资源类型对资源进行分类
/// </summary>
/// <param name="list">资源类型</param>
/// <returns>分类之后的资源类型</returns>
private static Dictionary<ResourceTypeSubEnum, List<GameResourceObject>> GetTypeDict(IEnumerable<GameResourceObject> list)
{
if (list == null || list.Count() == 0)
{
return new Dictionary<ResourceTypeSubEnum, List<GameResourceObject>>();
}
Dictionary<ResourceTypeSubEnum, List<GameResourceObject>> typeDict = new Dictionary<ResourceTypeSubEnum, List<GameResourceObject>>();
foreach (var item in list)
{
if (typeDict.ContainsKey(item.ResourceTypeSub) == false)
{
typeDict[item.ResourceTypeSub] = new List<GameResourceObject>();
}
typeDict[item.ResourceTypeSub].Add(item);
}
return typeDict;
}
/// <summary>
/// 获取资源处理对象
/// </summary>
/// <param name="type">资源类型</param>
/// <returns>资源对应的处理对象</returns>
private static IGameResource GetResourceImpl(ResourceTypeSubEnum type)
{
if (mResourceTypeImpl.ContainsKey(type) == false)
{
throw new Exception(String.Format("ResourceTypeSub={0}的资源未实现", type.ToString()));
}
return mResourceTypeImpl[type];
}
/// <summary>
/// 获取资源处理对象
/// </summary>
/// <param name="obj">资源对象</param>
/// <returns>资源对应的处理对象</returns>
private static IGameResource GetResourceImpl(GameResourceObject obj)
{
return GetResourceImpl(obj.ResourceTypeSub);
}
#endregion
}
以上我们就实现了一个简单的游戏内资源处理器,其他地方就可以很方面的调用处理相关资源了:
static void Main(string[] args)
{
List<GameResourceObject> tempList = new List<GameResourceObject>();
tempList.Add(new GameResourceObject(ResourceTypeSubEnum.Gold, 1));
Player p = new Player();
//判玩家资源是否足够
var res = GameResourceHandler.CheckIfGameResourceEnough(p, tempList);
Console.WriteLine($"判断资源是否足够:{res.ToString()}");
//获得玩家资源
GameResourceHandler.HandleGameResource(p, tempList, null, () =>
{
Console.WriteLine("玩家资源已经获得");
});
Console.WriteLine("p.Gold现在的值:" + p.Gold);
//判玩家资源是否足够
res = GameResourceHandler.CheckIfGameResourceEnough(p, tempList);
Console.WriteLine($"判断资源是否足够:{res.ToString()}");
//消耗玩家资源
//获得玩家资源
GameResourceHandler.HandleGameResource(p, null, tempList, () =>
{
Console.WriteLine("玩家资源已经消耗");
});
Console.WriteLine("p.Gold现在的值:" + p.Gold);
Console.ReadKey();
}
运行结果如下: