유니티: 리얼리티를 극대화하는 탄창 장전 시스템 만들기
게임에서 총기 시스템의 현실감은 사격 시의 타격감뿐만 아니라, 탄창을 비우고 다시 채우는 ‘장전’ 과정에서도 결정됩니다. 단순히 총알 수를 0으로 만들고 다시 채우는 것이 아니라, 장전에 필요한 시간과 애니메이션, 그리고 사운드가 더해져야 플레이어는 비로소 몰입감을 느낄 수 있습니다.
오늘은 유니티에서 총기의 탄약 상태를 관리하고, 플레이어의 입력에 따라 현실적인 장전 과정을 구현하는 스크립트를 단계별로 살펴보겠습니다. 이 코드는 총기의 현재 탄약, 총 보유 탄약, 그리고 장전 시간을 효과적으로 처리합니다.
1. 탄창 장전 스크립트 작성
WeaponSystem.cs
라는 이름으로 스크립트를 새로 만들고 아래 코드를 입력해 주세요. 이 스크립트는 게임 내 총기 오브젝트에 부착될 것입니다.
C#
using UnityEngine;
using System.Collections;
using TMPro; // UI 연동을 위해 TextMeshProUGUI를 사용합니다.
public class WeaponSystem : MonoBehaviour
{
[Header("탄약 관리")]
public int currentAmmo;
public int magazineSize = 30;
public int totalAmmo = 120;
[Header("장전 설정")]
public float reloadDuration = 2f;
private bool isReloading = false;
[Header("총기 발사 설정")]
public float fireRate = 0.5f; // 초당 발사 횟수
private float nextFireTime = 0f;
[Header("UI 연동")]
public TextMeshProUGUI ammoText;
void Start()
{
// 게임 시작 시 탄창을 가득 채웁니다.
currentAmmo = magazineSize;
UpdateAmmoUI();
}
void Update()
{
// 'R' 키를 누르면 장전 함수를 호출합니다.
if (Input.GetKeyDown(KeyCode.R) && !isReloading)
{
ReloadMagazine();
}
// 마우스 왼쪽 버튼을 누르면 발사 함수를 호출합니다.
if (Input.GetMouseButton(0) && Time.time > nextFireTime)
{
Shoot();
}
}
public void ReloadMagazine()
{
// 이미 장전 중이거나, 탄창이 이미 가득 찼거나, 총 보유 탄약이 0이면 장전하지 않습니다.
if (isReloading || currentAmmo == magazineSize || totalAmmo == 0)
{
return;
}
// 장전 코루틴을 시작합니다.
StartCoroutine(ReloadProcess());
}
IEnumerator ReloadProcess()
{
isReloading = true;
Debug.Log("탄창 장전 중...");
// 장전 애니메이션 시작 (애니메이터에 파라미터 전달)
// animator.SetBool("IsReloading", true);
// 장전 사운드 재생
// audioSource.PlayOneShot(reloadSound);
// reloadDuration만큼 게임을 멈추지 않고 기다립니다.
yield return new WaitForSeconds(reloadDuration);
// 장전해야 할 총알 수 계산
int bulletsNeeded = magazineSize - currentAmmo;
// 보유 탄약이 필요한 총알 수보다 많거나 같을 때
if (totalAmmo >= bulletsNeeded)
{
currentAmmo += bulletsNeeded;
totalAmmo -= bulletsNeeded;
}
// 보유 탄약이 부족할 때
else
{
currentAmmo += totalAmmo;
totalAmmo = 0;
}
Debug.Log("장전 완료! 현재 탄약: " + currentAmmo + " / 총 보유 탄약: " + totalAmmo);
isReloading = false;
// animator.SetBool("IsReloading", false);
UpdateAmmoUI();
}
public void Shoot()
{
if (currentAmmo > 0 && !isReloading)
{
currentAmmo--;
Debug.Log("발사! 남은 탄약: " + currentAmmo);
nextFireTime = Time.time + fireRate;
UpdateAmmoUI();
// 총기 발사 효과 구현 (파티클, 사운드 등)
}
else if (currentAmmo <= 0 && !isReloading)
{
Debug.Log("탄약이 부족합니다. 'R' 키로 장전하세요.");
}
}
private void UpdateAmmoUI()
{
if (ammoText != null)
{
ammoText.text = $"{currentAmmo} / {totalAmmo}";
}
}
}
2. 유니티 에디터에서 설정하기
위 스크립트를 총기 시스템에 적용하려면 몇 가지 단계를 거쳐야 합니다.
- UI 구성: 씬에
Canvas
를 생성하고,UI > Text - TextMeshPro
를 추가합니다. 이 텍스트 오브젝트는 남은 탄약 수를 표시하는 역할을 합니다. - 스크립트 부착: 작성한
WeaponSystem.cs
스크립트를 총기 오브젝트에 드래그하여 부착합니다. 총기 오브젝트가 없다면 빈 게임 오브젝트를 만들어도 됩니다. - 변수 연결: 인스펙터(Inspector) 창에서
WeaponSystem
컴포넌트의Ammo Text
필드에 방금 만든 UI 텍스트 오브젝트를 드래그하여 연결합니다. - 변수 설정:
Magazine Size
,Total Ammo
,Reload Duration
,Fire Rate
등의 값을 원하는 대로 설정하세요.
3. 코드 해설 및 확장
- 코루틴(
IEnumerator
): 장전처럼 시간이 필요한 작업은 게임의 메인 스레드를 멈추지 않도록 코루틴으로 처리하는 것이 좋습니다.yield return new WaitForSeconds(reloadDuration)
는 게임이 멈추지 않고 지정된 시간만큼 대기하는 핵심적인 구문입니다. isReloading
플래그: 이 불리언(boolean) 변수는 현재 장전 중인 상태를 나타내어, 장전이 완료되기 전에 다시 장전하거나 총을 쏘는 것을 방지합니다.UpdateAmmoUI()
:Update()
함수는 매 프레임 실행되므로,Update()
대신Shoot()
이나ReloadProcess()
와 같이 탄약 수에 변화가 생길 때만 UI를 업데이트하는 함수를 따로 만들면 성능을 더 효율적으로 관리할 수 있습니다.- 확장 아이디어: 이 코드를 기반으로 장전 애니메이션을 재생하거나, 장전 중에는 다른 행동을 할 수 없도록 플레이어의 움직임을 제한하는 로직을 추가할 수 있습니다. 또한, 탄약 줍기 아이템을 만들고
totalAmmo
를 증가시키는 기능을 구현하면 더욱 완성도 높은 총기 시스템을 만들 수 있습니다.