Moba案例帧同步

1.设置环境光

2.搭建客户端框架

a.对上述的模块基类(都是单例类,都有初始化方法)

b.隐藏登录页面(得到Canvas的子对象失活)

c.设置根对象过场景不被移除

tip:模块都是继承mono的,不能直接new出来使用,通过挂载对象,得到脚本组件来使用。

3.登录UI

a.声明LoginWnd 继承公共类WindowRoot

b.LoginSys调用相关的登录方法

4.添加登录界面音效

a.资源加载音效文件返回给音效模块使用

b.创建了一个系统基类,系统子类重写初始化方法

c.LoginSys调用相关的加载音乐方法

5.开始按钮的点击

6.设置tips界面

a.动画实现淡入淡出效果

b.避免多个tip混用,用队列实现一条tip实现

c.一条tip显示完之后,bool标志设为false。

d.多个模块都有tips,在根物体下声明tip类(UI模块都继承自Windowroot,其具有对根物体的引用,其他UI模块继承就直接可以用增加tip方法了),并定义增加tip的方法

************************客户端框架结束**********************

7.搭建服务端框架

8.引入KCP.dll

MobaSession 会话类 MobaProdof协议类

9.对网络消息的处理

        服务器的IO操作很密集,要服务多个客户端,要使用多线程,收到数据之后,数据不能都在多线程接中处理,有些核心敏感的数据必须放在单线程中处理(比如两个客户端在同一时间在多线程里收到了请求登录的消息:一样的账号和密码,只能一个客户端登录,这时候服务器是不能让这两台客户端同时上线的,先来的先登录,提示后来的此账号已登录),保证核心数据的可靠性。

        要实现上述,有两种方法:

1.接收到数据之后就进行加锁处理,逻辑部分就是在单线程中处理的。较简单。

2.接收数据还是在多线程中处理,最后拿数据的时候再进行单线程处理,确保数据的安全,较复杂,很多的加锁解锁

这里用的是第一种:声明一个字符串,锁这个字符串(就是锁一个固定的内存地址),用队列来实现消息的进出。

10.客户端和服务端成功建立连接

11.服务器处理分发消息

网络模块 NetSvc:HandleMsg分发给 登录系统 LoginSys

LoginSys 进行判定是否已经上线(依赖网络模块 CacheSvc)最后存储相应数据到 CacheSvc

12.客户端处理消息回应

NetSvc得到消息进行判断 调用登录系统的登录方法(保存用户数据)

用户数据写在GamRoot里 方便调用

Start界面的显示 按钮绑定  Img进度条的设置:

13.匹配计时界面的显示

14.请求匹配队列

框架思路:

a.定义Msg协议类(协议序列化类,匹配类型,声明类) 搞完之后要重新生成一下 再进unity才能使用

b.客户端点击相应的匹配按钮 发送相应的协议类消息给服务端 

服务端对这些消息进行分发处理,Session接收消息放进消息队列 update里面自动调用HandleMsg方法

c.匹配系统

15.匹配倒计时(TickTimer)

写在倒计时服务模块

参数0表示需要使用内部封装的update来驱动,其他数字代表新开线程来驱动。false表示不需要放到其他处理器字典进行处理,直接在update里面处理。

16.客户端跳转匹配确认界面

固定模式:UI模块  服务模块处理系统模块的函数 系统模块对应相应的UI处理函数

确认界面UI绑定 、音效、倒计时

17.确认信息同步到各个客户端

客户端发来确定信息=>服务端接收=>相应的系统处理(RoomSys)=>相应的房间根据session处理得到确认的id=>相应的房间状态(RoomStateConfirm)根据id进行处理:广播消息

18.服务端处理英雄确认消息
客户端发来英雄确定信息=>服务端接收=>相应的系统处理(RoomSys)=>房间(pvproom)根据session处理得到确认的id=>得到相应的房间状态(RoomStateSelect)根据id进行处理

战斗逻辑

1.初始化碰撞环境

2.声明框架类

3.客户端发送移动数据到服务器

4.服务端对移动操作的下发

战斗系统收到服务端的移动操作信息后,调用战斗管理类的输入方法,接着调用主要逻辑类的输入方法,判断是什么类型的操作,进入相应的移动,技能等子模块,子模块进行相应的处理。

5.运动预测算法逻辑 (69)

基于上一个逻辑帧的LogicPos进行预测,具体是 viewtarget +=方向*速度*Time.deltime。等到下一次逻辑帧的数据发过来就强制刷新到逻辑帧的位置。

同时,如果英雄转弯,直接把预测的位置赋值给position会导致一卡一卡的。平滑处理:调用V3.lerp函数,第三个参数是时间*平滑的加速度

转向预测平滑:

6.运动朝向的平滑处理

         因为不知道下一步玩家会向哪里转,不适合做预测处理,但是可以优化,具体步骤是:声明一个基于角度大小的一个变化量 ,转的角度越大变化就越快。

   void UpdateDirection()
   {
   if (logicUnit.isDirChanged)
   {
       viewTargetDir = GetUnitViewDir();
       logicUnit.isDirChanged = false;
   }
   //转向平滑处理
   if(SmoothDir) 
   {
       float threshold=Time.deltaTime*viewDirAccer;
       float angle=Vector3.Angle(RotationRoot.forward, viewTargetDir);   
       //基于角度大小的一个变化量 越大变化量就越大
       float angleMult=(angle/180)*AngleMultipLier*Time.deltaTime;
           if(viewTargetDir!=Vector3.zero)
           {
               Vector3 interDir=Vector3.Lerp(RotationRoot.forward, viewTargetDir,threshold+ angleMult);
               RotationRoot.rotation=CalcRotation(interDir);
           }
   }
   //强转
   else
   {
       RotationRoot.rotation = CalcRotation(viewTargetDir);
   }

7.玩家碰撞墙体后的朝向处理

       Hero类重写方法,与墙体碰撞后返回的是玩家UI输入的朝向。

8.添加普攻音效

        在skillcfg里添加三种音效=>ResSkillCfgs里去指定三种音效的路径=>AudioSvc(延迟)播放音效

        ViewUnit类(PlayAudio(AudioSvc(延迟)播放音效))=> MainLogicUnit 调用mainViewUnit的(PlayAudio(AudioSvc(延迟)播放音效))

        释放技能函数=>施法前摇函数(设置技能状态,播放音效)

9.飘字显示

MainLogicAttrs(飘字信息:hp;受到伤害之后的回调委托)=>MainViewUnit(实现了回调委托,更新了JumpUpdateInfo的3d信息位置)=>HpWnd(调用JumpNum的show方法,显示飘字效果)

基本就是逻辑层做委托回调,显示层更新位置信息,UI层进行显示处理。

10.技能计时器

一些参数:

驱动计时器TickTimer 运行:

延迟计时的倒计时,多余的时间放入Tick中处理;得到 delay循环的进度和总体循环进度,调用,延迟计时结束后调用Tick。

Tick 进行:

计时按钮案例

//test
MonoTimer timer;
public void ClickTime()
{
    SetText(txt, 5);
    timer?.DisableTimer();
    timer = CreateMonoTimer(
        //循环次数 设置时间
        // MonoTimer里面默认构造loopCount等于1
        (loopCounter) =>
        {
            this.Log("loop:" + loopCounter);
            SetText(txt, 5 - loopCounter);
        },
        1000,
        5,
        (isDelay, prgloop, prgall) =>
        {
            //每次循环 imgloop显示
            SetActive(imgloop, true);

            //是否延迟 数字是否显示
            if (isDelay)
            {
                SetActive(txt, false);
            }
            else
            {
                SetActive(txt, true);
            }
            //更新两者的fillAmount
            imgloop.fillAmount = 1 - prgloop;
            imgAll.fillAmount = prgall;

        },
        //结束置空
        () =>
        {
            SetActive(imgloop, false);
            imgAll.fillAmount = 0;
            this.ColorLog(PEUtils.LogColor.Green, "Loop End");
        }
        );
}

逻辑定时

哪些地方用到了逻辑定时?

1.上述的技能逻辑定时(技能延迟执行)

2.小兵出生的逻辑定时

一波里面的小兵出生计时

每波小兵的计时

3.金币的逻辑计时

以上除了技能逻辑定时是在对应的逻辑技能类中,其余都是在FigMgr的Tick里进行tick(都放入一个List)

11.亚瑟一技能的强化普攻处理(127)

技能本质上就是通过配置信息来起作用的,所以通过改变cfg的信息就可用实现不同的强化技能效果。

思路主要是用另外一个技能替换掉普攻,在MainSkillLogic中声明一个得到技能的方法(GetSkillbyID)并返回技能,skill类的技能替换函数调用GetSkillConfigByID函数判断是否是普攻进行cfg信息的替换,替换buff在相应的类中进行处理。

skill类初始化的时候通过skillid加载不同的cfg配置文件,就会改变cfg的信息

替换buff的两种结束状态:1.强化普攻释放完毕(Start函数),cfg信息变为原来的(End函数)。                                2.3s内没有进行普攻,也变为原来的cfg信息

12.移动下普攻不显示动画,朝向没有改变的问题

移动下普攻不显示动画:free Ani替代了atk Ani,在MainlogicView增加是否处于技能前摇的判定

朝向没有改变:HeroView的更新朝向都是UI输入方向,加个判定,如果处于技能前摇阶段就返回的是ViewTargetDir

13.释放技能时按方向键产生滑动;技能释放完之后方向键不松也会产生滑动idle。

        释放技能时按方向键产生滑动:在MainLogicMove里增加判定:存在技能释放前摇的时候,此时玩家的位置不被ui输入所影响。

        技能释放完之后方向键不松也会产生滑动idle:原因是技能释放完成后会调用相关的回调(end状态播放了free Ani)。技能释放完之后方向键不松:前摇结束了,期间一直有ui的输入,所以技能后摇没有,直接结束,但技能SkillTalTime还在计时,计时完毕就调用关的回调(end状态播放了free Ani);

        解决办法:在LogicUnit声明一个回调(UI输入改变),在LogicPos属性中set里进行调用。在MainLogicSkill里的初始化函数中进行回调的绑定;

14.亚瑟一技能标记加伤buff的实现

在声明的buff类中的Start和End分别调用,Onhurt这一受到伤害的回调,+= ,-=。

15.亚瑟一技能友军群体加速buff

注意点:tick中先恢复速度,再次进行targetList查找。

16.亚瑟大招跳跃向敌人buff

17.亚瑟大招击飞buffCfg

18.亚瑟大招持续伤害buff

15.智能移动攻击完之后玩家一直在移动;智能移动攻击的优先级大于UI输入

解决办法:1.在智能移动攻击完之后让玩家停止(BattleSys.instance.SendMoveKey(PEVector3.zero);)

                   2.playwnd判断是否有UI输入,BattleSys声明对应的函数。在智能移动攻击的buff里发送移动消息之前先判定是否有ui输入,存在就把buff状态置为None。

亚瑟普攻和一技能制作总结

技能释放流程:客户端NetSvc接收到NtfOpKey消息=>BattleSys的 NtfOpKey(MobaMsg msg)=>FIghtMgr中接收Battlesys的消息=>下发到MainLogicUnit=>根据消息类型(skill)对应的逻辑类(MainLogicSkill)进行处理(根据传来的技能消息包含的id和技能数组id比较)=>根据消息指定级技能的范围+Skill类进行释放技能的具体处理

buff释放流程

三地方创建buff或者是附加buff

一个是在MainLogicSkill的Init里面:创建被动buff

其余都是在Skill里的释放技能的时候:根据技能的释放情况创建hit之后产生的buff、技能释放后附加到自身的buff

移动、计时和buff的驱动:BattleSys驱动fightMgr=>fihtMgr驱动对应的英雄模块=>Hero模块Tick(没有实现,执行的是父类MainLogicUnit的LogicTick)=>MainLogicUnit的LogicTick(包括移动的驱动和技能的驱动:技能驱动又包括:buff驱动和计时驱动)

Skill类进行释放技能的具体处理:对应的流程在技能运行框架图结合代码。

亚瑟开发结束

****************************

后羿开发开始

后羿有七个技能:(括号是对应的buff)常规的分别是:普攻,一(10201,这个buff只是用来替换技能的,没有实质的效果)二( 10220, 10221, 10222, 10223)三技能(10230, 10231)

                        一技能强化普攻为散射替换技能1024(10240)

                           被动多重设计替换技能1025(10250)

                        一技能强化和被动强化结合替换技能1026(10260):这个buff的代码就是10240和10250的结合体。

二技能的中间额外伤害和减速是通过得到技能释放的LogicPos,根据LogicPos,再通过设置的buff的range不一样实现的。

1.子弹体积算法

2.驱动子弹

ResSvc 创建子弹函数(返回相应的子弹类型)=>FightMgr tick子弹(放到战斗管理器是因为要不受其他单元影响,独立),battleSys添加Add方法=>MainLogicSkill类创建具体的子弹,并初始化,放入List=>Skill技能生效函数,得到创建的子弹,设置回调。

3.后羿被动加攻速buff的实现

三件套:buff类,buff配置,ResSvc加载

buff类的具体逻辑:

初始化:各参数赋值,得到所有技能,把释放完的回调绑定声明的OnSpellSkillSucc函数。

OnSpellSkillSucc函数:计时和重置 ,调用ModifyAttackSpeed直接改变攻击速率。

Tick函数:每66ms检测一次

ResetSpeed重置函数:还原速率

3.被动的替换普攻buff的实现

三件套:buff类,buff配置,ResSvc加载

buff类的具体逻辑:

初始化:各参数赋值,得到所有技能,把释放完的回调绑定声明的OnSpellSkillSucc函数。

OnSpellSkillSucc函数:计时和重置 ,调用ReplaceSkillCfg直接替换技能。(注意是否提前按下了一技能)

ResetSpeed重置函数:还原技能

 3. 一技能强化普攻为散射替换技能1024(10240)

4.后羿二技能配置

技能没有设置,在buff里造成伤害


4.实现后羿一技能的曲线效果,设置子弹曲线弹道。

1.设置一个随机数种子

红绿两个向量合成为黄色。

绿色的向量就是垂直于红色向量和upx向量所构成的平面=>(所以可以直接用叉乘得到)

最后绿色的向量可以向上偏移随机正数个位置。(美术效果更好)

5.报错:在播放动画的时候有数值<0;

在修改攻击速率的时候,直接赋值了变量而不是其属性 导致属性里set的逻辑没执行。

技能替换后,时间还是原来的值不用改变。

6.伤害飘字的优化

beforre:飘字重叠,不太美观        

优化思路:把相关的飘字和血条结合在一起,伤害飘字的jump放到ItemHp类中的一个队列里面,出一个显示一个

相关的性能优化:取消了ItemHp里的生命周期Update,优化到了HpWnd里的Udate,判断DicItem里是否有东西才去调用,提升性能。

7.小地图制作思路

把对应地图上的英雄位置减去原点位置得到的v3赋值给小地图的RectTransForm的localPosition(进行了一定的缩放 scaler) 

8.小兵的AI实现

通过InputMoveForwarKey指定小兵的移动方向。

每五帧检测一次,是否有目标可以攻击,遇到敌方就攻击,若找不到目标,则启用智能攻击。若任然超出智能攻击范围,就继续执行InputMoveForwarKey指定的方向。

9.本地模式(模拟服务端)的实现流程(拦截消息)

主要就是playWnd里的移动消息发送(playWnd)和技能消息发送(SkiItem)

BattleSys发送消息

GM模式拦截 return

 在FixedUpdate里根据66ms的频率(手动设置)来tick整个逻辑帧。

在66ms内收到的所有操作数据,全部广播发送给客户端。

加入队列之后,update判断是否是GM模式,处理消息

10.再写服务端~

服务端处理操作消息流程:NetSvc接收消息=>RoomSys判断消息属于的房间=>pvpRoom判断房间状态=>RoomStateFight类进行处理

NetSvc接收消息

RoomSys判断消息属于的房间

pvpRoom判断房间状态

RoomStateFight类进行处理

11.聊天相关

服务端:定义相应的协议,最后到pvproom分发给各客户端

客户端:最后到PlayWnd处理

三个函数:

AddMsgContent:添加相应的聊天消息到数组中;超过消息最大数量就减去第一个

RefreshChatUI:刷新txt的UI显示,每条消息后加\n换行

UpdateChatList:驱动聊天文字显示时间

12.网络延迟,心跳消息相关

客户端updete里用Invoke发送ping消息给服务端

在Init里重置的时候(把消息队列给清空)取消掉invoke,不取消的话就会有很多个NetSvc的invoke发送消息

服务端定义对应的ping协议

接收客户端session发来的消息

根据pingID马上回复RspPing消息给对应的客户端

51:小兵逻辑

小兵的移动用的是InputFakeMoveKey直接传入移动的方向

tick里调用。

52.塔的逻辑

初始化判断水晶掉没掉,执行相应的代码

tick里调用AItick

遇到的问题:

1.

【Unity 导入项目时Resolving Packages卡住的解决方案】_resolving packages卡住了-CSDN博客

2.继承接口的类赋值给对应的接口对象

3.

4.序列化和反序列化的c#封装方法 压缩和反压缩

5.序列化

Unity基础篇:Serializable总结与深入研究。_unity serializable-CSDN博客

6.unity报空 结果进代码调式第一次都有值,需要一直点调试,直到第二次发现消息为空!找了三四个小时 0.0 发现是消息发错了 一个消息发了两遍 !!

7. float转换会损失精度 必须用显示转换 

8.c#implicit和explicit的区别

定点数运算课程:

8.技能拖拽位置一直不对,钳制在Canvas的左下角,是因为把localPosition错误设置成了Position!!!

9.传的是id而不是index

10.vs代码不能挂载到物体上,原因是因为vs脚本名和unity声明的脚本名不一致导致的!!

11.亚瑟二技能对亚瑟单位伤害很高,2s直接死亡,原因是二技能buff配置中的damage设置成了

int          解决办法:改成PEInt就没问题了

12.亚瑟大招过后立马按平A,平A的动画没有播放完就进入了free状态(其实是大招后摇没结束被打断所调用的free)。(每个技能结束后都会调用free)

解决办法:在调用free之前先清空free调用。

5.实现后羿三技能碰撞伤害时,传友军的问题

这个buff实际上是创建dir子弹,命中敌人所导致的。注意最后一句代码,是target创建的buff,即命中的敌人它自己创建的buff,所以上述buff配置中,作用的范围应该是敌方的友军。

类比 目标子弹 TODO

6.水晶爆炸,结束界面弹出时报错:SkillItem里的协程无法启动。

原因:游戏结束时,playwnd关闭,resWnd开启,这样playwnd里的effectRoot结点就看不到了。但是还是处于激活状态(所以这里判断只能用activeInHierarchy(场景中是否显示),而不是activeSelf(物体是否显示)

因为playwnd关闭,其下的所有子物体都是关闭的,但是落实到子物体可能还是处于激活状态);

解决办法:加上是否在场景中显示的判断。

7.水晶爆炸后点击继续按钮无法返回到大厅

原因:pvproom里面忘记把end状态添加到相应字典了。房间进入end状态广播消息给客户端是在Enter函数中而不是构造函数中。

8.水晶爆炸后,回到大厅再次匹配,进入英雄选择界面时,左侧有三个角色,两个后羿一个亚瑟。

原因:采用的直接遍历,没有清除干净。

解决:采用倒叙遍历清除scroll的内容

9.一直点普攻英雄就会一直攻击(频率很快就很恐怖,和实际游戏不一样)

原因:判断是否释放完技能函数那里有错误 写快了

10.遥感抬起后,英雄一直移动,没有停止

参数传错了!!!!  浪费好多时间

11.玩家遥感移动过程中释放技能完毕后,玩家任然朝原方向移动

原因:技能前摇阶段的v3.zero消息没有发送出去,导致技能释放完毕后,执行的移动还是释放技能之前的遥感输入

解决:取消这个判断

12.基于上一个问题解决后,移动是可以停止了,但是技能释放完之后不能播放到free动画

原因:技能前摇阶段发送了v3.zero的消息,而ui输入改变的时候就会触发回调,把free动画清理掉。

解决:加上判定,不是前摇阶段才清空。

13.低帧率下英雄血条抖动问题

原因:相机的位置更新快于血条的相关位置的映射

解决:把相机的位置更新从update改为放到lateupdate中

14.英雄特定方向行走(z轴负方向)模型颠倒问题

原因:四元数的计算出现了问题,

解决:不使用四元数来计算,之间把得到的朝向赋值给物体的forward即可

之前:

改为:

15.【修正】高帧率下高频输入延时问题

一个逻辑帧只能跑15次,每秒15帧

优化:1.服务端方面 对消息的处理是if判断 也就是说一个逻辑帧只能执行15次ui信息输入,改成while判断

           2.playwnd发送遥感消息方面,用FixedUpdate,设置每秒中发送消息的次数的最大值

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值