2024-05-30
깃허브! |
---|
★ https://github.com/ChangJin-Lee/SimpleShooter |
언리얼 엔진으로 개발한 3D 건슈팅 게임에서 총을 바꾸는 방법에 대해 적은 글입니다.
void AGun::PullTrigger()
{
UGameplayStatics::SpawnEmitterAttached(MuzzleFlash, Mesh, TEXT("MuzzleFlashSocket"));
UGameplayStatics::SpawnSoundAttached(MuzzleSound, Mesh, TEXT("MuzzleFlashSocket"));
FHitResult HitResult;
FVector ShotDirection;
bool bSuccess = GunTrace(HitResult, ShotDirection);
if(bSuccess)
{
DrawDebugPoint(GetWorld(), HitResult.Location, 20, FColor::Red, true);
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactEffect, HitResult.Location, ShotDirection.Rotation());
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ImpactSound, HitResult.Location);
AActor* DamagedActor = HitResult.GetActor();
if(DamagedActor)
{
FPointDamageEvent DamageEvent(Damage, HitResult, ShotDirection, nullptr);
AController *OwnerController = GetOwnerController(); // 두번 계산하더라도, 코드의 가독성이 좋아지고 동기화가 어긋날 수 있는 변수나 상태를 저장하지 않아도 된다
DamagedActor->TakeDamage(Damage, DamageEvent, OwnerController, this);
}
}
// DrawDebugCamera(GetWorld(), GetActorLocation(), GetActorRotation(), 90, 2, FColor::Red, true);
UE_LOG(LogTemp, Warning, TEXT("You Pull the Trigger!"));
}
bool AGun::GunTrace(FHitResult& HitResult, FVector& ShotDirection)
{
AController* OwnerController = GetOwnerController();
if(OwnerController == nullptr)
return false;
FVector Location;
FRotator Rotation;
OwnerController->GetPlayerViewPoint(Location,Rotation);
ShotDirection = -Rotation.Vector();
// 끝점을 위한 FVector
FVector End = Location + Rotation.Vector() * MaxRange;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
Params.AddIgnoredActor(GetOwner());
return GetWorld()->LineTraceSingleByChannel(HitResult, Location, End, ECollisionChannel::ECC_GameTraceChannel1,Params);
}
하고자 하는 것은...
곰곰이 생각해보니 캐릭터 Class에서 BP_Gun을 받아올때 하나의 변수로 받아오고 있었다.
이를 바꿔서 TArray 안에 TSubClassOf를 담는다면 여러 개의 총을 저장할 수 있다. 기능을 추가하면서 Category도 추가해주었다.
그리고 TArray의 인덱스를 관리해야하므로 WeaponActiveIndex라는 int 타입의 변수를 하나 생성해서 현재 들고 있는 무기의 인덱스를 알려주는 녀석을 만들었다.
그 다음 BindAction을 추가해서 키보드 숫자 1, 2에 해당하는 함수의 주소를 넘겨주었다.
수정 전
UPROPERTY(EditDefaultsOnly)
TSubclassOf<AGun> GunClass;
UPROPERTY()
AGun* Gun;
수정 후
UPROPERTY()
AGun* Gun;
UPROPERTY(EditDefaultsOnly, Category="Weapon")
TArray<TSubclassOf<AGun>> GunClassArray;
UPROPERTY()
TArray<AGun*> GunArray;
Gun = GetWorld()->SpawnActor<AGun>(GunClass);
GetMesh()->HideBoneByName(TEXT("weapon_r"), EPhysBodyOp::PBO_None);
Gun->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("WeaponSocket"));
Gun->SetOwner(this);
if(!ensure(!GunClassArray.IsEmpty())) return;
for(TSubclassOf<AGun> GunClass : GunClassArray)
{
Gun = GetWorld()->SpawnActor<AGun>(GunClass);
GetMesh()->HideBoneByName(TEXT("weapon_r"), EPhysBodyOp::PBO_None);
Gun->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("WeaponSocket"));
Gun->SetOwner(this);
Gun->SetActorHiddenInGame(true);
GunArray.Add(Gun);
}
GunArray[WeaponActiveIndex]->SetActorHiddenInGame(false);
void AShooterCharacter::ChangeWeapon1()
{
WeaponActiveIndex = 0;
ChangeWeapon();
}
void AShooterCharacter::ChangeWeapon2()
{
WeaponActiveIndex = 1;
ChangeWeapon();
}
void AShooterCharacter::ChangeWeapon()
{
for(int32 WeaponIndex = 0 ; WeaponIndex < GunArray.Num(); WeaponIndex++)
{
if(WeaponIndex == WeaponActiveIndex)
{
GunArray[WeaponIndex]->SetActorHiddenInGame(false);
}
else
{
GunArray[WeaponIndex]->SetActorHiddenInGame(true);
}
}
}
PlayerInputComponent->BindAction(TEXT("DrawWeapon1"), EInputEvent::IE_Pressed, this, &AShooterCharacter::ChangeWeapon1);