유니티 공부.. 목표 추적하는 ai 만들기
unity 유니티 공부 2024. 12. 13. 19:58 |
https://www.youtube.com/watch?v=FBY_cmtCNHw&ab_channel=%EA%B3%A8%EB%93%9C%EB%A9%94%ED%83%88
적 프리팹을 맵에 놓기
적(부모 최상단하이어라키)에 Rigidbody, Box Collider, Enemy스크립트 적용
프리즈 로테이션 x , z 할당
박스 콜라이더는 메쉬에 비슷하게 조정
에너미 스크립트의 헬스 값은 둘다 50정도로
Tag 와 Layer 를 Enemy 로 설정
레이어 설정시 확인 메시지 뜨면 맨왼쪽 Yes Children 설정
Enemy 태그 없으면 만들기
Enemy 스크립트를 비주얼 스튜디오에서 킨다
적 프리팹은
하이어라키 창 자식의 자식 속에 마테리알이 있기 때문에
mat = GetComponent<MeshRenderer>().material;
이게 아니라
mat = GetComponentInChildren<MeshRenderer>().material;
이렇게 되어야 함
저번에 만든 테스트 에너미는 맵 상에서 삭제
+
a i 만들기
컴포넌트에 Nav Mesh Agent 추
Enemy 스크립트 맨 위
using UnityEngine;
밑에
using UnityEngine.AI;
한줄 추가해야함
void Update()
{
nav.SetDestination(target.position);
}
실시간으로 타겟의 위치 를 목표로 삼는다는 뜻
Enemy 스크립트에 타겟 지정을 Player로 할당
( public Transform target; 을 만들어서 생긴 변수 칸임 )
상단 window - AI - Navigation 창
!!! 막힘
여기까지 강의가 6:45
강의 에선 Bake 버튼이 있지만, 최신버전에선 Bake 버전이 없음
아마 floor 바닥 오브젝트에 NavMeshSurface 컴포넌트를 추가해줘야 하는 듯함
바닥 오브젝트 선택 후 - NavMeshSurface 컴포추 - 아래 Bake 클릭후 청록색 필드 펼쳐지면 성공
Mesh Renderer 가 아니라
텍스쳐를 입힌 Skinned Mesh Renderer 면
오른쪽 스크립트에서도 SkinnedMeshRenderer 라고 써줘야함
한 오브젝트의 마테리알이 여러개면
mat = 어쩌구
가 아니라
mats = 어쩌구 가 되어야 함
그리고 맵 상에 TestEnemy 없어야함 얘네들은 마테리알 없어서 마테리알null 오류 남
그래서 지워버림
자세한건 챗봇이 써줌
void Awake()
{
rigid = GetComponent<Rigidbody>();
boxCollider = GetComponent<BoxCollider>();
SkinnedMeshRenderer skinnedMeshRenderer = GetComponentInChildren<SkinnedMeshRenderer>();
Material[] mats = skinnedMeshRenderer.materials;
mat = mats[0]; // 첫 번째 Material 할당
nav = GetComponent<NavMeshAgent>();
anim = GetComponentInChildren<Animator>();
Invoke("ChaseStart", 2); // 2초 뒤 ChaseStart 호출
}
위 스크립트는
마테리알 어쩌구 오류났던 거 수정한 부분임
보이드 어웨이크 부문 일부임
+
저번에 만든 벽을 복붙해서 기둥을 만들어보자
지형이 바뀌었으면 Bake 다시 누르기
+
rigid.velocity = Vector3.zero;
rigid.velocity 하면 취소선 오류남
rigid.linearVelocity = Vector3.zero;
rigid.linearVelocity 로 변형해야 함
+
애니메이션 지정하기
create - animation - animator controller 새로 만들기
이름은 AC_Enemy_A
root 뼈 위 오브젝트에게 AC_Enemy_A 컴포 할당 ?
애니메이션 컨트롤러 두번 클릭해서
Animator 창 들어감
아래 프리팹 을 열어보면 4 개의 애니메이션이 속해있는 걸 볼 수 있
Attak , Die , Idle , Walk
4개의 애니메이션을 애니메이터 창으로 다 드래그 해 오기
idle (사진에선 a pose) 를 우클 - default state 로 만들기
idle 과 walk 는 우클 make transition 으로 연결
어택은 워크와 쌍방향 연결
Any State 에서 Die 로 트랜지션 일방향 연결
좌상단 파라메터 세 개 생성
bool - isWalk
bool - isAttack
Trigger - doDie
Idle, Walk, Attack 의 Has Exit Time 은 다 체크 해제
die 의 컨디션은 doDie
워크 어택 트루 팔스는 위처럼 설정
+
if (curHealth > 0)
{ mat.color = Color.white; }
else
{
mat.color = Color.gray;
gameObject.layer = 12;
isChase = false;
nav.enabled = false;
anim.SetTrigger("doDie");
}
체력이 0 이 되면
회색빛이 되고
레이어태그가 인덱스넘버12인 EnemyDead가 되고
플레이어 쫓는걸 그만두고
자동이동 ai 를 끄고
죽는 모션을 재생한다는 뜻
+
지금껏 만든 Enemy 스크립트 내용 :
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Animations;
using UnityEngine.UIElements;
public class Enemy : MonoBehaviour
{
public int maxHealth;
public int curHealth;
public Transform target;
public bool isChase;
Material mat;
Rigidbody rigid;
BoxCollider boxCollider;
NavMeshAgent nav;
Animator anim;
void Awake()
{
rigid = GetComponent<Rigidbody>();
boxCollider = GetComponent<BoxCollider>();
SkinnedMeshRenderer skinnedMeshRenderer = GetComponentInChildren<SkinnedMeshRenderer>();
Material[] mats = skinnedMeshRenderer.materials;
mat = mats[0]; // 첫 번째 Material 할당
nav = GetComponent<NavMeshAgent>();
anim = GetComponentInChildren<Animator>();
Invoke("ChaseStart", 2); // 2초 뒤 ChaseStart 호출
}
void ChaseStart()
{
isChase = true;
anim.SetBool("isWalk", true);
}
void Update()
{
if (isChase && nav.isOnNavMesh)
{
nav.SetDestination(target.position);
}
}
void FreezeVelocity()
{
if (isChase)
{
rigid.linearVelocity = Vector3.zero;
rigid.angularVelocity = Vector3.zero;
}
}
void FixedUpdate()
{
FreezeVelocity();
}
void OnTriggerEnter(Collider other)
{
if(other.tag == "Melee")
{
Weapon weapon = other.GetComponent<Weapon>();
curHealth -= weapon.damage;
Vector3 reactVec = transform.position
- other.transform.position;
StartCoroutine(OnDamage(reactVec, false));
}
else if (other.tag == "Bullet")
{
Bullet bullet = other.GetComponent<Bullet>();
curHealth -= bullet.damage;
Vector3 reactVec = transform.position
- other.transform.position;
Destroy(other.gameObject);
StartCoroutine(OnDamage(reactVec, false));
}
}
public void HitByGrenade(Vector3 explosionPos)
{
curHealth -= 100;
Vector3 reactVec = transform.position - explosionPos;
StartCoroutine (OnDamage(reactVec, true));
}
IEnumerator OnDamage(Vector3 reactVec,
bool isGrenade)
{
mat.color = Color.red;
yield return new WaitForSeconds(0.1f);
if (curHealth > 0)
{ mat.color = Color.white; }
else
{
mat.color = Color.gray;
gameObject.layer = 12;
isChase = false;
nav.enabled = false;
anim.SetTrigger("doDie");
if (isGrenade)
{
reactVec = reactVec.normalized;
reactVec += Vector3.up * 2.2f ;
rigid.freezeRotation = false;
rigid.AddForce(reactVec * 4, ForceMode.Impulse);
rigid.AddTorque(reactVec * 10, ForceMode.Impulse);
}
else
{
reactVec = reactVec.normalized;
reactVec += Vector3.up;
rigid.AddForce(reactVec * 5, ForceMode.Impulse);
}
Destroy(gameObject, 3);
}
}
}
+
애니메이터 창에서 만약
motion 을 다른 캐릭터 애니메이션을 써 보면?
기본 idle 애니 사용시 | 다른 캐릭의 idle 애니 사용해봄 |
뼈 구조가 같은 오토리그 프로라 모션 공유는 가능 |