一、输入框UI脚本:
在后续好友菜单界面需要调用。
1、预制体结构:
2、脚本:
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class UIInputBox : MonoBehaviour
{
public Text title;
public Text message;
public Text tips;
public Button buttonYes;
public Button btnCancel;
public InputField input;
public Text buttonYesTitle;
public Text buttonNoTitle;
public delegate bool SubmitHandler(string inputText, out string tips);
public event SubmitHandler onSubmit;//通过事件暴露接口、给外部函数执行点击按钮的回调
public UnityAction OnCancel;
public string emptyTips;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void Init(string title, string message, string btnCancel = "", string btnOK = "", string emptyTips = "")
{
if (!string.IsNullOrEmpty(title)) this.title.text = title;
this.message.text = message;
this.tips.text = null;
this.onSubmit = null;
this.emptyTips = emptyTips;
if (!string.IsNullOrEmpty(btnOK)) this.buttonYesTitle.text = btnOK;
if (!string.IsNullOrEmpty(btnCancel)) this.buttonNoTitle.text = title;
this.buttonYes.onClick.AddListener(OnClickYes);
this.btnCancel.onClick.AddListener(OnClickCancel);
}
public void OnClickYes()
{
this.tips.text = "";
if (string.IsNullOrEmpty(input.text))
{
this.tips.text = this.emptyTips;
return;
}
if (onSubmit != null)
{
string tips;
if (onSubmit(this.input.text, out tips))
{
this.tips.text = tips;
return;
}
}
Destroy(this.gameObject);
}
public void OnClickCancel()
{
Destroy(this.gameObject);
if (this.OnCancel != null)
this.OnCancel();
}
}
实现对以上UI的进行调用和管理:
using UnityEngine;
class InputBox
{
static Object cacheObject = null;
//参数分别为 需要提示的信息、标题、确定按钮信息、取消按钮信息、输入框为空的提示
public static UIInputBox Show(string message, string title = "", string btnOK="",string btnCancel="",string emptyTips = "")
{
if (cacheObject == null)
{
cacheObject = Resloader.Load<Object>("prefab/UIInputBox");
}
GameObject go = (GameObject)GameObject.Instantiate(cacheObject);
UIInputBox inputbox = go.GetComponent<UIInputBox>();
inputbox.Init(title, message, btnOK, btnCancel, emptyTips);
return inputbox;
}
}
二、客户端相关代码
1.好友服务代码
由于加好友是双向的,因此这里的响应和请求也是双向的。
namespace Service
{
class FriendService : Singleton<FriendService>, IDisposable
{
public UnityAction OnFriendUpdate;
public void Init()
{
}
public FriendService()
{
MessageDistributer.Instance.Subscribe<FriendAddRequest>(this.OnFriendAddRequest);
MessageDistributer.Instance.Subscribe<FriendListResponse>(this.OnFriendList);
MessageDistributer.Instance.Subscribe<FriendAddResponse>(this.OnFriendAdd);
MessageDistributer.Instance.Subscribe<FriendRemoveResponse>(this.OnFriendRemove);
}
public void Dispose()
{
MessageDistributer.Instance.Unsubscribe<FriendAddRequest>(this.OnFriendAddRequest);
MessageDistributer.Instance.Unsubscribe<FriendListResponse>(this.OnFriendList);
MessageDistributer.Instance.Unsubscribe<FriendAddResponse>(this.OnFriendAdd);
MessageDistributer.Instance.Unsubscribe<FriendRemoveResponse>(this.OnFriendRemove);
}
//添加好友响应
private void OnFriendAdd(object sender, FriendAddResponse response)
{
if (response.Result == Result.Success)
MessageBox.Show(response.Request.ToName + "接受了你的请求。", "添加好友成功!");
else
MessageBox.Show(response.Errormsg, "添加好友失败!");
}
//删除好友响应
private void OnFriendRemove(object sender, FriendRemoveResponse response)
{
if (response.Result == Result.Success)
MessageBox.Show("删除成功!", "删除好友");
else
MessageBox.Show("删除失败!", "删除好友",MessageBoxType.Error);
}
//收到好友请求
private void OnFriendAddRequest(object sender, FriendAddRequest request)
{
var confirm = MessageBox.Show(string.Format("{0}请求添加你为好友。", request.FromName), "好友请求",
MessageBoxType.Confirm,"接受", "拒绝");
confirm.OnYes = () =>
{
this.SendFriendAddResponse(true, request);
};
confirm.OnYes = () =>
{
this.SendFriendAddResponse(false, request);
};
}
//发送好友请求
public void SendFriendAddRequest(int friendId, string friendName)
{
Debug.Log("SendFriendAdd");
NetMessage message = new NetMessage();
message.Request = new NetMessageRequest();
message.Request.friendAddReq = new FriendAddRequest();
message.Request.friendAddReq.FromId = User.Instance.CurrentCharacter.Id;
message.Request.friendAddReq.FromName = User.Instance.CurrentCharacter.Name;
message.Request.friendAddReq.ToId = friendId;
message.Request.friendAddReq.ToName = friendName;
NetClient.Instance.SendMessage(message);
}
//回复好友添加响应
public void SendFriendAddResponse(bool accept, FriendAddRequest request)
{
Debug.Log("SendFriendAdd Response");
NetMessage message = new NetMessage();
message.Request = new NetMessageRequest();
message.Request.friendAddRes = new FriendAddResponse();
message.Request.friendAddRes.Result = accept ? Result.Success : Result.Failed;
message.Request.friendAddRes.Errormsg = accept ? "添加好友成功!" : "对方拒绝了你的请求!";
message.Request.friendAddRes.Request = request;
NetClient.Instance.SendMessage(message);
}
public void SendFriendRemoveRequest(int itemid, int friendId)
{
Debug.Log("SendFriendRemoveRequest");
NetMessage message = new NetMessage();
message.Request = new NetMessageRequest();
message.Request.friendRemove = new FriendRemoveRequest();
message.Request.friendRemove.Id = itemid;
message.Request.friendRemove.friendId = friendId;
NetClient.Instance.SendMessage(message);
}
//更新好友列表
private void OnFriendList(object sender,FriendListResponse message)
{
Debug.Log("OnFriendList");
FriendManager.Instance.Init(message.Friends);
if (this.OnFriendUpdate != null) this.OnFriendUpdate();
}
}
}
2、好友管理器
用来管理好友列表数据
namespace Managers
{
class FriendManager: Singleton<FriendManager>
{
public List<NFriendInfo> allFriends;
public void Init(List<NFriendInfo> friends)
{
this.allFriends = friends;
}
}
}
3、好友界面UI
这里需要使用到之前编写的输入提示框UI和消息提示UI,由消息服务层来驱动刷新响应
UIFriend (好友列表大界面):
public class UIFriend : MonoBehaviour
{
public GameObject itemPrefab;
public ListView listMain;
public Transform itemRoot;
public UIFriendItem selectedItem;
private void Start()
{
FriendService.Instance.OnFriendUpdate = RefreshUI;//当好友更新时也会刷新UI
this.listMain.onItemSelected += this.OnFriendSelected;
RefreshUI();
}
private void RefreshUI()
{
ClearFriendList();
InitFriendItems();
}
private void InitFriendItems()
{
foreach (var item in FriendManager.Instance.allFriends)
{
GameObject go = Instantiate(itemPrefab, this.listMain.transform);
UIFriendItem ui = go.GetComponent<UIFriendItem>();
ui.SetFriendInfo(item);
this.listMain.AddItem(ui);
}
}
private void ClearFriendList()
{
this.listMain.RemoveAll();
}
public void OnFriendSelected(ListView.ListViewItem item,ListView listView)
{
this.selectedItem = item as UIFriendItem;
}
public void OnClickFriendAdd()
{
//显示提示框
InputBox.Show("请输入要添加的好友名称或ID", "添加好友").onSubmit += OnFriendAddSubmit;
}
public bool OnFriendAddSubmit(string input, out string tips)
{
tips = "";
int friendId = 0;
string friendName = "";
if (!int.TryParse(input, out friendId))//先尝试转换成ID
friendName = input;
if (friendId == User.Instance.CurrentCharacter.Id || friendName == User.Instance.CurrentCharacter.Name)
{
tips = "不能添加自己!";
return false;
}
FriendService.Instance.SendFriendAddRequest(friendId, friendName);//好友请求
return true;
}
public void OnClickFriendChat()
{
MessageBox.Show("以后再做");
}
public void OnClickdDelete()
{
if(selectedItem==null)
{
MessageBox.Show("请选择要删除的好友!");
return;
}
var msgbox = MessageBox.Show(string.Format("确定删除好友【[0]】吗?", selectedItem.Info.friendInfo.Name), "删除好友",
MessageBoxType.Confirm, "删除", "取消");
msgbox.OnYes = () =>
{
FriendService.Instance.SendFriendRemoveRequest(this.selectedItem.Info.Id, this.selectedItem.Info.friendInfo.Id);
};
}
}
UIFriendItem :单条好友记录
public class UIFriendItem : ListView.ListViewItem
{
public Text nickName;
public Text @class;
public Text level;
public Text status;
public Image background;
public Sprite normalBg;
public Sprite selectBg;
public override void OnSelected(bool selected)
{
this.background.overrideSprite = selected ? selectBg : normalBg;
}
public NFriendInfo Info;
void Start()
{
}
bool isEquiped = false;
// Update is called once per frame
public void SetFriendInfo(NFriendInfo info)
{
this.Info = info;
if (this.nickName != null) this.nickName.text = this.Info.friendInfo.Name;
if (this.@class != null) this.@class.text = this.Info.friendInfo.Class.ToString();
if (this.level != null) this.level.text = this.Info.friendInfo.Level.ToString();
if (this.status != null)
{
this.status.text = this.Info.Status == 1 ? "在线" : "离线";
this.status.color = Color.gray;
if (this.Info.Status == 1) this.status.color = Color.green;
}
}
}
三、服务端相关代码
协议内容
message NFriendInfo
{
int32 id = 1;
NCharacterInfo friendInfo = 2;
int32 status = 3;
}
message FriendAddRequest
{
int32 from_id = 1;
string from_name = 2;
int32 to_id = 3;
string to_name = 4;
}
message FriendAddResponse
{
RESULT result = 1;
string errormsg = 2;
FriendAddRequest request = 3;
}
message FriendListResponse
{
RESULT result = 1;
string errormsg = 2;
repeated NFriendInfo friends = 3;
}
message FriendRemoveRequest
{
int32 id = 1;
int32 friendId = 2;
}
message FriendRemoveResponse
{
RESULT result = 1;
string errormsg = 2;
int32 id = 3;
}
数据库表结构 与character做关联
1、服务层代码
namespace GameServer.Services
{
class FriendService: Singleton<FriendService>
{
public FriendService()
{
MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<FriendAddRequest>(this.OnFriendAddRequest);
MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<FriendAddResponse>(this.OnFriendAddResponse);
MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<FriendRemoveRequest>(this.OnFriendRemove);
}
private void OnFriendRemove(NetConnection<NetSession> sender, FriendRemoveRequest request)
{
Character character = sender.Session.Character;
Log.InfoFormat("OnFriendRemove: character:{0} FriendReletionID:{1}", character.Id, request.Id);
sender.Session.Response.friendRemove = new FriendRemoveResponse();
sender.Session.Response.friendRemove.Id = request.Id;
if (character.FriendManager.RemoveFriendByID(request.Id))//先做自己删除好友
{//然后在别人的好友列表数据并删除自己
sender.Session.Response.friendRemove.Result = Result.Success;
var friend = SessionManager.Instance.GetSession(request.friendId);
if (friend != null)
{//在线从管理器移除
friend.Session.Character.FriendManager.RemoveFriendByFriendId(character.Id);
}
else
{//不在线从数据找 再移除
this.RemoveFriend(request.friendId, character.Id);
}
DBService.Instance.Save();
sender.SendResponse();
}
}
private void RemoveFriend(int charId,int friendId)
{
var removeItem = DBService.Instance.Entities.CharacterFriends.FirstOrDefault(v => v.CharacterID == charId && v.FriendID == friendId);
if(removeItem != null)
{
DBService.Instance.Entities.CharacterFriends.Remove(removeItem);
}
}
private void OnFriendAddResponse(NetConnection<NetSession> sender, FriendAddResponse response)
{
Character character = sender.Session.Character;
Log.InfoFormat("OnFriendAddResponse: character:{0} Result:{1} FromId:{2} ToID:{3}",
character.Id, response.Result, response.Request.FromId, response.Request.ToId);
sender.Session.Response.friendAddRes = response;
if (response.Result == Result.Success)
{//接受了好友添加请求
var requester = SessionManager.Instance.GetSession(response.Request.FromId);
if (requester == null)
{
sender.Session.Response.friendAddRes.Result = Result.Failed;
sender.Session.Response.friendAddRes.Errormsg = "发起者已离线!";
}
else
{
//互相添加好友
character.FriendManager.AddFriend(requester.Session.Character);
requester.Session.Character.FriendManager.AddFriend(character);
DBService.Instance.Save();
requester.Session.Response.friendAddRes = response;
requester.Session.Response.friendAddRes.Result = Result.Success;
requester.Session.Response.friendAddRes.Errormsg = "添加好友成功!";
requester.SendResponse();
}
}
sender.SendResponse();
}
private void OnFriendAddRequest(NetConnection<NetSession> sender, FriendAddRequest request)
{
Character character = sender.Session.Character;
Log.InfoFormat("OnFriendAddRequest: FromID:{0} FromName:{1} ToID:{2} ToName:{3}",
request.FromId, request.FromName, request.ToId, request.ToName);
if (request.ToId == 0)
{//查找在线所有玩家
foreach(var cha in CharacterManager.Instance.Characters)
{
if (cha.Value.Data.Name == request.ToName)
{
request.ToId = cha.Key;
break;
}
}
}
NetConnection<NetSession> friend = null;
if (request.ToId > 0)
{
if (character.FriendManager.GetFriendInfos(request.ToId) != null)
{
sender.Session.Response.friendAddRes = new FriendAddResponse();
sender.Session.Response.friendAddRes.Result = Result.Failed;
sender.Session.Response.friendAddRes.Errormsg = "已经是好友了!";
sender.SendResponse();
return;
}
friend = SessionManager.Instance.GetSession(request.ToId);
}
if (friend == null)
{
sender.Session.Response.friendAddRes = new FriendAddResponse();
sender.Session.Response.friendAddRes.Result = Result.Failed;
sender.Session.Response.friendAddRes.Errormsg = "好友不在线或者不存在!";
sender.SendResponse();
return;
}
Log.InfoFormat("ForwardRequest: FromID:{0}, ToID:{2}", request.FromId, request.ToId);
friend.Session.Response.friendAddReq = request;//添加好友的响应直接为当前玩家请求 服务器做中转处理
friend.SendResponse();
}
}
}
2、好友管理器
做好友的增删查
namespace GameServer.Managers
{
class FriendManager
{
Character Owner;
List<NFriendInfo> friends = new List<NFriendInfo>();
bool friendChanged = false;//好友是否改变 用于同步好友信息
public FriendManager(Character cha)
{
this.Owner = cha;
this.InitFriends();
}
public void GetFriendInfos( List<NFriendInfo> list)
{
foreach(var f in this.friends)
{
list.Add(f);
}
}
public NFriendInfo GetFriendInfo(int friendId)
{
foreach(var f in this.friends)
{
if(f.friendInfo.Id == friendId)
{
return f;
}
}
return null;
}
private NFriendInfo GetFriendInfo(TCharacterFriend friend)
{
NFriendInfo friendInfo = new NFriendInfo();
//获取在线好友玩家的数据
var character = CharacterManager.Instance.GetCharacter(friend.FriendID);
friendInfo.friendInfo = new NCharacterInfo();
friendInfo.Id = friend.Id;
if(character == null)
{//构造离线数据(从数据库更新)
friendInfo.friendInfo.Id = friend.FriendID;
friendInfo.friendInfo.Name = friend.FriendName;
friendInfo.friendInfo.Class = (CharacterClass)friend.Class;
friendInfo.friendInfo.Level = friend.Level;
friendInfo.Status = 0;
}
else
{//构造在线数据(从在线玩家管理器更新)
friendInfo.friendInfo = this.GetBasicInfo(character.Info);
friendInfo.friendInfo.Name = character.Info.Name;
friendInfo.friendInfo.Class = character.Info.Class;
friendInfo.friendInfo.Level = character.Info.Level;
character.FriendManager.UpdateFriendInfo(this.Owner.Info, 1);
friendInfo.Status = 1;
}
return friendInfo;
}
private NCharacterInfo GetBasicInfo(NCharacterInfo info)
{
return new NCharacterInfo()
{
Id = info.Id,
Name = info.Name,
Class = info.Class,
Level = info.Level
};
}
public void InitFriends()
{
this.friends.Clear();
foreach(var friend in this.Owner.Data.Friends)
{
this.friends.Add(GetFriendInfo(friend));
}
}
public void AddFriend(Character friend)
{
TCharacterFriend tf = new TCharacterFriend()
{
FriendID = friend.Id,
FriendName = friend.Data.Name,
Class = friend.Data.Class,
Level = friend.Data.Level,
};
this.Owner.Data.Friends.Add(tf);
friendChanged = true;
}
public bool RemoveFriendByFriendId(int friendid)
{
var removeItem = this.Owner.Data.Friends.FirstOrDefault(v => v.FriendID == friendid);
if (removeItem != null)
{
DBService.Instance.Entities.CharacterFriends.Remove(removeItem);
}
friendChanged = true;
return true;
}
public bool RemoveFriendByID(int id)
{
var removeItem = this.Owner.Data.Friends.FirstOrDefault(v => v.Id == id);
if (removeItem != null)
{
DBService.Instance.Entities.CharacterFriends.Remove(removeItem);
}
friendChanged = true;
return true;
}
//处理玩家在线 离线状态
public void UpdateFriendInfo(NCharacterInfo friendInfo,int status)
{
foreach(var f in this.friends)
{
if(f.friendInfo.Id == friendInfo.Id)
{
f.Status = status;
break;
}
}
this.friendChanged = true;
}
//在SESSION发送消息前调用这个 同步好友列表
public void PostProcess(NetMessageResponse message)
{
if (friendChanged)
{
this.InitFriends();
if(message.friendList == null)
{
message.friendList = new FriendListResponse();
message.friendList.Friends.AddRange(this.friends);
}
}
friendChanged = false;
}
}
}