수류탄같은 투척무기를 만드는데에는 대표적으로 두가지 방법이 있다.
피직스시뮬레이션을 사용하는 방법과 프로젝타일을 사용하는 방법이다.
피직스 시뮬레이션을 사용하면 좀 더 자연스러운 투척이 가능하지만, 그만큼 일정한 제어가 어렵다.
슈팅게임에선 자연스러운 구현도 좋지만, 일정한 제어가 플레이어에게 더 쾌적한 경험을 제공할 수 있지 않을까 싶다.
1. 클래스 생성


- 수류탄을 스폰할 클래스

- 날아갈 수류탄 프로젝타일 클래스

2. 코드 구현
- 스폰할 클래스
void AThrowableWeapon::Spawn()
{
if (!bIsInHand && CurrentPossession > 0)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
ThrowableProjectile = GetWorld()->SpawnActor<AThrowableProjectile>(ThrowableProjectileClass, GetActorLocation(), FRotator::ZeroRotator, SpawnParams);
if (ThrowableProjectile)
{
if (ThrowableProjectile->AttachToComponent(this->RootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale))
{
CurrentPossession--;
bIsInHand = true;
}
}
}
}
void AThrowableWeapon::Throw()
{
if (ThrowableProjectile)
{
ThrowableProjectile->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
ThrowableProjectile->Thrown(ThrowForce);
bIsInHand = false;
}
}
- 프로젝타일 클래스
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
ProjectileMovement->InitialSpeed = 0.0f;
ProjectileMovement->MaxSpeed = 0.0f;
ProjectileMovement->bRotationFollowsVelocity = true;
ProjectileMovement->bShouldBounce = true;
ProjectileMovement->Bounciness = 0.3f;
ProjectileMovement->Friction = 0.2f;
ProjectileMovement->ProjectileGravityScale = 1.0f;
void AThrowableProjectile::Thrown(float ThrowForce)
{
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
ProjectileMovement->InitialSpeed = ThrowForce;
ProjectileMovement->MaxSpeed = ThrowForce;
DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
FRotator PlayerViewRotation = PlayerController->GetControlRotation();
FVector PlayerViewVector = PlayerViewRotation.Vector();
ProjectileMovement->Velocity = PlayerViewVector * ThrowForce;
}
if (!GetWorldTimerManager().IsTimerActive(WeaponTimerHandle))
{
ExplosionCount();
}
}
void AThrowableProjectile::ExplosionCount() // 폭발 카운트 시작
{
if (ThrowableWeaponMesh)
{
if (ReadyToAttackSound)
{
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ReadyToAttackSound, GetActorLocation()); // 핀을 뽑거나 불을 붙이거나
}
GetWorldTimerManager().SetTimer(WeaponTimerHandle, this, &AThrowableProjectile::Explode, TimeForExplosion, false);
}
}
void AThrowableProjectile::Explode() // 폭발
{
if (GetWorldTimerManager().IsTimerActive(WeaponTimerHandle))
{
GetWorldTimerManager().ClearTimer(WeaponTimerHandle);
}
ProjectileMovement->StopMovementImmediately();
if (ExplosionNiagara)
{
UE_LOG(LogTemp, Warning, TEXT("Exist Niagara"));
UNiagaraComponent* Explosion = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
GetWorld(),
ExplosionNiagara,
GetActorLocation(),
GetActorRotation(),
FVector(1.0f), true);
}
if (ExplosionSound)
{
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ExplosionSound, GetActorLocation());
}
TArray < AActor*> OverlappingActors;
DamageCollision->GetOverlappingActors(OverlappingActors);
for (AActor* Actor : OverlappingActors)
{
if (Actor && Actor->ActorHasTag("Pawn"))
{
UGameplayStatics::ApplyDamage(Actor, Damage, nullptr, this, UDamageType::StaticClass());
}
}
this->Destroy();
}
이렇게 하면 수류탄이 스폰되긴 하지만, 프로젝타일의 특성때문에 소켓에 붙어있지 않고 아래로 떨어져버린다.
이를 방지하기 위해선 손에 있는 동안엔 프로젝타일 무브먼트를 비활성화해줘야한다.
- 스폰할 클래스의 Spawn()에서
void AThrowableWeapon::Spawn()
{
if (!bIsInHand && CurrentPossession > 0)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
ThrowableProjectile = GetWorld()->SpawnActor<AThrowableProjectile>(ThrowableProjectileClass, GetActorLocation(), FRotator::ZeroRotator, SpawnParams);
if (ThrowableProjectile)
{
if (ThrowableProjectile->AttachToComponent(this->RootComponent, FAttachmentTransformRules::SnapToTargetNotIncludingScale))
{
ThrowableProjectile->SpawnSetting(); //추가
CurrentPossession--;
bIsInHand = true;
}
}
}
}
- 프로젝타일 클래스에서 비활성화를 담당할 함수 추가
void AThrowableProjectile::SpawnSetting()
{
if (ProjectileMovement)
{
ProjectileMovement->Deactivate();
}
}
void AThrowableProjectile::Thrown(float ThrowForce)
{
if (APlayerController* PlayerController = GetWorld()->GetFirstPlayerController())
{
ProjectileMovement->Activate(); // 무브먼트 활성화 추가
ProjectileMovement->InitialSpeed = ThrowForce;
ProjectileMovement->MaxSpeed = ThrowForce;
DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
FRotator PlayerViewRotation = PlayerController->GetControlRotation();
FVector PlayerViewVector = PlayerViewRotation.Vector();
ProjectileMovement->Velocity = PlayerViewVector * ThrowForce;
}
if (!GetWorldTimerManager().IsTimerActive(WeaponTimerHandle))
{
ExplosionCount();
}
}
이제 수류탄이 소켓에 얌전히 붙어있다가 Throw()를 호출하면 프로젝타일 무브먼트가 활성화되어 지정된 스피드로 날아간다.
3. 이펙트 추가

나이아가라를 이용해 폭발효과를 만들어준다.
그리고 미리 생성해둔 수류탄의 해당 디테일 패널의 항목에 넣어주면 된다.
마찬가지로 사운드도 넣어준다.
4. 결과

'게임 개발 공부 > 언리얼엔진' 카테고리의 다른 글
| UE5 | C++ - 조준점 정렬 (0) | 2025.02.28 |
|---|---|
| UE5 - 랜드스케이프 블렌드 머티리얼 (0) | 2025.02.27 |
| UE5 | c++ - ProjectileMovement->SetUpdatedComponent() (0) | 2025.02.24 |
| UE5 | C++ - Projectile (0) | 2025.02.21 |
| UE5 | C++ - FPlatformTime::Second() (2) (0) | 2025.02.19 |