[Unity]11.좀비 서바이버 따라하기 #5 - 내비게이션 시스템과 좀비 준비
- 게임 개발 - Unity3d
- 2019. 9. 14. 18:20
안녕하세요 유랑입니다.
실력향상을 위해서 오늘도 책을 따라하면서 공부하겠습니다.
궁금하신점 있으시면 댓글로 남겨주세요^^
1. 좀비 서바이버
이번 강의는 레트로님께서 만든 예제이며,
교재를 구입하시면 자세한 내용을 배우실 수 있습니다.
저는 책을 구입하였고, 스킬업을 위해서 복습겸 글을 올리겠습니다.
깃허브 사이트 => 깃허브
유튜브 사이트 => 유튜브
1-1) 내비게이션 시스템 - ㉠내비게이션
좀비가 플레이어를 따라가는 인공지능을 만들어 주겠습니다.
그러기 위해선 유니티에서 제공하는 내비게이션 시스템을 사용해야 합니다.
A* 길찾기 알고리즘으로써 비전공자도 쉽게 사용할 수 있습니다.
* 내비메시(NavMesh) : 에이전트가 걸어 다닐 수 있는 표면
* 내비메시 에이전트(NavMesh Agent) : 내비메시 위에서 경로를 계산하고 이동하는 캐릭터
* 내비메시 장애물(NavMesh Obstacle) : 에이전트의 경로를 막는 장애물
* 오프메시 링크(Off Mesh Linkg) : 끊어진 내비메시 영역 사이를 잇는 연결 지점
1-2) 내비게이션 시스템 - ㉡내비메시 굽기
내비메시는 내비메시 에이전트가 걸어 다닐 표면입니다.
내비메시는 정적 게임 오브젝트를 대상으로 미리 구워 생성할 수 있습니다.
Level Art는 이미 정적으로 체크되어 있기 때문에 따로 설정하지는 않겠습니다.
상단 메뉴바 Window => AI => Navigation을 실행시켜 주세요.
Bake를 선택 후 에이전트의 범위를 지정,
그리고 구워주시면 됩니다!!!
구워진 내비메시는 파란색으로 표시됩니다.
파란색으로 표시된 부분은 내비메시 에이전트가 이동 가능한 영역입니다.
1-3) 좀비 준비 - ㉠좀비 오브젝트
내비메시도 생성했으니 적 캐릭터 좀비를 추가하겠습니다.
위치와 애니메이터를 각각 설정해 주세요.
좀비 애니메이터의 구성은 다음과 같이 되어 있습니다.
따로 설정하지는 않겠습니다ㅎㅎ
1-4) 좀비 준비 - ㉡콜라이더
콜라이더는 두 개 준비해 줄텐데요.
Capsule은 장애물과 물리 충돌처리를 위해서,
Box는 캐릭터에게 데미지를 줄 때 사용합니다.
Box만 트리거를 체크해 주겠습니다.
1-5) 좀비 준비 - ㉢오디오 소스
좀비도 오디오 소스를 추가해 주겠습니다.
1-6) 좀비 준비 - ㉣AI 기능
좀비 인공지능 구성을 위해서 NavMeshAgent 컴포넌트와 Enemy 스크립트를 추가해 주세요.
NavMeshAgent는 내비메시를 바탕으로 움직이도록 도와줍니다.
1-7) 좀비 준비 - ㉤피탄 효과와 레이어 설정
좀비가 총알에 맞았을 때의 피탄 효과를 위해서 BloodSprayEffect를 추가해 주시고
좀비가 플레이어를 쫒아가도록 Player 레이어 설정을 해주겠습니다.
이 때 자식은 바꿔주지 마세요ㅎㅎ
1-8) 좀비 준비 - ㉥Enemy 스크립트
<Enemy>
Enemy 스크립트는 플레이어를 찾아 공격하고 사망 시 추적을 중단하는 내용을 담고 있습니다.
외부에서 Enemy의 초기 능력치 셋업도 가능하답니다^^
using System.Collections; using UnityEngine; using UnityEngine.AI; // AI, 내비게이션 시스템 관련 코드를 가져오기 // 적 AI를 구현한다 public class Enemy : LivingEntity { public LayerMask whatIsTarget; // 추적 대상 레이어 private LivingEntity targetEntity; // 추적할 대상 private NavMeshAgent pathFinder; // 경로계산 AI 에이전트 public ParticleSystem hitEffect; // 피격시 재생할 파티클 효과 public AudioClip deathSound; // 사망시 재생할 소리 public AudioClip hitSound; // 피격시 재생할 소리 private Animator enemyAnimator; // 애니메이터 컴포넌트 private AudioSource enemyAudioPlayer; // 오디오 소스 컴포넌트 private Renderer enemyRenderer; // 렌더러 컴포넌트 public float damage = 20f; // 공격력 public float timeBetAttack = 0.5f; // 공격 간격 private float lastAttackTime; // 마지막 공격 시점 // 추적할 대상이 존재하는지 알려주는 프로퍼티 private bool hasTarget { get { // 추적할 대상이 존재하고, 대상이 사망하지 않았다면 true if (targetEntity != null && !targetEntity.dead) { return true; } // 그렇지 않다면 false return false; } } private void Awake() { // 게임 오브젝트로부터 사용할 컴포넌트들을 가져오기 pathFinder = GetComponent(); enemyAnimator = GetComponent (); enemyAudioPlayer = GetComponent (); // 렌더러 컴포넌트는 자식 게임 오브젝트에게 있으므로 // GetComponentInChildren() 메서드를 사용 enemyRenderer = GetComponentInChildren (); } // 적 AI의 초기 스펙을 결정하는 셋업 메서드 public void Setup(float newHealth, float newDamage, float newSpeed, Color skinColor) { // 체력 설정 startingHealth = newHealth; // 공격력 설정 damage = newDamage; // 내비메쉬 에이전트의 이동 속도 설정 pathFinder.speed = newSpeed; // 렌더러가 사용중인 머테리얼의 컬러를 변경, 외형 색이 변함 enemyRenderer.material.color = skinColor; } private void Start() { // 게임 오브젝트 활성화와 동시에 AI의 추적 루틴 시작 StartCoroutine(UpdatePath()); } private void Update() { // 추적 대상의 존재 여부에 따라 다른 애니메이션을 재생 enemyAnimator.SetBool("HasTarget", hasTarget); } // 주기적으로 추적할 대상의 위치를 찾아 경로를 갱신 private IEnumerator UpdatePath() { // 살아있는 동안 무한 루프 while (!dead) { if (hasTarget) { // 추적 대상 존재 : 경로를 갱신하고 AI 이동을 계속 진행 pathFinder.isStopped = false; pathFinder.SetDestination( targetEntity.transform.position); } else { // 추적 대상 없음 : AI 이동 중지 pathFinder.isStopped = true; // 20 유닛의 반지름을 가진 가상의 구를 그렸을때, 구와 겹치는 모든 콜라이더를 가져옴 // 단, targetLayers에 해당하는 레이어를 가진 콜라이더만 가져오도록 필터링 Collider[] colliders = Physics.OverlapSphere(transform.position, 20f, whatIsTarget); // 모든 콜라이더들을 순회하면서, 살아있는 플레이어를 찾기 for (int i = 0; i < colliders.Length; i++) { // 콜라이더로부터 LivingEntity 컴포넌트 가져오기 LivingEntity livingEntity = colliders[i].GetComponent (); // LivingEntity 컴포넌트가 존재하며, 해당 LivingEntity가 살아있다면, if (livingEntity != null && !livingEntity.dead) { // 추적 대상을 해당 LivingEntity로 설정 targetEntity = livingEntity; // for문 루프 즉시 정지 break; } } } // 0.25초 주기로 처리 반복 yield return new WaitForSeconds(0.25f); } } // 데미지를 입었을때 실행할 처리 public override void OnDamage(float damage, Vector3 hitPoint, Vector3 hitNormal) { // 아직 사망하지 않은 경우에만 피격 효과 재생 if (!dead) { // 공격 받은 지점과 방향으로 파티클 효과를 재생 hitEffect.transform.position = hitPoint; hitEffect.transform.rotation = Quaternion.LookRotation(hitNormal); hitEffect.Play(); // 피격 효과음 재생 enemyAudioPlayer.PlayOneShot(hitSound); } // LivingEntity의 OnDamage()를 실행하여 데미지 적용 base.OnDamage(damage, hitPoint, hitNormal); } // 사망 처리 public override void Die() { // LivingEntity의 Die()를 실행하여 기본 사망 처리 실행 base.Die(); // 다른 AI들을 방해하지 않도록 자신의 모든 콜라이더들을 비활성화 Collider[] enemyColliders = GetComponents (); for (int i = 0; i < enemyColliders.Length; i++) { enemyColliders[i].enabled = false; } // AI 추적을 중지하고 내비메쉬 컴포넌트를 비활성화 pathFinder.isStopped = true; pathFinder.enabled = false; // 사망 애니메이션 재생 enemyAnimator.SetTrigger("Die"); // 사망 효과음 재생 enemyAudioPlayer.PlayOneShot(deathSound); } private void OnTriggerStay(Collider other) { // 자신이 사망하지 않았으며, // 최근 공격 시점에서 timeBetAttack 이상 시간이 지났다면 공격 가능 if (!dead && Time.time >= lastAttackTime + timeBetAttack) { // 상대방으로부터 LivingEntity 타입을 가져오기 시도 LivingEntity attackTarget = other.GetComponent (); // 상대방의 LivingEntity가 자신의 추적 대상이라면 공격 실행 if (attackTarget != null && attackTarget == targetEntity) { // 최근 공격 시간을 갱신 lastAttackTime = Time.time; // 상대방의 피격 위치와 피격 방향을 근삿값으로 계산 Vector3 hitPoint = other.ClosestPoint(transform.position); Vector3 hitNormal = transform.position - other.transform.position; // 공격 실행 attackTarget.OnDamage(damage, hitPoint, hitNormal); } } } }
스크립트 적용이 끝났으면 타겟 설정과 이펙트 그리고 좀비 사운드를 넣어주겠습니다~~
1-9) 프리팹화
만들어진 좀비는 프리팹화를 통해서 나중에 또 쓸수 있도록 만들어 줄게요.
우와 좀비가 빠르게 달려오네요ㅎㄷㄷ
2. 마무리
오늘 강의는 여기까지입니다.
좀비 서바이버를 따라하면서 내비게이션 시스템과 좀비를 만들어 보았습니다.
책이랑 순서나 내용이 다를 수 있습니다.
감사합니다.
수업자료: 좀비 서바이버 따라하기 #5 - 내비게이션 시스템과 좀비 준비