_哔哩哔哩_bilibili

接下来每一章可能抽取一部分观看,后续再进行补充

章节序号 章节名 进度 备注
1 介绍 2/2
2 创建多人游戏插件 0/23 等学习到网络回头写
3 创建项目 10/14 只有10节课
4 武器 2/24

插件编写(待)

这边我先开盖即用插件

Play设置

编译模式

模式 用途描述 编译特点 文件/性能特点
DebugGame 开发过程中进行调试。在游戏中设置断点、查看变量值以及代码调试。 以调试模式编译,包含调试符号(debug symbols)。 可执行文件较大且运行速度较慢。
Development 开发过程中进行内部测试和验证。相比DebugGame模式会进行更多优化,同时保留调试能力。 启用优化选项以提高运行性能,保留部分调试信息。 运行性能更高,但仍便于问题排查。
DebugGame Editor 在Unreal Editor中调试游戏功能(与DebugGame模式类似,但专用于编辑器环境)。 编译包含调试符号,便于在编辑器中调试。 可执行文件较大,运行效率较低。
Development Editor 在Unreal Editor中进行开发测试。优化编辑器运行性能,同时保留调试能力(与Development模式类似)。 启用优化选项提高编辑器性能,保留部分调试信息。 编辑器运行更流畅,同时支持问题排查。
Shipping 发布给玩家的正式版本构建。 全面优化编译,移除所有调试符号和调试信息。 可执行文件最小化,运行性能最高(无调试开销)。

网络模式(ENetMode)

更新插件代码

1
2
// 原弃用代码:bIsFocusable = true;
SetIsFocusable(true); // 使用UE5官方Setter方法[1,3](@ref)

基础3C

项目建立

  • 编译项目
    • 装入自定义网络插件
    • 装入Online SubSystem Steam
    • 设置ini:

在DefaultEngine.ini写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[OnlineSubsystem]
DefaultPlatformService=Steam

[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480

; If using Sessions
; bInitServerOnClient=true

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"

在DefaultGame.ini写入

1
2
[/Script/Engine.GameSession]
MaxPlayers=4

删除二进制文件夹

  • 创建关卡

    • 创建StartUpMap,LobbyMap
    • 在Project Setting - Map&Modes - 里面更换默认的Map
    • 在StartUpMap关卡蓝图里面调用Create WBP Menu widget -> Menu Setup (设置Lobby路径)
  • Bulid

    • 设置bulid的map : 在Project Setting - 搜索 maps to 设置俩个关卡
    • 平台-打包-windows-放入新建的build文件夹

建立网络连接

资源

Maximo

重定向(待定)重定向教程

3C

Character

新建一个cpp类 注意

1
#include "FCharacter.h"

头文件path要对,新建立的时候默认h会错误

每次更新都 ====在编辑器或游戏窗口中按下 **Ctrl + Alt + F11**,强制终止当前 Live Coding 进程,释放资源后重新编译

C++类创建蓝图 并且设置Mesh和pos

Camera

FCharacter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	
FCharacter.h
UPROPERTY(VisibleAnyWhere, Category = Camera)
class USpringArmComponent* CameraBoom;

UPROPERTY(VisibleAnyWhere, Category = Camera)
class UCameraComponent* FollowCamera;


FCharacter.cpp
AFCharacter::AFCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;


CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);

// 设置弹簧臂长度(摄像机与角色的默认距离)
CameraBoom->TargetArmLength = 500.f;

// 启用角色旋转控制弹簧臂 当玩家控制器旋转时,弹簧臂同步旋转(适用于第三人称视角)
CameraBoom->bUsePawnControlRotation = true;


FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);

// 设置摄像机的视角控制
FollowCamera->bUsePawnControlRotation = false; // 禁用摄像机的控制器旋转(通常用于第一人称视角)

}

Control

project setting - Engine Input (类似unity Input Manager)

设置人物BP_FCharacter - Pawn - 自动控制玩家 - 选择玩家0

FCharacter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Called to bind functionality to input
void AFCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);

PlayerInputComponent->BindAxis("MoveForward", this, &AFCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFCharacter::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &AFCharacter::Turn);
PlayerInputComponent->BindAxis("LookUp", this, &AFCharacter::LookUp);

}

void AFCharacter::MoveForward(float Value)
{
if (Controller && Value != 0.0f)
{
// 获取前向向量并应用移动

// 完整控制器旋转(包含俯仰角)
// const FRotator Rotation = Controller->GetControlRotation();
// 仅Yaw方向旋转(锁定水平移动)
const FRotator Rotation = FRotator(0.f, Controller->GetControlRotation().Yaw, 0.f);
const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}

void AFCharacter::MoveRight(float Value)
{
if (Controller && Value != 0.0f)
{
// 获取右向向量并应用移动
// 完整控制器旋转(包含俯仰角)
// const FRotator Rotation = Controller->GetControlRotation();
// 仅Yaw方向旋转(锁定水平移动)
const FRotator Rotation = FRotator(0.f, Controller->GetControlRotation().Yaw, 0.f);
const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
}

void AFCharacter::Turn(float Value)
{
// 处理水平旋转输入
AddControllerYawInput(Value);
}


void AFCharacter::LookUp(float Value)
{
// 处理垂直旋转输入
AddControllerPitchInput(Value);
}

Anim

FAnimInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void UFAnimInstance::NativeInitializeAnimation()
{
// 初始化动画实例时调用
Super::NativeInitializeAnimation();
//尝试获取角色实例
FCharacter = Cast<AFCharacter>(TryGetPawnOwner());
}


void UFAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
// 每帧更新动画实例时调用
Super::NativeUpdateAnimation(DeltaSeconds);

if(FCharacter == nullptr)
{
// 如果FCharacter为空,尝试获取角色实例
FCharacter = Cast<AFCharacter>(TryGetPawnOwner());
}

if (FCharacter == nullptr)
{
// 如果仍然为空,直接返回
return;
}

FVector Velocity = FCharacter->GetVelocity();

// 计算角色的速度
// 忽略垂直分量,通常用于地面移动速度计算
Velocity.Z = 0; // 忽略垂直速度分量
Speed = Velocity.Size(); // 计算速度大小

bIsInAir= FCharacter->GetMovementComponent()->IsFalling(); // 检查角色是否在空中
bIsAccelerating = FCharacter->GetCharacterMovement()->GetCurrentAcceleration().Size() > 0; // 检查角色是否正在加速

}
  • 动画蓝图创建

    • 类设置 - 父类设置为 F Anim Instance
  • 创建状态机

    • 创建状态
    • 创建连线
    • 创建连线条件
  • 创建混合空间(混合空间1D和混合空间的区别混合空间

    • 放入 Idle Walk Run 三个动画
    • X轴设置为Speed
    • 放入动画状态机 中 并且输入设置为CPP文件中的Speed

修改动画手感与摄像机小优化

自由摄像机视角

  • FCharacter.cpp
1
2
3
4
5
6
//动画后的优化
AFCharacter::AFCharacter(){
bUseControllerRotationYaw = false; // 禁止控制器偏航旋转
GetCharacterMovement()->bOrientRotationToMovement = true; // 角色朝向移动方向
}

  • 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void AFLobbyGameMode::PostLogin(APlayerController* NewPlayer)
{
// 调用父类实现,确保基础登录流程完成
Super::PostLogin(NewPlayer);

// 获取当前已登录玩家数量(游戏状态中的玩家数组人数)
int32 NumofPlayer = GameState.Get()->PlayerArray.Num();

// 当有2名玩家登录时,开始游戏
if(NumofPlayer == 2)
{
UWorld* World = GetWorld();
if(World)
{
bUseSeamlessTravel = true; // 启用无缝切换地图功能
// 无缝切换到战斗地图(使用监听服务端模式)
World->ServerTravel(FString("/Game/AFMaps/FBattleMap?listen"));
}
}
}

HUD

  • 创建HUD Class
  • 创建WBP继承与HUD Class
  • 在代码中绑定WBP
  • 在FCharacter中设置WBP类以及展示Canvas
  • 在FCharacter蓝图中反射调用UpdateText
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void UFPlayerHUDWidget::SetDisplayText(const  FString& TextToDisplay)
{
if (DisplayText)
{
DisplayText->SetText(FText::FromString(TextToDisplay));
}
}
void UFPlayerHUDWidget::UpdateHUD(APawn* InPawn)
{
ENetRole RemoteRole = InPawn->GetRemoteRole();
FString Role;
switch (RemoteRole)
{
case ENetRole::ROLE_Authority:
Role = FString("Authority");
break;
case ENetRole::ROLE_AutonomousProxy:
Role = FString("Autonomous Proxy");
break;
case ENetRole::ROLE_SimulatedProxy:
Role = FString("Simulated Proxy");
break;
case ENetRole::ROLE_None:
Role = FString("None");
break;
}
FString RemoteRoleString = FString::Printf(TEXT("Remote Role: %s"), *Role);
SetDisplayText(RemoteRoleString);
}

void UFPlayerHUDWidget::NativeDestruct()
{
RemoveFromParent();
Super::NativeDestruct();
}

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: IgnoreOverlapBlock 快速全局设置(如禁用所有碰撞)
SetCollisionResponseToChannel(ECollisionChannel Channel, ECollisionResponse Response) 设置组件对单个通道的响应 Channel: 目标通道(如ECC_WorldStaticResponse: 响应类型 精细化控制(如角色忽略子弹通道)
SetCollisionEnabled(ECollisionEnabled::Type Type) 启用/禁用碰撞检测 Type: NoCollisionQueryOnly(仅检测)、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
  • 编写代码

    • OnSphereOverlap 当有物体进入拾取区域时调用

    • 必须是U函数

    1
    void AFWeaponBase::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) 
    • 绑定事件委托
1
2
AreaSphere->OnComponentBeginOverlap.AddDynamic(this, &AFWeaponBase::OnSphereOverlap);  // 3.5 绑定重叠开始事件
AreaSphere->OnComponentEndOverlap.AddDynamic(this, &AFWeaponBase::OnSphereEndOverlap); // 3.6 绑定重叠结束事件

委托

[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
2
3
4
5
UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
class AFWeaponBase* OverlappingWeapon;

UFUNCTION()
void OnRep_OverlappingWeapon();
  • SetOverlappingWeapon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void AFCharacter::SetOverlappingWeapon(AFWeaponBase* Weapon)
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(false);
}
OverlappingWeapon = Weapon;
if (IsLocallyControlled())
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(true);
}
}
}

这边使用了IsLocallyControlled()

退出碰撞函数

1
2
UFUNCTION()
void OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
  • 设置Rep回调函数多一个变量

    1
    2
    UFUNCTION()
    void OnRep_OverlappingWeapon(AFWeaponBase* lastWeapon);

    注:这边lastWeapon 会有值(代表着被销毁的前一帧),但是OverlappingWeapon 可能为空,

网络复制

在虚幻引擎(Unreal Engine)中,UPROPERTY(Replicated) 是一个核心宏,用于声明一个网络复制的属性。其作用是标记该变量(属性)的值需要从服务器(Server)自动同步到所有相关的客户端(Client),以实现多人游戏中状态的一致性。

1. 声明复制属性

1
2
UPROPERTY(Replicated)
float Health; // 生命值需从服务器同步到客户端

2. 重写 GetLifetimeReplicatedProps 函数

在属性所属的类中重写此函数,注册需复制的变量:

1
2
3
4
5
6
#include "Net/UnrealNetwork.h"  // 必须包含此头文件

void AMyActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const {
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyActor, Health); // 注册Health为复制属性
}
  • DOREPLIFETIME 宏是复制注册的关键。

3. 启用Actor复制

在Actor的构造函数中设置:

1
2
3
AMyActor::AMyActor() {
SetReplicates(true); // 开启Actor的网络复制能力
}

该宏确保 OverlappingWeapon(角色当前重叠的武器对象)仅在特定条件下从服务器同步到客户端。通过 COND_OwnerOnly 条件,仅同步给控制该角色的客户端,其他客户端(如队友或敌人)不会收到此数据

**ReplicatedUsing**:指定同步后的回调函数为 OnRep_OverlappingWeapon,即属性值在客户端更新后自动触发此函数

数据容器(代替STL)

UE C++基础 | 常用数据容器 | TArray、TMap、TSet_emplace tmap-CSDN博客

DOREPLIFETIME宏

在虚幻引擎(Unreal Engine)中,DOREPLIFETIME(AFCharacter, OverlappingWeapon) 是一个核心宏,用于注册需要网络同步的属性,确保服务器上 AFCharacter 类中 OverlappingWeapon 变量的值能自动同步到所有客户端。以下是详细解析:


🔧 一、功能与作用

  1. 网络同步机制
    • 当服务器修改 OverlappingWeapon(如角色拾取武器)时,该宏会触发引擎自动将新值广播给所有客户端。
    • 客户端收到数据后更新本地副本,无需手动处理同步逻辑,确保多人游戏中状态一致。
  2. 使用场景
    • 适用于需跨客户端同步的 Actor 引用(如角色当前重叠的武器对象)。
    • 典型案例:角色靠近武器时,客户端需实时显示武器可拾取提示。

⚙️ 二、底层实现原理

  1. 依赖函数
    该宏需在 AFCharacter 类的 GetLifetimeReplicatedProps 函数中调用:

    1
    2
    3
    4
    void AFCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const {
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AFCharacter, OverlappingWeapon); // 注册同步属性
    }
    • 头文件要求:必须包含 #include "Net/UnrealNetwork.h"
  2. 属性声明
    OverlappingWeapon 需用 UPROPERTY(Replicated) 标记为可复制属性:

    1
    2
    UPROPERTY(Replicated)
    class AWeapon* OverlappingWeapon; // 声明为可复制引用
  3. Actor复制启用
    AFCharacter 构造函数中需设置:

    1
    2
    3
    AFCharacter::AFCharacter() {
    SetReplicates(true); // 启用Actor的网络复制能力
    }

📊 三、同步流程与数据流

步骤 服务器 客户端
属性修改 调用 SetOverlappingWeapon(NewWeapon) -
检测变化 引擎自动检测 OverlappingWeapon 变更 -
广播更新 发送新值给所有客户端 接收更新数据
应用更新 - 自动更新本地 OverlappingWeapon

⚠️ 注意:客户端不能直接修改 Replicated 属性,否则会与服务器值冲突。


🛠️ 四、高级用法

  1. 条件复制
    通过 DOREPLIFETIME_CONDITION 限制同步范围,优化带宽:

    1
    DOREPLIFETIME_CONDITION(AFCharacter, OverlappingWeapon, COND_OwnerOnly);
    条件类型 作用
    COND_InitialOnly 仅首次同步时复制(如初始装备)
    COND_OwnerOnly 仅同步给控制该角色的客户端
  2. 复制通知(RepNotify)
    使用 ReplicatedUsing 在属性同步后触发客户端逻辑:

    1
    2
    3
    4
    5
    6
    7
    UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
    AWeapon* OverlappingWeapon;

    UFUNCTION()
    void OnRep_OverlappingWeapon() {
    if (OverlappingWeapon) ShowPickupWidget(true); // 显示拾取UI
    }
    • 客户端属性更新后自动调用 OnRep_OverlappingWeapon

⚠️ 五、常见问题与注意事项

  1. 权限验证
    修改复制属性的代码需在服务器执行:

    1
    2
    3
    4
    5
    void AFCharacter::SetNewWeapon(AWeapon* Weapon) {
    if (HasAuthority()) { // 仅服务器可修改
    OverlappingWeapon = Weapon;
    }
    }
  2. 适用对象限制

    • 仅继承自 AActor 的类支持属性复制(如 AFCharacter)。
    • Actor 类(如 UObject)需通过 RPC 或序列化同步。
  3. 性能优化

    • 避免高频同步属性(如每帧更新的位置),优先使用内置组件(如 CharacterMovementComponent)。
    • 对低频属性(如装备状态、交互对象)使用复制更高效。

💎 总结

DOREPLIFETIME(AFCharacter, OverlappingWeapon) 是虚幻引擎网络同步的核心机制,通过声明-注册模式实现属性自动同步。结合 ReplicatedUsing 可扩展客户端响应逻辑,而条件复制能进一步优化带宽。在多人游戏中,合理使用该宏可显著提升状态同步的效率和可靠性。

IsLocallyControlled

在Unreal Engine的多人游戏开发中,类似 IsLocallyControlled() 的函数主要用于处理网络角色控制权、执行权限和同步逻辑的判断。以下是关键函数及其作用分类说明,结合了Unreal Engine的网络同步机制设计:


🔧 一、角色控制权与执行端判断

  1. IsLocallyControlled()

    • 功能:判断当前角色是否由本地玩家控制(客户端视角)。
    • 典型场景:客户端特效播放、本地UI更新(如武器拾取提示)。
  2. HasAuthority()

    • 功能:判断当前逻辑是否在服务端(Authority)执行。
    • 场景:关键状态修改(如生命值扣除)、生成网络同步对象(如武器掉落),确保仅服务端修改全局状态。
  3. IsNetMode(ENetMode Mode)

    • 功能:检测当前网络模式(如客户端、服务端、独立运行)。

    • 常用模式

      • NM_Client:当前为客户端。
      • NM_DedicatedServer:专用服务器。
      • NM_ListenServer:监听服务器(同时是主机客户端)。

📡 二、网络执行函数变体

  1. GetWorld()->IsServer() / GetWorld()->IsClient()

    • 功能:直接判断当前运行环境是服务端或客户端,与 HasAuthority() 类似但更直观。
  2. RunOnServer (RPC函数限定符)

    • 功能:通过标记 UFUNCTION(Server, Reliable),强制函数逻辑在服务端执行。
    • 场景:客户端发起攻击请求时,需服务端验证并广播同步。
  3. Client / NetMulticast RPC

    • 功能

      :从服务端向客户端广播事件:

      • Client:仅发送给控制该角色的客户端。
      • NetMulticast:发送给所有客户端。
    • 场景:播放全局特效(如爆炸)、更新多人UI。


⚙️ 三、属性同步与响应函数

  1. OnRep_[PropertyName]()
    • 功能:属性标记为 Replicated 后,当属性在客户端同步时自动触发的回调函数。
    • 场景:客户端根据同步后的生命值更新血条UI。
  2. GetNetMode()
    • 功能:返回当前网络模式枚举值(比 IsNetMode 更灵活),用于分支逻辑处理。

📊 关键函数对比表
函数/方法 判断目标 典型应用场景
IsLocallyControlled() 角色是否本地控制 本地UI显示、输入响应
HasAuthority() 是否在服务端执行 关键状态修改、生成同步对象
IsNetMode(NM_Client) 当前是否为客户端环境 环境适配逻辑(如禁用客户端物理)
OnRep_Health() 客户端属性同步完成 更新本地UI或播放特效
UFUNCTION(Server) 强制函数在服务端运行 客户端发起的动作请求(如拾取武器)
UFUNCTION(NetMulticast) 服务端向所有客户端广播 全局事件(如游戏开始通知)

💡 四、组合使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void AMyCharacter::TryPickupWeapon(AWeapon* Weapon) {
// 客户端发起请求
if (IsLocallyControlled()) {
Server_PickupWeapon(Weapon); // 调用Server RPC
}
}

UFUNCTION(Server, Reliable)
void Server_PickupWeapon(AWeapon* Weapon) {
// 服务端验证并执行
if (HasAuthority() && Weapon) {
Weapon->AttachToCharacter(this);
Multicast_PlayPickupFX(); // 广播特效
}
}

UFUNCTION(NetMulticast, Unreliable)
void Multicast_PlayPickupFX() {
// 所有客户端播放特效
SpawnPickupParticles();
}

此代码体现了以下设计原则:

  1. 本地控制判断(IsLocallyControlled)触发客户端请求 →
  2. 服务端权限验证(HasAuthority)执行逻辑 →
  3. 广播事件(NetMulticast)同步到所有客户端。

⚠️ 注意事项

  1. 避免在客户端修改同步属性:所有需网络同步的属性修改必须通过服务端执行,否则会被引擎覆盖。
  2. RPC的可靠性选择:关键事件(如角色死亡)用 Reliable RPC;高频非关键事件(如位置微调)用 Unreliable RPC
  3. 网络优化:通过 NetUpdateFrequency 控制属性同步频率,平衡带宽与实时性需求。

这些函数共同构成了Unreal Engine多人游戏开发的底层框架,理解其差异和适用场景是保证网络同步一致性与性能的关键。

4 装备武器