[Unity]09.좀비 서바이버 따라하기 #3 - 총과 슈터 만들기
- 게임 개발 - Unity3d
- 2019. 9. 14. 11:44
안녕하세요 유랑입니다.
실력향상을 위해서 오늘도 책을 따라하면서 공부하겠습니다.
궁금하신점 있으시면 댓글로 남겨주세요^^
1. 좀비 서바이버
이번 강의는 레트로님께서 만든 예제이며,
교재를 구입하시면 자세한 내용을 배우실 수 있습니다.
저는 책을 구입하였고, 스킬업을 위해서 복습겸 글을 올리겠습니다.
깃허브 사이트 => 깃허브
유튜브 사이트 => 유튜브
1-1) 총 세팅 - ㉠배치
드디어 총을 써볼 차례입니다.
추가해 주기 전 플레이어 캐릭터 자식으로 빈 오브젝트를 만들어 주세요.
이름은 Gun Pivot 그리고 트랜스폼 정보는 다음과 같습니다.
Gun Pivot 밑으로 Gun 오브젝트를 추가해 주세요.
Gun 프리팹은 총 3D 모델과 손이 위치할 곳을의 정보를 가지고 있습니다!!
1-2) 총 세팅 - ㉡라인 렌더러
라인 렌더러 컴포넌트를 추가해 주세요.
총을 발사하면 라인렌더러가 활성화 되는 형식입니다.
탄알 궤적 위에 그림자가 비칠 필요가 없으므로
Cast Shadows와 Receive Shadows를 비활성화 해주세요ㅎㅎ
1-3) 총 세팅 - ㉢오디오 소스
총 소리와 재장전 소리를 재생하기 위해 오디오 소스도 추가해 주겠습니다.
Player On Awake는 언체크 해주세요^^
1-4) 총 세팅 - ㉣사격 효과
총구 화염과 탄피 배출 효과도 있으면 좋겠죠?!
MuzzleFlashEffect와 ShellEjectEffect를 Gun 오브젝트 밑으로 넣어주세요.
미리 만들어진 파티클 오브젝트입니다.
1-5) 총 세팅 - ㉤Gun 스크립트
총의 동작 구현을 위해 Gun 스크립트를 추가해 주겠습니다.
<Gun>
Gun 스크립트는 총의 현재 상태를 표현하기 위해 State라는 enum 타입과
코루틴을 이용해 코드를 순서대로 실행시키며,
레이캐스트를 이용해 다른 오브젝트와 충돌 검사하고
총에 맞았는지 확인합니다.
using System.Collections; using UnityEngine; // 총을 구현한다 public class Gun : MonoBehaviour { // 총의 상태를 표현하는데 사용할 타입을 선언한다 public enum State { Ready, // 발사 준비됨 Empty, // 탄창이 빔 Reloading // 재장전 중 } public State state { get; private set; } // 현재 총의 상태 public Transform fireTransform; // 총알이 발사될 위치 public ParticleSystem muzzleFlashEffect; // 총구 화염 효과 public ParticleSystem shellEjectEffect; // 탄피 배출 효과 private LineRenderer bulletLineRenderer; // 총알 궤적을 그리기 위한 렌더러 private AudioSource gunAudioPlayer; // 총 소리 재생기 public AudioClip shotClip; // 발사 소리 public AudioClip reloadClip; // 재장전 소리 public float damage = 25; // 공격력 private float fireDistance = 50f; // 사정거리 public int ammoRemain = 100; // 남은 전체 탄약 public int magCapacity = 25; // 탄창 용량 public int magAmmo; // 현재 탄창에 남아있는 탄약 public float timeBetFire = 0.12f; // 총알 발사 간격 public float reloadTime = 1.8f; // 재장전 소요 시간 private float lastFireTime; // 총을 마지막으로 발사한 시점 private void Awake() { // 사용할 컴포넌트들의 참조를 가져오기 gunAudioPlayer = GetComponent(); bulletLineRenderer = GetComponent (); // 사용할 점을 두개로 변경 bulletLineRenderer.positionCount = 2; // 라인 렌더러를 비활성화 bulletLineRenderer.enabled = false; } private void OnEnable() { // 현재 탄창을 가득채우기 magAmmo = magCapacity; // 총의 현재 상태를 총을 쏠 준비가 된 상태로 변경 state = State.Ready; // 마지막으로 총을 쏜 시점을 초기화 lastFireTime = 0; } // 발사 시도 public void Fire() { // 현재 상태가 발사 가능한 상태 // && 마지막 총 발사 시점에서 timeBetFire 이상의 시간이 지남 if (state == State.Ready && Time.time >= lastFireTime + timeBetFire) { // 마지막 총 발사 시점을 갱신 lastFireTime = Time.time; // 실제 발사 처리 실행 Shot(); } } // 실제 발사 처리 private void Shot() { // 레이캐스트에 의한 충돌 정보를 저장하는 컨테이너 RaycastHit hit; // 총알이 맞은 곳을 저장할 변수 Vector3 hitPosition = Vector3.zero; // 레이캐스트(시작지점, 방향, 충돌 정보 컨테이너, 사정거리) if (Physics.Raycast(fireTransform.position, fireTransform.forward, out hit, fireDistance)) { // 레이가 어떤 물체와 충돌한 경우 // 충돌한 상대방으로부터 IDamageable 오브젝트를 가져오기 시도 IDamageable target = hit.collider.GetComponent (); // 상대방으로 부터 IDamageable 오브젝트를 가져오는데 성공했다면 if (target != null) { // 상대방의 OnDamage 함수를 실행시켜서 상대방에게 데미지 주기 target.OnDamage(damage, hit.point, hit.normal); } // 레이가 충돌한 위치 저장 hitPosition = hit.point; } else { // 레이가 다른 물체와 충돌하지 않았다면 // 총알이 최대 사정거리까지 날아갔을때의 위치를 충돌 위치로 사용 hitPosition = fireTransform.position + fireTransform.forward * fireDistance; } // 발사 이펙트 재생 시작 StartCoroutine(ShotEffect(hitPosition)); // 남은 탄환의 수를 -1 magAmmo--; if (magAmmo <= 0) { // 탄창에 남은 탄약이 없다면, 총의 현재 상태를 Empty으로 갱신 state = State.Empty; } } // 발사 이펙트와 소리를 재생하고 총알 궤적을 그린다 private IEnumerator ShotEffect(Vector3 hitPosition) { // 총구 화염 효과 재생 muzzleFlashEffect.Play(); // 탄피 배출 효과 재생 shellEjectEffect.Play(); // 총격 소리 재생 gunAudioPlayer.PlayOneShot(shotClip); // 선의 시작점은 총구의 위치 bulletLineRenderer.SetPosition(0, fireTransform.position); // 선의 끝점은 입력으로 들어온 충돌 위치 bulletLineRenderer.SetPosition(1, hitPosition); // 라인 렌더러를 활성화하여 총알 궤적을 그린다 bulletLineRenderer.enabled = true; // 0.03초 동안 잠시 처리를 대기 yield return new WaitForSeconds(0.03f); // 라인 렌더러를 비활성화하여 총알 궤적을 지운다 bulletLineRenderer.enabled = false; } // 재장전 시도 public bool Reload() { if (state == State.Reloading || ammoRemain <= 0 || magAmmo >= magCapacity) { // 이미 재장전 중이거나, 남은 총알이 없거나 // 탄창에 총알이 이미 가득한 경우 재장전 할수 없다 return false; } // 재장전 처리 시작 StartCoroutine(ReloadRoutine()); return true; } // 실제 재장전 처리를 진행 private IEnumerator ReloadRoutine() { // 현재 상태를 재장전 중 상태로 전환 state = State.Reloading; // 재장전 소리 재생 gunAudioPlayer.PlayOneShot(reloadClip); // 재장전 소요 시간 만큼 처리를 쉬기 yield return new WaitForSeconds(reloadTime); // 탄창에 채울 탄약을 계산한다 int ammoToFill = magCapacity - magAmmo; // 탄창에 채워야할 탄약이 남은 탄약보다 많다면, // 채워야할 탄약 수를 남은 탄약 수에 맞춰 줄인다 if (ammoRemain < ammoToFill) { ammoToFill = ammoRemain; } // 탄창을 채운다 magAmmo += ammoToFill; // 남은 탄약에서, 탄창에 채운만큼 탄약을 뺸다 ammoRemain -= ammoToFill; // 총의 현재 상태를 발사 준비된 상태로 변경 state = State.Ready; } }
스크립트 적용이 끝나셨으면 이펙트 오브젝트와 사운드를 넣어주시면 되겠습니다.
1-6) 슈터 만들기 - ㉠IK
어떤 애니메이션을 사용하든 상관없이 캐릭터의 손의 위치가 항상 손잡이에 위치하려면
애니메이터의 IK를 사용해야 합니다.
IK(역 운동학)는 자식 조인트의 위치를 먼저 결정하고
부모 조인트가 거기에 맞춰변형되는 방식입니다.
1-7) 슈터 만들기 - ㉡PlayerShooter 스크립트
플레이어가 총을 쏠 수 있도록 Player Shooter 스크립트를 추가해 주겠습니다^^
<Player Shooter>
PlayerShooter 스크립트는 총을 장전, 쏠 수 있으며
애니메이터의 IK를 갱신하는 코드를 담고 있습니다.
using UnityEngine; // 주어진 Gun 오브젝트를 쏘거나 재장전 // 알맞은 애니메이션을 재생하고 IK를 사용해 캐릭터 양손이 총에 위치하도록 조정 public class PlayerShooter : MonoBehaviour { public Gun gun; // 사용할 총 public Transform gunPivot; // 총 배치의 기준점 public Transform leftHandMount; // 총의 왼쪽 손잡이, 왼손이 위치할 지점 public Transform rightHandMount; // 총의 오른쪽 손잡이, 오른손이 위치할 지점 private PlayerInput playerInput; // 플레이어의 입력 private Animator playerAnimator; // 애니메이터 컴포넌트 private void Start() { // 사용할 컴포넌트들을 가져오기 playerInput = GetComponent(); playerAnimator = GetComponent (); } private void OnEnable() { // 슈터가 활성화될 때 총도 함께 활성화 gun.gameObject.SetActive(true); } private void OnDisable() { // 슈터가 비활성화될 때 총도 함께 비활성화 gun.gameObject.SetActive(false); } private void Update() { // 입력을 감지하고 총 발사하거나 재장전 if (playerInput.fire) { // 발사 입력 감지시 총 발사 gun.Fire(); } else if (playerInput.reload) { // 재장전 입력 감지시 재장전 if (gun.Reload()) { // 재장전 성공시에만 재장전 애니메이션 재생 playerAnimator.SetTrigger("Reload"); } } // 남은 탄약 UI를 갱신 UpdateUI(); } // 탄약 UI 갱신 private void UpdateUI() { if (gun != null && UIManager.instance != null) { // UI 매니저의 탄약 텍스트에 탄창의 탄약과 남은 전체 탄약을 표시 UIManager.instance.UpdateAmmoText(gun.magAmmo, gun.ammoRemain); } } // 애니메이터의 IK 갱신 private void OnAnimatorIK(int layerIndex) { // 총의 기준점 gunPivot을 3D 모델의 오른쪽 팔꿈치 위치로 이동 gunPivot.position = playerAnimator.GetIKHintPosition(AvatarIKHint.RightElbow); // IK를 사용하여 왼손의 위치와 회전을 총의 오른쪽 손잡이에 맞춘다 playerAnimator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f); playerAnimator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1.0f); playerAnimator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandMount.position); playerAnimator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandMount.rotation); // IK를 사용하여 오른손의 위치와 회전을 총의 오른쪽 손잡이에 맞춘다 playerAnimator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1.0f); playerAnimator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1.0f); playerAnimator.SetIKPosition(AvatarIKGoal.RightHand, rightHandMount.position); playerAnimator.SetIKRotation(AvatarIKGoal.RightHand, rightHandMount.rotation); } }
스크립트 적용이 끝났으면 건과 총을 잡을 정보 값을 넣어주세요~~
마우스 좌측 버튼을 이용해 사격과 단축키 R을 이용해 장전하시면 됩니다.
참 쉽조잉ㅎㅎ
2. 마무리
오늘 강의는 여기까지입니다.
좀비 서바이버를 따라하면서 총과 슈터는 만들어 보았습니다.
책이랑 순서나 내용이 다를 수 있습니다.
감사합니다.
수업자료: 좀비 서바이버 따라하기 #3 - 총과 슈터 만들기