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
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 !