装备系统步骤
作者:互联网
学习前准备工作
玩家与所接任务的关系
TCharacterQuest
ID |
QuestID | 任务配置表的任务ID
CharacterID | 玩家操控的角色ID
Target1 | 任务目标1
Target2 | 任务目标2
Target3 | 任务目标3
Status | 任务状态
QuestStatus 任务状态
IN_PROGRESS | 进行中
FINISHED | 已完成
往request和response里添加请求任务列表与接受任务的相关协议,往角色信息里添加记录任务状态的类。
message NetMessageRequest{
QuestListRequest questList = 12;
QuestAcceptRequest questAccept = 13;
}
message NetMessageResponse{
QuestListResponse questList = 12;
QuestAcceptResponse questAccept = 13;
}
message NCharacterInfo {
repeated NQuestInfo quests = 13;
}
//Quest System
enum QUEST_STATIS{
IN_PROGRESS = 0; //已接收,未完成
COMPLATED =1; //已完成,未提交
FINISHED =2; //已完成,已提交
FAILED = 3; //已失败
}
enum QUEST_LIST_TYPE{
ALL = 0;
IN_RPOGRESS = 1;
FINISHED =2;
}
message NQuestInfo{
int32 quest_id = 1; //任务ID
int32 quest_guid = 2; //唯一ID
QUEST_STATUS status= 3;
repeated int32 targets = 4;
}
message QuestListRequest{
QUEST_LIST_TYPE listType = 1;
}
message QuestListResponse{
RESULT result = 1;
string errormsg = 2;
repeated NQuestInfo quests = 3;
}
message QuestAcceptRequest{
int32 quest_id = 1;
}
message QuestAcceptResponse{
RESULT result =1 ;
string errormsg=2;
}
message QuestSubmitRequest{
int32 quest_id = 1;
}
message QuestSubmitResponse{
RESULT result = 1;
string errormsg = 2;
}
添加Define数据类
-
#QuestDefine
public enum QuestType { [Description("主线")] Main, [Description("支线")] Branch } public enum QuestTarget { None, Kill, Item } class QuestDefine { public int ID { get; set; } public string Name { get; set; } public int LimitLevel { get; set; } public CharacterClass LimitClass { get; set; } public int PreQuest { get; set; } public QuestType Type { get; set; } public int AcceptNPC { get; set; } public int SubmitNPC { get; set; } public string Overview { get; set; } public string Dialog { get; set; } public string DialogAccept { get; set; } public string DialogDeny { get; set; } public string DialogInComplete { get; set; } public string DialogFinish { get; set; } public QuestTarget Target1 { get; set; } public int Target1ID { get; set; } public int Target1Num{ get; set; } public QuestTarget Target2 { get; set; } public int Target2ID { get; set; } public int Target2Num { get; set; } public QuestTarget Target3 { get; set; } public int Target3ID { get; set; } public int Target3Num { get; set; } public int RewardGold { get; set; } public int RewardExp { get; set; } public int RewardItem1 { get; set; } public int RewardItem1Count { get; set; } public int RewardItem2 { get; set; } public int RewardItem2Count { get; set; } public int RewardItem3 { get; set; } public int RewardItem3Count { get; set; } }
客户端:
此部分主要是制作任务详情界面,分为了主线任务与支线任务,可通过按钮切换进行中的任务和可承接的任务。
拼完UI后,需要对任务界面做处理。
首先,叙述关于QuestSystem任务界面的初始化。
Quest这个任务的实体类,初始化有两个重载的构造方法,
1:利用NQuestInfo作为参数,允许初始化内部的NQuestInfo Info字段。NQuestInfo主要由服务器端的数据传输。
2:利用QuestDefine作为参数,此方法无法初始化NQuestInfo Info字段,故将其设为NULL。
初始化类的流程是:
DataManager加载Quest表格文件。
UserService→QuestManager→UIQuestSystem
在QuestManager中
在UserService中调用的QuestManager.Init初始化方法,有从UserService中接收到的服务端数据,也有DataManager加载的表格数据。
有3种数据结构保存数据,
一种保存了进行中的任务questInfos,该数据保存了直接由服务器端传输过来的数据。
public List<NQuestInfo> questInfos;
一种保存了所有的任务allQuests。由DataManager的数据和服务器端的数据一起初始化。
//任务ID,任务实体
public Dictionary<int, Quest> allQuests = new Dictionary<int, Quest>();
另一种保存了NPC和NPC拥有的任务npcQuests,保存了由npcId为key,以任务状态和任务列表组成的字典表作为Value。由DataManager的数据和服务器端的数据一起初始化。
public Dictionary<int, Dictionary<NPCQuestStatus, List<Quest>>> npcQuests =
//npcId 任务状态 任务组成的List表
new Dictionary<int, Dictionary<NPCQuestStatus, List<Quest>>>();
在初始化方法中,首先将questInfos的数据和DataManager加载的表格数据通过方法QuestManager.AddNpcQuest(int npcId, Quest quest)加载入npcQuests中。
然后UIQuestSystem作为UI层使用初始化方法调用QuestManager.npcQuests的数据进行界面的初始化。遍历生成Item时,通过识别Define里的任务类别来决定生成的位置是在主线框,还是支线框。
-
#UIQuestSystem
public class UIQuestSystem :UIWindow { public Text title; public GameObject itemPrefab; public TabView Tabs; public ListView listMain; //主线任务栏 public ListView listBranch; //支线任务栏 public UIQuestInfo questInfo; //是否显示可接任务 private bool showAvailableList = true; void Start() { this.listMain.onItemSelected += this.OnQuestSelected; this.listBranch.onItemSelected += this.OnQuestSelected; this.Tabs.OnTabSelect+=OnSelectTab; RefreshUI(); //QuestManager.Instance.OnQuestChanged +=RefreshUI; } private void RefreshUI() { ClearAllQuestList(); InitAllQuestItems(); } void OnDestroy() { //QuestManager.Instance.OnQuestChanged -=RefreshUI; } private void InitAllQuestItems() { foreach (var kv in QuestManager.Instance.allQuests) { if (showAvailableList) { //是否只展现可接任务 //info是服务端传的数据,如果为空 则说明一定没有接过任务。 if (kv.Value.Info != null) //是否是个可接任务 { continue; } }else{ if (kv.Value.Info == null) { continue; } } GameObject go = Instantiate(itemPrefab,kv.Value.Define.Type == QuestType.Main?this.listMain.transform:this.listBranch.transform); UIQuestItem ui = go.GetComponent<UIQuestItem>(); ui.SetQuestInfo(kv.Value); if (kv.Value.Define.Type == QuestType.Main) { this.listMain.AddItem(ui as ListView.ListViewItem); } else { this.listBranch.AddItem(ui as ListView.ListViewItem); } } } private void ClearAllQuestList() { this.listMain.RemoveAll(); this.listBranch.RemoveAll(); } public void OnSelectTab(int idx) { //切换是否展现可接任务 showAvailableList = idx == 1; RefreshUI(); } //点击任务栏里的任务时 private void OnQuestSelected(ListView.ListViewItem item) { //设置任务详情 UIQuestItem questItem = item as UIQuestItem; this.questInfo.SetQuestInfo(questItem.quest); } }
场景是点击任务列表栏的单任务Item,需要在任务详情页面显示详情,这个类绑定在Item的Prefab中。
-
#UIQuestItem
public class UIQuestItem : ListView.ListViewItem { public Text title; public Image background; public Sprite normalBg; public Sprite selectBg; public override void onSelected(bool selected) { this.background.overrideSprite = selected ? selectBg : normalBg; } public Quest quest; void Start() { } bool isEquiped = false; public void SetQuestInfo(Quest quest) { this.quest = quest; if (this.title != null) this.title.text = this.quest.Define.Name; } }
ListView通用继承类,包含了Item。
-
#ListView
public class ListView : MonoBehaviour { public UnityAction<ListViewItem> onItemSelected; public class ListViewItem : MonoBehaviour, IPointerClickHandler { private bool selected; public bool Selected { get { return selected; } set { selected = value; onSelected(selected); } } public virtual void onSelected(bool selected) { } public ListView owner; //Item拥有点击事件。点击时将ListView(相当于这个Item的父对象)设为自身。 public void OnPointerClick(PointerEventData eventData) { if (!this.selected) { this.Selected = true; } if (owner != null && owner.SelectedItem != this) { owner.SelectedItem = this; } } } List<ListViewItem> items = new List<ListViewItem>(); private ListViewItem selectedItem = null; public ListViewItem SelectedItem { get { return selectedItem; } private set { if (selectedItem!=null && selectedItem != value) { selectedItem.Selected = false; } selectedItem = value; if (onItemSelected != null) onItemSelected.Invoke((ListViewItem)value); } } public void AddItem(ListViewItem item) { item.owner = this; this.items.Add(item); } public void RemoveAll() { foreach(var it in items) { Destroy(it.gameObject); } items.Clear(); } }
UIQuestDialog框,指的是点击NPC后弹出来的任务框。
该UI的初始化在于点击NPC时执行。
数据和初始化流程为Controller触发点击,执行NpcManager里的方法:将npcId传给QuestManager.Instance.OpenNPCQuest(npcId)方法,该方法会向ShowQuestDialog方法传递任务quest。优先度 已完成→正在完成→可接任务
/// <summary>
/// 根据npcId从npcQuests里获取任务列表,并且将任务信息作为参数调用UI显示。
/// </summary>
/// <param name="npcId"></param>
/// <returns></returns>
public bool OpenNPCQuest(int npcId)
{
Dictionary<NPCQuestStatus, List<Quest>> status = new Dictionary<NPCQuestStatus, List<Quest>>();
if (this.npcQuests.TryGetValue(npcId, out status))
{ //获得NPC任务
if (status.ContainsKey(NPCQuestStatus.Complete) && status[NPCQuestStatus.Complete].Count > 0)
{ //
return ShowQuestDialog(status[NPCQuestStatus.Complete].First());
}
if (status.ContainsKey(NPCQuestStatus.InComplete) && status[NPCQuestStatus.InComplete].Count > 0)
{
return ShowQuestDialog(status[NPCQuestStatus.InComplete].First());
}
if (status.ContainsKey(NPCQuestStatus.Available) && status[NPCQuestStatus.Available].Count > 0)
{
return ShowQuestDialog(status[NPCQuestStatus.Available].First());
}
}
return false;
}
直接看代码吧
/// <summary>
/// 显示任务Dialog
/// </summary>
/// <param name="quest"></param>
/// <returns></returns>
private bool ShowQuestDialog(Quest quest)
{ //没有接任务或者任务已完成
if (quest.Info == null || quest.Info.Status == QuestStatus.Completed)
{ //
UIQuestDialog dlg = UIManager.Instance.Show<UIQuestDialog>();
dlg.SetQuest(quest);
//往UIQuestDialog的父类UIWindow的handler注册方法,调用时机是关闭窗口时。
dlg.OnClose += OnQuestDialogClose;
return true;
}
//接了任务 任务已完成
if (quest.Info != null || quest.Info.Status == QuestStatus.Completed)
{
if (!string.IsNullOrEmpty(quest.Define.DialogInComplete))
{ //显示文本
MessageBox.Show(quest.Define.DialogInComplete);
}
}
return true;
}
-
#UIQuestDialog
public class UIQuestDialog:UIWindow { public UIQuestInfo questInfo; public Quest quest; public GameObject openButtons; public GameObject submitButtons; void Start() { } public void SetQuest(Quest quest) { this.quest = quest; this.UpdateQuest(); //没有接这个任务 if (this.quest.Info == null) { //接任务btn显示 openButtons.SetActive(true); //提交任务btn隐藏 submitButtons.SetActive(false); } else { if (this.quest.Info.Status == SkillBridge.Message.QuestStatus.Completed) { openButtons.SetActive(false); submitButtons.SetActive(true); } else { openButtons.SetActive(false); submitButtons.SetActive(false); } } } private void UpdateQuest() { //将任务信息更新到任务面板的详情页面 if (this.quest != null) { if (this.questInfo != null) { this.questInfo.SetQuestInfo(quest); } } } }
在UIWorldElement/UIWorldElementManager上(世界元素管理器)增加任务的感叹号。
-
#UIWorldElementManager
public class UIWorldElementManager :MonoSingleton<UIWorldElementManager> { // Use this for initialization public GameObject nameBar; public GameObject npcStatusPrefab; private Dictionary<Transform, GameObject> elementNames = new Dictionary<Transform, GameObject>(); private Dictionary<Transform, GameObject> elementStatus = new Dictionary<Transform, GameObject>(); void Start () { } public void AddCharacterNameBar(Transform owner,Character character) { GameObject newNameBar = Instantiate(nameBar, transform); newNameBar.name = string.Format("{0}_[{1}]_NameBar",character.Name,character.entityId); newNameBar.GetComponent<UINameBar>().character = character; newNameBar.GetComponent<UIWorldElement>().owner= owner; newNameBar.GetComponent<UIWorldElement>().height =2f; newNameBar.SetActive(true); elementNames.Add(owner,newNameBar); } public void RemoveCharacterNameBar(Transform owner) { if (elementNames.ContainsKey(owner)){ Destroy(this.elementNames[owner]); elementNames.Remove(owner); } } public void AddNPCQuestStatus(Transform owner, NPCQuestStatus status) { if (this.elementNames.ContainsKey(owner)) { elementStatus[owner].GetComponent<UIQuestStatus>().SetQuestStatus(status); } else { GameObject go = Instantiate(npcStatusPrefab,this.transform); go.name = "NPCQuestStatus:"+owner.name; go.GetComponent<UIWorldElement>().owner = owner; go.GetComponent<UIQuestStatus>().SetQuestStatus(status); go.SetActive(true); this.elementNames[owner] = go; } } public void RemoveNPCQuestStatus(Transform owner) { if (this.elementStatus.ContainsKey(owner)) { Destroy(this.elementStatus[owner]); this.elementStatus.Remove(owner); } } }
第二部分
事先准备:
此部分主要处理:点击NPC弹出任务详情,可以点击接受任务,提交任务,得到任务奖赏。
首先需要处理接受任务后实际视觉层的变化,接任务后人物头顶的任务状态标识会改变。
所以需要在NpcController里往QuestManager里注册方法,当QuestManager更新完内存里的任务后,触发这个注册方法。
-
#NpcController
public class NpcController : MonoBehaviour { NPCQuestStatus questStatus; // Use this for initialization void Start() { //往QuestManager里注册方法。 QuestManager.Instance.onQuestStatusChanged += OnQuestStatusChanged; } void OnQuestStatusChanged(Quest quest) { if (quest.Define.AcceptNPC == NpcId|| quest.Define.SubmitNPC == NpcId) { this.RefreshNPCStatus(); } } /// <summary> /// Controller用于刷新NPC头顶的任务状态, /// </summary> private void RefreshNPCStatus() { questStatus = QuestManager.Instance.GetNPCQuestStatusByNPC(NpcId); UIWorldElementManager.Instance.AddNPCQuestStatus(transform,questStatus); } void OnDestory() { QuestManager.Instance.onQuestStatusChanged -= OnQuestStatusChanged; if (UIWorldElementManager.Instance != null) { //移除 UIWorldElementManager.Instance.RemoveNPCQuestStatus(transform); } }
首先会从Dialog部分开始,UIQuestDialog里为关闭Dialog的事件注册了OnQuestDialogClose方法,(上接 dlg.OnClose += OnQuestDialogClose;)
/// <summary>
/// 监控窗口关闭 是因什么原因关闭的,并且触发相应方法。
/// </summary>
/// <param name="sender"></param>
/// <param name="result"></param>
void OnQuestDialogClose(UIWindow sender, UIWindow.WindowResult result)
{
UIQuestDialog dlg = (UIQuestDialog)sender;
//如果是点击了提交或者承接任务
if (result == UIWindow.WindowResult.Yes)
{
//没接任务,则属于点击了承接任务按钮
if (dlg.quest.Info == null)
{
QuestService.Instance.SendQuestAccept(dlg.quest);
//如果任务已完成,则属于点击了完成按钮。
}
else if (dlg.quest.Info.Status == QuestStatus.Completed)
{
QuestService.Instance.SendQuestSubmit(dlg.quest);
}
//点击了放弃任务的情况
}
else if (result == UIWindow.WindowResult.No)
{
MessageBox.Show(dlg.quest.Define.DialogDeny);
}
}
//UIWindow部分
public event CloseHandler OnClose;
public delegate void CloseHandler(UIWindow sender, WindowResult result);
public virtual Type Type { get { return this.GetType(); } }
public void Close(WindowResult result = WindowResult.None) {
UIManager.Instance.Close(this.Type);
if (this.OnClose != null) {
this.OnClose(this, result);
}
this.OnClose = null;
}
SendQuestAccept(dlg.quest)与SendQuestSubmit(dlg.quest);各为相应的传输服务端方法,只需要传输questId即可。
public bool SendQuestSubmit(Quest quest)
{
Debug.Log("SendQuestSubmit");
NetMessage message = new NetMessage();
message.Request = new NetMessageRequest();
message.Request.questSubmit = new QuestSubmitRequest();
message.Request.questSubmit.QuestId = quest.Define.ID;
NetClient.Instance.SendMessage(message);
return true;
}
当客户端收到有关任务系统的响应后,
//服务端返回响应后触发。
private void OnQuestSubmit(object sender, QuestAcceptResponse message)
{
Debug.LogFormat("OnQuestSubmit:{0},{1}", message.Result, message.Errormsg);
if (message.Result == Result.Success)
{
QuestManager.Instance.OnQuestSubimted(message.Quest);
}
else {
MessageBox.Show("任务接受失败","错误",MessageBoxType.Error);
}
}
private void OnQuestAccept(object sender, QuestAcceptResponse message)
{
Debug.LogFormat("OnQuestAccept:{0},{1}", message.Result, message.Errormsg);
if (message.Result == Result.Success)
{
QuestManager.Instance.OnQuestAccepted(message.Quest);
}
else
{
MessageBox.Show("任务接受失败", "错误", MessageBoxType.Error);
}
}
//触发OnQuestSubimted和OnQuestAccepted
public void OnQuestAccepted(NQuestInfo info)
{ //更新客户端内的allQuests列表,改变里面的任务状态
var quest = this.RefreshQuestStatus(info);
MessageBox.Show(quest.Define.DialogAccept);
}
public void OnQuestSubimted(NQuestInfo info)
{
var quest = this.RefreshQuestStatus(info);
MessageBox.Show(quest.Define.DialogFinish);
}
/// <summary>
/// 当任务状态发生改变 其他层要做的动作
/// </summary>
public UnityAction<Quest> onQuestStatusChanged;
/// <summary>
/// 从网络接收到数据后(网络接收的说明是点击承接了的任务),把allQuests的任务状态更新
/// </summary>
/// <param name="quest"></param>
/// <returns></returns>
Quest RefreshQuestStatus(NQuestInfo quest) {
this.npcQuests.Clear();
Quest result;
//把NQuestInfo的数据更新进allQuests
if (this.allQuests.ContainsKey(quest.QuestId))
{
//更新新的任务状态
this.allQuests[quest.QuestId].Info = quest;
result = this.allQuests[quest.QuestId];
}
else {
result = new Quest(quest);
this.allQuests[quest.QuestId] = result;
}
//从DataManager的表格数据里更新allQuests。
CheckAvailableQuests();
foreach (var kv in this.allQuests) {
this.AddNpcQuest(kv.Value.Define.AcceptNPC, kv.Value);
this.AddNpcQuest(kv.Value.Define.SubmitNPC, kv.Value);
}
//更新人物头顶的任务状态,注册自NpcController
if (onQuestStatusChanged != null) {
onQuestStatusChanged(result);
}
return result;
}
服务器端:
-
#QuestManager
public class QuestManager { private Character owner; public QuestManager(Character owner) { this.owner = owner; } public void GetQuestInfos(List<NQuestInfo> list) { foreach (var quest in this.owner.Data.Quests) { list.Add(GetQuestInfo(quest)); } } public NQuestInfo GetQuestInfo(TCharacterQuest quest) { return new NQuestInfo { QuestId = quest.QuestId, Status = (QuestStatus)quest.Status, Targets = new int[3] { quest.Target1, quest.Target2, quest.Target3 } }; } public Result AcceptQuest(NetConnection<NetSession> sender, int questId) { Character character = sender.Session.Character; QuestDefine define; if (DataManager.Instance.Quests.TryGetValue(questId, out define)) { var dbQuest = DBService.Instance.Entities.CharacterQuests.Create(); dbQuest.QuestId = questId; if (define.Target1 == QuestTarget.None) { //没有目标直接完成 dbQuest.Status = (int)QuestStatus.Completed; } else { dbQuest.Status = (int)QuestStatus.InProgress; } sender.Session.Response.questAccept.Quest = this.GetQuestInfo(dbQuest); character.Data.Quests.Add(dbQuest); DBService.Instance.Save(); return Result.Success; } else { sender.Session.Response.questAccept.Errormsg = "任务不存在"; return Result.Failed; } } public Result SubmitQuest(NetConnection<NetSession> sender, int questId) { Character character = sender.Session.Character; QuestDefine define; if (DataManager.Instance.Quests.TryGetValue(questId, out define)) { //得到数据库数据 var dbQuest = character.Data.Quests.Where(q => q.QuestId == questId).FirstOrDefault(); if (dbQuest != null) { //该任务的状态还不是已完成。Completed:任务完成还未提交 Finished:任务已完成已提交 if (dbQuest.Status != (int)QuestStatus.Completed) { //还不是完成状态 sender.Session.Response.questSubmit.Errormsg = "任务未完成"; return Result.Failed; } dbQuest.Status = (int)QuestStatus.Finished; sender.Session.Response.questSubmit.Quest = this.GetQuestInfo(dbQuest); DBService.Instance.Save(); //处理任务奖励 if (define.RewardGold > 0) { character.Gold += define.RewardGold; } if (define.RewardExp > 0) { //character.EXP += define.RewardExp; } if (define.RewardItem1 > 0) { character.itemManager.AddItem(define.RewardItem1, define.RewardItem1Count); } if (define.RewardItem2 > 0) { character.itemManager.AddItem(define.RewardItem2, define.RewardItem2Count); } if (define.RewardItem3 > 0) { character.itemManager.AddItem(define.RewardItem3, define.RewardItem3Count); } DBService.Instance.Save(); return Result.Success; } sender.Session.Response.questSubmit.Errormsg = "任务不存在[2]"; return Result.Failed; } else { sender.Session.Response.questSubmit.Errormsg = "任务不存在[1]"; return Result.Failed; } } }
-
#QuestService
using Common; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Network; using SkillBridge.Message; using GameServer.Entities; namespace GameServer.Services { class QuestService:Singleton<QuestService> { public QuestService() { MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<QuestAcceptRequest>(this.OnQuestAccept); MessageDistributer<NetConnection<NetSession>>.Instance.Subscribe<QuestSubmitRequest>(this.OnQuestSubmit); } public void Init() { } private void OnQuestSubmit(NetConnection<NetSession> sender, QuestSubmitRequest request) { Character character = sender.Session.Character; Log.InfoFormat("OnAcceptRequest:Character:{0} QuestId:{1}", character.Id, request.QuestId); sender.Session.Response.questSubmit = new QuestSubmitResponse(); Result result = character.questManager.SubmitQuest(sender, request.QuestId); sender.Session.Response.questSubmit.Result = result; sender.SendResponse(); } private void OnQuestAccept(NetConnection<NetSession> sender, QuestAcceptRequest request) { Character character = sender.Session.Character; Log.InfoFormat("OnAcceptRequest:Character:{0} QuestId:{1}",character.Id, request.QuestId); sender.Session.Response.questAccept = new QuestAcceptResponse(); Result result = character.questManager.AcceptQuest(sender,request.QuestId); sender.Session.Response.questAccept.Result = result; sender.SendResponse(); } } }
标签:set,get,步骤,装备,系统,任务,quest,void,public 来源: https://blog.csdn.net/yfinalfantasy/article/details/120589957