Hello,
I have a problem with TArray with custom structs I have created. Even though i marked it as UPROPERTY it crashes the editor when i give it some values in blueprint. I tried to go around the issue by creating another structs that stores the same array, but without success.
header file
Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "EnemyAttackHandlingComponent.generated.h"
class AEnemy;
class AProjectile;
UENUM(BlueprintType)
enum EEnemyAttackType
{
None UMETA(DisplayName = "None"),
Melee UMETA(DisplayName = "Melee"),
Ranged UMETA(DisplayName = "Ranged")
};
USTRUCT(BlueprintType)
struct FAttackData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Damage = 10.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Range = 1000.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AProjectile> Projectile;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UAnimMontage* AttackMontage;
bool Equal(FAttackData Other)
{
return Damage == Other.Damage && Range == Other.Range && AttackMontage == Other.AttackMontage;
}
};
USTRUCT(BlueprintType)
struct FAttackArray
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FAttackData> AttackArray;
};
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class SHOOTERGAME_API UEnemyAttackHandlingComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UEnemyAttackHandlingComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
void AttackStart(EEnemyAttackType AttackType);
//Utility functions
void SwitchAttackColliders(bool CollisionEnabled);
void SpawnProjectile(TSubclassOf<AProjectile> Projectile);
EEnemyAttackType ChooseNextAttackType(AActor* Target);
FAttackData GetRandomAttack(FAttackArray AttackTypes);
FAttackData PreviousAttack;
AEnemy* EnemyOwner;
UPROPERTY(EditDefaultsOnly)
float MaxMeleeAttackRange = 100.f;
UPROPERTY(EditDefaultsOnly)
float MaxRangedAttackRange = 10000.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack types")
FAttackArray MeleeAttacks;
UPROPERTY(EditDefaultsOnly, Category = "Attack types")
FAttackArray RangedAttacks;
/*UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FAttackData> ProjectileAttacks;*/
};
cpp file
#include "EnemyAttackHandlingComponent.h"
#include "Enemy.h"
#include "Projectile.h"
#include "Kismet/KismetMathLibrary.h"
UEnemyAttackHandlingComponent::UEnemyAttackHandlingComponent()
{
PrimaryComponentTick.bCanEverTick = false;
EnemyOwner = Cast<AEnemy>(GetOwner());
}
void UEnemyAttackHandlingComponent::BeginPlay()
{
Super::BeginPlay();
}
void UEnemyAttackHandlingComponent::AttackStart(EEnemyAttackType AttackType)
{
FAttackData ChosenAttack;
FAttackArray ChosenAttacksArray;
//Get random attack
switch (AttackType)
{
case EEnemyAttackType::Melee:
//ChosenAttacksArray.AttackArray = MeleeAttacks;
ChosenAttack = GetRandomAttack(MeleeAttacks);
break;
case EEnemyAttackType::Ranged:
//ChosenAttacksArray.AttackArray = RangedAttacks;
ChosenAttack = GetRandomAttack(MeleeAttacks);
break;
}
//EnemyOwner->PlayAttackMontage(ChosenAttack.AttackMontage, 1.f);
}
//UTILITY FUNCTIONS
void UEnemyAttackHandlingComponent::SwitchAttackColliders(bool CollisionEnabled)
{
if (CollisionEnabled)
{
EnemyOwner->TurnOnMeleeCollision();
}
else
{
EnemyOwner->TurnOffMeleeCollision();
}
}
void UEnemyAttackHandlingComponent::SpawnProjectile(TSubclassOf<AProjectile> Projectile)
{
APawn* PlayerPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
if (Projectile && PlayerPawn)
{
FVector ProjectileDirection = PlayerPawn->GetActorLocation() - EnemyOwner->GetMesh()->GetSocketLocation("ProjetileSpawnSocket");
ProjectileDirection = ProjectileDirection.GetSafeNormal();
FVector Location = EnemyOwner->GetMesh()->GetSocketLocation("ProjetileSpawnSocket");
FRotator Rotation = UKismetMathLibrary::FindLookAtRotation(EnemyOwner->GetMesh()->GetSocketLocation("ProjetileSpawnSocket"), PlayerPawn->GetActorLocation());
FActorSpawnParameters Params;
Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
Params.Owner = EnemyOwner;
AProjectile* SpawnedProjectile = GetWorld()->SpawnActor<AProjectile>(Projectile, Location, Rotation, Params);
//Set velocity to max velocity in correct direction
if (SpawnedProjectile)
{
SpawnedProjectile->ProjectileMovement->Velocity = ProjectileDirection * SpawnedProjectile->ProjectileMovement->MaxSpeed;
}
}
}
EEnemyAttackType UEnemyAttackHandlingComponent::ChooseNextAttackType(AActor* Target)
{
float Distance = (EnemyOwner->GetActorLocation() - Target->GetActorLocation()).Length();
if (Distance <= MaxMeleeAttackRange)
{
return EEnemyAttackType::Melee;
}
else if (Distance <= MaxRangedAttackRange)
{
return EEnemyAttackType::Ranged;
}
return EEnemyAttackType::None;
}
FAttackData UEnemyAttackHandlingComponent::GetRandomAttack(FAttackArray AttackTypes)
{
int ArraySize = AttackTypes.AttackArray.Num(), Index = UKismetMathLibrary::RandomIntegerInRange(0, ArraySize - 1);
if (ArraySize != 1)
{
while (PreviousAttack.Equal(AttackTypes.AttackArray[Index]))
{
Index = UKismetMathLibrary::RandomIntegerInRange(0, ArraySize - 1);
}
return AttackTypes.AttackArray[Index];
}
else
{
FAttackData None;
return None;
}
if (GEngine)
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Cyan, FString::Printf(TEXT("Array size: %i"), ArraySize));
FAttackData None;
return None;
}