目录
0 引言
单机游戏的开发和网络游戏的开发差别还是很大的,那么在进行网络开发时,需要时刻注意什么呢?
1 服务器和客户端逻辑
在多人游戏网络开发时,一定要清晰的区分好服务端和客户端的职责。这样思路才能更加清晰。
1.1 服务器职责
① 游戏逻辑的权威性:
- 服务器是游戏逻辑的权威,负责处理所有关键的游戏逻辑和状态更新。
- 例如,角色的移动、攻击、物品拾取等操作都应该在服务器上进行验证和处理。
② 状态同步:
- 服务器负责将游戏状态同步到所有客户端。
- 例如,角色的位置、生命值、物品状态等需要通过网络复制(Replication)机制同步到客户端。
③ 安全性和反作弊:
- 服务器需要确保游戏的安全性,防止作弊行为。
- 例如,服务器需要验证客户端发送的所有请求,防止非法操作。
④ AI控制:
- 服务器通常负责AI的控制和行为逻辑。
- 例如,AI角色的路径规划、攻击逻辑等都应该在服务器上执行。
1.2 客户端职责
① 用户输入处理:
- 客户端负责处理玩家的输入,并将输入请求发送到服务器。
- 例如,玩家的移动、攻击指令等需要发送到服务器进行验证和处理。
② 渲染和表现:
- 客户端负责渲染游戏画面和音效,提供给玩家视觉和听觉反馈。
- 例如,角色的动画、特效、UI等都在客户端进行处理。
③ 本地预测和插值:
- 为了提供流畅的游戏体验,客户端可以进行本地预测和插值。
- 例如,角色移动的本地预测可以减少网络延迟带来的影响。
④ UI和HUD:
- 客户端负责显示用户界面(UI)和头部显示器(HUD)。
- 例如,生命值条、得分、物品栏等都在客户端进行处理。
2 函数会在客户端执行还是服务端?
- 其实有些函数既可以在客户端执行也可以在服务端执行
- 有些函数只能在服务端执行
- 有些函数只能在客户端执行
2.1 只在客户端执行的函数
RepNotify
- RepNotify是指带有
ReplicatedUsing
属性的变量,当这些变量在服务器上发生变化时,客户端会收到通知并调用指定的回调函数。 - 这些回调函数只会在客户端执行,用于处理客户端的状态更新。
UCLASS()
class AYourActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health;
UFUNCTION()
void OnRep_Health();
};
void AYourActor::OnRep_Health()
{
// 只在客户端执行的逻辑
UE_LOG(LogTemp, Warning, TEXT("Health updated to: %f"), Health);
}
Client RPC
- Client RPC(Remote Procedure Call)是从服务器调用并在特定客户端上执行的函数。
- 这些函数使用
Client
关键字标记,并且只会在客户端执行。
UFUNCTION(Client, Reliable)
void ClientShowMessage(const FString& Message);
void AYourActor::ClientShowMessage_Implementation(const FString& Message)
{
// 只在客户端执行的逻辑
UE_LOG(LogTemp, Warning, TEXT("Message from server: %s"), *Message);
}
Multicast RPC
- Multicast RPC是从服务器调用并在所有客户端上执行的函数。
- 这些函数使用
NetMulticast
关键字标记,并且会在所有客户端执行。
UFUNCTION(NetMulticast, Reliable)
void MulticastPlaySound();
void AYourActor::MulticastPlaySound_Implementation()
{
// 在所有客户端执行的逻辑
UGameplayStatics::PlaySoundAtLocation(this, SoundToPlay, GetActorLocation());
}
2.2 只在服务端执行的函数
GameMode
- GameMode类中的函数只会在服务器上执行。GameMode是服务器专用的类,客户端不会拥有GameMode的实例。
void AYourGameMode::StartMatch()
{
// 只在服务器执行的逻辑
Super::StartMatch();
UE_LOG(LogTemp, Warning, TEXT("Match started!"));
}
Server RPC
- Server RPC是从客户端调用并在服务器上执行的函数。
- 这些函数使用
Server
关键字标记,并且只会在服务器执行。
UFUNCTION(Server, Reliable, WithValidation)
void ServerPerformAction();
void AYourActor::ServerPerformAction_Implementation()
{
// 只在服务器执行的逻辑
UE_LOG(LogTemp, Warning, TEXT("Action performed on server"));
}
bool AYourActor::ServerPerformAction_Validate()
{
// 验证逻辑
return true;
}
2.3 在两端都可以执行的函数
GetNetMode() 和 HasAuthority()
- GetNetMode():返回当前网络模式,可以是
NM_Standalone
(单机)、NM_Client
(客户端)、NM_ListenServer
(监听服务器)或NM_DedicatedServer
(专用服务器)。 - HasAuthority():返回当前实例是否拥有权威(通常是服务器)。
- Role和Remote Role也可以判断。
void AYourActor::SomeFunction()
{
if (GetNetMode() == NM_Client)
{
// 只在客户端执行的逻辑
UE_LOG(LogTemp, Warning, TEXT("Executing on client"));
}
else if (GetNetMode() == NM_DedicatedServer)
{
// 只在专用服务器执行的逻辑
UE_LOG(LogTemp, Warning, TEXT("Executing on dedicated server"));
}
if (HasAuthority())
{
// 只在服务器执行的逻辑
UE_LOG(LogTemp, Warning, TEXT("Executing on server with authority"));
}
else
{
// 只在客户端执行的逻辑
UE_LOG(LogTemp, Warning, TEXT("Executing on client without authority"));
}
}
总结
- 只在客户端执行的函数:RepNotify、Client RPC、Multicast RPC。这些函数主要用于处理客户端的状态更新和客户端特定的逻辑。
- 只在服务端执行的函数:GameMode中的函数、Server RPC。这些函数主要用于处理服务器的游戏逻辑和验证客户端请求。
- 在两端都可以执行的函数:通过
GetNetMode()
或HasAuthority()
来区分执行环境。这些函数可以根据当前的网络模式或权威性来执行不同的逻辑。
通过合理地使用这些函数和机制,可以确保游戏逻辑在客户端和服务器之间正确分工,保证游戏的正确性和性能。