首先制定实体类和传输协议。
-
#ShopDefine 商店类型实体
public class ShopDefine { public int ID{get;set;} // public stirng Name {get;set;} public string Icon {get;set;} public string Description {get;set;} public int Status {get;set;} }
-
#ShopItemDefine 商店里的物品实体
public class ShopItemDefine { public int ItemID{get;set;} public int Count {get;set;} public int Price {get;set;} public int Status{get;set;} }
-
传输协议:
message NCharacterInfo { int64 gold = 12; --往人物信息增加金钱 } --状态协议 enum STATUS_ACTION { UPDATE = 0; --数量更新 ADD = 1; --增加道具等 DELETE = 2; --删除道具等 } enum STATUS_TYPE --变动的类型 { MONEY = 0; EXP = 1; SKILL_POINT = 2; ITEM = 3; } enum STATUS_SOURCE { UPDATE = 0; ADD = 1; DELETE = 2; } message NStatus --状态的结构 { STATUS_TYPE type = 1; STATUS_ACTION action = 2; int32 Id = 3; int32 value = 4; } message StatusNotify --状态更新,里面是个数组 包含了所有更新。 { repeated NStatus status = 1; } message NetMessageRequest { ItemBuyRequest itemBuy = 10; --增加购买的请求 } message NetMessageResponse { ItemBuyResponse itemBuy = 10; --增加购买的服务器反馈 StatusNotify statusNotify = 100; --反馈的状态 } message ItemBuyRequest --增加购买的请求 { int32 shopId = 1; int32 shopItemId = 2; } message ItemBuyResponse --增加购买的回馈 { RESULT result = 1; string errormsg = 2; }
个人见解
构建UIShop管理商店页面,与背包系统类似,需要管理商店UI的标题Title,显示的金钱数money,需要根据导入的ShopItems并instantiate的自定义Prefab:UIShopItem,Tab页。
对于UIShop:
UIShop首先需要将DataManager的数据加载到本地类中,并且使用
Dictionary<int ShopID,Dictionary<int id,Item item>>保存,创建一个public方法,参数为ShopId,作用是每当玩家点击不同的商家时,加载该商家的数据,并且遍历指定商家的道具,创建UIShopItem。
玩家点击购买button后会遍历Tab页下的ShopItem,统计物品的购买数量。
对于UIShopItem:
有关于道具的各项属性,例如购买价格,鼠标移到物品上有物品的信息,有选定物品数量的按钮。
-
遇到问题1:
UISHOP里的数据信息何时才会更新。
类职能阐述:
客户端
UIShop:
掌管Shop商店UI,包括了初始化商店物品列表,商店的text信息,物品的点击事件等。
UIShopItem:
掌管Shop商店的单个物品信息,有初始化自身Text信息的public方法。自身被UIShop用做物品列表里的物品元素。响应点击事件,当发生点击时,将自身传回UIShop的点击方法中,让UIShop做处理。
UIShopManager:
创建响应点击NPC时要执行的方法,注册给NpcManager,要执行的方法是取出要显示的商店数据,并且显示商店UI。
响应购买物品动作,与Service层(ItemService)交互。
ItemService:
与服务器端交互,传递商店ID与商品物品ID。
服务器端
ItemService:
响应购买服务的方法,
ShopManager:
响应购买服务的方法,要为所有购买的请求服务,所以要把所有购买的请求session作为参数传进来,进行购买操作,扣除金币,存储数据库等,而后返回一个结果值。
UserService:
新增初始金币。
Character:
增加金币
StatusManager:
管理角色操作的状态,例如当发生金钱变动,物品变动时,可在角色Character实体中的StatusManager增加相应的状态,并把变化量也记录下来,会有一个List将其保存。
服务器端:
新增状态管理器StatusManager,并且改动了服务端传输Response的操作。
以前的传输方式:
NetMessage message = new NetMessage();
message.Response = new NetMessageResponse();
message.Response.userRegister = new UserRegisterResponse();
byte[] data = PackageHandler.PackMessage(message);
message.Response.userRegister.Result = Result.Failed;
sender.SendData(data, 0, data.Length);
修改后:
首先对存放会话的Session做修改,
NetSession:
NetMessage response;
public NetMessageResponse Response {
get {
if (response == null) {
response = new NetMessage();
}
if (response.Response == null) {
response.Response = new NetMessageResponse();
}
return response.Response;
}
}
public byte[] GetResponse() {
if (response != null) {
if (this.Character != null && this.Character.statusManager.HasStatus) {
//将所有需要更新的状态都压入NetMessageResponse.statusNotify这个容器里
this.Character.statusManager.ApplyResponse(Response);
}
//包装成字节流。
byte[] data = PackageHandler.PackMessage(response);
response = null;
return data;
}
return null;
}
而NetConnection则只需要增加
public void SendResponse() {
byte[] data = session.GetResponse();
this.SendData(data,0,data.Length);
}
购买经过的流程为
大体上:
ItemService.OnItemBuy→ShopManager.BuyItem
→NetSession.Character.itemManager
→NetSession.Character.statusManager
→ShopManager.DBService.Instance.Save()
→ItemService
→NetSession.GetResponse
→NetSession.Character.statusManager.ApplyResponse →PackageHandler.PackMessage
ItemService接收到请求
→将session,request里的shopId和shopItemId传入ShopManager的购买方法
→ShopManager进行商店与商店物品的判断,对session里character实体类的各项manager做操作(ItemManager等)。
→ItemManager等有关于各项数据库更新的方法,都附带了Character.statusManager.AddItemChange —更新物品
Character.statusManager.AddGoldChange —更新金钱
把购买的操作以状态的形式记录下来。记录的位置是
session(NetConnection.NetSession).Character.statusManager.Status(List<NStatus> Status)
→在准备发送给客户端的Response里,在构造完NetMessageResponse里请求所需的消息后(如购买道具则是):
sender.Session.Response.itemBuy = new ItemBuyResponse();
sender.Session.Response.itemBuy.Result = result;
还需要调用NetSession的GetResponse()方法,该方法会先将Character.statusManager.Status里的状态遍历压入NetMessageResponse.statusNotify中,并将整个NetMessageResponse打包成字节流,再发送到客户端。
一切的操作都在OnItemBuy的会话参数NetConnection<NetSession> sender中。
-
#StatusManager:
public class StatusManager { Character Owner; private List<NStatus> Status { get; set; } public bool HasStatus { get { return this.Status.Count > 0; } } public StatusManager(Character owner) { this.Owner = owner; this.Status = new List<NStatus>(); } //将物品的状态更新压入状态存储List里。 public void AddStatus(StatusType type,int id,int value,StatusAction action) { this.Status.Add(new NStatus() { Type = type, //操作了什么类型,MONEY金钱为0:MONEY,EXP,SKILL_POINT,ITEM Id = id, //操作了的ID:ItemId什么的。 Value = value, //操作的值 Action = action //操作是什么:UPDATE更新操作,ADD增加操作,DELETE删除操作 }); } //金钱操作调用这个 public void AddGoldChange(int goldDelta) { if (goldDelta > 0) { this.AddStatus(StatusType.Money,0,goldDelta,StatusAction.Add); } if (goldDelta < 0) { this.AddStatus(StatusType.Money,0,-goldDelta,StatusAction.Delete); } } //物品操作调用这个 public void AddItemChange(int id,int count,StatusAction action) { this.AddStatus(StatusType.Item,id,count,action); } public void ApplyResponse(NetMessageResponse message) { if (message.statusNotify == null) {//为response新增一个状态存储容器。 message.statusNotify = new StatusNotify(); } //将上面操作过的所有状态都压入response参数的状态存储容器中。 foreach (var status in this.Status) { message.statusNotify.Status.Add(status); } this.Status.Clear(); //清空掉存储的状态容器。 } }
客户端:
新增StatusService用于接收服务端传来的状态信息,创建注册用的Dictionary容器eventMap,将StatusType状态类型作为key,delegate bool StatusNotifyHandle(NStatus status)作为value,当有相同的key时,利用+=叠加deletegate值。
当接收到状态信息时,利用foreach遍历出里面的NStatus数据,并且逐个对比注册事件表eventMap,执行他们。
-
#StatusService
class StatusService : Singleton<StatusService>, IDisposable { public delegate bool StatusNotifyHandler(NStatus status); Dictionary<StatusType, StatusNotifyHandler> eventMap = new Dictionary<StatusType, StatusNotifyHandler>(); HashSet<StatusNotifyHandler> handles = new HashSet<StatusNotifyHandler>(); public void Init() { } public StatusService() { MessageDistributer.Instance.Subscribe<StatusNotify>(this.OnStatusNotify); } public void Dispose() { MessageDistributer.Instance.Unsubscribe<StatusNotify>(this.OnStatusNotify); } //function参数作为状态信息可理解为行为,action为该行为下要做出的动作。 public void RegisterStatusNotify(StatusType function, StatusNotifyHandler action) { //避免注册重复的action if (handles.Contains(action)) return; if (!eventMap.ContainsKey(function)) { eventMap[function] = action; } else { //该行为下可能会做出不止一种动作。 eventMap[function] += action; } handles.Add(action); } private void OnStatusNotify(object sender, StatusNotify notify) { foreach (NStatus status in notify.Status) { Notify(status); } } public void Notify(NStatus status) { Debug.LogFormat("StatusNotify:[{0}][{1}]{2}:{3}",status.Type,status.Action,status.Id,status.Value); if (status.Type == StatusType.Money) { if (status.Action == StatusAction.Add) { User.Instance.AddGold(status.Value); } if (status.Action == StatusAction.Delete) { User.Instance.AddGold(-status.Value); } } StatusNotifyHandler handler; if (eventMap.TryGetValue(status.Type, out handler)) { handler(status); } } }
ItemManager增加OnItemNotify方法,并且将方法注册给StatusService,ItemManager管理道具的删减,所以注册的状态为StatusType.Item
(StatusService.Instance.RegisterStatusNotify(StatusType.Item,OnItemNotify);),
作用是当有状态是增加道具时(Status.Action==StatusAction.Add),调用AddItem方法增加道具。
-
#AddItem
private void AddItem(int itemId, int count) { Item item = null; //先从玩家已拥有的数据里取。 if (this.Items.TryGetValue(itemId, out item)) { item.Count += count; } else { //玩家没有,就构建道具导入。 item = new Item(itemId,count); this.Items.Add(itemId,item); } BagManager.Instance.AddItem(itemId,count); }
作用是当有状态是减少道具时(Status.Action==StatusAction.Delete),调用RemoveItem方法增加道具。
-
#RemoveItem
void RemoveItem(int itemId, int count) { if (!this.Items.ContainsKey(itemId)) { return; } Item item = this.Items[itemId]; if (item.Count < count) { return; } item.Count -= count; BagManager.Instance.RemoveItem(itemId,count); }
之后道具的增加还要实施到背包的UI中,
有关整个道具系统的变化是客户端Service接收到服务端的状态消息(StatusService接受信息),然后通知注册表里的方法(通知ItemManager进行用户物品的加减),然后再执行背包系统的道具加减(通知BagManager进行物品的加减,注意物品上线数量的拆分。)
(注意)
需要在Common的NetWork里配置消息分发器。