[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 - 내비게이션 시스템과 좀비 준비