보스 공격_원거리 공격에 사용할 Projectile 구현

2025. 2. 24. 23:23·Project/Gunfire Paragon

 

보스는 기본적으로 원거리 마법 공격을 하는 형태로 구현하였다.
기본 공격은 마법 투사체를 발사하고, 이를 5번 연속으로 공격하는 Barrage Skill 그리고 하늘에서 떨어지는 운석을 떨어트리는 Meteor 스킬을 만들 것이다.

이에 사용될 발사체 클래스는 BaseMagicProjectile을 구현하고 Fireball Projectile, Meteor Projectile 이 상속 받는 구조가 될 것이다.

 

✅ BaseMagicProjectile.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BaseMagicProjectile.generated.h"

UCLASS()
class GUNFIREPARAGON_API ABaseMagicProjectile : public AActor
{
	GENERATED_BODY()
	
public:	
	ABaseMagicProjectile();
	
	void SetVelocity(FVector Direction);

protected:
	virtual void BeginPlay() override;

	UFUNCTION()
	void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);

	void CheckDestroyCondition();

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Projectile")
	TObjectPtr<class USphereComponent> SphereComp;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Projectile")
	TObjectPtr<class UParticleSystemComponent> ProjectileParticle;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Projectile")
	TObjectPtr<class UProjectileMovementComponent> ProjectileMovement;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
	float Speed;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
	float DamageMultiplier;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Effects")
	UParticleSystem* HitImpactEffect;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Effects")
	UParticleSystem* DisappearEffect;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Effects")
	USoundBase* ImpactSound;

	FVector InitialLocation;
	FTimerHandle DestroyCheckTimer;
};
Pargon::Gideon 에셋에 적당한 Particle이 있어서 따로 Static Mesh는 사용하지않았고 충돌을 처리할 Sphere Component와 외형을 보여줄 Particle System Component 그리고 발사체 움직임 기능을 위한 Projectile Movement Component를 부착해주었다.

Damage의 경우엔 보스가 자체적으로 가지고 있는 값이 있어서, 데미지 배율로 각 Projectile의 최종 데미지를 적용해주었다.
Fireball의 경우엔 플레이어나 월드 상의 다른 오브젝트에 맞지 않는다면 계속 날라가기 때문에 초기 스폰된 위치를 저장할 InitialLocation 변수와 일정 거리를 벗어나고 있는지 체크할 타이머가 필요해 DestroyCheckTimer를 선언해주었다.

 

 

✅ BaseMagicProjectile.cpp

#include "AI/BaseMagicProjectile.h"
#include "AI/BossEnemy.h"
#include "Components/SphereComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "GameFramework/DamageType.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h"

ABaseMagicProjectile::ABaseMagicProjectile()
{
	PrimaryActorTick.bCanEverTick = false;
	
    // 충돌 컴포넌트 생성 및 설정
	SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
	SphereComp->InitSphereRadius(15.0f);
	SphereComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
	SphereComp->SetCollisionObjectType(ECC_PhysicsBody);
	SphereComp->SetCollisionResponseToAllChannels(ECR_Ignore);
	SphereComp->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);
	SphereComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block); 
	SetRootComponent(SphereComp);
	
    // 파티클 추가(메쉬의 역할)
	ProjectileParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ProjectileParticle"));
	ProjectileParticle->SetupAttachment(SphereComp);
	ProjectileParticle->bAutoActivate = true;
	
    // 투사체 속성 초기화
	Speed = 1500.0f;
	DamageMultiplier = 1.0f;
	InitialLocation = FVector::ZeroVector;

	// 발사체 움직임 설정
	ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
	ProjectileMovement->InitialSpeed = Speed;
	ProjectileMovement->MaxSpeed = Speed;
	ProjectileMovement->bRotationFollowsVelocity = true;
	ProjectileMovement->bShouldBounce = false;
	
    // 충돌 이벤트 바인딩
	SphereComp->OnComponentHit.AddDynamic(this, &ABaseMagicProjectile::OnHit);
}

void ABaseMagicProjectile::BeginPlay()
{
	Super::BeginPlay();

	// 초기 위치 저장
	InitialLocation = GetActorLocation();
	
    // 일정 간격(0.2초)마다 사거리를 벗어나는지 체크
	GetWorldTimerManager().SetTimer(DestroyCheckTimer, this, &ABaseMagicProjectile::CheckDestroyCondition, 0.2f, true);
}

void ABaseMagicProjectile::SetVelocity(FVector Direction)
{
    // 발사체를 스폰되는 시점에 사용되는 함수
    // 투사체의 속도를 설정해서 특정 방향으로 날라가도록 설정
	ProjectileMovement->Velocity = Direction * ProjectileMovement->InitialSpeed;
}

void ABaseMagicProjectile::CheckDestroyCondition()
{
	// 발사체가 이동한 거리가 발사체 주체의 사거리보다 길어지면 Destroy
	AActor* OwnerActor = GetOwner();
	if (!OwnerActor) return;

	ABossEnemy* Boss = Cast<ABossEnemy>(OwnerActor);
	if (!Boss) return;

	float BossAttackRange = Boss->AttackRange;
	if (FVector::Dist(InitialLocation, GetActorLocation()) >= BossAttackRange)
	{
		Destroy();

		if (DisappearEffect)
		{
			UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), DisappearEffect, GetActorLocation());
		}

		GetWorld()->GetTimerManager().ClearTimer(DestroyCheckTimer);
	}
}

void ABaseMagicProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
	// 콜리전 컴포넌트와 충돌 발생시 호출되는 함수
	if (OtherActor && OtherActor->ActorHasTag("Player"))
	{
		AActor* OwnerActor = GetOwner();
		if (!OwnerActor || OwnerActor == OtherActor || OtherActor == this) return;

		ABossEnemy* Boss = Cast<ABossEnemy>(OwnerActor);
		if (!Boss) return;

		float FinalDamage = Boss->Damage * DamageMultiplier;
		UGameplayStatics::ApplyDamage(OtherActor, FinalDamage, GetInstigatorController(), this, UDamageType::StaticClass());
		GEngine->AddOnScreenDebugMessage(3, 2.0f, FColor::Green, FString::Printf(TEXT("Boss Attack Hit Damage %f"), FinalDamage));
	}

	if (HitImpactEffect)
	{
		UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), HitImpactEffect, Hit.ImpactPoint);
	}

	if (ImpactSound)
	{
		UGameplayStatics::PlaySoundAtLocation(this, ImpactSound, Hit.ImpactPoint);
	}

	Destroy();
}
생성자에서 SphereComp에 대한 설정이 복잡해보이는데, SetCollisionProfileName(TEXT("PhysicsActor"))로 설정하면 한번에 프리셋이 적용될 수 있다.
SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
SphereComp->InitSphereRadius(15.0f);
SphereComp->SetCollisionProfileName(TEXT("PhysicsActor"));
SetRootComponent(SphereComp);

 

 

 

🔔 발사체 클래스 정리

BaseMagicProjectile 클래스에 기본적인 기능을 구현해놓고, 상속 받은 클래스에서 발사체 속도와 데미지 배율만 따로 설정해주면 되어서 추가적인 발사체 확장이 편해졌다.
이후에 AI가 스킬을 사용해 발사체를 발사하는 과정에 대해 정리하겠다.

 

 

'Project > Gunfire Paragon' 카테고리의 다른 글

AIController와 BT 설계  (0) 2025.03.06
일반 AI_공격 구현  (0) 2025.03.04
일반 AI 공격 Test/ UWorld::SweepSingleByChannel  (0) 2025.02.20
AIController, Behavior Tree 초기 Test  (0) 2025.02.19
'Project/Gunfire Paragon' 카테고리의 다른 글
  • AIController와 BT 설계
  • 일반 AI_공격 구현
  • 일반 AI 공격 Test/ UWorld::SweepSingleByChannel
  • AIController, Behavior Tree 초기 Test
동그래님
동그래님
  • 동그래님
    개발자 동그래
    동그래님
  • 전체
    오늘
    어제
    • 분류 전체보기 (207) N
      • 공부 (51)
        • Code Cata (50)
      • 내배캠 (148) N
        • TIL (50)
        • C++ (37)
        • Unreal Engine (48)
        • GAS(Gameplay Ability System.. (13) N
      • Project (7)
        • Gunfire Paragon (5)
        • Arena Fighters (1)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    동그래님
    보스 공격_원거리 공격에 사용할 Projectile 구현
    상단으로

    티스토리툴바