지난번에 이어 게임을 만들어보자.
이번에 참고할 강의는 아래 강의이다.
https://www.youtube.com/watch?v=njqRlH3Hj3Q&list=PLctzObGsrjfyevwpeEVQ9pxGVwZtS7gZK&index=7
이번 강의에서는 적의 공격 시스템을 만들어본다.
시작해보자.
기본적인 구조는 적이 플레이어 주변의 한계 거리까지 들어왔을 때,
색상이 더 붉게 변하며 플레이어를 찌르는 방식으로 한다.
Enemy 스크립트를 수정하자.
| Enemy 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(UnityEngine.AI.NavMeshAgent))]
// 현재 오브젝트에 NavMeshAgent 를 포함
public class Enemy : LivingEntity
{
public enum State { Idle, Cashing, Attacking };
// 적의 상태, { 기본(아무것도 안함), 추적, 공격 }
State currentState; // 현재 상태
UnityEngine.AI.NavMeshAgent pathfinder; // 내비게이션 레퍼런스
Transform target; // 적의 타겟(플레이어) 트랜스폼
Material skinMatreial; // 현재 오브젝트의 마테리얼
Color originalColor; // 현재 오브젝트의 기존 색상
float attackDistanceThreshold = 0.5f;
// 공격 거리 임계값(사거리). 유니티에서 단위 1은 1 meter 이다..!!
float timeBetweenAttacks = 1;
// 공격 시간 간격
float nextAttackTime;
// 실제 다음 공격 시간
float myCollisionRadius; // 자신의 충돌 범위 반지름
float targetConllisionRadius; // 타겟의 충돌 범위 반지름
protected override void Start()
// override 로 부모 클래스의 메소드를 재정의.
{
base.Start(); // base 를 통해 부모 클래스의 기존 메소드를 호출.
pathfinder = GetComponent<UnityEngine.AI.NavMeshAgent>();
// NavMeshAgent 레퍼런스 생성
skinMatreial = GetComponent<Renderer>().material; // 현재 오브젝트의 마테리얼 저장.
originalColor = skinMatreial.color; // 현재 색상 저장
currentState = State.Cashing; // 기본 상태를 추적 상태로 설정
target = GameObject.FindGameObjectWithTag("Player").transform;
// 타겟으로 "Player" 라는 태그를 가진 오브젝트의 트랜스폼을 저장
myCollisionRadius = GetComponent<CapsuleCollider>().radius;
// 자신의 충돌체(collider)의 반지름을 저장
targetConllisionRadius = target.GetComponent<CapsuleCollider>().radius;
// 타겟의 충돌체(collider)의 반지름을 저장
StartCoroutine(UpdatePath());
// 지정된 시간마다 목적지 쪽으로 위치를 갱신하는 코루틴 실행
}
void Update()
{
if(Time.time >= nextAttackTime) // 공격 시간이 지나면
{
float sqrDstToTarget = (target.position - transform.position).sqrMagnitude;
/* 자신(적)과 목표(플레이어) 사이의 거리를 저장.
두 오브젝트 사이의 거리를 측정하기 위해 Vector3 의 Distace 메소드를
쓰는 방법도 있지만 벡터에 제곱 연산이 들어가다 보니 연산 수가 많다.
두 오브젝트 사이의 실제 거리가 필요한 것이 아닌 비교값이 필요 한
것이므로 위와 같이 연산을 줄여 볼 수 있다. */
if (sqrDstToTarget <= Mathf.Pow(attackDistanceThreshold + targetConllisionRadius + myCollisionRadius, 2))
// 거리의 제곱과 임계 거리의 제곱을 비교하여 공격 범위 내인지 확인
{
nextAttackTime = Time.time + timeBetweenAttacks;
// 다음 공격 시간 지정
StartCoroutine(Attack());
// 공격 코루틴 실행
}
}
}
IEnumerator Attack() // 적의 공격 코루틴
{
currentState = State.Attacking; // 공격 상태로 변경
pathfinder.enabled = false; // 공격 중 플레이어 추적 중지
Vector3 originalPosition = transform.position; // 자신(적)의 위치
Vector3 dirToTarget = (target.position - transform.position).normalized;
// 타겟으로의 방향 벡터 계산
Vector3 attackPosition = target.position - dirToTarget * (myCollisionRadius);
// 타겟(플레이어)의 위치
float attackSpeed = 3; // 공격 속도
float percent = 0;
skinMatreial.color = Color.magenta; // 공격 시 색상 지정
while(percent <= 1) // 찌르는 거리가 1 이하일 때 루프
{
percent += Time.deltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
/* 대칭 함수를 사용
원지점->공격지점 이동에서 보간 값을 참조한다. */
transform.position = Vector3.Lerp(originalPosition, attackPosition, interpolation);
/* Lerp 메소드는 내분점을 반환한다.
원점, 공격지점, 보간 값을 참조값으로 전달한다
보간 값이 0이면 원점에, 1이면 공격지점에 있게 된다. */
yield return null;
/* while 루프의 처리 사이에서 프레임을 스킵합니다.
이 지점에서 작업이 멈추고 Update 메소드 작업이 끝나고
다음 프레임으로 넘어가면 이 아래의 코드나 루프가 실행 */
}
skinMatreial.color = originalColor; // 공격이 끝나면 기존 색상으로 변경
currentState = State.Cashing; // 공격이 끝나면 추적 상태로 변경
pathfinder.enabled = true; // 공격이 끝나면 다시 플레이어 추적
}
IEnumerator UpdatePath()
// 해당 코루틴 실행 시 지정한 시간마다 루프문 내부 코드 반복
{
float refreshRate = .25f; // 루프 시간
while(target != null) // 타겟이 있을 때 반복
{
if(currentState == State.Cashing)
// 상태가 추적 상태인 경우
{
Vector3 dirToTarget = (target.position - transform.position).normalized;
// 타겟으로의 방향 벡터 계산
Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetConllisionRadius + attackDistanceThreshold / 2);
/* 자신의 위치에서 자신과 타겟의 반지름 길이에 공격 사거리의 절반을 더하고, 방향 벡터를 곱한 값을 뺀다.
즉, 타겟(플레이어) 공격 가능한 위치를 타겟 위치로 정한다. */
if (!dead) // 죽지 않은 경우
{
pathfinder.SetDestination(targetPosition);
// 내비게이션의 목적지를 타겟(플레이어)의 위치로 설정
}
}
yield return new WaitForSeconds(refreshRate);
// refreshRate 시간만큼 기다림
}
}
}
코드가 굉장히 많이 바뀌었다..
적의 공격을 위해 플레이어 공격 가능 사거리 계산,
공격 코루틴에서 공격 모션과 색상 변경,
공격 적용으로 인한 추적 위치 계산 및 변경 등..
여러가지가 변경되고 추가되었다.
적의 추적과 공격을 위해 플레이어 기본 위치를 변경했다.
이제 한번 실행해보자.
적이 따라와서 공격 사거리까지 근접하면 공격 하는 것을 볼 수 있다.
다음 시간에는 Enemy 클래스에 기능을 추가해보자.
'Unity > 게임개발' 카테고리의 다른 글
[Unity] 게임 개발 일지 | 탑다운 슈팅 8 (0) | 2023.11.29 |
---|---|
[Unity] 게임 개발 일지 | 탑다운 슈팅 7 (0) | 2023.11.29 |
[Unity] 게임 개발 일지 | 탑다운 슈팅 5 (1) | 2023.11.23 |
[Unity] 게임 개발 일지 | 탑다운 슈팅 4 (0) | 2023.11.22 |
[Unity] 게임 개발 일지 | 탑다운 슈팅 3 (1) | 2023.11.22 |