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 애니 사용해봄
  뼈 구조가 같은 오토리그 프로라
모션 공유는 가능

 

 

 

반응형
Posted by 이름이 익명
: