Reputation: 21
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:
Behavior now:
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;
Upvotes: 0
Views: 1945
Reputation: 21
Thanks for answers all.
Problem solved by :
Moving
PlayerControllerPtr = Cast<AMyPlayerController>(this->GetOwningPlayer());
from widget's constructor to widget's BeginPlay() event.
Upvotes: 1
Reputation: 597
Please provide the exact Crash details.
To me this looks like your attempting to use GetOwningPlayer()
to cache in PlayerControllerPtr
of your Widget, however when creating the Widget in MyGameHUD::DrawUI()
you are not specifying to the Widget what its Owning Player is.
Please call SetOwningPlayer(PlayerControllerPtr)
on the Widget when you add it to the Viewport so that it knows which Player owns it. This will cause the GetOwningPlayer()
function to actually return a value.
I suspect that your crash is actually occurring inside this function
void UMainUIWidget::ConstructBuilding(FString BuildingName)
{
PlayerControllerPtr->ConstructBuilding(FString BuildingName);
}
You should always check validity of Pointers before using them, optionally making some sort of log when it does not pass as expected.
void UMainUIWidget::ConstructBuilding(FString BuildingName)
{
if(PlayerControllerPtr)
{
PlayerControllerPtr->ConstructBuilding(FString BuildingName);
}
}
GetWorld()
will always be valid when called inside of a PlayerController as it is an AActor and cannot exist without a World.
Upvotes: 2