虚幻多人游戏在运行时交换材质

问题描述

我对 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 组件,它是玩家角色类的一部分(附在蓝图中)。

希望这对其他人有帮助!

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...