728x90
코드 정렬 꿀팁
ctrl + a 를 눌러서 전체 선택한 다음
ctrl + k + f 을 누르면 c#에 맞춰서 정렬이 된다!!!!!!!!
Zombie.cs 작성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI; // AI, 내비게이션 시스템 관련 코드 가져오기
// 좀비 AI 구현
public class Zombie : LivingEntity
{
public LayerMask whatIsTarget; // 추적 대상 레이어
private LivingEntity targetEntity; // 추적 대상
private NavMeshAgent navMeshAgent; // 경로 계산 AI 에이전트
public ParticleSystem hitEffect; // 피격 시 재생할 파티클 효과
public AudioClip deathSound; // 사망 시 재생할 소리
public AudioClip hitSound; // 피격 시 재생할 소리
private Animator zombieAnimator; // 애니메이터 컴포넌트
private AudioSource zombieAudioPlayer; // 오디오 소스 컴포넌트
private Renderer zombieRenderer; // 렌더러 컴포넌트
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()
{
// 초기화
// 게임 오브젝트로부터 사용할 컴포넌트 가져오기
navMeshAgent = GetComponent<NavMeshAgent>();
zombieAnimator = GetComponent<Animator>();
zombieAudioPlayer = GetComponent<AudioSource>();
// 렌더러 컴포넌트는 자식 게임 오브젝트에 있으므로
// GetComponentInChilderen() 메서드 사용
zombieRenderer = GetComponentInChildren<Renderer>();
}
// 좀비 AI의 초기 스펙을 결정하는 셋업 메서드
public void Setup(ZombieData zombieData)
{
// 체력 설정
startingHealth = zombieData.health;
health = zombieData.health;
// 공격력 설정
damage = zombieData.damage;
// 내비메시 에이전트의 이동 속도 설정
navMeshAgent.speed = zombieData.speed;
// 렌더러가 사용 중인 머티리얼의 컬러를 변경, 외형 색이 변함
zombieRenderer.material.color = zombieData.skinColor;
}
private void Start()
{
// 게임 오브젝트 활성화와 동시에 AI의 추적 루틴 시작
StartCoroutine(UpdatePath());
}
private void Update()
{
// 추적 대상의 존재 여부에 따라 다른 애니메이션 재생
zombieAnimator.SetBool("HasTarget", hasTarget);
}
// 주기적으로 추적할 대상의 위치를 찾아 경로 갱신
private IEnumerator UpdatePath()
{
// 살아 있는 동안 무한 루프
while (!dead)
{
if (hasTarget)
{
// 추적 대상 존재 : 경로를 갱신하고 AI 이동을 계속 진행
navMeshAgent.isStopped = false;
navMeshAgent.SetDestination(
targetEntity.transform.position);
}
else
{
// 추적 대상 없음 : AI 이동 중지
navMeshAgent.isStopped = true;
// 20유닛의 반지름을 가진 가상의 구를 그렸을 때 구와 겹치는 모든 콜라이더를 가져옴
// 단, whatIsTarget 레이어를 가진 콜라이더만 가져오도록 필터링
Collider[] colliders =
Physics.OverlapSphere(transform.position, 20f, whatIsTarget);
// 모든 콜라이더를 순회하면서 살아 있는 LivingEntity 찾기
for (int i = 0; i < colliders.Length; i++)
{
// 콜라이더로부터 LivingEntity 컴포넌트 가져오기
LivingEntity livingEntity = colliders[i].GetComponent<LivingEntity>();
// LivingEntity 컴포넌트가 존재하며, 해당 LivingEntity가 살아 있다면
if (livingEntity != null && !livingEntity.dead)
{
// 추적 대상을 해당 LivingEntity로 설정
targetEntity = livingEntity;
// for 문 루프 즉시 정지
// 나중에 멀티플레이어가 됐을 때 가까운 1명의 플레이어만 검출하기 위해서
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);
// Quaternion.LookRotation() 방향벡터를 입력받아 해당 방향을 바라보는 쿼터니언 회전값을 반환
hitEffect.Play(); // 파티클!
// 피격 효과음 재생
zombieAudioPlayer.PlayOneShot(hitSound);
}
// LivingEntity의 OnDamage()를 실행하여 대미지 적용
base.OnDamage(damage, hitPoint, hitNormal);
}
// 사망 처리
public override void Die()
{
// LivingEntity의 Die()를 실행하여 기본 사망 처리 실행
base.Die();
// 다른 AI를 방해하지 않도록 자신의 모든 콜라이더를 비활성화
Collider[] zombieColliders = GetComponents<Collider>();
for (int i = 0; i < zombieColliders.Length; i++)
{
zombieColliders[i].enabled = false;
}
// AI 추적을 중지하고 내비메시 컴포넌트 비활성화
navMeshAgent.isStopped = true;
navMeshAgent.enabled = false;
// 사망 애니메이션 재생
zombieAnimator.SetTrigger("Die");
// 사망 효과음 재생
zombieAudioPlayer.PlayOneShot(deathSound);
}
private void OnTriggerStay(Collider other)
{
// 트리거 충돌한 상대방 게임 오브젝트가 추적 대상이라면 공격 실행
// 자신이 사망하지 않았으며,
// 최근 공격 시점에서 timeBetAttack 이상 시간이 지났다면 공격 가능
if (!dead && Time.time >= lastAttackTime + timeBetAttack)
{
// 상대방의 LivingEntity 타입 가져오기 시도
LivingEntity attackTarget = other.GetComponent<LivingEntity>();
// 상대방의 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);
}
}
}
- public LayerMask whatIsTarget;
- 레이어 마스크 (LayerMask) : 특정 레이어를 가진 게임 오브젝트에 물리 또는 그래픽 처리 등을 적용시킬 때 사용. 레이캐스트에서 많이 쓰인다!
- 아래의 저 Layer를 말한다 (그리고 레이어는 8번 이후부터 써줌)
- private bool hasTarget
{
get
{ } }
읽기 전용 프로퍼티!
- 일반적으로 무한 루프는 컴퓨터 자원을 과도하게 사용하여 프로그램을 망가뜨림.
하지만 코루틴을 사용하면 루프 회차 사이에 적절한 휴식 시간을 삽입하여 에러 없는 무한 루프를 구현할 수 있음
- 내비메시 에이전트 컴포넌트
- isStopped 필드 : 이동 중단 여부
- SetDestination() 메서드 : 목표 위치를 입력받아 이동 경로 계산
- Pysics.OverlapSphere() 메서드 : 중심 위치와 반지름을 입력받아 가상의 구를 그리고, 구에 겹치는 모든 콜라이더 반환.
세 번째 값으로 레이어 마스크를 입력하여 특정 레이어만 감지 (whatIsTarget)
- Quaternion.LookRotation() 메서드 : 방향벡터를 입력받아 해당 방향을 바라보는 쿼터니언 회전값을 반환
- NavMeshAgent.enabled = false;
- 내비메시 에이전트들은 서로를 방해하지 않도록 경로를 계산하기 때문에 완전히 비활성화해준다
Layer 생성
8번에 Player
Player Character 프리팹
와 피가 닳는다
다른 좀비도 불러와오자
캐릭터, 애니메이션이 있는 사이트!
https://www.mixamo.com/#/?page=1&type=Character
Mixamo
www.mixamo.com
좀비를 검색하고 다운로드를 눌러 준다.
로그인 해야 다운 가능!
유니티 에디터에 불러와보자
Humanoid로 바꾸고 Apply
Configure 눌렀을 때 이렇게 되면 휴머노이드로 잘 적용된 것
하얀색이라서 Material을 적용한다
근데..... 유실돼서 분홍색이 된다..............는 잘못한 것! 아래 사진 처럼 바로 Materials 에서 적용하면 안 된다
분홍분홍....
Materals에서 Textures, Materials
표시한 부분으로 불러와야 모델이 적용됨
근데 뭔가 반투명해보임
저걸로 바꿈
'Hello, World! > Unity' 카테고리의 다른 글
유니티 3D 좀비 서바이벌 게임 만들자 (18) (0) | 2022.06.01 |
---|---|
유니티 3D 좀비 서바이벌 게임 만들자 (17) (0) | 2022.06.01 |
유니티 3D 좀비 서바이벌 게임 만들자 (15) (0) | 2022.06.01 |
유니티 3D 좀비 서바이벌 게임 만들자 (14) (0) | 2022.05.30 |
유니티에서 VSCode를 쓰자 (0) | 2022.05.30 |
댓글