Jan 1, 0001

23 mins read

GAS网络部分有了解吗?

server管理属性值、GE应用、技能执行

client发起技能激活请求ServerTryActivateAbility

UE的属性同步

1.属性必须加UPROPERTY(Replicated)

2.Actor必须开启复制bReplicates = true;

3.在 GetLifetimeReplicatedProps 注册

void AMyActor::GetLifetimeReplicatedProps(
    TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AMyActor, Health);
}

可以加上同步条件:

条件 含义
COND_None 所有人
COND_OwnerOnly 仅拥有者
COND_SkipOwner 除拥有者
COND_InitialOnly 只同步一次

GAS赋予能力的三种方法

  1. ASC赋予能力GiveAbility 角色初始就有的能力——生成武器、装配武器
  2. 通过GE赋予能力——狂暴状态解锁技能
  3. GA赋予能力

EQS底层是什么

EQS 底层就是批量生成候选点,然后对这些点进行可达性、可视性、距离等测试,计算分数并排序,从而返回最佳点。

遍历所有items然后遍历所有Test(距离Test / Pathfinding Test / Dot Test)

  1. 生成一批候选点(Items)
  2. 然后对每个点执行Tests(过滤/打分)
  3. 按分数排序
  4. 返回最佳点

fps使用状态同步在弱网环境下 导致的问题

  • (服务器权威)你在客户端移动很流畅,但是服务器延迟高,服务器收到位置后,发现跑远了然后给你拉回权威位置。
  • (包丢失/延迟)角色移动不连续
  • (射击决策用客户端时刻)客户端做表现子弹已经发出去了,但是服务器射击判定延迟,命中体验感差。

根本原因是状态同步是“结果式同步”,弱网导致状态包丢失或延迟后,客户端缺乏预测与回滚机制,无法平滑补偿,也无法保证时序一致。

那应该如何解决弱网带来的问题?

输入同步+客户端预测+服务器回滚矫正+命中回溯

1.输入同步:客户端不发当前位置,而是发当前输入,服务器能够重演玩家的行为,不会影响最终状态。

2.客户端预测:客户端在按下移动/跳跃/开枪的瞬间立刻模拟,不等待服务器。

3.服务器回滚矫正:如果客户端预测的结果和服务器不同,客户端会收到服务器的权威状态,回到服务器的位置。

4.命中回溯:服务器对“开枪时间点”进行还原的一种算法。客户端开枪时附带“开枪时间戳”,服务器收到开枪包时,会回到“子弹发射那一刻”的游戏世界状态,重新做命中判定。

GA中endabiliy去调用一个定时器会出现问题吗?定时器会不会随着GA结束而销毁

不会随着GA结束而销毁定时器,因为UE 的 FTimerManager 是挂在 UWorld 层级的,跟 GA 完全独立。

但是必须注意:定时器回调不能继续访问GA内部数据

为什么不用AbilityTask去做连击

GA 已经 EndAbility,但我还需要在“技能结束后的窗口期”继续等待一段时间,用来触发“下一段连击”。

GA 结束 → Task 必定清理 → 任务逻辑完全中断。

有一个攻击能力命中率75%,成功命中就受伤,不命中就显示Miss。该怎么去写在GAS框架中?

命中判定放在GE自定义计算类中处理:

如果没有命中给目标添加Tag,返回;

命中了,正常计算伤害,然后改变属性。

bool bHit = (FMath::RandRange(0, 99) < HitChance); // 75% 命中率

不在GA中写的原因是因为GA是行为层,而GE是数值层。

而且GA允许客户端预测,GE仅在服务器执行。如果让GA做命中判定,玩家可以轻松改客户端逻辑。所以命中、暴击等必须要在服务器的Execution计算。

游戏中的据点,怎么让在玩家攻击其中一个AI之后,让所有AI攻击玩家。

据点必须是一个组,不能让AI独立判断,通常是一个Actor存放着AI成员、当前目标、警戒半径、重置半径等。

玩家攻击了某个AI,触发了受击事件,将这个事件发送到所属的据点 Actor。

控制器广播给组内所有的AI:攻击玩家,然后所有AI改变状态、战斗。

小地图怎么显示玩家的位置,如果有敌方和己方?假如发射了信号枪,地图上该怎么显示?

将世界坐标转换成一张2D UI坐标,**注意:**不能从服务器每帧同步队友位置到客户端!!因为量太大延迟很高。但是要保证客户端的坐标不能被篡改,所以FPS游戏中权威永远在服务器。

服务器每秒发10~30次位置同步包,客户端去插值更新坐标让点平稳移动。

如果暂时没收到下一包,就用速度和方向做短暂预测。客户端的UI每帧更新。

如果发射信号弹/技能(比如探测箭),由服务器决定哪些敌人被暴露,在暴露持续期间内以固定频率给全队客户端推送暴露敌人的位置。客户端去缓存暴露敌人的最新位置快照,每帧插值位置让图标平滑移动。

如果说分为敌方阵营和我方阵营,因为我项目中用到的是IGenericTeamAgentInterface去赋予队伍ID,然后通过队伍ID的值判断是否是敌对关系。那么这样会导致,如果有16种目标,可以让每个类比如说敌人类和玩家类,去设置16种目标哪个可以被伤害,你这个时候怎么去给策划配置?让他每个角色配置16行表格吗?

16种目标对应16位二进制位

bit 0 → Player
bit 1 → Enemy
bit 2 → Monster
...
bit 15 → Vehicle

策划只需要配置0101 0010 0000 0011就能表达哪些目标能被伤害。

比如说第2位bit1是Enemy值为1,说明Enemy可以被攻击。

那么程序会将Enemy映射为0000 0000 0000 0010(把1向左移动type个bit),和配置表按位与&就能得到是否可被攻击。

ECS存在什么样的问题 缺点

ECS架构的核心思想是将数据和行为分离。实体(Entity)作为承载组件的容器,组件(Component)是纯粹的数据结构,而系统(System)则负责处理这些数据。这种分离使得系统之间的耦合度极低,开发者可以轻松添加或移除组件和系统,而不会影响其他部分的功能。

ECS 把同类组件连续存储,系统只要线性遍历这一块内存就行,缓存命中率高,性能比传统对象引用式的乱跳内存快很多。

缺点就是:

1、代码复杂

2、系统调度复杂——是因为逻辑不再由对象显式调用而是依靠数据驱动,多个系统通过数据和事件间接影响彼此,导致依赖链变长,执行顺序不直观,难以追踪。

3、由于组件在内存中连续存放,因此频繁增删会性能开销大;组件池多路扩容会导致内存碎片问题。

有个画图功能,需要加入撤销恢复功能怎么设计

**操作回滚:**Undo栈和Redo栈(限制栈大小的话需要用deque双端队列),将每个操作放入到Undo栈中,当需要撤销时,就将Undo栈中的操作对象弹出来,调用操作对象消除的方法;然后将该操作对象push到Redo栈中。缺点是某些操作Undo逻辑复杂。

**状态快照:**每次操作后保存整个画布的状态,Undo就是恢复上一个快照。缺点是内存占用大。

现在有一份乱序排行榜,十万名玩家,请问某名玩家要如何知道自己在排行榜中的位置?如何优化性能?

Redis的Sorted Set,结合了哈希表跳表

成员唯一:每个 member(玩家 ID)在集合中只出现一次

分数排序:每个 member 关联一个分数(score),Sorted Set 会根据分数进行自动排序

有序访问:支持按分数范围、索引范围访问成员

跳表示意图:

Level 3:      1 ------------------------ 11
Level 2:      1 --------- 5 --------- 9 -------- 11
Level 1:      1 --- 3 --- 5 --- 7 --- 9 --- 11
Level 0:      1 → 3 → 5 → 7 → 9 → 11 // 完整链表

玩家在地图中移动,如何快速找到离玩家最近的城镇?

1.网格分块:将地图划分成小网格,每个网格维护一个城镇列表。玩家只需要查询所在网格和相邻几个网格。

2.KD-Tree:适合静态点集(城镇固定不动),KD树每层按x / y坐标(奇数层x,偶数层y)划分空间。玩家坐标进入树种查找,就像是在二叉搜索树中比大小一样。

可以参考该文讲解kd树:KNN(K近邻)算法之——KD-Tree构建及查找原理 - hello_nullptr - 博客园

kdtree

3.四叉树:将空间划分为四个方格,递归处理。玩家如果在其中一个方格则继续划分。遍历其中所有的城镇,计算距离找到最近的。

32名玩家,每个人指挥100个士兵,同时往某个地点扔1个手雷,那么同时会有3200个手雷发生爆炸,这段时间会很卡,怎么优化?

首先如果同时3200个手雷爆炸会导致——每个爆炸需要碰撞检测、粒子特效渲染、网络同步压力过大。

  • 因此可以将空间接近的爆炸合并为一个逻辑爆炸——在比如说5m以内的爆炸合并为一个爆炸事件,伤害和范围累加,渲染时只播放1个粒子特效,这在玩家感知上没有差别。
  • 将时间错开(给每个手雷增加微小随机延迟0~100ms玩家感觉是“同时爆炸”,实际 CPU/GPU 处理分摊在几帧)
  • 粒子合批(Batching),相同类型爆炸粒子尽量合并渲染 → GPU draw call 减少。LOD和屏幕外剔除,玩家看不到的爆炸不渲染。

第三人称射击怎么能实现在准星瞄准发射的,但是枪口会朝着目标发射子弹,这个子弹是朝着哪个方向发射?

从摄像机中心发射射线,得到准星对应的命中点;再以枪口为起点,朝该命中点计算发射方向。

RayStart = CameraLocation;
RayDir   = CameraForward;
RayEnd   = RayStart + RayDir * MaxDistance;
FireDir = Normalize(HitPoint - MuzzleLocation);

UE的垃圾回收是用的哪一个?为什么不用分代GC?

基于反射系统的标记清除法,因为UE的UObject生命周期长,分代GC适合大量短生命周期对象(新生代)。UObject带反射信息

但是UE做了优化:

  • 增量GC:拆分成多帧执行,GC不会一次性跑完,减少卡顿;
  • Cluster对象聚类:将强相关的Uobject组成一个Cluster,作为整体处理,减少遍历成本。

USTRUCT UCLASS UENUM这些哪个参与反射?

都参与反射。

  • UCLASS() - 告知虚幻引擎生成类的反射数据。类必须派生自 UObject。
  • USTRUCT() - 告知虚幻引擎生成结构体的反射数据。但是不参与GC。
  • UENUM() - 告知虚幻引擎生成枚举的反射数据。但是不参与GC。

IK原理

已知末端位置/朝向,反推各个关节的角度。

比如说角色上楼梯,去解决脚步在每一步踩在地面,可以使用Two Bones IK,两根骨头(大腿骨、小腿骨)长度以及由大腿根部到台阶的距离,就可以确定一个三角形,因此可以确定关节的旋转角度。

01

IK的joint约束

CCD(Cyclic Coordinate Decent)算法:从末端往根节点,一个关节一个关节翻。最常用的算法。

FABRIK(Forward And Backward Reaching IK)

雅可比矩阵

为什么TCP不可能保证传输有序?

TCP 并不保证网络中数据包按发送顺序到达(乱序到达),但是它保证有序交付。

TCP之所以有序传输是因为:

  • 每个TCP包都带有一个唯一的序列号,接收端使用此序列号来重新排列接收到的数据包;
  • 接收端在收到数据包后会发送一个ACK给发送端,还包含了下一次期望接受的序列号;
  • 滑动窗口机制允许发送端在确认应答前发送多个数据包。如果数据包丢失或者乱序,接收端只会确认最后一个按序接收的数据包。
  • 重传机制如果发送端在一定时间内没有收到对某个数据包的确认应答,它会假定该数据包丢失并重新发送。这确保了所有数据包最终都能到达接收端。

TCP心跳包(Keepalive)——TCP保活机制:

进程间通信 死锁 如何解决死锁

Sharing is caring!