Search code examples
c++unreal-engine4

UE4 UserWidget Button bind with spawning actor in PlayerController


Hello my fellow coders - I am in need of an assistance for this problem

I have MyPlayerController.c (.h), MyGameHUD.c (.h) and MainUIWidget.c (.h)

MyGameHUD is custom class default HUD for MyPlayerController in MyGameMode MainUIWidget is custom class - simple widget with few buttons MyPlayerController is custom class

I want to spawn actor on MyPlayerController when button on MainUIWidget is pressed

Until now I got only access violations because it seems that UWorld is not accesible from MainUIWidget

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();
}

Expected behavior is to:

  1. click on button1 in widget
  2. Spawn Actor into world from MyPlayerController

Behavior now:

  1. click on button1 in widget
  2. trigger function in MyPlayerController
  3. MyPlayerController->GetWorld() is empty and SpawnActor will crash due to read access violation.

Any ideas please?

EDIT -> added 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;

Solution

  • Thanks for answers all.

    Problem solved by :

    Moving

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

    from widget's constructor to widget's BeginPlay() event.