AnimNotify를 활용한 샷건 1발씩 장전하기

김지윤·2025년 3월 5일

UE5_MultiplayerShooter

목록 보기
19/25
post-thumbnail

AnimNotify는 애니메이션과 함수 호출을 결합할 수 있는 매우 강력한 기능이다.
기본적으로 사운드 재생, 나이아가라 재생 등을 지원하고 있고

Notifies 트랙을 우클릭해서 NewNotify로 추가해 이름을 지정해주면

이런 노티파이가 생성된다.

Skeleton 에셋의 Animation Notifies탭에서 현재 어떤 노티파이들이 있는지 확인할 수 있다.

일치하는 스켈레톤의 애니메이션 블루프린트 이벤트 그래프에서 AnimNotify를 검색하면 이벤트 노드를 생성할 수 있다.

해당 애니메이션 몽타주가 재생되다가 Notify를 만나면 이게 호출되는 거다.
ShotgunShellReload는 BlueprintCallable로 열어놓은 함수다.

void UCombatComponent::ShotgunShellReload()
{
	if (Character && Character->HasAuthority())
	{
		UpdateShotgunAmmoValues();
	}
}

void UCombatComponent::UpdateShotgunAmmoValues()
{
	if (Character == nullptr || EquippedWeapon == nullptr)
	{
		return;
	}

	if (CarriedAmmoMap.Contains(EquippedWeapon->GetWeaponType()))
	{
		CarriedAmmoMap[EquippedWeapon->GetWeaponType()] -= 1;
		CarriedAmmo = CarriedAmmoMap[EquippedWeapon->GetWeaponType()];
	}
	Controller = Controller == nullptr ? Cast<ABlasterPlayerController>(Character->Controller) : Controller;
	if (Controller)
	{
		Controller->SetHUDCarriedAmmo(CarriedAmmo);
	}
	EquippedWeapon->AddAmmo(1);

	bool bNeedMoreReload = !EquippedWeapon->IsFull() && CarriedAmmo > 0;
	JumpToShotgunMoreReload(bNeedMoreReload);
}

Shell 노티파이를 만나면 소지한 샷건 Ammo를 1발 줄이고, 장전된 Ammo를 1 늘린다.
그 다음 !EquippedWeapon->IsFull() && CarriedAmmo > 0 이 조건을 JumpToShotgunMoreReload의 매개변수로 보낸다.

void UCombatComponent::JumpToShotgunMoreReload(bool bNeedMoreReload)
{
	UAnimInstance* AnimInstance = Character->GetMesh()->GetAnimInstance();
	if (AnimInstance && Character->GetReloadMontage())
	{
		if (bNeedMoreReload)
		{
			AnimInstance->Montage_JumpToSection(FName("ShotgunStart"));
		}
		else
		{
			AnimInstance->Montage_JumpToSection(FName("ShotgunEnd"));
		}
	}
}

더 장전해야 하면 true, 더 장전할 수 없으면 false가 된다.
그 뒤 일치하는 섹션으로 보내 몽타주를 다시 재생한다.

자세히 보면 첫 Shell보다 미세하게 ShotgunStart 섹션이 앞서있다.
더 장전이 필요하면 저기로 가서 2번째 Shell 노티파이를 만나고
더이상 장전이 필요하지 않으면 ShotgunEnd 섹션으로 가게 된다.

강의에선 이걸 구현하지 않아서 샷건의 탄창이 아무리 커도 4발만 넣고 끝냈었는데, 나는 무한히 장전할 수 있도록 구현했다.

void UCombatComponent::OnRep_CarriedAmmo()
{	
	Controller = Controller == nullptr ? Cast<ABlasterPlayerController>(Character->Controller) : Controller;
	if (Controller)
	{
		Controller->SetHUDCarriedAmmo(CarriedAmmo);
	}
	bool bJumpToShotgunEnd = CombatState == ECombatState::ECS_Reloading
		&& EquippedWeapon != nullptr
		&& EquippedWeapon->GetWeaponType() == EWeaponType::EWT_Shotgun
		&& (CarriedAmmo == 0 || EquippedWeapon->IsEmpty());
	JumpToShotgunMoreReload(!bJumpToShotgunEnd);
}

위 구현이 없으면 서버에서만 무한 장전이 작동하기 때문에 클라이언트도 알 수 있도록 OnRep에 작성해줬다.

bool UCombatComponent::CanFire()
{
	if (EquippedWeapon == nullptr)
	{
		return false;
	}

	if (!EquippedWeapon->IsEmpty() && bCanFire && CombatState == ECombatState::ECS_Reloading && EquippedWeapon->GetWeaponType() == EWeaponType::EWT_Shotgun)
	{
		// 샷건 장전 중엔 예외로 사격 가능
		return true;
	}

	return !EquippedWeapon->IsEmpty() && bCanFire && CombatState == ECombatState::ECS_Unoccupied;
}

그 외에 샷건 장전 중엔 예외로 사격 가능하도록 처리도 해줬다.

profile
공부한 거 시간 날 때 작성하는 곳

0개의 댓글