问题描述
我对 Unreal 很陌生,如果我犯了一个简单的错误,我深表歉意。
我一直在开发一个小型多人游戏原型,其中一个角色会跑过一个皮卡,然后将关联组件上的布尔值更改为 true 或 false,然后该组件将交换该角色网格材料到不同的材料。
我已经尝试了尽可能多的在线方法来执行此操作,但只有一种有效,但效果不佳。这种有效方法是检查角色网格上的材质,而不是使用 bool(这似乎不起作用)。有人可以告诉我我做错了什么,使其无法正常工作,并让我知道我需要做什么才能使其正常工作吗?我觉得我在做这件事很困难,所以任何关于如何更好地使用 Unreal 编程多人游戏的方向也将不胜感激。
PlayerCharacter.h:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "PlayerCharacter.generated.h"
class UStaticmeshComponent;
UCLASS()
class Game_API PlayerCharacter : public ACharacter
{
GENERATED_BODY()
public:
APlayerCharacter();
public:
UPROPERTY(EditAnywhere)
class USkeletalMeshComponent* CharacterMesh;
protected:
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "Components")
class UPlayerShieldComponent* ShieldComp;
public:
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
UFUNCTION()
void ToggleShield();
UFUNCTION(Server,Reliable,WithValidation)
void Server_ToggleShield();
};
PlayerCharacter.cpp
#include "Character/PlayerCharacter.h"
#include "GameFramework/Controller.h"
#include "Components/StaticmeshComponent.h"
#include "Components/PlayerShieldComponent.h"
#include "Net/UnrealNetwork.h"
APlayerCharacter::APlayerCharacter()
{
PrimaryActorTick.bCanEverTick = true;
CharacterMesh = Cast<USkeletalMeshComponent>(GetDefaultSubobjectByName("CharacterMesh0"));
ShieldComp = CreateDefaultSubobject<UPlayerShieldComponent>(TEXT("ShieldComp"));
}
void APlayerCharacter::BeginPlay()
{
Super::BeginPlay();
}
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("Interact",IE_pressed,this,&APlayerCharacter::ToggleShield);
}
void APlayerCharacter::ToggleShield()
{
if (GetLocalRole() == ROLE_Authority)
{
if (ShieldComp)
{
ShieldComp->DoToggleShield();
}
}
else
{
Server_ToggleShield();
}
}
bool APlayerCharacter::Server_ToggleShield_Validate()
{
return true;
}
void APlayerCharacter::Server_ToggleShield_Implementation()
{
if (UPlayerShieldComponent* sc = Cast<UPlayerShieldComponent>(GetDefaultSubobjectByName("ShieldComp")))
{
sc->DoToggleShield();
}
}
PlayerShieldComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Character/PlayerCharacter.h"
#include "PlayerShieldComponent.generated.h"
UCLASS(ClassGroup = (Game),Meta = (BlueprintSpawnableComponent))
class Game_API UPlayerShieldComponent : public UActorComponent
{
GENERATED_BODY()
public:
UPlayerShieldComponent();
UPROPERTY(Replicated,BlueprintReadOnly)
bool bShieldisOn;
protected:
virtual void BeginPlay() override;
UFUNCTION(NetMulticast,WithValidation)
void Multi_ToggleShield();
public:
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = Materials)
class UMaterialInstance* OffMaterial;
UPROPERTY(EditAnywhere,Category = Materials)
class UMaterialInstance* OnMaterial;
void DoToggleShield();
};
PlayerShieldComponent.cpp
#include "Components/PlayerShieldComponent.h"
#include "Net/UnrealNetwork.h"
#include "Components/StaticmeshComponent.h"
#include "Character/PlayerCharacter.h"
// Sets default values for this component's properties
UPlayerShieldComponent::UPlayerShieldComponent()
{
PrimaryComponentTick.bCanEverTick = false;
OnMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("On Material"));
OffMaterial = CreateDefaultSubobject<UMaterialInstance>(TEXT("Off Material"));
bShieldisOn = false;
}
void UPlayerShieldComponent::BeginPlay()
{
Super::BeginPlay();
}
void UPlayerShieldComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UPlayerShieldComponent,bShieldisOn);
}
void UPlayerShieldComponent::DoToggleShield()
{
if (GetownerRole() == ROLE_Authority)
{
bShieldisOn = !bShieldisOn;
Multi_ToggleShield(); // From the server.
}
}
void UPlayerShieldComponent::Multi_ToggleShield_Implementation()
{
if (APlayerCharacter* character = Cast<APlayerCharacter>(Getowner()))
{
if (USkeletalMeshComponent* cm = Cast<USkeletalMeshComponent>(character->GetDefaultSubobjectByName("CharacterMesh0")))
{
GEngine->AddOnScreenDebugMessage(-1,5.0f,FColor::Red,character->GetFName().ToString());
if (cm->GetMaterial(0) == OnMaterial)
{
cm->SetMaterial(0,OffMaterial);
}
else
{
cm->SetMaterial(0,OnMaterial);
}
}
}
}
bool UPlayerShieldComponent::Multi_ToggleShield_Validate()
{
return true;
}
解决方法
过了一段时间,我找到了解决方案。
首先,这很重要,请检查实现组件的蓝图,并检查其 IsReplicated 标志是否为真。
然后,更新标题如下:
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Character/PlayerCharacter.h"
#include "PlayerShieldComponent.generated.h"
UCLASS(ClassGroup = (Game),meta = (BlueprintSpawnableComponent))
class Game_API UPlayerShieldComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UPlayerShieldComponent ();
UPROPERTY(ReplicatedUsing=OnRep_ShieldIsOn,BlueprintReadOnly)
bool bShieldIsOn;
UFUNCTION()
void OnRep_ShieldIsOn();
protected:
// Called when the game starts
virtual void BeginPlay() override;
void ChangeShieldMaterial(bool shieldIsOn);
public:
UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = Materials)
class UMaterialInstance* OffMaterial;
UPROPERTY(EditAnywhere,Category = Materials)
class UMaterialInstance* OnMaterial;
void DoToggleShield();
};
接下来,更新CPP文件如下:
#include "Components/PlayerShieldComponent.h"
#include "Net/UnrealNetwork.h"
#include "Components/StaticMeshComponent.h"
#include "Character/PlayerCharacter.h"
// Sets default values for this component's properties
UPlayerShieldComponent::UPlayerShieldComponent()
{
// Set this component to be initialized when the game starts,and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = false;
bShieldIsOn = false;
}
// Called when the game starts
void UPlayerShieldComponent::BeginPlay()
{
Super::BeginPlay();
}
void UPlayerShieldComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UPlayerShieldComponent,bShieldIsOn);
}
void UPlayerShieldComponent::OnRep_ShieldIsOn()
{
this->ChangeShieldMaterial(bShieldIsOn);
}
void UPlayerShieldComponent::DoToggleShield()
{
if (GetOwnerRole() == ROLE_Authority)
{
bShieldIsOn = !bShieldIsOn;
OnRep_ShieldIsOn();
}
}
void UPlayerShieldComponent::ChangeShieldMaterial(bool shieldIsOn)
{
if (APlayerCharacter* character = Cast<APlayerCharacter>(GetOwner()))
{
if (USkeletalMeshComponent* cm = Cast<USkeletalMeshComponent>(character->GetDefaultSubobjectByName("CharacterMesh0")))
{
if (!bShieldIsOn)
{
cm->SetMaterial(0,OffMaterial);
}
else
{
cm->SetMaterial(0,OnMaterial);
}
}
}
}
答案基本上是,我没有在蓝图中复制 Shield 组件,它是玩家角色类的一部分(附在蓝图中)。
希望这对其他人有帮助!