unreal中actor的property replicate简单小节

本文介绍如何在Unreal Engine 5.1中实现多人游戏的属性同步,包括客户端回调函数的编写、伤害处理机制及调试技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先参考的网址是官网中的:
https://docs.unrealengine.com/5.1/en-US/multiplayer-programming-quick-start-for-unreal-engine/

unreal引擎的版本是5.1

还原的过程相对比较简单,主要的精力花在了编译报错和调试的过程。
属性复制的流程如下:
在这里插入图片描述
ClientA和CleintB的的通信,由Server转发。
ClientA开枪打了ClientB,造成伤害后,由Server,将伤害同步给两个客户端。

完成这个需求使用属性同步的方式。
1、书写客户端回调函数

声明一个属性,添加必要的复制标签。

UPROPERTY(ReplicatedUsing = xxxOnRep_CurrentHealth)
float CurrentHealth;

这里的ReplicatedUsing作为关键字和Replicated的都可以,但是ReplicatedUsing 后面可以配置一个回调函数。这里xxxOnRep_CurrentHealth为ClientA或者B收到服务器的通知之后,执行的函数,函数名字任意。

回调函数xxxOnRep_CurrentHealth的实现:

void ADedicatedServerDemoCharacter::xxxOnRep_CurrentHealth()
{
	UE_LOG(LogTemp, Error, TEXT("yyyyyyyyyyyyyyyyyy"));
	OnHealthUpdate();
}

void ADedicatedServerDemoCharacter::OnHealthUpdate()
{
	//Client-specific functionality
	if (IsLocallyControlled())
	{
		FString healthMessage = FString::Printf(TEXT("You now have %f health remaining."), CurrentHealth);
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, healthMessage);

		if (CurrentHealth <= 0)
		{
			FString deathMessage = FString::Printf(TEXT("You have been killed."));
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, deathMessage);
		}
		UE_LOG(LogTemp, Error, TEXT("xxxxxxxxxxxxxxxxxxxxxx"));
	}

	//Server-specific functionality
	if (GetLocalRole() == ROLE_Authority)
	{
		FString healthMessage = FString::Printf(TEXT("%s now has %f health remaining."), *GetFName().ToString(), CurrentHealth);
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, healthMessage);
		UE_LOG(LogTemp, Error, TEXT("%s now has %f health remaining."), *GetFName().ToString(), CurrentHealth);
	}

	//Functions that occur on all machines. 
	/*  
		Any special functionality that should occur as a result of damage or death should be placed here. 
	*/
}

这里可以让客户端做展示使用。这里IsLocallyControlled,是用来判断是否是客户端。
参考:https://zhuanlan.zhihu.com/p/548761791

而:if (GetLocalRole() == ROLE_Authority)用来判断是否是服务器。

这里要注意,此函数OnHealthUpdate是跑在所有端,仅仅通过IsLocallyControlled和GetLocalRole() == ROLE_Authority)来区分是客户端还是服务器。
在这里插入图片描述
2、如何造成伤害
unreal中,造成伤害是很容易。由于我们的class ADedicatedServerDemoCharacter : public ACharacter具有这种继承关系:

//直接声明TakeDamage函数
UFUNCTION(BlueprintCallable, Category = "Health")
	float TakeDamage( float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser ) override;

//TakeDamage函数具体实现
float ADedicatedServerDemoCharacter::TakeDamage(float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	float damageApplied = CurrentHealth - DamageTaken;
	SetCurrentHealth(damageApplied);
	return damageApplied;
}

//这段函数跑在服务器
void ADedicatedServerDemoCharacter::SetCurrentHealth(float healthValue)
{
	if (GetLocalRole() == ROLE_Authority)
	{
		CurrentHealth = FMath::Clamp(healthValue, 0.f, MaxHealth);
		OnHealthUpdate();
	}
}

当收到伤害之后,会调用SetCurrentHealth函数设置当前血量。注意此函数要判断是否是服务器,只有服务器才能修改这个值。

那我什么时候被伤害呢?即TakeDamage函数,是谁调用的呢?
是子弹击中人的时候,造成伤害,这个调用伤害的地方,应该在子弹类中实现:

void ABullet::OnBulletHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
	if (OtherActor)
	{
		UGameplayStatics::ApplyPointDamage(OtherActor, Damage, NormalImpulse, Hit, GetInstigator()->Controller, this, DamageType);
	}

	Destroy();
}

当子弹发生碰撞的时候,要调用伤害函数。
在这里插入图片描述
有我们的ADedicatedServerDemoCharacter重写了TakeDamage函数,所以逻辑是通了。

那这个子弹的啥时候调用OnBulletHit呢?

ABullet::ABullet()
{
 	PrimaryActorTick.bCanEverTick = true;
	bReplicates = true;
	
	SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
	SphereComponent->InitSphereRadius(37.5f);
	SphereComponent->SetCollisionProfileName(TEXT("BlockAllDynamic"));
	RootComponent = SphereComponent;

	//Registering the Projectile Impact function on a Hit event.
	if (GetLocalRole() == ROLE_Authority)
	{
		SphereComponent->OnComponentHit.AddDynamic(this, &ABullet::OnBulletHit);
	}

要使用SphereComponent 进行注册:OnComponentHit.AddDynamic
同时判断只有在服务器上的if (GetLocalRole() == ROLE_Authority) 才能进行注册这个回调函数。

到目前位置,我们在不用rpc的方式,而是为属性添加标签ReplicatedUsing的方式,实现了属性的同步。他的唯一的缺点是,要时刻区分初,当前这段代码是在客户端还是在服务器。

理解的逻辑过程为:子弹攻攻击人、人受击后,服务器计算伤害,然后同步给客户端。
这里存疑的地方是,子弹的碰撞是在服务器上的?还是在客户端上的。TakeDamage函数是谁调用的?这个在调试的时候,可以看看堆栈。

3、另外补充下,调试的方式:
在这里插入图片描述
在编辑器下,选择Net Mode为Play As Listen Server,然后Number of Players为2,运行之后,然后使用Attach to Process即可调试,也可以打log方式,在控制台看log。

另外如果是打包成服务器和客户端的exe的方式,还是看log吧。还要注意,改一行代码,也要打包server,否则连不上服务器。

如何构建服务器,参考:https://blog.csdn.net/wodownload2/article/details/128452317?spm=1001.2014.3001.5501

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值