저번에 만든 탄환을 베이스로 3종류의 총알을 만들것이다.
1. 일반탄 - 적이나 구조물에 충돌하면 사라지고 적에게 피해를 줌
2. 관통탄 - 구조물에 맞으면 사라지지만 적에게 맞으면 피해를 주고 뚫고 지나감
3. 폭발탄 - 적이나 구조물에 맞으면 폭발하고 범위피해를 줌
이렇게 3가지의 온힛을 가진 총알들을 만들어보겠다.
1. 일반탄환
일반탄환은 액터에 부딪히면 사라지게 하고싶고, 부딪힌 액터가 적이라면 데미지를 주고싶다.
ANomalBullet::ANomalBullet()
{
Speed = 1000.0f;
BulletDamage = 0.0f;
}
//적이나 구조물 중 하나에 부딪히면 사라짐
void ANomalBullet::OnHit(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
)
{
if (OtherActor && OtherActor != this)
{
UE_LOG(LogTemp, Warning, TEXT("OnHit called with %s"), *OtherActor->GetName());
if (OtherActor && OtherActor->IsA(AEnemyCharacterAi::StaticClass()))
{
AEnemyCharacterAi* HitEnemy = Cast<AEnemyCharacterAi>(OtherActor);
if (HitEnemy)
{
HitEnemy->EnemyTakeDamage(BulletDamage);
}
Destroy();
UE_LOG(LogTemp, Warning, TEXT("Damage Applied: %.2f"), BulletDamage); // [[4]](#__4)
}
else
{
Destroy();
}
}
}
부딪힌 액터가 액터고, 이 액터가 아니라면 EnemyCharacterAi클래스인지 체크하고 맞다면 EnemyTakeDamage를 불러와서 피해를 주고 Destroy 아니라면 그냥 Destroy한다. 어렵지 않다. 참고로 EnemyTakeDamage는 그냥 EnemyCharacterAi클래스에 구현해놓은 HP - Damage이다.
2. 관통탄
#include "Bullet/PiercingBullet.h"
APiercingBullet::APiercingBullet()
{
Speed = 500.0f;
BulletDamage = 0.0f;
}
void APiercingBullet::OnHit(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
)
{
if (OtherActor == GetOwner())
{
return;
}
if (OtherActor && OtherActor != this)
{
UE_LOG(LogTemp, Warning, TEXT("OnHit called with %s"), *OtherActor->GetName());
if (OtherActor && OtherActor->IsA(AEnemyCharacterAi::StaticClass()))
{
if (AEnemyCharacterAi* HitEnemy = Cast<AEnemyCharacterAi>(OtherActor))
{
if (HitEnemy)
{
HitEnemy->EnemyTakeDamage(BulletDamage);
}
UE_LOG(LogTemp, Warning, TEXT("Damage Applied: %.2f"), BulletDamage); // [[4]](#__4)
}
}
else
{
Destroy();
}
}
}
이번에는 관통탄이다.이걸로 잘 되면 좋겠지만 자꾸 총알이 몬스터를 밀고가서 어려웠다. 하지만 이건 코드가 아니라 블루프린트 상에서 비꿔야됐다.
스태틱 메쉬는 NoCollision으로
이렇게 설정해주고
HitCollision은
OverlapDynamic으로 설정을 해주면 몬스터를 뚫고 지나간다... (이것땜에 시간을 엄청 썼다.)
생각보다 잘 안될 때 블루프린트로 해결이 된다.
여기까진 코드로 쉬웠다... 하지만 폭발탄이 문제다
3. 폭발탄
1.접촉 시
void AExplosiveBullet::OnHit(
UPrimitiveComponent* OverlappedComponent,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
)
{
if (OtherActor == GetOwner())
{
return;
}
UE_LOG(LogTemp, Warning, TEXT("OnHit called with %s"), *OtherActor->GetName());
if (OtherActor && OtherActor != this)
{
Explode(GetActorLocation());
Destroy();
Super::BulletEffects();
}
}
우선 온힛이다. 온힛이 되면 Explode를 터트리고, 제거한 후 폭발이펙트와 사운드가 나오도록 설정했다.
2. 폭발함수
void AExplosiveBullet::Explode(FVector Location)
{
TArray<AActor*> IgnoredActors;
// 플레이어 캐릭터 추가
if (APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0))
{
IgnoredActors.AddUnique(PlayerPawn);
UE_LOG(LogTemp, Warning, TEXT("Added Player: %s"), *GetNameSafe(PlayerPawn));
}
AController* DamageInstigator = nullptr;
if (AActor* ValidInstigator = GetInstigator())
{
IgnoredActors.AddUnique(ValidInstigator);
DamageInstigator = ValidInstigator->GetInstigatorController();
}
// 모든 적을 가져와서 범위 내의 적을 찾아서 데미지 적용
TArray<AActor*> AllEnemies;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AEnemyCharacterAi::StaticClass(), AllEnemies);
for (AActor* Actor : AllEnemies)
{
if (Actor && FVector::Dist(Actor->GetActorLocation(), Location) <= ExplosionRadius)
{
// 적에게 데미지 적용
if (AEnemyCharacterAi* Enemy = Cast<AEnemyCharacterAi>(Actor))
{
Enemy->EnemyTakeDamage(BulletDamage);
UE_LOG(LogTemp, Warning, TEXT("Enemy %s took damage: %.2f"), *Enemy->GetName(), BulletDamage);
}
}
}
// 원형 피해 적용 (적은 제외하고)
UGameplayStatics::ApplyRadialDamage(
GetWorld(),
BulletDamage,
Location,
ExplosionRadius,
nullptr,
IgnoredActors,
this, // Damage Causer
DamageInstigator // Controller
);
// 디버그 로그
UE_LOG(LogTemp, Warning, TEXT("Damage Applied: %.2f"), BulletDamage);
DrawDebugSphere(GetWorld(), Location, ExplosionRadius, 12, FColor::Red, false, 3.0f);
}
내 플레이어 캐릭터를 IgnoredActors에 추가한 후 데미지 적용 시 IgnoredActors를 제외한 적에게 피해를 입히는 형식이였다. 데미지가 제대로 적용 됐지만 문제는 내 캐릭터가 폭발 범위 내에 있으면 전부 다 데미지를 받지 않는것이다. 그 이유는 찾지 못했지만 새로운 해결책을 찾아냈다.
어차피 범위 내에 있는 객체중에 EnemyTakeDamage 함수를 가지고 있는건 적 캐릭터 뿐이니까 그냥 피어싱 불릿과 똑같은느낌으로 처리하면 되지 않을까 하고 새로 만들어봤다.
void AExplosiveBullet::Explode(FVector Location)
{
TArray<AActor*> IgnoredActors;
// 1. 무시할 액터 설정 (플레이어와 발사자)
if (APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0))
{
IgnoredActors.AddUnique(PlayerPawn);
}
if (AActor* InstigatorActor = GetInstigator())
{
IgnoredActors.AddUnique(InstigatorActor);
}
// 2. 범위 내 모든 AEnemyCharacterAi 찾기
TArray<AActor*> AllEnemies;
UGameplayStatics::GetAllActorsOfClass(
GetWorld(),
AEnemyCharacterAi::StaticClass(),
AllEnemies
);
// 3. 범위 내 적에게 직접 EnemyTakeDamage 호출
for (AActor* Enemy : AllEnemies)
{
if (Enemy &&
FVector::Dist(Enemy->GetActorLocation(), Location) <= ExplosionRadius)
{
if (AEnemyCharacterAi* EnemyChar = Cast<AEnemyCharacterAi>(Enemy))
{
// 적 클래스의 EnemyTakeDamage 직접 호출
EnemyChar->EnemyTakeDamage(BulletDamage);
UE_LOG(LogTemp, Warning, TEXT("Enemy %s took damage"), *EnemyChar->GetName());
}
}
}
// 4. 디버그 및 이펙트 처리
DrawDebugSphere(GetWorld(), Location, ExplosionRadius, 12, FColor::Red, false, 3.0f);
Destroy();
}
이렇게 하니까 아주 잘된다
이것들을 만들면서 배운점
내 코드만 의심하지 말고 블루프린트도 의심하자!
로그는 도중도중에 계속 넣어두자!
'언리얼 공부 > C++' 카테고리의 다른 글
총알만들기 (0) | 2025.02.27 |
---|---|
총 만들기 (0) | 2025.02.25 |
순수가상함수, 추상클래스 그리고 인스턴스 (0) | 2025.02.20 |
플로우 차트 (0) | 2025.02.18 |
FPS무기 구조 생각 (0) | 2025.02.17 |