You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UE5.4多人模式下非主机客户端无法调用Door的ServerInteract RPC

UE5.4监听服务器模式下非主机客户端无法调用门Actor的ServerRPC问题

这是我制作的第一个带复制功能的多人游戏Actor,欢迎提供更优实现方案。

使用UE5.4版本,采用监听服务器(listen server)架构,包含两名玩家。当前正在开发可交互开关的门:玩家与门交互时,客户端调用ServerInteract服务器RPC,该RPC会更新门Actor的bOpen属性。此属性标记为ReplicatedUsing,本应同步到所有客户端并触发OnRep函数,根据bOpen状态播放开门或关门动画。

问题现象:主机客户端(客户端0)与门交互时,复制功能完全正常;但非主机客户端(客户端1)与门交互时,即便Actor拥有有效的NetOwner,ServerInteract RPC也无法被调用。


相关代码

Door.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "../InteractiveActor.h"
#include "Door.generated.h"

UCLASS()
class ESCAPEGAME_API ADoor : public AInteractiveActor
{
    GENERATED_BODY()
    
public: 
    // Sets default values for this actor's properties
    ADoor();
    
    // Handle interaction
    virtual void ServerInteract_Implementation(APawn* InteractionSender) override;

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

    UFUNCTION(BlueprintCallable)
    bool ToggleDoor();

    UFUNCTION()
    void OnRep_bOpen();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
    
protected:
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Door)
    UAnimSequence* DoorOpening_Animation;
    
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Door)
    UAnimSequence* DoorClosing_Animation;

    UPROPERTY(ReplicatedUsing=OnRep_bOpen ,EditAnywhere, BlueprintReadOnly, Category = Door)
    bool bOpen;

public:
    UPROPERTY(EditAnywhere, category = Door)
    USkeletalMeshComponent* DoorSkeletalMesh;

    UPROPERTY(EditAnywhere, category = Door)
    USceneComponent* DoorRoot;
    
};

Door.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Door.h"
#include "Net/UnrealNetwork.h"

// Sets default values
ADoor::ADoor()
{
    PrimaryActorTick.bCanEverTick = false;

    DoorRoot = CreateDefaultSubobject<USceneComponent>(TEXT("DoorRoot"));
    SetRootComponent(DoorRoot);

    DoorSkeletalMesh = CreateDefaultSubobject<USkeletalMeshComponent>("DoorMesh");
    DoorSkeletalMesh->SetupAttachment(DoorRoot);

    // Initiate variables
    bOpen = false;
    bReplicates = true;
}

// Called when the game starts or when spawned
void ADoor::BeginPlay()
{
    Super::BeginPlay();
}

bool ADoor::ToggleDoor()
{
    UE_LOG(LogTemp, Warning, TEXT("ToggleDoor : This actor has %s"), ( HasAuthority() ? TEXT("authority") : TEXT("no authority") ));
    
    bOpen = !bOpen;

    if (HasAuthority())
    {
        DoorSkeletalMesh->PlayAnimation(bOpen ? DoorOpening_Animation : DoorClosing_Animation, false);
    }
    
    return bOpen;
}

void ADoor::OnRep_bOpen()
{
    UE_LOG(LogTemp, Warning, TEXT("OnRep_bOpen Has authority : %s"), (HasAuthority() ? TEXT("yes") : TEXT("no")));
    UE_LOG(LogTemp, Warning, TEXT("bOpen : %s"), (bOpen ? TEXT("yes") : TEXT("no")));

    DoorSkeletalMesh->PlayAnimation(bOpen ? DoorOpening_Animation : DoorClosing_Animation, false);
}

void ADoor::ServerInteract_Implementation(APawn* player)
{
    Super::ServerInteract_Implementation(player);

    UE_LOG(LogTemp, Warning, TEXT("Server : ServerInteract"));
    
    ToggleDoor();
}

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

    DOREPLIFETIME(ADoor, bOpen);
}

InteractiveActor.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "InteractiveActor.generated.h"

UCLASS(Abstract)
class ESCAPEGAME_API AInteractiveActor : public AActor
{
    GENERATED_BODY()

public:
    // Interact function
    UFUNCTION(Server, Reliable, WithValidation)
    virtual void ServerInteract(APawn* InteractionSender);
    virtual void ServerInteract_Implementation(APawn* InteractionSender);
    virtual bool ServerInteract_Validate(APawn* InteractionSender);

};

InteractiveActor.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "InteractiveActor.h"

void AInteractiveActor::ServerInteract_Implementation(APawn* InteractionSender)
{
    UE_LOG(LogTemp, Warning, TEXT("ServerInteract with %s, Has authority: %s"), *InteractionSender->GetName(), (HasAuthority() ? TEXT("Yes") : TEXT("No")));
}

bool AInteractiveActor::ServerInteract_Validate(APawn* InteractionSender)
{
    return true;
}

玩家F键触发的交互函数

void AEscapeGameCharacter::Interact()
{
    UE_LOG(LogTemp, Warning, TEXT("AEscapeGameCharacter::Interact"));

    FVector StartLocation = FirstPersonCameraComponent->GetComponentLocation();
    FVector EndLocation = FirstPersonCameraComponent->GetForwardVector() * 200 + StartLocation;
    FHitResult Hit;
    
    GetWorld()->LineTraceSingleByChannel(Hit, StartLocation, EndLocation, ECC_Visibility);
    DrawDebugLine(GetWorld(), StartLocation, EndLocation, FColor::Red, false, 5, 0, 5);

    if (!Hit.bBlockingHit) { return; }
    
    if (Hit.GetActor()->IsA(AInteractiveActor::StaticClass()))
    {
        AInteractiveActor* InteractiveActor = Cast<AInteractiveActor>(Hit.GetActor());
        
        if (AEscapeGamePlayerController* controller = Cast<AEscapeGamePlayerController>(GetController()))
        {
            UE_LOG(LogTemp, Warning, TEXT("Net Rep Responsible Owner: %s"), (HasNetOwner() ? TEXT("Yes") : TEXT("No")));
            InteractiveActor->SetOwner(this);
            InteractiveActor->ServerInteract(this);
        }
        
    } else
    {
        UE_LOG(LogTemp, Warning, TEXT("Hitted something"));
    }
    
}

内容的提问来源于stack exchange,提问作者thacaout

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 10:25:01