Blueprintable custom c++ class makes UE crash

Hello,

I was trying to implement an UObject derived class to handle spells casting projectiles, (with the official tutorial to make a FPS shooter) but when I made the class Blueprintable, (and BlueprintType) creating a Blueprint derived from my class made the Unreal editor crash (I tried 3-4 times).

But now, the editor crash at launch, so I removed the Blueprintable and BlueprintType to my class but it changes nothing and I can’t access to my project through the editor. It would be very nice if somebody knows where the “critical error” causing this comes from :slight_smile:

I derived the class FreezeBall, coming from SpellMissile (coming from Spell).
Here are my classes :

Spell.h :

#pragma once

class AMyCharacter;

#include "Object.h"
#include "Resource.h"
#include "Spell.generated.h"

/**
 * 
 */
UCLASS(Abstract, Blueprintable, BlueprintType)
class BACASABLE_API USpell : public UObject, public FTickableGameObject
{
	GENERATED_BODY()
public:
	
	// Setting default values
	USpell();
	virtual void PostInitProperties() override;

	// Implementing FTickableGameObject functions
	void Tick(float DeltaTime);
	bool IsTickable() const;
	TStatId GetStatId() const;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float ManaCost;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	float Cooldown;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	int32 ChargesMax;

	UPROPERTY(EditAnywhere)
	int32 Range;

	UPROPERTY(BlueprintReadOnly)
	int32 ActualCharges;

	UFUNCTION(BlueprintCallable, Category = "Stats")
	float GetActualCooldown() const;

	UFUNCTION()
	virtual void EndInit(AMyCharacter* Owner);

	UFUNCTION()
	bool CastSpell();

	UPROPERTY()
	UResource* CooldownResource;

protected:
	// Must be overwritten
	UFUNCTION()
	virtual void Effect();

	UFUNCTION()
	bool TestCastBase() const;

	UFUNCTION()
	virtual bool TestCast() const;

	UFUNCTION()
	void BeginCast();

	UPROPERTY()
	AMyCharacter* Caster;
};

Spell.cpp :

#include "BacASable.h"
#include "Spell.h"
#include "MyCharacter.h"

USpell::USpell()
{
	ChargesMax = 1;
	ManaCost = 0;
	Cooldown = 5;
	Range = 1500;
	Caster = 0;
}

void USpell::PostInitProperties()
{
	Super::PostInitProperties();

	ActualCharges = ChargesMax;
	CooldownResource = NewObject<UResource>(this);
}

void USpell::Tick(float DeltaTime)
{
	if (ActualCharges == ChargesMax)
	{
		CooldownResource->Add(CooldownResource->ValueMax);
	}
	else if (CooldownResource->IsEmpty())
	{
		CooldownResource->Add(CooldownResource->ValueMax);
		ActualCharges++;
	}
}

bool USpell::IsTickable() const
{
	return true;
}

TStatId USpell::GetStatId() const
{
	return Super::GetStatID();
}

float USpell::GetActualCooldown() const
{
	return CooldownResource->ActualValue;
}

void USpell::EndInit(AMyCharacter* Owner)
{
	Caster = Owner;
	CooldownResource->MakeAsCooldown(Cooldown);
}

bool USpell::CastSpell()
{
	if (Caster && TestCastBase())
	{
		BeginCast();

		return true;
	}
	else
	{
		return false;
	}
}

bool USpell::TestCastBase() const
{
	if (ActualCharges >= 1 && ManaCost <= Caster->GetManaPoints())
	{
		return TestCast();
	}
	else
	{
		return false;
	}
}

bool USpell::TestCast() const
{
	return true;
}

void USpell::BeginCast()
{
	ActualCharges--;
	Caster->ManaResource->Add(-ManaCost);

	Effect();
}

void USpell::Effect()
{
	// Debug text
	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("Abstract spell casted"));
	}
}

SpellMissile.h :

#pragma once

#include "Spell.h"
#include "SpellMissile.generated.h"

/**
 * 
 */
UCLASS(Abstract)
class BACASABLE_API USpellMissile : public USpell
{
	GENERATED_BODY()
public:

	USpellMissile();

	UPROPERTY(EditDefaultsOnly, Category = Missile)
	TSubclassOf<class AMissile> MissileClass;

	UPROPERTY()
	float LifeTime;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	bool MuzzleEqualsTarget;

	UFUNCTION()
	void EndInit(AMyCharacter* Owner) override;

	UFUNCTION()
	void Effect() override;
};

SpellMissile.cpp :

#include "BacASable.h"
#include "SpellMissile.h"
#include "MyCharacter.h"
#include "Missile.h"

USpellMissile::USpellMissile()
{
	MuzzleEqualsTarget = false;
}

void USpellMissile::EndInit(AMyCharacter* Owner)
{
	Super::EndInit(Owner);

	LifeTime = Range / Cast<AMissile>(MissileClass->GetDefaultObject())->ProjectileMovementComponent->MaxSpeed;
}

void USpellMissile::Effect()
{
	if (MissileClass)
	{
		// Get the camera
		FVector CameraLocation;
		FRotator CameraRotation;
		Caster->GetActorEyesViewPoint(CameraLocation, CameraRotation);

		FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(Caster->Muzzle);
		FRotator MuzzleRotation = CameraRotation;

		UWorld* World = GetWorld();
		if (World)
		{
			FActorSpawnParameters SpawnParams;
			SpawnParams.Owner = Caster;
			SpawnParams.Instigator = Caster->Instigator;

			AMissile* Missile = World->SpawnActor<AMissile>(MissileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
			if (Missile)
			{
				FVector LaunchDirection = MuzzleRotation.Vector();
				Missile->FireInDirection(LaunchDirection, LifeTime);
			}
		}
	}
}

FreezeBall.h :

#pragma once

#include "SpellMissile.h"
#include "FreezeBall.generated.h"

/**
 * 
 */
UCLASS()
class BACASABLE_API UFreezeBall : public USpellMissile
{
	GENERATED_BODY()
public:
	UFreezeBall();
	
protected:
	void Effect() override;

};

FreezeBall.cpp :

#include "BacASable.h"
#include "FreezeBall.h"

UFreezeBall::UFreezeBall()
{
	ManaCost = 30;
	Cooldown = 4;
	ChargesMax = 2;
	Range = 1500;
}

void UFreezeBall::Effect()
{
	Super::Effect();
	// Debug text
	if (GEngine)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("FreezeBall casted"));
	}
}

If you have a little more time for me, I have two little questions :

When the Blueprint values replace the default ones in the C++ constructor ? I supposed that in PostInitProperties(), the Blueprint values already replaced the default ones but I’m not sure if this is the best function for initializing my object params using Blueprint values.

Does it have an utility to place an empty macro UPROPERTY() or UFUNCTION() apart from preventing dangling pointer crashes and USTRUCTS() ? (for exemple, to write UPROPERTY() float Value; )

Thank you in advance !

Hello DwarfObserver,

when the editor crashes a call stack should appear. Could you please post it? Alternatively, somewhere in the call stack you should find a type name you defined and the function in which the error was caused, have a look in that function and line.

Usually these crashes which happen before the editor has fully loaded are caused by you dereferencing a null pointer in your code. Remember that when the editor loads up it creates a new world, calls BeginPlay, PostInitProperties, EndInit, Tick and other functions. Thus, you error must be in one of those functions. Try making a null pointer check before you dereference anything, even if you are sure it cannot be null. Specifically, make a null pointer check for CooldownResource (this field is allocated using NewObject and a null pointer check is always in order as it may fail to allocate new memory), MissileClass->GetDefaultObject(), and Cast(MissileClass->GetDefaultObject())->ProjectileMovementComponent. Also note that Cast(NULL) crashes instead of returning NULL, which I find counter-intuitive. Again, please post the call stack as that will tell what happened.

The values defined in the details panel replace the default values defined in the constructor between the time after the constructor has finished and before BeginPlay is called. I always do all initialising which is supposed to overwrite blueprint values in BeginPlay.

There is an utility in marking fields and functions with empty macros indeed. At the simplest level, you can iterate those fields using the reflection framework provided by Unreal. You can do some cool tricks with that(what I am about to describe to you is actually done by the editor itsself). For example, say you are writting a drop down menu (for your game or as extension to the Unreal Editor) and you want each element to perform a certain command. Instead of updating your UI code everytime you code a new command, you can simply make every command inherit from UCommand, and have it have a UFUNCTION marked as exec. Then when you create the drop down menu, you simply iterate all subclasses of UCommand and get its function marked as exec and execute it when the user clicks on that particular element. For the drop down menu element’s name you could have a UPROPETY() FString ElementName, and simply look for a UPROPERTY named “ElementName” using reflection again.

Except for this nice trick, it is required for delegates to be UFUNCTIONs; with delegates I mean objects like OnActorBeginOverlap declared in AActor, which you use to add a callback function when the actor overlaps.

Cheers,
Univise

Hi,

Thanks Univise for your great answer !

Sorry for not linking the lags, here they are :

link text

I’ll check my code thanks to your explanation :slight_smile:

The issue was due to the utilisation of a Blueprint value in PostInitProperties() so I’ll use them in BleginPlay() as you said :slight_smile: I still got a crash everytime I create a Blueprint based on my Spell class though ( link text )

The log indicates that things are going wrong because USpell is marked as abstract. Not sure what is trying to instantiate an instance of USpell, possibly some blueprint machinery.

Hey, thanks for your answer and sorry for the delay !

I deleted then added one per one the content of my class USpell and found that the function Tick was causing the crash when creating the Blueprint (The abstract tag does not seem to affect the crash) :

void USpell::Tick(float DeltaTime)
{
	if (ActualCharges == ChargesMax)
	{
		CooldownResource->Add(CooldownResource->ValueMax);
	}
	// else if (CooldownResource->IsEmpty())
	// UNCOMMENT THIS LINE
	{
		// CooldownResource->Add(CooldownResource->ValueMax);
            // OR THIS LINE
		ActualCharges++;
	}
}

Uncomment one of the two lines result in a crash when creating the Blueprint. I don’t really see how the crash is caused, maybe by accessing two or more time at the same FTickableObject in a Tick function ?

Edit : This is the code of my UResource :

Resource.h :

#pragma once

#include "Object.h"
#include "Resource.generated.h"

/**
 * 
 */
UCLASS()
class BACASABLE_API UResource : public UObject, public FTickableGameObject
{
	GENERATED_BODY()
public:

	// Setting default values
	UResource();
	virtual void PostInitProperties() override;
	
	// Implementing FTickableGameObject functions
	void Tick(float DeltaTime);
	bool IsTickable() const;
	TStatId GetStatId() const;
	
	UPROPERTY()
	float ValueMax;

	UPROPERTY()
	float ValueInit;

	UPROPERTY()
	float ActualValue;

	UPROPERTY()
	float Regeneration;

	UFUNCTION()
	void Add(float Value);

	UFUNCTION()
	bool IsEmpty() const;

	UFUNCTION()
	bool IsFull() const;

	UFUNCTION()
	void ResetValueMax(float NewMax);

	UFUNCTION()
	void MakeAsCooldown(float NewMax = 0.0f);
};

Resource.cpp :

#include "BacASable.h"
#include "Resource.h"


UResource::UResource()
{
	ValueMax = 100;
	Regeneration = 1;
	ValueInit = 100;
}

void UResource::PostInitProperties()
{
	Super::PostInitProperties();

	ValueInit = FMath::Clamp(ValueInit, 0.0f, ValueMax);
	ActualValue = ValueInit;
}

void UResource::Tick(float DeltaTime)
{
	Add(DeltaTime * Regeneration);

}

bool UResource::IsTickable() const
{
	return true;
}

TStatId UResource::GetStatId() const
{
	return Super::GetStatID();
}

void UResource::Add(float Value)
{
	ActualValue = FMath::Clamp(ActualValue + Value, 0.0f, ValueMax);
}

bool UResource::IsEmpty() const
{
	return (ActualValue == 0);
}

bool UResource::IsFull() const
{
	return (ActualValue == ValueMax);
}

void UResource::ResetValueMax(float NewMax)
{
	if (ValueInit == ValueMax) {
		ValueInit = NewMax;
	}
	ValueMax = NewMax;
	ActualValue = ValueInit;
}

void UResource::MakeAsCooldown(float NewMax)
{
	if (NewMax != 0.0f)
	{
		ValueMax = NewMax;
	}
	ValueInit = 0;
	ActualValue = 0;
	Regeneration = -1;
}

Edit - I marked the topic as unresolved for this issue (I don’t know if it’s accepted)

If your crash is still happening from those two lines in the Tick function, it’s probably because Tick is running while CooldownResource is still null. Try putting a nullptr check before you use it.

i.e.

if (!CooldownResource) {
    return;

}

If that stops the crash, you’ve at least found the problem. It could be because the CooldownResource object isn’t properly created, or because Tick is executing before you create the object.

Hi, thanks for your answer !

I feel bad for missing this, now it works well.

Glad to hear it’s working.

No reason to feel bad. There’s a lot to code, sometimes even “obvious” things are hard to see.

TTFN