目录
第7章: 强化升级系统
制作强化界面UI
给StrongWnd添加同名脚本,并把打开的方法添加的主界面的按钮点击上。
图片点击事件注册
给装备图片添加事件注册
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class StrongWnd : WindowRoot
{
public Transform posBtnTrans;
protected override void InitWnd()
{
base.InitWnd();
RegClickEvt();
}
protected void RegClickEvt()
{
for(int i =0;i<posBtnTrans.childCount;i++)
{
Image img = posBtnTrans.GetChild(i).GetComponent<Image>();
OnClick(img.gameObject, (PointerEventData evt) =>
{
audioSvc.PlayUIAudio(Constants.UIClickBtn);
ClickPosItem();
});
}
}
private void ClickPosItem()
{
PECommon.Log("Click Item");
}
}
这里使用的是IPointerClickHandler接口。
点击事件传参
修改PEListener
public Action<object> onClick;
public object args;
public void OnPointerClick(PointerEventData eventData)
{
if (onClick != null)
{
onClick(eventData);
}
}
在WindowRoot中继续进行修改
public void OnPointerClick(PointerEventData eventData)
{
if (onClick != null)
{
onClick(args);
}
}
在StrongWnd中进行修改
protected void RegClickEvt()
{
for(int i =0;i<posBtnTrans.childCount;i++)
{
Image img = posBtnTrans.GetChild(i).GetComponent<Image>();
OnClick(img.gameObject, (object args) =>
{
audioSvc.PlayUIAudio(Constants.UIClickBtn);
ClickPosItem((int)args);
},i);
}
}
private void ClickPosItem(int index)
{
PECommon.Log("Click Item: " + index);
}
这样就把图片参数通过按钮点击传递
位置列表显示控制
完善方法,让箭头可以随着鼠标点击显示
private Image[] imgs = new Image[6];
private int curtIndex;
protected void RegClickEvt()
{
for(int i =0;i<posBtnTrans.childCount;i++)
{
Image img = posBtnTrans.GetChild(i).GetComponent<Image>();
OnClick(img.gameObject, (object args) =>
{
audioSvc.PlayUIAudio(Constants.UIClickBtn);
ClickPosItem((int)args);
},i);
//初始化图片
imgs[i] = img;
}
}
private void ClickPosItem(int index)
{
curtIndex = index;
for(int i=0;i<imgs.Length;i++)
{
Transform trans = imgs[i].transform;
if(i == curtIndex)
{
//箭头表示
SetSprite(imgs[i], PathDefine.ItemArrowBG);
trans.localPosition = new Vector3(10, trans.localPosition.y, 0);
trans.GetComponent<RectTransform>().sizeDelta = new Vector2(250, 95);
}
else
{
SetSprite(imgs[i], PathDefine.ItemPlatBG);
transform.localPosition = new Vector3(0, transform.localPosition.y, 0);
trans.GetComponent<RectTransform>().sizeDelta = new Vector2(220, 85);
}
}
}
配置数据生成技巧
将xls的strong导出到untiy中。
强化升级数据解析
public class StrongCfg:BaseData<StrongCfg>
{
public int pos;
public int startlv;
public int addhp;
public int addhurt;
public int adddef;
public int minlv;
public int coin;
public int crystal;
}
private Dictionary<int, Dictionary<int, StrongCfg>> strongDic = new Dictionary<int, Dictionary<int, StrongCfg>>();
private void InitStrongCfg(string path) {
TextAsset xml = Resources.Load<TextAsset>(path);
if (!xml) {
PECommon.Log("xml file:" + path + " not exist", LogType.Error);
}
else {
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml.text);
XmlNodeList nodLst = doc.SelectSingleNode("root").ChildNodes;
for (int i = 0; i < nodLst.Count; i++) {
XmlElement ele = nodLst[i] as XmlElement;
if (ele.GetAttributeNode("ID") == null) {
continue;
}
int ID = Convert.ToInt32(ele.GetAttributeNode("ID").InnerText);
StrongCfg sd = new StrongCfg {
ID = ID
};
foreach (XmlElement e in nodLst[i].ChildNodes) {
int val = int.Parse(e.InnerText);
switch (e.Name) {
case "pos":
sd.pos = val;
break;
case "starlv":
sd.startlv = val;
break;
case "addhp":
sd.addhp = val;
break;
case "addhurt":
sd.addhurt = val;
break;
case "adddef":
sd.adddef = val;
break;
case "minlv":
sd.minlv = val;
break;
case "coin":
sd.coin = val;
break;
case "crystal":
sd.crystal = val;
break;
}
}
Dictionary<int, StrongCfg> dic = null;
if (strongDic.TryGetValue(sd.pos, out dic)) {
dic.Add(sd.startlv, sd);
}
else {
dic = new Dictionary<int, StrongCfg>();
dic.Add(sd.startlv, sd);
strongDic.Add(sd.pos, dic);
}
}
}
}
public StrongCfg GetStrongData(int pos, int starlv) {
StrongCfg sd = null;
Dictionary<int, StrongCfg> dic = null;
if (strongDic.TryGetValue(pos, out dic)) {
if (dic.ContainsKey(starlv)) {
sd = dic[starlv];
}
}
return sd;
}
开发思路讲解
首先在服务器中的PlayerData类中添加一个强化列表,用来存储强化数据
public int[] strongArr;
在数据库中新添加一栏用来存储这些数据,因为数据库中没有数组类型,所有我们使用varchar类型代替。
强化升级存储结构
这节主要负责强化数据的初始化,添加和查询。
int[]_strongArr = new int[6];
string[] strongArr = reader.GetString("strong").Split('#');
for(int i=0;i<strongArr.Length;i++)
{
if (strongArr[i] == "")
continue;
if(int.TryParse(strongArr[i],out int starLv))
{
_strongArr[i] = starLv;//将int类型赋值给数组
}
else
{
PECommon.Log("Parse Strong Data Error", LogType.Error);
}
}
playerData.strongArr = _strongArr;
如果不存在初始数据,就全部设置为0.
strongArr = new int[6],
添加和查询强化数据,需要将整型数组变成 1#2#1# 这样的形式。
string strongInfo = "";
for(int i=0;i<pd.strongArr.Length;i++)
{
strongInfo += pd.strongArr[i];
strongInfo += "#";
}
cmd.Parameters.AddWithValue("strong", strongInfo);
强化升级系统逻辑1
在StrongWnd中定义好需要的text和image组件
public Image imgCurtPos;
public Text txtStartLv;
public Transform startTransGrp;
public Text porpHP1;
public Text porpHurt1;
public Text porpeDef1;
public Text porpHP2;
public Text porpHurt2;
public Text porpDef2;
public Image propArr1;
public Image propArr2;
public Image propArr3;
public Text txtNeedLv;
public Text txtCostCoin;
public Text txtCostCrystal;
public Text txtCoin;
添加一个刷新方法,用来刷新这些参数
private void RefreshItem()
{
//金币
SetText(txtCoin, pd.coin);
switch(curtIndex)
{
case 0:
SetSprite(imgCurtPos, PathDefine.ItemToukui);
break;
case 1:
SetSprite(imgCurtPos, PathDefine.ItemBody);
break;
case 2:
SetSprite(imgCurtPos, PathDefine.ItemYaobu);
break;
case 3:
SetSprite(imgCurtPos, PathDefine.ItemHand);
break;
case 4:
SetSprite(imgCurtPos, PathDefine.ItemLeg);
break;
case 5:
SetSprite(imgCurtPos, PathDefine.ItemFoot);
break;
}
SetText(txtStartLv, pd.strongArr[curtIndex] + "星级");
int curtStartLv = pd.strongArr[curtIndex];
for(int i=0;i<startTransGrp.childCount;i++)
{
Image img = startTransGrp.GetChild(i).GetComponent<Image>();
if(i<curtStartLv)
{
SetSprite(img, PathDefine.SpStar2);
}
else
{
SetSprite(img, PathDefine.SpStar1);
}
}
}
在ClickPosItem中调用这个刷新方法,每次点击一个类型的装备都会刷新。
强化升级系统逻辑2
在ResSvc中添加一个强化数值的累加方法
//获取强化的累加数值
public int GetPropAddValPreLv(int pos,int starlv,int type)
{
Dictionary<int, StrongCfg> posDic = null;
int val = 0;
if(strongDic.TryGetValue(pos,out posDic))
{
for(int i=0;i<starlv;i++)
{
StrongCfg sd;
if(posDic.TryGetValue(i,out sd))
{
switch(type)
{
case 1://hp
val += sd.addhp;
break;
case 2://hurt
val += sd.addhurt;
break;
case 3://def
val += sd.adddef;
break;
}
}
}
}
return val;
}
在RefreshItem刷新方法中继续完善数值显示的方法
int sumAddHp = resSvc.GetPropAddValPreLv(curtIndex, curtStartLv, 1);
int sumAddHurt = resSvc.GetPropAddValPreLv(curtIndex, curtStartLv, 2);
int sumAddDef = resSvc.GetPropAddValPreLv(curtIndex, curtStartLv, 3);
SetText(propHP1, "生命 +" + sumAddHp);
SetText(propHurt1, "伤害 +" + sumAddHurt);
SetText(propeDef1, "防御 +" + sumAddDef);
强化升级系统逻辑3
添加下个星级强化的数值
//下一个星级强化的数值
int nextStarLv = curtStartLv + 1;
StrongCfg nextSd = resSvc.GetStrongData(curtIndex, nextStarLv);
if(nextSd != null)
{
//显示属性
SetActive(propHP2);
SetActive(propHurt2);
SetActive(propDef2);
SetActive(costTransRoot);
SetActive(propArr1);
SetActive(propArr2);
SetActive(propArr3);
SetText(propHP2, "强化后 +" + nextSd.addhp);
SetText(propHurt2, "+" + nextSd.addhurt);
SetText(propDef2, "+" + nextSd.adddef);
SetText(txtNeedLv, "需要消耗:" + nextSd.minlv);
SetText(txtCostCoin, "需要消耗: " + nextSd.coin);
//SetText(txtCostCrystal, nextSd.crystal + "/" + pd.);
}
else//强化到达满级
{
SetActive(propHP2, false);
SetActive(propHurt2, false);
SetActive(propDef2, false);
SetActive(costTransRoot, false);
SetActive(propArr1, false);
SetActive(propArr2, false);
SetActive(propArr3, false);
}
强化升级系统逻辑4
完善之前没有做的crystal部分。
强化升级系统逻辑5
这节主要负责强化数据的服务器端和客户端通信。
首先在服务器端定义我们的消息
[Serializable]
public class ReqStrong
{
//只需要传是什么部位
public int pos;
}
[Serializable]
public class RspStrong
{
public int coin;
public int crystal;
public int hp;
public int ad;
public int ap;
public int addef;
public int apdef;
public int[] strongArr;
}
然后在客户端完成发送消息到服务器端
public void ClickStrongBtn()
{
audioSvc.PlayUIAudio(Constants.UIClickBtn);
//客户端先进行本地数据校验
if(pd.strongArr[curtIndex]<10)
{
if(pd.lv<nextSd.minlv)
{
GameRoot.AddTips("角色等级不够");
return;
}
if (pd.coin < nextSd.coin)
{
GameRoot.AddTips("金币数量不够");
return;
}
if (pd.crystal < nextSd.crystal)
{
GameRoot.AddTips("水晶数量不够");
return;
}
//请求服务器修改数据
netSvc.SendMsg(new GameMsg
{
cmd = (int)CMD.ReqStrong,
reqStrong = new ReqStrong
{
pos = curtIndex
}
});
}
else
{
GameRoot.AddTips("星级已经升满");
}
}
强化升级系统逻辑6
服务器端创建一个StrongSys专门处理强化服务器端的信息
using PEProtocol;
public class StrongSys
{
private static StrongSys instance = null;
public static StrongSys Instance
{
get
{
if (instance == null)
{
instance = new StrongSys();
}
return instance;
}
}
private CacheSvc cacheSvc = null;
public void Init()
{
cacheSvc = CacheSvc.Instance;
PECommon.Log("StrongSys Init Done.");
}
public void ReqStrong(MsgPack pack)
{
ReqStrong data = pack.msg.reqStrong;
GameMsg msg = new GameMsg
{
cmd =(int)CMD.ReqStrong
};
PlayerData pd = cacheSvc.GetPlayerDataBySession(pack.session);
int curtStarLv = pd.strongArr[data.pos];
//条件判断
//资源扣除
//增加属性
}
}
然后在服务器端ResSvc来加载强化升级配置,与客户端相似
private Dictionary<int, Dictionary<int, StrongCfg>> strongDic = new Dictionary<int, Dictionary<int, StrongCfg>>();
private void InitStrongCfg(string path) {
TextAsset xml = Resources.Load<TextAsset>(path);
if (!xml) {
PECommon.Log("xml file:" + path + " not exist", LogType.Error);
}
else {
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml.text);
XmlNodeList nodLst = doc.SelectSingleNode("root").ChildNodes;
for (int i = 0; i < nodLst.Count; i++) {
XmlElement ele = nodLst[i] as XmlElement;
if (ele.GetAttributeNode("ID") == null) {
continue;
}
int ID = Convert.ToInt32(ele.GetAttributeNode("ID").InnerText);
StrongCfg sd = new StrongCfg {
ID = ID
};
foreach (XmlElement e in nodLst[i].ChildNodes) {
int val = int.Parse(e.InnerText);
switch (e.Name) {
case "pos":
sd.pos = val;
break;
case "starlv":
sd.startlv = val;
break;
case "addhp":
sd.addhp = val;
break;
case "addhurt":
sd.addhurt = val;
break;
case "adddef":
sd.adddef = val;
break;
case "minlv":
sd.minlv = val;
break;
case "coin":
sd.coin = val;
break;
case "crystal":
sd.crystal = val;
break;
}
}
Dictionary<int, StrongCfg> dic = null;
if (strongDic.TryGetValue(sd.pos, out dic)) {
dic.Add(sd.startlv, sd);
}
else {
dic = new Dictionary<int, StrongCfg>();
dic.Add(sd.startlv, sd);
strongDic.Add(sd.pos, dic);
}
}
}
}
public StrongCfg GetStrongData(int pos, int starlv) {
StrongCfg sd = null;
Dictionary<int, StrongCfg> dic = null;
if (strongDic.TryGetValue(pos, out dic)) {
if (dic.ContainsKey(starlv)) {
sd = dic[starlv];
}
}
return sd;
}
public int GetPropAddValPreLv(int pos, int starlv, int type) {
Dictionary<int, StrongCfg> posDic = null;
int val = 0;
if (strongDic.TryGetValue(pos, out posDic)) {
for (int i = 0; i < starlv; i++) {
StrongCfg sd;
if (posDic.TryGetValue(i, out sd)) {
switch (type) {
case 1://hp
val += sd.addhp;
break;
case 2://hurt
val += sd.addhurt;
break;
case 3://def
val += sd.adddef;
break;
}
}
}
}
return val;
}
强化升级系统逻辑7
完善处理强化消息的方法
public void ReqStrong(MsgPack pack)
{
ReqStrong data = pack.msg.reqStrong;
GameMsg msg = new GameMsg
{
cmd =(int)CMD.RspStrong
};
PlayerData pd = cacheSvc.GetPlayerDataBySession(pack.session);
int curtStarLv = pd.strongArr[data.pos];
StrongCfg nextSd = CfgSvc.Instance.GetStrongData(data.pos, curtStarLv + 1);
//条件判断(服务器和客户端双重校验)
if(pd.lv<nextSd.minlv)
{
msg.err = (int)ErrorCode.LackLevel;
}
else if (pd.coin < nextSd.coin)
{
msg.err = (int)ErrorCode.LackCoin;
}
else if (pd.crystal < nextSd.crystal)
{
msg.err = (int)ErrorCode.LackCrystal;
}
else
{
//资源扣除
pd.coin -= nextSd.coin;
pd.crystal -= nextSd.crystal;
pd.strongArr[data.pos] += 1;
//增加属性
pd.hp += nextSd.addhp;
pd.ad += nextSd.addhurt;
pd.ap += nextSd.addhurt;
pd.addef += nextSd.adddef;
pd.apdef += nextSd.adddef;
}
//更新数据库
if(!cacheSvc.UpdatePlayerData(pd.id,pd))
{
msg.err = (int)ErrorCode.UpdateDBError;
}
else
{
msg.rspStrong = new RspStrong
{
coin = pd.coin,
crystal = pd.crystal,
hp = pd.hp,
ad = pd.ad,
addef = pd.addef,
apdef = pd.apdef,
strongArr = pd.strongArr
};
}
pack.session.SendMsg(msg);
}
强化升级系统逻辑8
这节完成客户端的强化消息处理。
首先完善NetSvc中消息处理函数。
private void ProcessMsg(GameMsg msg) {
if (msg.err != (int)ErrorCode.None) {
switch ((ErrorCode)msg.err) {
case ErrorCode.ServerDataError:
PECommon.Log("服务器信息异常", LogType.Error);
GameRoot.AddTips("客户端数据");
break;
case ErrorCode.UpdateDBError:
PECommon.Log("数据库更新异常", LogType.Error);
GameRoot.AddTips("网络不稳定");
break;
case ErrorCode.AcctIsOnline:
GameRoot.AddTips("当前账号已经上线");
break;
case ErrorCode.WrongPass:
GameRoot.AddTips("密码错误");
break;
case ErrorCode.LackLevel:
GameRoot.AddTips("角色等级不够");
break;
case ErrorCode.LackCoin:
GameRoot.AddTips("金币数量不够");
break;
case ErrorCode.LackCrystal:
GameRoot.AddTips("水晶数量不够");
break;
}
return;
}
switch ((CMD)msg.cmd) {
case CMD.RspLogin:
LoginSys.Instance.RspLogin(msg);
break;
case CMD.RspRename:
LoginSys.Instance.RspRename(msg);
break;
case CMD.RspGuide:
MainCitySys.Instance.RspGuide(msg);
break;
case CMD.RspStrong:
MainCitySys.Instance.RspStrong(msg);
break;
}
}
在MainCitySys中添加RspStrong方法
public void RspStrong(GameMsg msg)
{
int zhanliPre = PECommon.GetFightByProps(GameRoot.Instance.PlayerData);//原始战力
GameRoot.Instance.SetPlayerDataByStrong(msg.rspStrong);
int zhanliNow = PECommon.GetFightByProps(GameRoot.Instance.PlayerData);//当前战力
GameRoot.AddTips(Constants.Color("战力提升"+(zhanliNow-zhanliPre),TxtColor.Blue));
}
SetPlayerDataByStrong方法如下
public void SetPlayerDataByStrong(RspStrong data)
{
playerData.coin = data.coin;
playerData.crystal = data.crystal;
playerData.hp = data.hp;
playerData.ad = data.ad;
playerData.ap = data.ap;
playerData.addef = data.addef;
playerData.apdef = data.apdef;
playerData.strongArr = data.strongArr;
}
强化升级系统逻辑9
之前遗留一些小的问题,比如第一次强化时战斗力会减少,还有星级不能及时刷新
首先完善客户端处理消息的方法,让它能实时刷新星级。
public void RspStrong(GameMsg msg)
{
int zhanliPre = PECommon.GetFightByProps(GameRoot.Instance.PlayerData);//原始战力
GameRoot.Instance.SetPlayerDataByStrong(msg.rspStrong);
int zhanliNow = PECommon.GetFightByProps(GameRoot.Instance.PlayerData);//当前战力
GameRoot.AddTips(Constants.Color("战力提升"+(zhanliNow-zhanliPre),TxtColor.Blue));
//更新显示
strongWnd.UpdateUI();
maincityWnd.RefreshUI();
}
修改RefreshItem方法的局部内容。
//已经强化的数值
int nextStarLv = curtStartLv + 1;
//获取下一星级之前所有的数组加成
int sumAddHp = resSvc.GetPropAddValPreLv(curtIndex, nextStarLv, 1);
int sumAddHurt = resSvc.GetPropAddValPreLv(curtIndex, nextStarLv, 2);
int sumAddDef = resSvc.GetPropAddValPreLv(curtIndex, nextStarLv, 3);
SetText(propHP1, "生命 +" + sumAddHp);
SetText(propHurt1, "伤害 +" + sumAddHurt);
SetText(propeDef1, "防御 +" + sumAddDef);
解决战力计算bug
每次打开强化系统的第一次强化都会显示战斗力减少的BUG。
服务器发送消息给客户端时,忘记写ap。