- 이번 포스트에서는 전 포스트에서 생성한 캐릭터에게 무기(총기) 시스템을 구현하는 내용을 다룬다.
- 총기를 구현하기 위한 기본적인 Gun 스크립트 변수 선언
public class Gun : MonoBehaviour
{
public string gunName; // 총 이름
public float range; // 사정 거리
public float accuracy; // 정확도
public float fireRate; // 연사속도
public float reloadTime; // 재장전 속도
public int damage; // 총의 데미지
public int reloadBulletCount; // 총알 재장전 개수
public int currentBulletCount; // 현재 탄알집에 남아있는 총알의 개수
public int maxBulletCount; // 최대 소유 가능 총알 개수
public int carryBulletCount; // 현재 소유중 총알 개수
public float retroActionForce; // 반동 세기
public float retroActionFineSightForce; // 정 조준시 반동 세기
public Vector3 fineSightOriginPos; // 발사 시야
public Animator anim; // 총기 애니메이션 적용
public ParticleSystem muzzleFlash; // 총 발사 효과
public AudioClip fire_Sound; // 총 발사 효과음
}
- Animator를 통한 총기 애니메이션 적용
- Animator : 각각의 모션으로 분할된 캐릭터 애니메이션을 흐름과 조건에 맞추어 구성한 뒤 게임 속에서 캐릭터의 자연스러운 움직임을 구현하는 기능
- Entry : 기본 상태 애니메이션을 지정
- Any State : 어떤 상태에서라도 넘어갈 수 있는 애니메이션 지정
- Exit : 무조건 애니메이션 종료
- Hit() 공격 : Raycast라는 전방 감지 레이저 기능을 통해 전방 공간에 존재하는 물체를 발사 시 감지하여 그에 대한 정보를 출력하거나 효과를 반영시키는 내용
private void Hit()
{
if (Physics.Raycast(theCam.transform.position, theCam.transform.forward +
new Vector3(Random.Range(-theCrosshair.GetAccuracy() - currentGun.accuracy, theCrosshair.GetAccuracy() + currentGun.accuracy),
Random.Range(-theCrosshair.GetAccuracy() - currentGun.accuracy, theCrosshair.GetAccuracy() + currentGun.accuracy),
0)
, out hitInfo, currentGun.range, layerMask))
{ //전방의 물체를 감지하고 해당 물체에 대한 타격 내용을 출력한다.
GameObject clone = Instantiate(hit_effect_prefab, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));
Destroy(clone, 2f);
}
}
- 재장전 : 키보드 R키 또는 터치패드 버튼을 누르면 재장전 애니메이션과 함께 부족한 총알 수가 새로운 탄창으로 교체되며 채워진다.
private void TryReload() // 재장전 시도
{
if(Input.GetKeyDown(KeyCode.R) && !isReload && currentGun.currentBulletCount < currentGun.reloadBulletCount)
{ //키보드 R키를 누르면 잔여 총알 수가 기본 총알 수보다 낮고 재장전 상태가 아닐 시 재장전 코루틴을 실행하여 재장전 진행
CancelFineSight();
StartCoroutine(ReloadCoroutine());
}
}
public void CancelReload()
{
if (isReload) //재장전을 멈추기위해 모든 코루틴을 일시적 종료
{
StopAllCoroutines();
isReload = false;
}
}
IEnumerator ReloadCoroutine() // 재장전
{
if(currentGun.carryBulletCount > 0)
{
isReload = true;
currentGun.anim.SetTrigger("Reload"); //재장전 애니메이션 발동
currentGun.carryBulletCount += currentGun.currentBulletCount;
currentGun.currentBulletCount = 0; // 기존 잔여 총알 수에 새로운 재장전 총알 수를 추가 부여
yield return new WaitForSeconds(currentGun.reloadTime);
if (currentGun.carryBulletCount >= currentGun.reloadBulletCount)
{ //새로운 탄창으로 교체하여 총 잔여 총알 수가 차감되는 경우
currentGun.currentBulletCount = currentGun.reloadBulletCount;
currentGun.carryBulletCount -= currentGun.reloadBulletCount;
}
else
{ // 모든 잔여 총알 수가 완전히 0이 남을 경우
currentGun.currentBulletCount = currentGun.carryBulletCount;
currentGun.carryBulletCount = 0;
}
isReload = false;
}
else
{
Debug.Log("소유한 총알이 없음");
}
}
- 정조준 : 마우스 오른쪽 버튼을 누르면 정조준 모드를 활성화/비활성화 가능
private void TryFineSight() // 정조준 시도
{
if (Input.GetButtonDown("Fire2") && !isReload)
{
FineSight();
}
}
public void CancelFineSight() // 정조준 취소
{
if (isFineSightMode)
FineSight();
}
private void FineSight() // 정조준 가동
{
isFineSightMode = !isFineSightMode;
currentGun.anim.SetBool("FineSightMode", isFineSightMode); // 정조준 애니메이션 가동
theCrosshair.FineSightAnimation(isFineSightMode);
if (isFineSightMode)
{
StopAllCoroutines(); //모든 코루틴을 중지하고 정조준 활성화 코루틴만을 실행한다.
StartCoroutine(FineSightActivateCoroutine());
}
else
{
StopAllCoroutines(); //모든 코루틴을 중지하고 정조준 비활성화 코루틴만을 실행한다.
StartCoroutine(FineSightDeactivateCoroutine());
}
}
IEnumerator FineSightActivateCoroutine() // 정조준 활성화
{
while(currentGun.transform.localPosition != currentGun.fineSightOriginPos)
{ // 플레이어가 바라보는 시야에서 z값 (0.2f) 만큼의 시야 조정과 정조준 위치값을 지정하여 정조준 모드를 활성화한다.
currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, currentGun.fineSightOriginPos, 0.2f);
yield return null;
}
}
IEnumerator FineSightDeactivateCoroutine() // 정조준 비활성화
{
while (currentGun.transform.localPosition != originPos)
{ // 정조준 상태의 플레이어 시야가 다시 기본 originPos 시야로 복귀할수 있도록 한다.
currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, originPos, 0.2f);
yield return null;
}
}
- 반동 : Vector3를 통해 저장된 좌표값을 변경해가며 화면의 앞/뒤 흔들림 정도를 조정하여 발사 시 반동 효과를 구현한다.
IEnumerator RetroActionCoroutine() // 반동
{
Vector3 recoilBack = new Vector3(currentGun.retroActionForce, originPos.y, originPos.z);
Vector3 retroActionRecoilBack = new Vector3(currentGun.retroActionFineSightForce, currentGun.fineSightOriginPos.y, currentGun.fineSightOriginPos.z);
if (!isFineSightMode)
{
currentGun.transform.localPosition = originPos;
// 반동 시작
while(currentGun.transform.localPosition.x <= currentGun.retroActionForce - 0.02f)
{
currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, recoilBack, 0.4f);
yield return null;
}
// 원위치
while(currentGun.transform.localPosition != originPos)
{
currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, originPos, 0.1f);
yield return null;
}
}
else
{
currentGun.transform.localPosition = currentGun.fineSightOriginPos;
// 반동 시작
while (currentGun.transform.localPosition.x <= currentGun.retroActionFineSightForce - 0.02f)
{
currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, retroActionRecoilBack, 0.4f);
yield return null;
}
// 원위치
while (currentGun.transform.localPosition != currentGun.fineSightOriginPos)
{
currentGun.transform.localPosition = Vector3.Lerp(currentGun.transform.localPosition, currentGun.fineSightOriginPos, 0.1f);
yield return null;
}
}
}
- 총기 발사 효과 : Paricle System
- Particle System : 액체, 구름 및 불꽃 같은 유체 엔티티
- 효과의 빛, 모양, 발생 지속 시간 등을 임의로 지정하여 개발자가 원하는 이펙트를 직접 제작할 수 있다.
- 총기 구현 및 발사 효과가 적용 완료된 모습