-메카님(Mecanim)을 사용해 애니메이션 제어
-애니메이션 타입에 Legacy, Generic, Humanoid가 있다.
몬스터가 2족 보행을 하므로 Humanoid로 변경한다.
-Configure 버튼을 누르면 본 구조의 매핑 정보를 확인할 수 있는 씬 뷰가 Avatar Configuration 화면으로 바뀐다.
Avatar 에셋은 본 구조의 매핑 정보를 저장한다.
본 구조가 동일하다면 다른 모델에서 사용한 아바타 에셋을 재사용할 수 있다.
-> 필수 본만 같다면 다른 애니메이션 클립 연결 가능
-Muscles & Settings 탭은 각 관절의 정상적인 동작 여부를 시각적으로 확인할 수 있다.
-Pre-Muscle Settings에서 관절의 회전 범위를 설정한다.
mixamo에서 upload character후 애니메이션이 되는지 확인해 보았다.
-애니메이션 클립을 선택하고 모델을 드래그앤 드롭해주면 미리볼 수 있다.
-새 애니메이션 컨트롤러 생성, Idle 애니메이션 클립 추가: 처음에 추가하면 주황색으로 된다.(기본값)
=> 다른 애니메이션 클립을 기본값으로 변경하려면 Set as Layer Default State해주어야함
-몬스터에 MonsterController 할당
-walk 애니메이션 추가하고 transition 설정. IsTrace를 bool타입으로 추가.
유니티에서 제공하는 내비게이션의 구현 방식은 스테이지를 구성하고있는 3D 메시를 분석해 내비메시 데이터를 미리 생성하는 것이다.
=> 추적할 수 있는 영역(Walkable Area)과
장애물이라 지나갈 수 없는 영역(Non Walkable area)의 데이터를 메시로 미리 생성한다.
AI Navigation 패키지 설치
NavMeshSurface
새로운 내비게이션은 navigation static 플래그 설정이 필요없고, NavMeshSurface 컴포넌트를 추가하면 된다.
-파란색 메시로 채워지지 않고 구멍이 나 있는 영역은 지나갈 수 없는 영역으로 인식해 장애물로 판단한다.
-use Geometry 속성을 Physics Colliders로 설정하면 콜라이더 컴포넌트가 포함된 게임오브젝트만 내미메시를 생성한다.
-플레이어는 장애물로 인식하지 않게 하기위해 Y축으로 올려 Floor에서 분리시킨뒤 다시 베이크하고 내렸다.
-비활성화하고 해도 된다.
-Object Collection/Include Layers 속성에서 별도의 layer설정으로 장애물로 인식하지 말아야할 객체를 제외시킬 수 있다.
NavMeshAgent
-NavMeshAgent 컴포넌트는 내미메시 데이터를 기반으로 목적지까지 최단거리를 계산해 이동하게하며, 장애물과 다른 NPC간의 충돌을 회피하는 기능도 제공한다.
=>A Pthfinding 알고리즘을 사용한다.
-agent Type 속성을 수정할수 있다.
destionation 속성으로 이동할 좌푯값을 지정하는 방법
agent.destination = playerTr.positions;
agent.SetDestination(playerTr.position);
-둘중 한가지를 쓰면 된다.
* Find ~ 계열의 함수는 처리 속도가 느리므로 update말고 Awake,Start 함수에서 변수에 할당한 후 사용하자.
-base offset -0.1로 설정
유한상태 머신(FSM: Finite State Machine)
-캐릭터가 알아서 스스로 주변 환경에 적응하거나 반응에 적절하게 반작용하도록 구현한 것
=>피격을 당해 일정 데미지가 누적되면 사망하고 소멸하는 구조이기 때문에 상태가 유한해서 "유한 상태 머신"이라 함.
-FSM은 자신의 상태 값을 갖고 있어야 하며, 현재 어떤 상태인지 갱신하고, 해당 상태에 맞는 행동을 취한다.
적 캐릭터의 상태를 순찰, 추적, 공격, 사망으로 정의하자.
행동시나리오: 처음 생성 시 불규칙적으로 순찰하다 Player가 근접하면 추적을 시작, 공격 사정거리 이내일 때 공격
=> 몬스터와 Player의 거리를 측정, 몬스터의 상태를 주기적 업데이트, 상태에 따른 동작을 취하게 한다.
-Update 함수에서 처리하면 오버헤드를 줄 수 있으므로 코루틴을 활용해본다.
(굳이 매프레임마다 상태를 확인하지는 않아도 되기 때문이다.)
적 캐릭터의 상태 체크
- 0.3초마다 상태를 체크해 갱신한다.
-Player와 자신의 Distance를 측정해 추적 사정거리와 공격 사정거리 내에 들어왔는지 판단 => 상태변경
몬스터의 상태: 멈춤, 추적, 공격, 죽음
적 캐릭터의 행동 구현
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UIElements;
public class MonsterController : MonoBehaviour
{
public enum eState
{//몬스터 상태 정의
IDLE, TRACE, ATTACK, DIE
}
public eState state = eState.IDLE;//몬스터의 현재 상태
private bool isDie;//몬스터의 사망 여부
private NavMeshAgent agent;
private Transform playerTrans;
private Animator anim;
[SerializeField]private float traceDistance = 10.0f;//추적 사거리
[SerializeField]private float attackDistance = 2.0f;//공격 사거리
// Start is called before the first frame update
void Start()
{
this.anim = GetComponent<Animator>();
this.agent = this.GetComponent<NavMeshAgent>();//agent 컴포넌트 가져옴
GameObject playerGo = GameObject.FindGameObjectWithTag("Player");
this.playerTrans = playerGo.GetComponent<Transform>();
// Vector3 playerPosition = playerTrans.position;
// this.agent.destination = playerPosition;//destination 속성을 사용해 이동할 좌푯값 지정
StartCoroutine(this.CheckState());//상태 갱신
StartCoroutine(this.MonsterAction());//상태에따라 몬스터 행동 수행
}
// Update is called once per frame
void Update()
{
}
private IEnumerator CheckState()
{//상태 갱신 코루틴 함수: 일정 간격으로 몬스터의 행동상태 체크
while (!isDie)
{
yield return new WaitForSeconds(0.3f);
float distance = Vector3.Distance(this.transform.position, this.playerTrans.position);
Debug.Log(distance);
if (distance <= this.traceDistance)//추적 사거리에 들어왔다면
{
this.state = eState.TRACE;//추적상태로 변경
if (distance <= this.attackDistance) //공격 사거리에 들어왔다면
{
this.state = eState.ATTACK;//공격상태로 변경
}
}
else//멈춤
{
this.state = eState.IDLE;
}
}
}
private IEnumerator MonsterAction()
{
while (!isDie)
{
switch (state)
{
case eState.IDLE:
this.agent.isStopped = true;//추적 중지
anim.SetBool("IsTrace", false);
break;
case eState.TRACE:
this.agent.SetDestination(this.playerTrans.position);
this.agent.isStopped = false;
anim.SetBool("IsTrace", true);
break;
case eState.ATTACK:
break;
case eState.DIE:
break;
}
yield return new WaitForSeconds(0.3f);
}
}
private void OnDrawGizmos()
{
if(this.state == eState.TRACE)
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(this.transform.position, traceDistance);
}
else if(this.state == eState.ATTACK)
{
Gizmos.color= Color.green;
Gizmos.DrawWireSphere(this.transform.position,attackDistance);
}
}
}
'유니티 심화' 카테고리의 다른 글
몬스터 수정- 해시값 전달, 몬스터의 공격, 피격시 혈흔 효과, Player 수정 (0) | 2023.08.24 |
---|---|
FindGameObjectWithTag, FindWithTag (0) | 2023.08.24 |
오디오, 코루틴으로 총구 효과 (0) | 2023.08.22 |
파티클 활용, 폭발, random texture,폭발력 적용(AddExplosionForce) (0) | 2023.08.21 |
총 발사, 총알 발사 궤적 효과 만들기 - Trail Renderer (0) | 2023.08.18 |