[真机Bug]解决服务器强制定位玩家位置后NPC加载超时

2024-06-15 04:52

本文主要是介绍[真机Bug]解决服务器强制定位玩家位置后NPC加载超时,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题复现

image.png
图1 玩家在指定任务交付地点发现NPC未出现
image.png
这是一个剧情演出的任务,在播放TV结束后,角色会被强制位置到一个指定地点。而NPC的加载来源于 【场景分块加载】和【玩家的视野区(本质玩家位置)】.

  • 场景分块加载是资源优化的一种手段。对于大型地图(类似河阳城主城),由于玩家途经的位置有限。没有必要把所有GameObject放在一个scene当中。而是将一个scene按绝对位置切分为8块、16块甚至更多。保证渲染质量。
  • 视野区是一种加载物体优化的一种手段。可以对照视锥体剔除的概念理解,对于系统判断在离玩家坐标比较远的位置的物体可以不加载,如果已加载。可以让其失活进入对象池。最大程度减少hierarchy列表中可见物体的数量,从而保证渲染质量。

问题排查

首先对于这个问题,我优先想到的是检查场景分块加载的逻辑。因为最终定的位置离初始任务位置比较远,这部分的场景是后续分块加载的。那么有一种可能性是场景在TV快要结束的时候才被加载,人物加载是后于场景加载的。那么人物加载可能会延期。
于是我在场景分块加载的管理器中打了日志,标识场景id 和时间。发现,其实在播TV的前3s场景已经完成加载了,而TV总共有10s,在剩余7s的时间之内应该正常加载完成npc吧。让我们来看看加载NPC的细节。

using ViewObjIdMap = std::map<int32,bool> // obj id - 是否可见

bool Obj_Player::Tick_ViewNpc()
{Packet::GC_CREATE_NPC_PAK pak;for(auto it = m_viewNPCIdMap.begin() ; it !=m_viewNPCIdMap.end(); it++){if(IsInSight(it->first)){//如果在视野内,插入到创建map里m_viewCurNpcIdMap.insert(std::make_pair(it->first,true));//消息包插入待创建npc数据pak.m_PacketData.add_npcdata();curCount++;//一批最大创建数量(10)if(curCount >= VIEW_NPC_MAX_COUNT){braek;} } }if(curCount > 10){sendPacket(pak);//发GC_CREATE_NPC_PAK创建的包for(auto it = m_viewCurNPCIdMap.begin() ; it !=m_viewCurNPCIdMap.end(); it++){//设置每个NPC位置,发GC_MOVE的包,状态同步}}}

上面是服务器Tick里的部分逻辑,可以看出npc的创建依赖于

  1. m_viewCurNpcIdMap 是否插入了有效id
  2. m_viewCurNpcIdMap的位置,是在第几批创建,我们可以推算,一次创建10,一次tick是1000ms,那么7s正常是可以创建70 个npc实例,那么对于需求来说是完全足够的。这还是挤在单一场景中。
  3. 创建了,但是同步位置出错,导致不可见(已在客户端排除)

所以问题范围缩小到判断m_viewCurNpcIdMap的内容。

于是我在所以insert m_viewCurNpcIdMap的位置断了点。逐帧调试,在字典添加的时候打印一下日志
image.png
发现在14:25:22 ~14:25:29 这里有点奇怪,首先是没有插入别的有效id,然后耽搁了非常多时
发现扫描的id是不变的。
这说明什么呢。说明玩家的位置要么是没有变化,要么是以前已经到达的位置( ps:已经达到过的位置npc已经被标记,不会再次进入map.由于篇幅限制,这里不再展示扫描部分的代码。重点在于理解)

于是我又打印了玩家的位置,发现其在这个时间段,玩家在朝着起点位置移动,移动了一段时间后,才被强制定位到目标位置。
那么就可以解释的通了,正是因为玩家没有第一时间定位,导致玩家的服务器位置没有及时更新,那么依赖于玩家服务器位置的视野扫描在这一时间段扫描的还是旧位置,那么目标NPC便不会在这段时间被纳入字典,也不会创建了。

问题分析

那么为什么会在执行强制位置的时候往回走呢?初步推测这多半跟自动寻路有关系。
先看看强制位置部分的逻辑是怎么做的

void Obj_Char::ForceSetScene(ScenePos val,bool bMoveToPos ,int32 type)
{SetScenePos(val);if(IsSceneValid()){//设置新位置,准备发包给客户端Packet::GC_FORCE_STEPOS_PAK pak;pak.m_packetData.set_serverid(GetId());pak.m_packetData.set_posX(m_ScenePos.m_fX);pak.m_packetData.set_posY(m_ScenePos.m_fY);pak.m_packetData.set_posZ(m_ScenePos.m_fZ);//状态同步GetrScene().BroadCast_InSight_Inclue(pak,GetId());}if(IsPlayer()){Obj_Player& rPlayer = dynamic_cast<Obj_Player&>(*this);//玩家强制设置位置后,手动改变消息包的版本号,//防止接受到的位置之前的CG_MOVE消息包,导致位置异常。rPlayer.IncMovePakVersion();}}

然鹅好像没有发现什么跟寻路有关的东西,于是我又换了一种思路,从获取玩家位置的引用地方查,终于。。
找到了这么一个地方。

if(rRealEndPos!=GetScenePos())
{bool bMove1 = IsMoveing();StopMove(true,false);m_TargetPos = rRealEndPosl;m_fStopRange = 0.01f;m_PathCont.CleanUp();m_PahtCont.PushBack(PathNode(GetScenePos(),rRealEndPos))//---
}

发现如果m_PathCont缓存不为空,并且当前位置没有校验成功,会朝目标位置移动。所以如果在ForceSetScene的时候m_PathCont 不为空,而这一帧的时候客户端的实际位置又和服务器位置不一样(正常,因为服务器这边强改了位置,要通知客户端改本地的locationPos嘛,符合位置校验没有成功的情况,而m_PathCont 数据又没有清空,此时玩家的【服务器位置】会朝原始位置修正。而为什么修正一段时间后,又成功的定位到了目标位置,可以推测这时刻客户端正确收到了GC_FORCE_STEPOS_PAK的消息,修改了自身的位置,此时服务器校验通过,【服务器位置】变化中止]),这也就解释了为什么实际NPC是延迟加载出来 而不是完全加载不出。
image.png
这里方便阅读做一个补充。
(【客户端位置】就是unity工程中玩家的自身坐标它既依赖于服务器位置消息包的数据而变化(寻路、状态同步),又可以通过自身改变从而影响【服务器位置】,比如客户端主动的行为(按键等等))上文说的位置全是【服务器位置】

问题解决

在ForceSetScene 类似的服务器主动大范围修改玩家位置的地方之前,将m_PathCont 位置缓存清空即可。这样保证了下一帧服务器不会触发位置修正。而在客户端正确收到位置消息包从而改变【客户端位置】后,正常向服务器发送CG_MOVE消息的时候再校验。

这篇关于[真机Bug]解决服务器强制定位玩家位置后NPC加载超时的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062479

相关文章

2024.6.24 IDEA中文乱码问题(服务器 控制台 TOMcat)实测已解决

1.问题产生原因: 1.文件编码不一致:如果文件的编码方式与IDEA设置的编码方式不一致,就会产生乱码。确保文件和IDEA使用相同的编码,通常是UTF-8。2.IDEA设置问题:检查IDEA的全局编码设置和项目编码设置是否正确。3.终端或控制台编码问题:如果你在终端或控制台看到乱码,可能是终端的编码设置问题。确保终端使用的是支持你的文件的编码方式。 2.解决方案: 1.File -> S

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

【服务器运维】MySQL数据存储至数据盘

查看磁盘及分区 [root@MySQL tmp]# fdisk -lDisk /dev/sda: 21.5 GB, 21474836480 bytes255 heads, 63 sectors/track, 2610 cylindersUnits = cylinders of 16065 * 512 = 8225280 bytesSector size (logical/physical)

【服务器运维】CentOS6 minimal 离线安装MySQL5.7

1.准备安装包(版本因人而异,所以下面的命令中版本省略,实际操作中用Tab自动补全就好了) cloog-ppl-0.15.7-1.2.el6.x86_64.rpmcpp-4.4.7-23.el6.x86_64.rpmgcc-4.4.7-23.el6.x86_64.rpmgcc-c++-4.4.7-23.el6.x86_64.rpmglibc-2.12-1.212.el6.x86_64.r

【服务器运维】CentOS7 minimal 离线安装 gcc perl vmware-tools

0. 本机在有网的情况下,下载CentOS镜像 https://www.centos.org/download/ 1. 取出rpm 有的情况可能不需要net-tools,但是如果出现跟ifconfig相关的错误,就把它安装上。另外如果不想升级内核版本的话,就找对应内核版本的rpm版本安装 perl-Time-Local-1.2300-2.el7.noarch.rpmperl-Tim

加载资源文件失败

背景         自己以前装了一个海康的深度学习算法平台,试用期是一个月,过了一个月之后,因为没有有效注册码或者加密狗的支持了导致无法使用,于是打算卸载掉,在卸载一个软件的时候,无论是使用控制面板还是软件自带的卸载功能,总是卸载不掉,提示“加载资源文件失败”。该软体主要包括以下两部分: 用自带卸载功能卸载的时候分别提示如下:     用控制面板卸载的时候反应很慢,最后也是提示这个

chart 完成拓扑图单节点拖拽不影响其他节点位置

就是做这种的功能,箭头原本是可以动态重复移动的,但不知道哪里问题导致没箭头了,然后补了个edgeSymbol: ['','arrow'], 字段,才增加了箭头。 拖拽某个节点,只有关联到的线条会跟着变动其他的节点位置不变。 参考 https://gallery.echartsjs.com/editor.html?c=x8Fgri22P9 https://echarts.baidu.com/exa

vue同页面多路由懒加载-及可能存在问题的解决方式

先上图,再解释 图一是多路由页面,图二是路由文件。从图一可以看出每个router-view对应的name都不一样。从图二可以看出层路由对应的组件加载方式要跟图一中的name相对应,并且图二的路由层在跟图一对应的页面中要加上components层,多一个s结尾,里面的的方法名就是图一路由的name值,里面还可以照样用懒加载的方式。 页面上其他的路由在路由文件中也跟图二是一样的写法。 附送可能存在

vue+elementui分页输入框回车与页面中@keyup.enter事件冲突解决

解决这个问题的思路只要判断事件源是哪个就好。el分页的回车触发事件是在按下时,抬起并不会再触发。而keyup.enter事件是在抬起时触发。 so,找不到分页的回车事件那就拿keyup.enter事件搞事情。只要判断这个抬起事件的$event中的锚点样式判断不等于分页特有的样式就可以了 @keyup.enter="allKeyup($event)" //页面上的//js中allKeyup(e

vue+elementui--$message提示框被dialog遮罩层挡住问题解决

最近碰到一个先执行this.$message提示内容,然后接着弹出dialog带遮罩层弹框。那么问题来了,message提示框会默认被dialog遮罩层挡住,现在就是要解决这个问题。 由于都是弹框,问题肯定是出在z-index比重问题。由于用$message方式是写在js中而不是写在html中所以不是很好直接去改样式。 不过好在message组件中提供了customClass 属性,我们可以利用