UE4 UserWidget Button 与 PlayerController 中的生成 actor 绑定

UE4 UserWidget Button bind with spawning actor in PlayerController

大家好我的程序员们- 我需要帮助解决这个问题

我有 MyPlayerController.c (.h)、MyGameHUD.c (.h) 和 MainUIWidget.c (.h)

MyGameHUD 是自定义的 class MyGameMode 中 MyPlayerController 的默认 HUD MainUIWidget 是自定义的 class - 带有几个按钮的简单小部件 MyPlayerController 是自定义的 class

我想在按下 MainUIWidget 上的按钮时在 MyPlayerController 上生成 actor

直到现在我只收到访问冲突,因为似乎无法从 MainUIWidget 访问 UWorld

MainUIWidget.h

UCLASS()
class MY_API UMainUIWidge: public UUserWidget
{
GENERATED_BODY()
void ConstructBuilding(FString BuildingName);
     
MyPlayerController* PlayerControllerPtr;

UPROPERTY(EditDefaultsOnly, BluePrintReadWrite, Category = "Buttons", meta = (BindWidget))
class UButton* Button_1;
};

MainUIWidget.cpp

UMainUIWidget::UMainUIWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
  PlayerControllerPtr = Cast<AMyPlayerController>(GetOwningPlayer());
}
void UMainUIWidget::NativeConstruct()
{
  Super::NativeConstruct();
  // Bind delegates here
  Button_1->OnClicked.AddDynamic(this, &UMainUIWidget::SendSignalBuildBuildingBP_Button1);
}
void UMainUIWidget::ConstructBuilding(FString BuildingName)
{
  PlayerControllerPtr->ConstructBuilding(FString BuildingName);
}  

void UMainUIWidget::SendSignalBuildBuildingBP_Button1()
{
  GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, TEXT("Signal to build!"));
  ConstructBuilding(FString("SmallHouse"));
}

MyGameHUD.h

class MY_API AMyGameHUD : public AHUD
{
    GENERATED_BODY()
public:
  AMyGameHUD ();
  virtual void Tick(float DeltaSeconds) override;
  virtual void BeginPlay() override;
  virtual void DrawHUD() override;
  void DrawUI();

  UPROPERTY(EditAnywhere, BluePrintReadWrite, Category = "Widgets")
  TSubclassOf<UUserWidget> UMainUIWidgetClass;
private:
  UMainUIWidget* UMainUIWidget;

MyGameHUD.cpp

MyGameHUD::MyGameHUD()
{
  PlayerControllerPtr = Cast<AMyPlayerController>(this->GetOwningPlayerController());
}
void MyGameHUD::BeginPlay()
{
  Super::BeginPlay();
  DrawUI();
}
void MyGameHUD::DrawUI()
{
  if (UMainUIWidgetClass)
  {
    UMainUIWidget= CreateWidget<UMainUIWidget>(GetWorld(), UMainUIWidgetClass);
    if (UMainUIWidget)
    {
      UMainUIWidget->AddToViewport();
    }
  }
}
void MyGameHUD::Tick(float DeltaSeconds)
{
  Super::Tick(DeltaSeconds);
}

void MyGameHUD::DrawHUD()
{
  Super::DrawHUD();
}

预期行为是:

  1. 单击小部件中的按钮 1
  2. 从 MyPlayerController 将 Actor 生成到世界中

现在的行为:

  1. 单击小部件中的按钮 1
  2. MyPlayerController中的触发函数
  3. MyPlayerController->GetWorld() 为空,SpawnActor 将因读取访问冲突而崩溃。

有什么想法吗?

编辑 -> 添加了 MyPlayerController

MyPlayerController.cpp

AMyPlayerController::AMyPlayerController()
{
  DefaultMouseCursor = EMouseCursor::Crosshairs;
  bShowMouseCursor = true;
  bEnableClickEvents = true;
  bEnableMouseOverEvents = true;

// pre-load class for SpawnActor
      static ConstructorHelpers::FClassFinder<BuildingClass> SmallHouseBPClass(TEXT("Reference/To/Blueprint/Class/BP.BP_C"));
      SmallHouseClass = SmallHouseBPClass.Class;

}
void AMyPlayerController::ConstructBuilding(FString BuildingName)
{
  if (BuildingName.Compare("SmallHouse") == 0)
  {
    GetWorld()->SpawnActor<BuildingClass>(SmallHouseClass);
  }
}

AMyPlayerController.h

UCLASS()
class AMyPlayerController : public APlayerController
{
    GENERATED_BODY()

public:
    AMyPlayerController ();
    void ConstructBuilding(FString BuildingName);

  UPROPERTY(EditAnywhere, BluePrintReadOnly, Category = "BuildingClasses")
    TSubclassOf<class BuildingClass> SmallHouseClass;

请提供确切的崩溃详细信息。

对我来说,这看起来像是您尝试使用 GetOwningPlayer() 缓存在您的 Widget 的 PlayerControllerPtr 中,但是在 MyGameHUD::DrawUI() 中创建 Widget 时,您没有向 Widget 指定什么它的拥有者是。

请在将 Widget 添加到 Viewport 时在 Widget 上调用 SetOwningPlayer(PlayerControllerPtr),以便它知道哪个 Player 拥有它。这将导致 GetOwningPlayer() 函数实际上 return 一个值。

我怀疑你的崩溃实际上是在这个函数内部发生的

void UMainUIWidget::ConstructBuilding(FString BuildingName)
{
  PlayerControllerPtr->ConstructBuilding(FString BuildingName);
}  

在使用指针之前,您应该始终检查它们的有效性,当它没有按预期通过时,可以选择进行某种记录。

void UMainUIWidget::ConstructBuilding(FString BuildingName)
{
    if(PlayerControllerPtr)
    {
        PlayerControllerPtr->ConstructBuilding(FString BuildingName);
    }
}  

GetWorld() 在 PlayerController 内部调用时始终有效,因为它是一个 AActor,没有世界就无法存在。

感谢大家的回答。

问题解决者:

移动

PlayerControllerPtr = Cast<AMyPlayerController>(this->GetOwningPlayer());

从小部件的构造函数到小部件的 BeginPlay() 事件。