从0开始的网游ARPG实战案例:暗黑战神(第七章:强化升级系统)

目录

第7章: 强化升级系统

制作强化界面UI

图片点击事件注册

点击事件传参

位置列表显示控制

配置数据生成技巧

强化升级数据解析

开发思路讲解

强化升级存储结构

强化升级系统逻辑1

强化升级系统逻辑2

强化升级系统逻辑3

强化升级系统逻辑4

强化升级系统逻辑5

强化升级系统逻辑6

强化升级系统逻辑7

强化升级系统逻辑8

强化升级系统逻辑9

解决战力计算bug


第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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值