[UE5] 3D 건슈팅게임에서 총은 어떻게 바꿀까?

ChangJin·2024년 5월 29일
0

Unreal Engine5

목록 보기
64/114
post-thumbnail

2024-05-30

깃허브!
https://github.com/ChangJin-Lee/SimpleShooter

언리얼 엔진으로 개발한 3D 건슈팅 게임에서 총을 바꾸는 방법에 대해 적은 글입니다.


1. 하나의 총으로 적을 맞추기


  • 기존의 게임 프로젝트에서는 하나의 총으로 총알을 쏴서 적에게 맞으면 적의 HP가 줄어드는 방식으로 구현했다.
  • Gun 이라는 Actor Class를 만들었다.
  • 이후에 Gun 클래스 안에 총을 당기는 행위인 PullTrigger 함수를 만들었다.
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!"));
}
  • 그 다음 발사된 총알이 맞는 위치를 계산하는 GunTrace 함수를 만들었다.
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);
}
  • Gun을 블루프린트로 만들어주고 캐릭터 Class에서 이 BP_Gun을 불러와서 사용하면 된다.


하고자 하는 것은...

  • 이를 확장하여 키보드 숫자 1, 2를 누를때마다 총을 바꿀 수 있게 만들고자 한다.


2. 여러 개의 총을 사용하기

  • 곰곰이 생각해보니 캐릭터 Class에서 BP_Gun을 받아올때 하나의 변수로 받아오고 있었다.

  • 이를 바꿔서 TArray 안에 TSubClassOf를 담는다면 여러 개의 총을 저장할 수 있다. 기능을 추가하면서 Category도 추가해주었다.

  • 그리고 TArray의 인덱스를 관리해야하므로 WeaponActiveIndex라는 int 타입의 변수를 하나 생성해서 현재 들고 있는 무기의 인덱스를 알려주는 녀석을 만들었다.

  • 그 다음 BindAction을 추가해서 키보드 숫자 1, 2에 해당하는 함수의 주소를 넘겨주었다.


  • 캐릭터 Class의 헤더파일 부분이다.

수정 전

	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<AGun> GunClass;

	UPROPERTY()
	AGun* Gun;

수정 후


	UPROPERTY()
	AGun* Gun;

	UPROPERTY(EditDefaultsOnly, Category="Weapon")
	TArray<TSubclassOf<AGun>> GunClassArray;

	UPROPERTY()
	TArray<AGun*> GunArray;

  • 이렇게 하고 cpp 파일의 BeginPlay 부분을 보면 for문으로 살짝 감싸주면 코드를 거의 그대로 사용이 가능하다.
  • 모든 총은 Spawn은 하지만 SetActorHiddenInGame으로 게임에서는 보이지 않도록 만들었다.

	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);

  • BindAction을 위한 함수도 추가해주었다. BindAction 메소드에 이 함수의 주소를 넘겨주면 잘 바인드된다.

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);
profile
게임 프로그래머

0개의 댓글