BossFight - Damage

๊น€๋Œ€๊ฒธยท2025๋…„ 5์›” 12์ผ

Damage

์ด๋ฒˆ์—๋Š” Trace ํŒ์ •ํ›„ ํ”ผ๊ฒฉ ๋˜๋ฉด Damage๋ฅผ ์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ ํ•ด๋ณด์ž.

๐ŸŽฎ IWeaponInterface.h

class BOSSFIGHT_API IWeaponInterface
{
	GENERATED_BODY()
	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual void AttackTrace() = 0;
	virtual void OnHitActor(AActor* HitActor) = 0;
	virtual void AttackEnd() = 0;
};

IWeaponInterface์—์„œ ํ”ผ๊ฒฉ ์‹œ ๋ฐ๋ฏธ์ง€๋ฅผ ์ฃผ๊ฑฐ๋‚˜, ํ”ผ๊ฒฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ž‘๋™ ์‹œํ‚ฌ ํ•จ์ˆ˜ OnHitActor()์™€ ๊ณต๊ฒฉ ์ข…๋ฃŒ๋ฅผ ์•Œ๋ฆฌ๋Š” AttackEnd()ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

๐ŸŽฎ AWeaponBase.h

UCLASS()
class BOSSFIGHT_API AWeaponBase : public APropBase, public IWeaponInterface
{
	GENERATED_BODY()
	// ์ค‘๋žต
protected:
	// ์ค‘๋žต
	AActor* HitedActor;
};

์ดํ›„ AWeaponBase์— ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ •์˜ํ•œ ํ•จ์ˆ˜์™€, AActor ํฌ์ธํ„ฐํ˜•์˜ ํ”ผ๊ฒฉ๋œ ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•  ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ ํ•˜์˜€๋‹ค.

AttackEnd() ํ•จ์ˆ˜๋Š” ๋‹จ์ˆœํ•˜๊ฒŒ HitedActor๋ฅผ nullptr๋กœ ๋ณ€๊ฒฝ ํ•ด์ฃผ๋„๋ก ๊ตฌํ˜„ ํ•˜์˜€๋‹ค.

๐ŸŽฎ APlayerWeapon.h

UCLASS()
class BOSSFIGHT_API APlayerWeapon : public AWeaponBase
{
	GENERATED_BODY()
    // ์ค‘๋žต
	virtual void OnHitActor(AActor* HitActor) override;
	virtual void OnHitActor(AActor* HitActor, int32 PartNum);
};

๐ŸŽฎ APlayerWeapon.cpp

void APlayerWeapon::AttackTrace()
{
	// ์ค‘๋žต
	if (HitResult.GetActor() && HitResult.GetComponent())
	{
		if (UBFCombetBoxComponent* HitComponent = Cast<UBFCombetBoxComponent>(HitResult.GetComponent()))
		{
			if (HitComponent->GetPartNum() != 0)
			{
				OnHitActor(HitResult.GetActor(), HitComponent->GetPartNum());
			}
			else if (HitComponent->GetPartNum() == 0)
			{
				OnHitActor(HitResult.GetActor());
			}
		}
	}
}

void APlayerWeapon::OnHitActor(AActor* HitActor)
{
	if (HitedActor == HitActor) { return; }

	HitedActor = HitActor;

	FGameplayEventData Data;
	Data.Instigator = GetOwner();
	Data.Target = HitActor;

	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwner(), BFGameplayTag::Shared_Event_OnDamaged, Data);
}

void APlayerWeapon::OnHitActor(AActor* HitActor, int32 PartNum)
{
	if (HitedActor == HitActor) { return; }

	HitedActor = HitActor;

	FGameplayEventData Data;
	Data.Instigator = GetOwner();
	Data.Target = HitActor;

	switch (PartNum)
	{
	case 1:
		UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwner(), BFGameplayTag::Shared_Event_OnDamaged_Part1, Data);
		break;
	case 2:
		UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwner(), BFGameplayTag::Shared_Event_OnDamaged_Part2, Data);
		break;
	default:
		break;
	}
}

๋‹ค์Œ์œผ๋กœ APlayerWeapon์—์„œ OnHitActor() ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ ํ•ด์ฃผ์—ˆ๋‹ค. ์ด๋•Œ ์  ์บ๋ฆญํ„ฐ๋Š” ๋ถ€์œ„ํŒŒ๊ดด๋ฅผ ์œ„ํ•ด BoxComponent์— PartNum์ด ๋ถ€์—ฌ ๋˜์–ด ์žˆ๊ธฐ์— ํ•ด๋‹น PartNum์— ๋”ฐ๋ผ OnHitActor()๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ ์šฉ ํ•˜๊ธฐ์œ„ํ•ด ์˜ค๋ฒ„๋กœ๋”ฉ์„ ํ•˜์˜€๋‹ค.

OnHitActor()ํ•จ์ˆ˜๋Š” Trace์ค‘ ์ฒ˜์Œ์œผ๋กœ ํ”ผ๊ฒฉ๋œ ์ˆœ๊ฐ„ ์ž‘๋™ ํ•˜๋ฉฐ, ์ค‘๋ณต์œผ๋กœ ์ž‘๋™๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ ํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ํ•˜์˜€๋‹ค.

๋˜ํ•œ FGameplayEventData๋ฅผ ํ†ตํ•ด Instigator์™€ Target์„ ์ง€์ • ํ•ด์ฃผ์—ˆ๊ณ . UAbilitySystemBlueprintLibrary::SendGameplayEventToActor()๋ฅผ ํ†ตํ•ด Gameplay Event๋ฅผ ๋ฐœ์†ก ํ•˜๋„๋ก ๊ตฌํ˜„ ํ•˜์˜€๋‹ค.

๋‹ค์Œ์œผ๋กœ Player์˜ ๊ณต๊ฒฉ์— WaitGameplayEvent๋ฅผ ํ†ตํ•ด GameplayEvent๋ฅผ ๋Œ€๊ธฐํ•˜๊ณ , Damage๋ฅผ ์ฃผ๋„๋ก ๊ตฌํ˜„ ํ•ด๋ณด์ž.

์ „์ฒด์ ์ธ ๋…ธ๋“œ๋Š” ์œ„์™€ ๊ฐ™์ด ๊ตฌ์„ฑ ํ•˜์˜€๊ณ , OnDamage ํ•จ์ˆ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑ ํ•˜์˜€๋‹ค.

GameplayEffectSelet()ํ•จ์ˆ˜๋Š” Evnent Data๋กœ ์ „๋‹ฌ๋œ Tag๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ ์šฉํ•  GameplayEffect๋ฅผ ์„ ํƒํ•˜๋Š” ํ•จ์ˆ˜ ์ด๋‹ค.

์„ ํƒ๋œ ํ•จ์ˆ˜๋ฅผ Target์— ์ ์šฉํ•˜๋„๋ก ๊ตฌํ˜„ ํ•˜์˜€๋‹ค.

์ž‘๋™ ์˜์ƒ

์ ์ด ํ”ผ๊ฒฉ์‹œ ์ด HP๊ฐ€ ๊ฐ์†Œํ•˜๊ณ , ๋ถ€์œ„ํŒŒ๊ดด ๊ฐ€๋Šฅ๋ถ€์œ„(์•ž๋ฐœ)์— ํ”ผ๊ฒฉ์‹œ ํ•ด๋‹นํ•˜๋Š” PartHP๊ฐ€ ๊ฐ์†Œํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•˜์˜€๋‹ค.

๋‹ค์Œ์œผ๋กœ ์ ์˜ ๊ณต๊ฒฉ์— ๋Œ€ํ•œ ํ”Œ๋ ˆ์ด์–ด์˜ Damage ์ ์šฉ์ด๋‹ค.

void AEnemyWeapon::AttackTrace()
{
	//์ค‘๋žต
	WeaponCollision->SetCollisionResponseToChannel(ECC_Pawn, ECR_Ignore);
	//์ค‘๋žต
	if (HitResult.GetActor() && UBFFunctionLibrary::IsTargetPawnHostile(Cast<APawn>(GetOwner()),  Cast<APawn>(HitResult.GetActor())))
	{
		OnHitActor(HitResult.GetActor());
	}
}

void AEnemyWeapon::OnHitActor(AActor* HitActor)
{
	if (HitedActor == HitActor) { return; }

	HitedActor = HitActor;
	if (UBFFunctionLibrary::NativeDoseActorHaveTag(HitedActor, BFGameplayTag::Player_Status_Avoiding)) { return; }

	FGameplayEventData Data;
	Data.Instigator = GetOwner();
	Data.Target = HitActor;

	// TODO : ๊ฐ• ๊ณต๊ฒฉ, ์•ฝ๊ณต๊ฒฉ์„ Tag๋ฅผ ํ†ตํ•ด ๊ตฌ๋ถ„ํ•˜์—ฌ, SendGameplayEventToActor() ๋ฐœ์†ก ํ•˜๋„๋ก ๊ตฌํ˜„
	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwner(), BFGameplayTag::Shared_Event_OnDamaged, Data);
}

void AEnemyWeapon::AttackEnd()
{
	Super::AttackEnd();
	WeaponCollision->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);
}

๊ธฐ๋ณธ์ ์œผ๋กœ ํ”Œ๋ ˆ์ด์–ด์™€ ๋™์ผํ•œ ๋ฐฉ์‹์ด๋‹ค. ๋‹ค๋งŒ, ์  ๊ณต๊ฒฉ์‹œ ์  Collision์— ์ถฉ๋Œํ•˜์—ฌ ํ”Œ๋ ˆ์ด์–ด ์บ๋ฆญํ„ฐ๊ฐ€ ๋ฐ€๋ ค๋‚˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ ํ•˜๊ธฐ์œ„ํ•ด SetCollisionResponseToChannel()์„ ํ†ตํ•ด, Pawn์˜ ๋ฐ˜์‘์„ ์ผ์‹œ์ ์œผ๋กœ ๋ฌด์‹œํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

๋˜ํ•œ ์ดํ›„ ์  ์บ๋ฆญํ„ฐ์˜ ๊ณต๊ฒฉ์—๋Œ€ํ•œ ํ”ผ๊ฒฉ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌ๋ถ„ ํ•˜๋„๋ก TODO๋ฆฌ์ŠคํŠธ๋ฅผ ์ž‘์„ฑ ํ•˜์˜€๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ํšŒํ”ผ ์ค‘์ด๋ฉด, GameplayEvent๋ฅผ ๋ฐœ์†ก ํ•˜์ง€ ์•Š๋„๋ก ๊ตฌํ˜„ ํ•˜์˜€๋‹ค.

์ ์บ๋ฆญํ„ฐ์˜ Ability์˜ ๋ณ€๊ฒฝ์€ ํ”Œ๋ ˆ์ด์–ด ์บ๋ฆญํ„ฐ์™€ ๋™์ผ ํ•˜๊ธฐ์— ์ƒ๋žตํ•˜์˜€๋‹ค.

์ž‘๋™ ์˜์ƒ

๋‹ค์Œ์—๋Š” ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ํ”ผ๊ฒฉ ์‹œ ๊ฐ Type์— ๋งž๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ถœ๋ ฅ ํ•˜๋„๋ก ๊ตฌํ˜„ ํ•ด๋ณด๊ฒ ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€