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赋予能力的三种方法
EQS底层是什么
EQS 底层就是批量生成候选点,然后对这些点进行可达性、可视性、距离等测试,计算分数并排序,从而返回最佳点。
遍历所有items然后遍历所有Test(距离Test / Pathfinding Test / Dot Test)
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 - 博客园

3.四叉树:将空间划分为四个方格,递归处理。玩家如果在其中一个方格则继续划分。遍历其中所有的城镇,计算距离找到最近的。
32名玩家,每个人指挥100个士兵,同时往某个地点扔1个手雷,那么同时会有3200个手雷发生爆炸,这段时间会很卡,怎么优化?
首先如果同时3200个手雷爆炸会导致——每个爆炸需要碰撞检测、粒子特效渲染、网络同步压力过大。
第三人称射击怎么能实现在准星瞄准发射的,但是枪口会朝着目标发射子弹,这个子弹是朝着哪个方向发射?
从摄像机中心发射射线,得到准星对应的命中点;再以枪口为起点,朝该命中点计算发射方向。
RayStart = CameraLocation;
RayDir = CameraForward;
RayEnd = RayStart + RayDir * MaxDistance;
FireDir = Normalize(HitPoint - MuzzleLocation);
UE的垃圾回收是用的哪一个?为什么不用分代GC?
基于反射系统的标记清除法,因为UE的UObject生命周期长,分代GC适合大量短生命周期对象(新生代)。UObject带反射信息
但是UE做了优化:
USTRUCT UCLASS UENUM这些哪个参与反射?
都参与反射。
IK原理
已知末端位置/朝向,反推各个关节的角度。
比如说角色上楼梯,去解决脚步在每一步踩在地面,可以使用Two Bones IK,两根骨头(大腿骨、小腿骨)长度以及由大腿根部到台阶的距离,就可以确定一个三角形,因此可以确定关节的旋转角度。

IK的joint约束
CCD(Cyclic Coordinate Decent)算法:从末端往根节点,一个关节一个关节翻。最常用的算法。
FABRIK(Forward And Backward Reaching IK)
雅可比矩阵
为什么TCP不可能保证传输有序?
TCP 并不保证网络中数据包按发送顺序到达(乱序到达),但是它保证有序交付。
TCP之所以有序传输是因为:
TCP心跳包(Keepalive)——TCP保活机制:
进程间通信 死锁 如何解决死锁
Sharing is caring!