UE- Blast (正在更新)
这边是Blaster 笔记 自己整理的有点杂乱 是本人 unity转ue的一个重要项目,
采用前视频后源码 去了解这个项目。
中间有其他资料与AI辅助。
插件编写(待)
这边我先开盖即用插件
Play设置
编译模式
模式 | 用途描述 | 编译特点 | 文件/性能特点 |
---|---|---|---|
DebugGame | 开发过程中进行调试。在游戏中设置断点、查看变量值以及代码调试。 | 以调试模式编译,包含调试符号(debug symbols)。 | 可执行文件较大且运行速度较慢。 |
Development | 开发过程中进行内部测试和验证。相比DebugGame模式会进行更多优化,同时保留调试能力。 | 启用优化选项以提高运行性能,保留部分调试信息。 | 运行性能更高,但仍便于问题排查。 |
DebugGame Editor | 在Unreal Editor中调试游戏功能(与DebugGame模式类似,但专用于编辑器环境)。 | 编译包含调试符号,便于在编辑器中调试。 | 可执行文件较大,运行效率较低。 |
Development Editor | 在Unreal Editor中进行开发测试。优化编辑器运行性能,同时保留调试能力(与Development模式类似)。 | 启用优化选项提高编辑器性能,保留部分调试信息。 | 编辑器运行更流畅,同时支持问题排查。 |
Shipping | 发布给玩家的正式版本构建。 | 全面优化编译,移除所有调试符号和调试信息。 | 可执行文件最小化,运行性能最高(无调试开销)。 |
网络模式(ENetMode)
更新插件代码
1 | // 原弃用代码:bIsFocusable = true; |
基础3C
项目建立
- 编译项目
- 装入自定义网络插件
- 装入Online SubSystem Steam
- 设置ini:
在DefaultEngine.ini写入
1 | [/Script/Engine.GameEngine] |
在DefaultGame.ini写入
1 | [/Script/Engine.GameSession] |
删除二进制文件夹
创建关卡
- 创建StartUpMap,LobbyMap
- 在Project Setting - Map&Modes - 里面更换默认的Map
- 在StartUpMap关卡蓝图里面调用Create WBP Menu widget -> Menu Setup (设置Lobby路径)
Bulid
- 设置bulid的map : 在Project Setting - 搜索 maps to 设置俩个关卡
- 平台-打包-windows-放入新建的build文件夹
建立网络连接
- 在内容浏览器里面ADD 第三人称资产
- 重点概念 input (待定)
- GameMode设置第三人称 -
资源
重定向(待定)重定向教程
3C
Character
新建一个cpp类 注意
1 | #include "FCharacter.h" |
头文件path要对,新建立的时候默认h会错误
每次更新都 ====在编辑器或游戏窗口中按下 **Ctrl + Alt + F11
**,强制终止当前 Live Coding 进程,释放资源后重新编译
C++类创建蓝图 并且设置Mesh和pos
Camera
FCharacter
1 |
|
Control
project setting - Engine Input (类似unity Input Manager)
设置人物BP_FCharacter - Pawn - 自动控制玩家 - 选择玩家0
FCharacter
1 | // Called to bind functionality to input |
Anim
FAnimInstance
1 | void UFAnimInstance::NativeInitializeAnimation() |
动画蓝图创建
- 类设置 - 父类设置为 F Anim Instance
创建状态机
- 创建状态
- 创建连线
- 创建连线条件
创建混合空间(混合空间1D和混合空间的区别)混合空间
- 放入 Idle Walk Run 三个动画
- X轴设置为Speed
- 放入动画状态机 中 并且输入设置为CPP文件中的Speed
修改动画手感与摄像机小优化
自由摄像机视角
- FCharacter.cpp
1 | //动画后的优化 |
- BP_FCharacter - 使用控制器旋转Yaw - false
- CharMoveComp - 将旋转朝向运动 - true
下落bug
- 下落动画Loop
无缝切换
(99+ 封私信 / 80 条消息) 关卡系统四、无缝切换 - 知乎
对于地图切换(也即关卡切换),UE还提供了无缝切换(Seamless Travel)和非无缝切换(Non-Seamless Travel),无缝切换使用异步加载关卡资源,是非阻塞式切换,而非无缝切换即为前面介绍的同步加载关卡资源,是阻塞式切换(传送门),在网络联机游戏中,无缝切换不会导致网络断开,而非无缝会导致网络断开后重连,UE推荐在网络联机游戏中使用无缝切换,感兴趣可以看看官方文档(有点晦涩难懂T_T,因此需要深入研究一番)。
GameMode
创建FLobbyGameMode
设置Pawn为FCharacter
创建过度关卡
在Project Setting 里面设置过度关卡
在GameMode中设置无缝切换
1 | void AFLobbyGameMode::PostLogin(APlayerController* NewPlayer) |
HUD
- 创建HUD Class
- 创建WBP继承与HUD Class
- 在代码中绑定WBP
- 在FCharacter中设置WBP类以及展示Canvas
- 在FCharacter蓝图中反射调用UpdateText
1 | void UFPlayerHUDWidget::SetDisplayText(const FString& TextToDisplay) |
GetRemoteRole
主要API
11.Camera
CreateDefaultSubobject
是 Unreal Engine(UE)中用于创建组件或子对象的核心函数,主要在 C++ 中实现 Actor 或组件的初始化。
1 | T* CreateDefaultSubobject<TReturnType>(FName SubobjectName, bool bIsRequired = true, bool bIsTransient = false); |
- **
TReturnType
**:需创建的组件类型(如USpringArmComponent
)。 SubobjectName
:组件唯一标识(同一 Actor 内不可重复)- **
bIsTransient
**:若为true
,组件不会被序列化(适用于临时对象)
SetupAttachment
是用于建立组件层级关系的核心方法,主要作用是将一个组件(子组件)附加到另一个组件(父组件)上,形成父子依赖关系。
1 | void USceneComponent::SetupAttachment(USceneComponent* Parent, FName SocketName = NAME_None); |
反射 - UPROPERTY
Weapon
这边开始以阅读源码方式去处理代码部分,可能涉及到下面课程的部分,但主要还是看引擎操作流程。
1-Weapon Class
创建CPP FWeaponBase
编写代码
- 代码内容为AFWeaponBase 的创建组件与网格碰撞(详见主要API 碰撞)的设置
- 创建组件如下的1.2,1.5-1.10
- 网格碰撞如下的3.1-3.4
创建BP
- BP继承与FWeaponBase
- 设置Mesh
碰撞
UE4 物理碰撞(C++)_setcollisionresponsetochannel-CSDN博客
SetCollisionResponseToAllChannels(ECollisionResponse Response) |
设置组件对所有通道的统一响应 | Response : Ignore 、Overlap 或 Block |
快速全局设置(如禁用所有碰撞) |
---|---|---|---|
SetCollisionResponseToChannel(ECollisionChannel Channel, ECollisionResponse Response) |
设置组件对单个通道的响应 | Channel : 目标通道(如ECC_WorldStatic ) Response : 响应类型 |
精细化控制(如角色忽略子弹通道) |
SetCollisionEnabled(ECollisionEnabled::Type Type) |
启用/禁用碰撞检测 | Type : NoCollision 、QueryOnly (仅检测)、PhysicsOnly (仅物理)、QueryAndPhysics |
动态开关碰撞检测(如死亡后禁用) |
通道名称 | 枚举值 | 主要用途 | 典型应用场景 |
---|---|---|---|
WorldStatic | ECC_WorldStatic |
静态环境物体(不可移动) | 墙壁、地面、建筑物等场景静态元素的碰撞阻挡 。 |
WorldDynamic | ECC_WorldDynamic |
动态物体(可移动或受物理影响) | 可移动平台、可破坏物、开关门等动态交互对象 。 |
Pawn | ECC_Pawn |
玩家或AI控制的角色 | 角色移动碰撞、AI视线检测、玩家与NPC的交互 。 |
PhysicsBody | ECC_PhysicsBody |
受物理模拟影响的物体 | 滚石、箱子、布娃娃等物理驱动对象的碰撞检测 。 |
Visibility | ECC_Visibility |
可见性检测(非物理阻挡) | 光线追踪、玩家视线判断、渲染剔除优化 。 |
Camera | ECC_Camera |
摄像机碰撞检测 | 防止摄像机穿墙、第三人称视角的镜头避障 。 |
Destructible | ECC_Destructible |
可破坏物体 | 栅栏、玻璃、爆炸物等可破坏对象的碰撞响应 。 |
Vehicle | ECC_Vehicle |
载具类对象 | 汽车、坦克、飞行器等载具的物理碰撞与交互 。 |
Gameplay | ECC_GameTraceChannelN |
预留的自定义游戏逻辑通道(N=1~8 ) |
武器检测(Weapon )、拾取物(Pickup )、陷阱触发等专属交互 。 |
2-Pickup Widget
创建WBP Pickup
加入Text
编写代码 - 添加子物体
- 创建WBP引用
BP_Weapon中
- 设置Pickup Widget
- Space-Screen
- Widget Class - WBP Pickup
- 所需大小绘制-true
- 设置Pickup Widget
编写代码
OnSphereOverlap 当有物体进入拾取区域时调用
必须是U函数
1
void AFWeaponBase::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
- 绑定事件委托
1 | AreaSphere->OnComponentBeginOverlap.AddDynamic(this, &AFWeaponBase::OnSphereOverlap); // 3.5 绑定重叠开始事件 |
委托
[UE4]委托代理:单播委托,多播委托,动态单播委托,动态多播委托,事件_ue 多播委托-CSDN博客
特性 | 单播委托 | 多播委托 | 动态单播委托 | 动态多播委托 |
---|---|---|---|---|
绑定数量 | 仅绑定 1个 函数 | 可绑定 多个 函数 | 仅绑定 1个 函数 | 可绑定 多个 函数 |
执行方式 | .Execute() |
.Broadcast() |
.Execute() |
.Broadcast() |
蓝图支持 | ❌ 不支持蓝图绑定 | ❌ 不支持蓝图绑定 | ✅ 支持蓝图绑定(需UFUNCTION ) |
✅ 支持蓝图绑定(需UFUNCTION ) |
线程安全 | 非线程安全 | 非线程安全 | 非线程安全 | 非线程安全 |
声明宏 | DECLARE_DELEGATE[_Xxx] |
DECLARE_MULTICAST_DELEGATE[_Xxx] |
DECLARE_DYNAMIC_DELEGATE[_Xxx] |
DECLARE_DYNAMIC_MULTICAST_DELEGATE[_Xxx] |
典型用例 | 一对一回调(如任务完成通知) | 事件广播(如伤害事件通知多个系统) | 蓝图与C++交互的单次回调 | 蓝图可订阅的事件系统(如UI事件) |
3- Variable Replication
复制变量设置
- 在Character设置新变量为复制变量(详见主要API 复制变量)
- 重写 virtual void GetLifetimeReplicatedProps(TArray
& OutLifetimeProps) const override; - 这边TArray(详见主要API 数据容器)
- DOREPLIFETIME
内联宏 FORCEINLINE(详见主要API 内联宏)
DOREPLIFETIME_CONDITION(AFCharacter,OverlappingWeapon,COND_OwnerOnly)
复制时回调函数
1 | UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon) |
- SetOverlappingWeapon
1 | void AFCharacter::SetOverlappingWeapon(AFWeaponBase* Weapon) |
这边使用了IsLocallyControlled()
退出碰撞函数
1 | UFUNCTION() |
设置Rep回调函数多一个变量
1
2UFUNCTION()
void OnRep_OverlappingWeapon(AFWeaponBase* lastWeapon);注:这边lastWeapon 会有值(代表着被销毁的前一帧),但是OverlappingWeapon 可能为空,
网络复制(复制变量三部曲)
在虚幻引擎(Unreal Engine)中,UPROPERTY(Replicated)
是一个核心宏,用于声明一个网络复制的属性。其作用是标记该变量(属性)的值需要从服务器(Server)自动同步到所有相关的客户端(Client),以实现多人游戏中状态的一致性。
1. 声明复制属性
**ReplicatedUsing
**:指定同步后的回调函数为 OnRep_OverlappingWeapon
,即属性值在客户端更新后自动触发此函数
1 | UPROPERTY(Replicated) |
2. 重写虚函数
重写 GetLifetimeReplicatedProps
函数 – 在.h声明虚函数
1 | virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; |
3. 使用DOREPLIFETIME宏
- DOREPLIFETIME` 宏是复制注册的关键。
- 头文件要求:必须包含
#include "Net/UnrealNetwork.h"
。 - 详见DOREPLIFETIME宏解析
该宏确保 OverlappingWeapon
(角色当前重叠的武器对象)仅在特定条件下从服务器同步到客户端。通过 COND_OwnerOnly
条件,仅同步给控制该角色的客户端,其他客户端(如队友或敌人)不会收到此数据
在属性所属的类中重写此函数,注册需复制的变量:
1 |
|
4. 启用Actor复制
(不包含三部曲中,如果要复制Actor的话用这个代替第一步)
在Actor的构造函数中设置:
1 | AMyActor::AMyActor() { |
数据容器(代替STL)
UE C++基础 | 常用数据容器 | TArray、TMap、TSet_emplace tmap-CSDN博客
DOREPLIFETIME宏解析
在虚幻引擎(Unreal Engine)中,DOREPLIFETIME(AFCharacter, OverlappingWeapon)
是一个核心宏,用于注册需要网络同步的属性,确保服务器上 AFCharacter
类中 OverlappingWeapon
变量的值能自动同步到所有客户端。以下是详细解析:
🔧 一、功能与作用
- 网络同步机制
- 当服务器修改
OverlappingWeapon
(如角色拾取武器)时,该宏会触发引擎自动将新值广播给所有客户端。 - 客户端收到数据后更新本地副本,无需手动处理同步逻辑,确保多人游戏中状态一致。
- 当服务器修改
- 使用场景
- 适用于需跨客户端同步的
Actor
引用(如角色当前重叠的武器对象)。 - 典型案例:角色靠近武器时,客户端需实时显示武器可拾取提示。
- 适用于需跨客户端同步的
⚙️ 二、底层实现原理
依赖函数
该宏需在AFCharacter
类的GetLifetimeReplicatedProps
函数中调用:1
2
3
4void AFCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const {
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AFCharacter, OverlappingWeapon); // 注册同步属性
}-
属性声明
OverlappingWeapon
需用UPROPERTY(Replicated)
标记为可复制属性:1
2UPROPERTY(Replicated)
class AWeapon* OverlappingWeapon; // 声明为可复制引用Actor复制启用
在AFCharacter
构造函数中需设置:1
2
3AFCharacter::AFCharacter() {
SetReplicates(true); // 启用Actor的网络复制能力
}
📊 三、同步流程与数据流
步骤 | 服务器 | 客户端 |
---|---|---|
属性修改 | 调用 SetOverlappingWeapon(NewWeapon) |
- |
检测变化 | 引擎自动检测 OverlappingWeapon 变更 |
- |
广播更新 | 发送新值给所有客户端 | 接收更新数据 |
应用更新 | - | 自动更新本地 OverlappingWeapon 值 |
⚠️ 注意:客户端不能直接修改
Replicated
属性,否则会与服务器值冲突。
🛠️ 四、高级用法
条件复制
通过DOREPLIFETIME_CONDITION
限制同步范围,优化带宽:1
DOREPLIFETIME_CONDITION(AFCharacter, OverlappingWeapon, COND_OwnerOnly);
条件类型 作用 COND_InitialOnly
仅首次同步时复制(如初始装备) COND_OwnerOnly
仅同步给控制该角色的客户端 复制通知(RepNotify)
使用ReplicatedUsing
在属性同步后触发客户端逻辑:1
2
3
4
5
6
7UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
AWeapon* OverlappingWeapon;
UFUNCTION()
void OnRep_OverlappingWeapon() {
if (OverlappingWeapon) ShowPickupWidget(true); // 显示拾取UI
}- 客户端属性更新后自动调用
OnRep_OverlappingWeapon
。
- 客户端属性更新后自动调用
⚠️ 五、常见问题与注意事项
权限验证
修改复制属性的代码需在服务器执行:1
2
3
4
5void AFCharacter::SetNewWeapon(AWeapon* Weapon) {
if (HasAuthority()) { // 仅服务器可修改
OverlappingWeapon = Weapon;
}
}适用对象限制
- 仅继承自
AActor
的类支持属性复制(如AFCharacter
)。 - 非
Actor
类(如UObject
)需通过 RPC 或序列化同步。
- 仅继承自
性能优化
- 避免高频同步属性(如每帧更新的位置),优先使用内置组件(如
CharacterMovementComponent
)。 - 对低频属性(如装备状态、交互对象)使用复制更高效。
- 避免高频同步属性(如每帧更新的位置),优先使用内置组件(如
💎 总结
DOREPLIFETIME(AFCharacter, OverlappingWeapon)
是虚幻引擎网络同步的核心机制,通过声明-注册模式实现属性自动同步。结合 ReplicatedUsing
可扩展客户端响应逻辑,而条件复制能进一步优化带宽。在多人游戏中,合理使用该宏可显著提升状态同步的效率和可靠性。
IsLocallyControlled
在Unreal Engine的多人游戏开发中,类似 IsLocallyControlled()
的函数主要用于处理网络角色控制权、执行权限和同步逻辑的判断。以下是关键函数及其作用分类说明,结合了Unreal Engine的网络同步机制设计:
🔧 一、角色控制权与执行端判断
IsLocallyControlled()
- 功能:判断当前角色是否由本地玩家控制(客户端视角)。
- 典型场景:客户端特效播放、本地UI更新(如武器拾取提示)。
HasAuthority()
- 功能:判断当前逻辑是否在服务端(Authority)执行。
- 场景:关键状态修改(如生命值扣除)、生成网络同步对象(如武器掉落),确保仅服务端修改全局状态。
IsNetMode(ENetMode Mode)
功能:检测当前网络模式(如客户端、服务端、独立运行)。
常用模式:
NM_Client
:当前为客户端。NM_DedicatedServer
:专用服务器。NM_ListenServer
:监听服务器(同时是主机客户端)。
📡 二、网络执行函数变体
GetWorld()->IsServer()
/GetWorld()->IsClient()
- 功能:直接判断当前运行环境是服务端或客户端,与
HasAuthority()
类似但更直观。
- 功能:直接判断当前运行环境是服务端或客户端,与
RunOnServer
(RPC函数限定符)- 功能:通过标记
UFUNCTION(Server, Reliable)
,强制函数逻辑在服务端执行。 - 场景:客户端发起攻击请求时,需服务端验证并广播同步。
- 功能:通过标记
Client
/NetMulticast
RPC功能:从服务端向客户端广播事件:
Client
:仅发送给控制该角色的客户端。
NetMulticast
:发送给所有客户端。- 场景:播放全局特效(如爆炸)、更新多人UI。
⚙️ 三、属性同步与响应函数
OnRep_[PropertyName]()
- 功能:属性标记为
Replicated
后,当属性在客户端同步时自动触发的回调函数。 - 场景:客户端根据同步后的生命值更新血条UI。
- 功能:属性标记为
GetNetMode()
- 功能:返回当前网络模式枚举值(比
IsNetMode
更灵活),用于分支逻辑处理。
- 功能:返回当前网络模式枚举值(比
📊 关键函数对比表
函数/方法 | 判断目标 | 典型应用场景 |
---|---|---|
IsLocallyControlled() |
角色是否本地控制 | 本地UI显示、输入响应 |
HasAuthority() |
是否在服务端执行 | 关键状态修改、生成同步对象 |
IsNetMode(NM_Client) |
当前是否为客户端环境 | 环境适配逻辑(如禁用客户端物理) |
OnRep_Health() |
客户端属性同步完成 | 更新本地UI或播放特效 |
UFUNCTION(Server) |
强制函数在服务端运行 | 客户端发起的动作请求(如拾取武器) |
UFUNCTION(NetMulticast) |
服务端向所有客户端广播 | 全局事件(如游戏开始通知) |
💡 四、组合使用示例
1 | void AMyCharacter::TryPickupWeapon(AWeapon* Weapon) { |
此代码体现了以下设计原则:
- 本地控制判断(
IsLocallyControlled
)触发客户端请求 → - 服务端权限验证(
HasAuthority
)执行逻辑 → - 广播事件(
NetMulticast
)同步到所有客户端。
⚠️ 注意事项
- 避免在客户端修改同步属性:所有需网络同步的属性修改必须通过服务端执行,否则会被引擎覆盖。
- RPC的可靠性选择:关键事件(如角色死亡)用
Reliable RPC
;高频非关键事件(如位置微调)用Unreliable RPC
。 - 网络优化:通过
NetUpdateFrequency
控制属性同步频率,平衡带宽与实时性需求。
这些函数共同构成了Unreal Engine多人游戏开发的底层框架,理解其差异和适用场景是保证网络同步一致性与性能的关键。
4 装备武器
创建一个ActorComponent的子类:FShootingComponent
- 在FCharactor中添加 FShootingComponent 的 U变量
- FShootingComponent 加入静态变量并设置 SetIsReplicated –true
input 设置Equip
- FCharactor设置UInputComponent按键回调
设置FShootingComponent友元给 FCharactor
- 友元类 可以全访问
重载PostInitializeComponents
1 | virtual void PostInitializeComponents() override; |
添加武器插槽
- 在SK(骨骼)中hand_r右键添加武器插槽
- 点击预览模型
- 可以在Anim中查看预览模型位置以调整插槽位置
写SetWeapon
1
2
3
4
5
6
7
8
9
10
11USkeletalMeshComponent* mesh = Character->GetMesh(); // 获取角色的网格组件
const USkeletalMeshSocket* hand = mesh-> GetSocketByName(FName("RightHandSocket")); // 将武器附加到角色的右手插槽
if (hand)
{
hand->AttachActor(CurWeapon, mesh); // 将武器附加到角色的右手插槽
}
else
{
UE_LOG(LogTemp, Warning, TEXT("RightHandSocket not found!"));
}
CurWeapon ->SetOwner(Character); // 设置武器的拥有者为角色在FCharactor中判断 HasAuthority() 并调用SetWeapon
5 RPC
- Sever函数中装备武器:
1 | UFUNCTION(Server, Reliable) |
- EquipButtonPressed 区分双端
1 | EquipButtonPressed |
EquippedWeapon->SetOwner(Character);
在这边Owner也是复制变量,OnRep_Owner为回调函数
1
2UPROPERTY(ReplicatedUsing=OnRep_Owner)
TObjectPtr<AActor> Owner;
把 AFWeapon 的State 变成复制变量 回调函数为 OnRep_WeaponState (如复制变量三部曲)
若 EWS_Equipped 则
1 | // 如果武器状态为已装备,隐藏拾取UI |
- 那么在UFShootingComponent 中会调用 SetState 从而实现 回调 OnRep_WeaponState
RPC流程
在虚幻引擎(UE)中,RPC(Remote Procedure Call,远程过程调用) 是实现网络同步的核心机制,允许客户端与服务器之间跨网络调用函数。以下是完整的RPC制作流程及关键要点,结合C++和蓝图实现方式:
🛠️ 一、RPC类型与选择
RPC分为三类,需根据场景选择:
- Server RPC
- 作用:客户端调用 → 服务器执行(如请求跳跃、交互验证)。
- 标记:
UFUNCTION(Server, Reliable/Unreliable)
。 - 命名规范:函数名前缀
Server_
(如Server_Jump()
)。
- Client RPC
- 作用:服务器调用 → 特定客户端执行(如播放动画、更新UI)。
- 标记:
UFUNCTION(Client, Reliable/Unreliable)
。 - 命名规范:函数名前缀
Client_
(如Client_PlayMontage()
)。
- Multicast RPC
- 作用:服务器调用 → 所有客户端执行(如爆炸特效、全局事件)。
- 标记:
UFUNCTION(NetMulticast, Reliable/Unreliable)
。 - 命名规范:函数名前缀
Multicast_
(如Multicast_Explode()
)。
RPC类型 | 调用端 | 执行端 | 典型场景 |
---|---|---|---|
Server | 客户端 | 服务器 | 动作请求、状态变更 |
Client | 服务器 | 特定客户端 | 动画播放、UI更新 |
Multicast | 服务器 | 所有客户端 | 全局特效、环境事件 |
可靠性选择:
Reliable
:保证必达(关键操作如跳跃、伤害)。Unreliable
:允许丢包(高频更新如位置同步)。
📝 二、RPC声明与定义
C++实现
头文件声明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// MyCharacter.h
UCLASS()
class AMyCharacter : public ACharacter {
GENERATED_BODY()
public:
// Server RPC示例
UFUNCTION(Server, Reliable, WithValidation)
void Server_Jump();
virtual bool Server_Jump_Validate(); // 参数验证(防作弊)
virtual void Server_Jump_Implementation();
// Client RPC示例
UFUNCTION(Client, Reliable)
void Client_PlayMontage(UAnimMontage* Montage);
virtual void Client_PlayMontage_Implementation(UAnimMontage* Montage);
};源文件实现:
1
2
3
4
5
6
7
8
9
10// MyCharacter.cpp
void AMyCharacter::Server_Jump_Implementation() {
if (CanJump()) Jump(); // 服务器逻辑
Multicast_Jump(); // 广播到所有客户端
}
bool AMyCharacter::Server_Jump_Validate() { return true; } // 验证逻辑
void AMyCharacter::Client_PlayMontage_Implementation(UAnimMontage* Montage) {
if (IsLocallyControlled()) PlayAnimMontage(Montage); // 仅本地客户端执行
}
蓝图实现
- 创建自定义事件:
- 在蓝图编辑器中右键 → Add Event → Custom Event。
- 设置事件名称(如
PlayMontageEvent
)。
- 标记为RPC:
- 在事件详情面板 → Replicates → 选择 Server/Client/Multicast。
🔐 三、权限控制与调用条件
调用者权限:
- Server RPC:仅客户端可调用,且Actor需被客户端控制(如
IsLocallyControlled()
)。 - Client/Multicast RPC:仅服务器可调用。
- Server RPC:仅客户端可调用,且Actor需被客户端控制(如
执行端检查:
在RPC实现中,使用
1
GetLocalRole()
或
1
HasAuthority()
区分执行环境:
1
2
3
4void AMyCharacter::Client_PlayMontage_Implementation(UAnimMontage* Montage) {
if (GetLocalRole() == ROLE_SimulatedProxy) return; // 非主控端跳过
PlayAnimMontage(Montage);
}
Actor复制要求:
- RPC调用的Actor必须开启复制(
bReplicates = true
)。
- RPC调用的Actor必须开启复制(
⚙️ 四、参数处理与序列化
- 支持类型:基础类型(
int
、float
、FString
)、结构体、UObject
指针(需网络同步)。 - 对象参数限制:
- 传递的
UObject*
(如UAnimMontage*
)必须在所有客户端加载,否则需用SoftObjectPath
异步加载。
- 传递的
- 结构体序列化:
- 若结构体包含动态数据,需重写
NetSerialize()
函数优化流量。
- 若结构体包含动态数据,需重写
📡 五、RPC调用流程
触发调用:
客户端触发Server RPC
(如输入事件):
1
2
3void AMyCharacter::OnJumpInput() {
if (IsLocallyControlled()) Server_Jump();
}服务器触发Client RPC(如播放动画):
1
2
3void AMyCharacter::TriggerMontageOnClient() {
if (HasAuthority()) Client_PlayMontage(JumpMontage);
}
网络传输:
- 可靠RPC立即发送,不可靠RPC随属性同步发送。
- 数据顺序:可靠RPC → 属性复制 → 不可靠RPC(同一Actor通道内有序)。
🧪 六、调试与测试
- 多玩家测试:
- 编辑器开启多个PIE窗口:1个为服务器,其余为客户端。
- 网络模拟:
- 控制台命令
net.PktLoss=20
模拟丢包,验证RPC可靠性。
- 控制台命令
- 日志追踪:
- 使用
UE_LOG(LogNet, Log, TEXT("RPC Called"))
输出调用记录。
- 使用
⚠️ 七、常见问题与优化
- 时序问题:
- 避免在Actor初始化前调用RPC(如
BeginPlay
中),优先在PostInitializeComponents()
后调用。
- 避免在Actor初始化前调用RPC(如
- 流量优化:
- 高频更新(如位置同步)用 不可靠RPC + 属性复制 组合。
- 安全性:
- Server RPC必须添加
WithValidation
验证参数(如检测坐标是否合法)。
- Server RPC必须添加
💎 总结流程
- 设计RPC类型 → 2. 声明函数并标记UFUNCTION → 3. 实现逻辑与验证 → 4. 权限检查 → 5. 参数传递优化 → 6. 网络测试与调试。
通过合理使用RPC,可高效实现多人游戏的实时交互,同时确保安全性与流畅性。
6 动画
在FAnim中声明一个变量 : 武器是否装配(bool)
把FShootingComponent组件中的武器提升成复制变量(如复制变量三部曲)
在新建一个子FSM – 在Blend Poses by bool 节点上 – 武器是否装配(bool) 来当 Condition
- 这个子FSM代表的是已装配FSM - Equipped
- 之前上一章的FSM是未装配FSM - Unequipped
Equipped 创建idle State
7 下蹲
继承与Character Croush 有 bIsCroushed 的bool 这个还是一个复制变量
在AFAnim中设置 this-> bIsCroushed = bIsCroushed 然后在FSM设置Condition
8 瞄准
tmp:动画 Play Idle_Rifle_Ironsights Play Idle_Rifle_Hip idle状态中
Croush 中也一样
bAnim (bool)定义
在FCharactor(取Component的),FAnim ,shootingComponent
bAnim 提升成复制变量
- 这边只能从S->C 才同步
使用RPC
- 这边才能C-> C and S
1 | void UFShootingComponent::SetAiming(bool b) |
这边HasAuthority其实不必要
在C调用Server RPC 是S执行,后调整bAnim数值
在S调用Server RPC 是S执行,里面的逻辑不会同步到All C,但这边 bAnim为复制变量可以同步All C
源码阅读笔记
BlasterCharacter
input(完结)
Input == SetupPlayerInputComponent
前后左右:
用欧拉角判断Vector
欧拉角:
pitch():俯仰,将物体绕X轴旋转(localRotationX)
yaw():航向,将物体绕Y轴旋转(localRotationY)
roll():横滚,将物体绕Z轴旋转(localRotationZ)
Jump( ):
蹲伏
CrouchButtonPressed ( )
用UE自带的Character重写
装备–EquipButtonPressed( )
真正逻辑在Combat中
瞄准
AimButtonPressed( )
AimButtonReleased( )
开火
FireButtonPressed( )
FireButtonReleased( )
换弹
ReloadButtonPressed( )
投掷
GrenadeButtonPressed( )
装备
EquipButtonPressed( )
HUD (完结)
OnRep_Health
- PlayHitReactMontage( ) ==>详见动画 TODO
- UpdateHUDHealth ==> BlasterPlayerController->SetHUDHealth ==> BlasterHUD
OnRep_Shield 盾
- PlayHitReactMontage( ) ==>详见动画 TODO
- UpdateHUDShield ==> BlasterPlayerController->SetHUDShield ==> BlasterHUD
UpdateHUDGrenade ==> BlasterPlayerController->SetHUDGrenades==> BlasterHUD
- UpdateHUDAmmo弹药
- BlasterPlayerController->SetHUDCarriedAmmo SetHUDWeaponAmmo ==> BlasterHUD
动画调用
PlayFireMontage( )
PlayReloadMontage( )
PlayElimMontage( )
PlayThrowGrenadeMontage ( )
PlaySwapMontage( )
PlayHitReactMontage( )
提供给Anim的接口
GetCombatState
IsLocallyReloading
IsAiming
CalculateSpeed
CalculateAO_Pitch
AimOffset
Charactor 生命周期
Elim( )死亡
DropOrDestroyWeapons( ) 丢弃武器
- DropOrDestroyWeapon( )
- Weapon Actor::Destroy( ) or Dropped( ) 结束
- DropOrDestroyWeapon( )
MulticastElim( ) –TODO 多播淘汰效果
- StartDissolve UpdateDissolveMaterial
- ElimTimerFinished
ServerLeaveGame 服务器离开游戏
Destroyed (Actor)
ElimBotComponent ==>DestroyComponent (SceneCompent)
Combat 销毁武器
init
GetLifetimeReplicatedProps (ACharacter) 注册属性复制
SpawnDefaultWeapon 获得默认武器
Combat->EquipWeapon(StartingWEeapon);
PostInitializeComponents (ACharacter) 初始化组件
- Combat
- Buff
- LagCompensation
- OnPlayerStateInitialized 初始化PlayerState
- SetSpawnPoint
- SetTeamColor
- ReceiveDamage TODO 造成伤害
Tick
RotateInPlace
HideCameraIfCharacterClose
PollInit
prop and rep
GetTeam
SetTeamColor
SetOverlappingWeapon
OnRep_OverlappingWeapon
Flag
SetHoldingTheFlag
IsHoldingTheFlag
UCombatComponent
- 瞄准 SetAiming
- RPC
- 服务器权威 瞄准移动速度降低
- 瞄准移动速度降低 设置原生Charactor MaxWalkSpeed
- 狙击枪开镜 UI
- 预测(网络延迟)
- SetAiming 记录本地玩家的瞄准按钮状态
- OnRep_Aiming
- RPC
预测
在虚幻引擎的多人游戏开发中,OnRep_Aiming
(复制回调函数)与SetAiming
(本地预测函数)结合使用,能够有效缓解网络延迟带来的瞄准状态同步问题。其核心原理在于客户端预测 + 服务器权威验证 + 延迟补偿修正的协同机制,具体分析如下:
一、网络延迟问题的本质
网络延迟导致客户端操作(如按下瞄准键)与服务器状态同步之间存在时间差。若仅依赖服务器同步(如通过RPC或变量复制),玩家会感受到明显的操作延迟(如按下右键后角色需等待数百毫秒才举枪)。这种延迟会严重破坏实时游戏的体验。
二、SetAiming
的作用:本地预测与快速响应
即时状态更新
当玩家按下瞄准键时,
SetAiming
立即更新本地客户端的bAiming
状态(如bAiming = true
),并调整角色移动速度、显示狙击镜UI等视觉效果。这实现了操作的“零延迟”响应。发起服务器同步
通过
ServerSetAiming
RPC 将操作请求发送给服务器,要求服务器权威验证并更新状态。预测执行
客户端在等待服务器确认期间,假定操作会被服务器接受,提前执行相关逻辑(如移动速度变化)。这种预测机制是减少感知延迟的核心。
三、OnRep_Aiming
的作用:延迟补偿与状态修正
响应服务器同步
OnRep_Aiming
是虚幻引擎的复制回调函数,当服务器通过变量复制将bAiming
的新值同步到客户端时,该函数自动触发。覆盖复制的延迟状态
若网络延迟较高,服务器同步的
bAiming
值可能过时(例如玩家已松开右键,但服务器仍同步“瞄准中”状态)。此时OnRep_Aiming
会用本地记录的bAimButtonPressed
(实时记录按键状态)覆盖复制的值,确保客户端状态与玩家实时操作一致。1
2
3
4
5void UCombatComponent::OnRep_Aiming() {
if (Character && Character->IsLocallyControlled()) {
bAiming = bAimButtonPressed; // 用本地按键状态修正复制值
}
}避免状态回滚抖动
若未使用此机制,客户端会在收到旧状态时错误地回退到过时状态(如突然收起狙击镜再重新举起),导致画面抖动。
OnRep_Aiming
的修正逻辑避免了这一问题。
四、协作机制:解决延迟的核心逻辑
预测-验证-修正流程
- 客户端预测:
SetAiming
立即执行本地操作并通知服务器。 - 服务器验证:服务器通过
ServerSetAiming
更新权威状态并复制到所有客户端。 - 客户端修正:
OnRep_Aiming
在收到复制值时,用本地实时状态覆盖延迟的复制值。
- 客户端预测:
权限隔离
- 仅对本地控制角色(
IsLocallyControlled()
)启用修正逻辑,远程角色直接使用复制值,避免逻辑冲突。
- 仅对本地控制角色(
降低感知延迟
玩家始终看到与自身操作一致的状态(即使网络延迟存在),而服务器仍保持状态权威性,防止作弊。
五、对比传统方案的优势
方案 | 响应速度 | 状态一致性 | 抗延迟能力 |
---|---|---|---|
纯RPC同步 | 低(需等待RPC往返) | 高 | 弱 |
纯变量复制 | 低(依赖复制周期) | 中(可能过时) | 中 |
SetAiming +OnRep |
高(本地预测) | 高(延迟补偿) | 强 |
六、适用场景与局限性
- 适用场景:需快速响应的操作(如瞄准、射击、跳跃)。
- 局限性:
- 需严格区分本地/远程角色逻辑。
- 高丢包率下预测可能频繁出错(需结合重传机制)。
- 复杂状态(如物理模拟)需更精细的预测回滚算法。
总结
OnRep_Aiming
与 SetAiming
的协作本质是客户端预测执行 + 服务器权威验证 + 复制延迟补偿:
SetAiming
实现本地操作的即时反馈(预测);OnRep_Aiming
在服务器状态同步后修正网络延迟导致的误差(补偿)。这一机制在保持服务器权威性的前提下,最大限度消除了玩家感知到的操作延迟,是实时多人游戏中网络同步设计的核心模式。
TODO
开火 FireButtonPressed
开火流程(以 UCombatComponent::Fire()
为核心)如下:
按下射击按钮
玩家按下射击按钮,调用FireButtonPressed(true)
,进而调用Fire()
。条件检查
Fire()
内部通过CanFire()
检查是否满足开火条件(如弹药、冷却、状态等)。锁定射击
满足条件后,设置bCanFire = false
,防止连发。分类型射击
根据当前武器类型(抛射物、射线、霰弹枪)分别调用FireProjectileWeapon()
、FireHitScanWeapon()
或FireShotgun()
。本地预测
如果是客户端(非服务器),先本地执行LocalFire()
或ShotgunLocalFire()
,立即播放动画和武器效果,实现射击预测。服务器同步
客户端通过 RPC(如ServerFire()
、ServerShotgunFire()
)通知服务器进行权威判定。多播同步
服务器通过MulticastFire()
或MulticastShotgunFire()
广播所有客户端,确保所有玩家看到一致的射击效果。冷却计时
调用StartFireTimer()
启动射击冷却,冷却结束后允许再次射击。
整个流程保证了射击的即时反馈(本地预测)和网络同步(服务器权威+多播)。
换弹Reload
换弹(Reload)流程如下:
触发换弹
玩家点击换弹按钮,调用Reload()
。条件检查
Reload()
检查携带弹药、武器状态、是否满弹、是否正在本地换弹等,只有全部满足才继续。本地与服务器同步
- 客户端调用
ServerReload()
,请求服务器同步换弹。 - 客户端本地执行
HandleReload()
,播放换弹动画,并设置bLocallyReloading = true
。
- 客户端调用
服务器处理
ServerReload_Implementation()
设置CombatState = ECS_Reloading
。- 非本地控制角色时,服务器也调用
HandleReload()
播放动画。
动画结束/换弹完成
- 动画结束后,调用
FinishReloading()
。 - 服务器端:更新弹药数据,
CombatState
设为ECS_Unoccuiped
。 - 客户端:重置
bLocallyReloading
,如射击按钮仍按下则自动开火。
- 动画结束后,调用
弹药同步
UpdateAmmoValues()
更新弹匣和携带弹药,并同步到 HUD。
整个流程保证了本地即时反馈、服务器权威同步和弹药数据一致性。
投掷 ThrowGrenade
投掷手雷(ThrowGrenade)流程如下:
触发投掷
玩家点击投掷手雷按钮,调用ThrowGrenade()
。条件检查
检查手雷数量、战斗状态和武器有效性,只有全部满足才继续。本地动画与显示
- 设置
CombatState = ECS_ThrowingGrenade
。 - 本地控制角色播放投掷动画,切换武器到左手,显示手雷模型。
- 设置
服务器同步
- 非服务器端调用
ServerThrowGrenade()
,请求服务器同步投掷。 - 服务器端减少手雷数量,更新HUD,播放动画和显示。
- 非服务器端调用
动画结束/投掷完成
- 动画结束后,调用
ThrowGrenadeFinished()
,恢复战斗状态,武器回到右手。
- 动画结束后,调用
手雷发射
- 调用
LaunchGrenade()
,本地显示手雷消失,并通过ServerLaunchGrenade()
在服务器生成手雷抛射物。
- 调用
整个流程保证了本地即时反馈、服务器权威同步和手雷数量一致性。
ULagCompensationComponent
ULagCompensationComponent
主要用于延迟补偿(Lag Compensation),确保网络射击命中判定的公平性。各函数作用如下:
构造函数/BeginPlay/TickComponent
初始化组件,Tick 时保存每帧的碰撞盒快照(FramePackage)。InterpBetweenFrames
在两帧之间插值,重建某一时刻角色的碰撞盒状态。ConfirmHit/ProjectileConfirmHit/ShotgunConfirmHit
用于命中判定:ConfirmHit
:命中检测(如射线武器),优先检测头部,再检测身体。ProjectileConfirmHit
:用于投射物武器,预测弹道后检测碰撞。ShotgunConfirmHit
:霰弹枪多目标多弹丸命中检测,分别统计头部和身体命中数。
CacheBoxPositions/MoveBoxes/ResetHitBoxes/EnableCharacterMeshCollision
CacheBoxPositions
:缓存角色当前所有碰撞盒的位置、旋转、大小。MoveBoxes
:将角色碰撞盒移动到指定快照状态。ResetHitBoxes
:恢复碰撞盒到原始状态并关闭碰撞。EnableCharacterMeshCollision
:设置角色网格体的碰撞开关。
ShowFramePackage
用于调试,显示某一帧的所有碰撞盒。ServerSideRewind/ProjectileServerSideRewind/ShotgunServerSideRewind
服务器端回溯到指定时间点,进行命中判定。GetFrameToCheck
根据时间戳,从历史帧中获取或插值出需要判定的帧。ServerScoreRequest_Implementation/ProjectileServerScoreRequest_Implementation/ShotgunServerScoreRequest_Implementation
服务器端处理命中结果,应用伤害。SaveFramePackageServer/SaveFramePackage
保存当前帧的碰撞盒快照到历史记录中。
Weapon
Weapon.cpp
1 | WeaponMesh->SetCustomDepthStencilValue(CUSTOM_DEPTH_BLUE); |
Bloom效果
霰弹枪与步枪区别
在你的代码中,霰弹枪(Shotgun)和步枪(如步枪/步枪类武器,AssaultRifle)主要区别体现在以下几个方面:
- 武器类型区分
通过EWeaponType
和EFireType
枚举区分,霰弹枪通常是EWT_Shotgun
,步枪是EWT_AssaultRifle
,它们的FireType
也不同:
- 步枪:
EFireType::EFT_HitScan
(射线检测,单发命中) - 霰弹枪:
EFireType::EFT_Shotgun
(一次发射多颗弹丸,散射)
- 开火逻辑
- 步枪调用
FireHitScanWeapon()
,只处理一个命中点,单发或连发。 - 霰弹枪调用
FireShotgun()
,会生成多个命中点(弹丸散射),并用Shotgun->ShotgunTraceEndWithScatter()
计算所有弹丸的落点,Shotgun->FireShotgun()
处理多弹丸伤害。
- 换弹逻辑
- 步枪换弹一次性填满弹匣,调用
UpdateAmmoValues()
。 - 霰弹枪采用“逐发装填”,每次装填一发,调用
UpdateShotgunAmmoValues()
,并在弹匣满或无子弹时跳转到装填动画结束。
- 射击动画与同步
- 步枪射击只需同步一次命中点。
- 霰弹枪射击需要同步所有弹丸的命中点数组(
TArray<FVector_NetQuantize>
)。
- CanFire 特殊处理
霰弹枪在装填过程中允许中断射击(即装填时也能开火),步枪则不允许。
总结:
- 步枪是单发/连发、单点命中,射线检测,换弹一次性填满。
- 霰弹枪是多弹丸散射、逐发装填,射击和同步都处理多个命中点,装填时可中断射击。
这些差异体现在武器类型、开火方式、换弹方式和网络同步等核心逻辑上。