找回密码
 立即注册
搜索

[UE4/UE5][网络]虚幻引擎的网络框架(虚幻的复制系统)

2024-11-20 15:56| 发布者: admin| 查看: 76| 评论: 0|来自: 知乎

摘要: 参考资料Multiplayer in Unreal Engine: How to Understand Network Replication - YouTube 虚幻引擎框架或者虚幻运行原理 - 知乎 (zhihu.com)replicate的基本概念gameserver游戏服务器维护了绝对权威的世界状态。当 ...
 参考资料

Multiplayer in Unreal Engine: How to Understand Network Replication - YouTube

[UE4/UE5][C++] 虚幻引擎框架或者虚幻运行原理 - 知乎 (zhihu.com)

replicate的基本概念

gameserver

游戏服务器维护了绝对权威的世界状态。当游戏服务器的世界的状态改变,游戏服务器会把改变的状态传给每个客户端游戏的世界。这个过程称之为replicate(复制)。虚幻的复制系统让我们很容易开发出网络游戏,而不必去关注任何网络细节。你只要说我想要这个property replicate(复制)一下,然后它就可以被传输到客户端。

netmode(world的网络模式)

netmode是world的property之一。

World.h里的GetNetMode

netmode包含以下4个重要成员,NM_Standalone, NM_DedicatedServer, NM_ClientSever, NM_Client。

EngineBaseTypes.h

关于netmode的灵魂三问

这个游戏可以玩吗?

我们的gameinstance有一个localplayer吗?我们可以处理这个Player的input,并把世界渲染到viewport吗?

我们是一个服务器吗?换句话说,我们是否有最权威的世界副本,是否有gamemode这个actor?如果我们是服务器,我们是否对远程连接请求开放?其他的Player能加入,并扮演客户端?

这些问题的答案决定你的游戏的netmode。如下表格。

灵魂三问的表格

在我之前的一片文章中说过,engine的loadmap会去寻找一个url,这个url可以使本地的,也可以使远程的。

engine的loadmap

如果你的游戏已经连接了一个远程服务器,你的world就是NM_Client网络模式。所以你的world只能按照服务器的world来更新。

如果你的游戏本地加载世界,你的world就是NM_Standalone网络模式。因此你的游戏既是服务器也是客户端。你的游戏在本地运行,且不对任何外部请求开放。

但是如果你在本地运行,但是有监听选项,那么你的world就是listen sever网络模式。这个基本就是个NM_Standalone网络模式,但是别的本地游戏实例依然能作为客户端访问。

如果你的游戏实例是dedicate server网络模式,这个游戏实例既没有localplayer,也没有viewport。这只是一个服务器端的应用,玩家可以作为服务端连接。

区别图

虚幻复制系统基础

为了让虚幻复制系统有效运行,这三个类非常重要。UNetDriver,UNetConnection,UChannel。

一个server,两个客户端

无论客户端还是服务器都有GameEngine,而每个GameEngine都有自己的GameNetDriver。当server启动时,gameengine创建UNetDriver,UNetDriver开始监听远程连接请求。当客户端启动时,game engine同样创建UNetDriver,UNetDriver开始向服务器发送连接请求。一旦连接建立,server就会建立一个UNetConnection来维护连接。server会为每个客户端游戏建立一个UNetConnection。但是客户端只有一个UNetConnection来维护自己与服务器的连接。

每个UnetConnection有非常多的Channel,VoiceChannel,ActorChannel。

GameEngine.h

actor同步

如果你需要一个actor通过网络保持连接,你就需要设置bReplicates为TRUE。然后用IsNotRelevantFor来检测这个actor属于哪个player,然后通过netconnection中的actorchannel来交换信息。

Actor.h
Actor.h

如果服务器生成了一个actor,然后服务器会通知client要复制自己的actor。如果这个actor在服务器上被删除,客户端同样也会被删除。同样的,每个actor可能也有replicated properties, 这个property给我的感觉就是成员变量。如果actor的property被标记了replicated,那么如果服务器上的property改变,客户端的property也会随之改变。

例子

在你自定义的actor比如weapon设置bReplicates = true,这个bReplicates = true,就意味着如果服务器创建了这个weapon,那么这个actor就会把这个weapon副本给客户端。

AWeapon::AWeapon()
{
bReplicates = true;
}

在使用这个weapon的Character类里,设置UPROPERTY(ReplicatedUsing=OnRepXXX)。并重写GetLifetimeReplicatedProps。被标注的OverlappingWeapon在服务器如果发生改变,服务器就会传给客户端。但是on_RepXXX这里只会在客户端执行。On_RepXXX实际上是一个接收到服务器replicate后需要执行的函数,replicate过程是单向的,所以On_RepXXX只能再客户端执行。

class BLASTER_API ABlasterCharacter : public ACharacter
{
UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
class AWeapon* OverlappingWeapon;

UFUNCTION()
void OnRep_OverlappingWeapon();

virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

};

实现GetLifetimeReplicatedProps,这里也是设置replicate条件的地方。

void ABlasterCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME(ABlasterCharacter, OverlappingWeapon);
DOREPLIFETIME_CONDITION(ABlasterCharacter, OverlappingWeapon, COND_OwnerOnly);//只对拥有者replicate
}

这里的OverlappingWeapon是一个指针,当它从null变为不是null的时候,就会触发客户端执行OnRep_OverlappingWeapon这个函数。UMG永远不会同步,Do you also play as a client when using listen server? - Programming & Scripting / Blueprint - Epic Developer Community Forums (unrealengine.com)

这里画个图简单说明一下,这里在世界放置个AWeapon,Overlapping Weapon类型为AWeapon

如何在蓝图设置replicated?

Property Replication | Unreal Engine Documentation

代码设置和蓝图设置

ownership(所有权)

所有权对于actor的replication也很重要。但是ownership同样可以在runtime时设置。

playerController的ownership

playerController的所有权很重要。基本上,每个网络连接都代表着一个player。一旦这个player完全进入游戏,那个这个网络连接同样和playerController相连接。从服务器的角度来说,这个网络连接拥有这个playerController。除此之外,这个网络连接可以拥有这个playerController所拥有的所有actor。打个比方,如果你的游戏一个人物扔了个手榴弹,服务器可以追踪到这个手榴弹是你游戏人物扔的,并复制给其他所有的玩家。

相关性

并不是所有的actor都需要复制给每个客户端。所以我们这里需要设置相关性。但是有的actor就是对于所有的客户端都复制。比如下面这个。

PlayerState等actor对所有客户端复制

但是有些actor只对于一个客户端有相关性,所以这个actor只会复制给这个客户端。比如PlayerController这个actor只对自己的客户端有相关性,也只会复制给自己的客户端。

相关性还能被网络距离决定,如果一个actor没有设置相关性,但是如果你离一个actor过“近”的化,它也是和你有相关性。

更新频率和优先级

更新频率和优先级直接决定了服务器给相关客户端发送更新的频率。NetUpdateFrequency直接决定了服务器要多久check一下客户端的actor,并给他发送新的更新数据。但是网络延迟和网络带宽同样也是重要影响因素,所以玩游戏时常常会卡成ppt。服务器的netdriver会根据网络带宽和actor的优先级来决定放弃哪些数据的传播。优先级并不是固定的,比如离服务器近的actor就会有高优先级,很长时间没有更新的就有高优先级。但是同样的,你也可以手动设置这些actor的优先级。

手动设置NetPriority

RPC (remote procedure calls)

如果你把一个方法设计为多播。当你在服务器上调用一个函数时,服务器将要发送信息让每个客户端调用同样的方法。多播RPC不能用于复制永久数据给所有客户端。

可靠RPC和不可靠RPC

这个就是TCP和UDP的区别。

RPC代码编写

RPCs | Unreal Engine Documentation

这个大致过程就是,我的客户端在服务器上的地图捡到一个武器,但是捡到这个武器,并赋与客户端玩家这个函数需要在服务器上执行。在捡到武器的时候,客户端向服务器发送一个rpc请求,让服务器执行这个函数。在上面那个actor同步例子中,我们把weapon的bReplicate设置为了true,说明了这个weapon只能由服务器创建,然后replicate给客户端,所以当我们捡到这个武器时,事实上是在服务器上执行了将这个武器的owner设置为我的客户端,并replicate其他客户端。注意这个函数实现的命名形式,加了一个Implementation。

UFUNCTION(Server, Reliable)
void ServerEquip();

void ABlasterCharacter::ServerEquip_Implementation()
{

}

Replicated Properties vs RPCs

Replicated Properties vs RPCs - Programming & Scripting / Multiplayer & Networking - Epic Developer Community Forums (unrealengine.com)

replicated properties同样和网络相关。尽管replicated properties可能发生延时,比如和你的pawn相关性没有了,但是一旦条件达成,最终会把property复制给你的pawn。可参见视频里的例子,那个例子视频里举得很好。

Local role和Remote role

这个代码时获取local role和remote role

	/** Returns how much control the local machine has over this actor. */
UFUNCTION(BlueprintCallable, Category=Networking)
ENetRole GetLocalRole() const { return Role; }

/** Returns how much control the remote machine has over this actor. */
UFUNCTION(BlueprintCallable, Category=Networking)
ENetRole GetRemoteRole() const;
角色role代码

Standalone local

这个是在standalone模式下,本地机器对这个可爱的小人的权限。这里拥有绝对的权限。

standalone
local role:authority


路过

雷人

握手

鲜花

鸡蛋

QQ|Archiver|手机版|小黑屋|软件开发编程门户 ( 陇ICP备2024013992号-1|甘公网安备62090002000130号 )

GMT+8, 2024-11-21 14:20 , Processed in 0.034856 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.