本篇三部分:
道具分类
道具系统的接口设计(C/S)
道具系统的组成(各种小方法)
配置表,协议,数据库
//Array:打开宝箱获得多种道具
tables同目录下:Excel2Json.cmd生成表格
Common.Data放配置表字段
namespace Common.Data
{public enum ItemFunction{RecoverHP,RecoverMP,AddBuff,AddExp,AddMoney,AddItem,AddSkillPoint,}public class ItemDefine{public int ID { get; set; }public string Name { get; set; } public string Description { get; set; }//未来会将某种道具通过网络发到客户端中,把类型的定义放在协议中public ItemType Type { get; set; }public string Category { get; set; }public bool CanUse { get; set; } public float UseCD { get; set; } public int Price { get; set; } public int SellPrice { get; set; } public ItemFunction Function { get; set; }public int Param { get; set; } public List<int> Params { get; set; } }
}
协议;
//若需要同步信息,可以通过NCharacterInfo一次同步多种信息给服务端
点击 genproto.cmd生成协议
在GameServer:Entities中加道具表TCharacterItem
建立关联
根据模型生成数据库
是是是
并执行sql文件:选择对应得数据库和true
//重新生成数据库模型和执行sql文件数据库中得数据会被清空
GameServer:Item.cs
构造:传入基本参数
增加,删除,使用,显示基本字段
class Item
{TCharacterItem characterItem;public int ItemID;public int Count;public Item(TCharacterItem characterItem){this.characterItem = characterItem;this.ItemID=(short)characterItem.ItemID;this.Count=(short)characterItem.ItemCount;}public void Add(int count){this.Count += count;characterItem.ItemCount=this.Count;}public void Remove(int count){this.Count-=count;characterItem.ItemCount = this.Count;}public bool Use(int count=1){//return false;}public override string ToString(){//重载简化输出return string.Format("ID:{0},Count:{1}",this.ItemID,this.Count);}
}
GameServer:ItemManager.cs
获取角色身上所有的道具
使用道具
判断道具是否存在
获取某个道具
增加道具
删除道具
把内存数据转到网络数据
class ItemManager
{Character Owner;//字典维护角色身上的所有道具public Dictionary<int,Item> Items=new Dictionary<int, Item>(); public ItemManager(Character owner){this.Owner = owner;foreach(var item in owner.Data.Items){//取得character上的所有道具this.Items.Add(item.ItemID, new Item(item));}}public bool UserItem(int itemId,int count=1){//使用道具Log.InfoFormat("[{0}] UseItem[{1}:{2}]", this.Owner.Data.ID, itemId, count);Item item = null;//判断有无这个道具,再取出if(this.Items.TryGetValue(itemId, out item)){if (item.Count < count)return false;item.Remove(count);return true; }//没有道具return false; }public bool HasItem(int itemId){//判断道具是否存在Item item = null;if(this.Items.TryGetValue(itemId,out item))return item.Count > 0;return false; }public Item GetItem(int itemId){//获取道具Item item = null;this.Items.TryGetValue(itemId, out item);Log.InfoFormat("[{0}]GetItem[{1}:{2}]", this.Owner.Data.ID, itemId, item);return item; }public bool AddItem(int itemId,int count){//增加道具,若列表中没有,要新建Item item = null;if(this.Items.TryGetValue(itemId,out item))item.Add(count);else{//在DB中插入新数据TCharacterItem characterItem = new TCharacterItem();characterItem.CharacterID=Owner.Data.ID;characterItem.Owner = Owner.Data;characterItem.ItemID = itemId;characterItem.ItemCount = count;Owner.Data.Items.Add(characterItem);//插进字典item=new Item(characterItem);this.Items.Add(itemId, item);}Log.InfoFormat("[{0}]AddItem[{1}] addCount:{2}",this.Owner.Data.ID,item,count);DBService.Instance.Save();return true; }public bool RemoveItem(int itemId,int count){if (!this.Items.ContainsKey(itemId)) return false;Item item = this.Items[itemId];if (item.Count < count) return false;item.Remove(count);Log.InfoFormat("[{0}]RemoveItem[{1}] removeCount:{2}",this.Owner.Data.ID,item,count);DBService.Instance.Save();return true; }public void GetItemInfos(List<NItemInfo> list){//在协议中有NItemInfo;这里为了把内存数据转为网络数据foreach (var item in this.Items)list.Add(new NItemInfo() { Id = item.Value.ItemID, Count = item.Value.Count });}
}
DService.save
//回档float time = 0;//每隔一定时间 就自动保存public void Save(){//异步保存:函数会立刻返回;游戏逻辑和服务器的调用是同时进行的//if (DateTime.Now.Ticks - time > 10f)//{//这样一次save会间隔至少十秒// entities.SaveChangesAsync();// time = DateTime.Now.Ticks;//}entities.SaveChangesAsync(); }
给角色加上道具管理器:
加上这两句:
//为这个角色构建道具管理器:
this.ItemManager=new ItemManager(this);
//把网络数据填充上
this.ItemManager.GetItemInfos(this.Info.Items);
OnGameEnter->AddCharacter->Character()->Character中的构造方法
在OnGameEnter中,我们要填充 完整UserGameEnterResponse协议(在前面新加了一个字段NCharacterInfo)
加上这一句
此时如果身上有道具,登录游戏后,客户端可以通过这一句收到数据
测试用例
在OnGameEnter的UserGameEnterResponse协议填充完毕之后,添加测试类
//道具系统测试
int itemId = 1;
bool hasItem=character.ItemManager.HasItem(itemId);
Log.InfoFormat("hasItem:[{0}]{1}", itemId, hasItem);
if (hasItem)character.ItemManager.RemoveItem(itemId, 1);//删除1个1号道具
else character.ItemManager.AddItem(itemId, 2);//加两个
//取得道具
Models.Item item=character.ItemManager.GetItem(itemId);Log.InfoFormat("Item:[{0}][{1}]",itemId, item);
结果:
退出再选同一个角色进入:
把协议考到客户端
Client:Item.cs
public class Item
{public int Id;public int Count;public Item(NItemInfo nItem){//客户端不接触DB,使用得是网络协议中得itemthis.Id = nItem.Id;this.Count = nItem.Count;}public override string ToString(){return string.Format(" Id:{0},Count:{1} ",this.Id,this.Count);}
}
ItemManager.cs
填充items
public class ItemManager:Singleton<ItemManager>
{//客户端每个人管理自己得道具;就不需要先拿到角色,直接ItemManager.Instancepublic Dictionary<int,Item>Items=new Dictionary<int, Item>(); internal void Init(List<NItemInfo> items){//从网络协议填充过来this.Items.Clear();foreach (var iteminfo in items){Item item=new Item(iteminfo);this.Items.Add(item.Id, item);UnityEngine.Debug.LogFormat("ItemManager:Init[{0}]", item);}}//后面的逻辑是从服务端发送权限给客户端才能使用public ItemDefine GetItem(int itemId){return null;}public bool UseItem(int itemId){return false;}public bool UseItem(ItemDefine itemDefine){return false ;}
}
在UserService的OnGameEnter:
演示:
这里是服务端UserOnGameEnter先给客户端消息,再add,remove item操作
因此Item:[1] [ID:1,count:2]的结果在一次进入主城才能在日志上看到