유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

[유니티 기초 - B18] 몬스터 AI 구현하기

[유니티 기초 - B19] 플레이어 피격 이벤트 구현하기 

영상을 보고 쓰여진 게시글 입니다.

이번시간에는 드디어 몬스터에 대한 구현을 한번 해보도록 할탠데요

인공지능을 통해 자동으로 움직이는 것과 플레이어가 몬스터와 싸우는 모션까지 한번 해보도록 하겠습니다.

사실 AI가 거창하게 들리지만 스스로 생각해서 판단내릴 수만 있으면 AI라고 하는? 뭐 여튼 범위가 엄청 넓을 겁니다

롤에서도 AI모드라고 하지않나요 봇전? 걔들..멍청하죠.. 물론 코드는 복잡하겠지만 

엄청 어렵고 복잡한 AI를 사용하면 알파고마냥 우리보다 잘하겠지만 게임에서 봇들도 다 AI라고 한다고 합니다.

기초이기 때문에 매우 기본적인 AI를 이용하기(움직임과 멈춤만 스스로 판단)로 합니다. 

몬스터 AI구현하기 

준비하기 

몬스터 애니메이션을 먼저 넣도록 합시다 

Monster_Walk로 애니메이션 폴더에 저장해주고 (이미 Idle은 넣은 상태라 생략)

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

애니메이터에 들어가서

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

마찬가지로 isWalking변수를 만들고 Idle 과 Monster사이의 트렌지션을 추가한 후

Condition / Has Exit Time / 간격을 조절해줍니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

또한 일단 저번 코드에서 카메라를 캐릭터와 분리해주시고, 몬스터 이동에 필요한 충분한 플랫폼을 구성해 주신뒤

몬스터가 잘 보이도록 카메라를 배치 후 시작하시는 것이 좋습니다.

몬스터 기본이동 

MonsterMove.cs 스크립트를 생성하고 Monster에 적용시켜줍니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

일단 속력을 주어야 하므로 Rigidbody2D변수를 사용하고 

우리가 키보드 입력을 통하여 적을 움직이는게 아니므로 일단 한쪽방향으로 이동하도록 했습니다.

    Rigidbody2D rigid;

    // Start is called before the first frame update
    private void Awake() {
        rigid = GetComponent<Rigidbody2D>();
    }


    // Update is called once per frame
    void FixedUpdate()
    {
       rigid.velocity = new Vector2(-1,rigid.velocity.y); //단순 왼쪽방향 이동  
    }

이렇게 되면 왼쪽방향으로 쭉 음직이는 것을 볼 수 있습니다. 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI
행동설정 (인공지능 부여)

우리는 한쪽방향이 아닌 스스로 어떤 구간을 왕복하거나 중간에 쉬거나하는 AI를 만들어야 하므로

행동을 설정해주어야 합니다.

그 전에 기획단계를 거쳐

몬스터가 어떤 행동을 어떤 타이밍에 하는지 

어떤 구간을 돌아다니는지에 대하여 기획을 해주셔야합니다.

여기서는 단순하게 오른쪽 이동 / 왼쪽 이동 / 멈춤 정도로 설정하도록 합니다.

nextMove라는 변수를 주어 랜덤함수(Random.Range(min,max))를 이용해 

-1 / 0 / 1 의 난수를 생성해서 이를 Vector2의 x값으로 부여하면 각각 왼쪽, 정지, 오른쪽 으로 이동 할 것입니다.

사용함수

// min<= 수 <max 사이의 난수 생성   
//최대 값이 포함되지 않으므로 max+1을 해주어서 쓰는것이 max까지 나올수 있음
Random.Range(min,max);
   
   
//함수의 재귀호출 시 딜레이를 넣은 재귀 호출에 이용
Invoke("함수이름", delay sec);

이와같이 코드를 작성해 주면 5초에 한번씩 어느방향으로 갈지 생각(Think함수) 하여 움직이게 됩니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MonsterMove : MonoBehaviour
{

    Rigidbody2D rigid;
    public int nextMove;//다음 행동지표를 결정할 변수

    // Start is called before the first frame update
    private void Awake() {
        rigid = GetComponent<Rigidbody2D>();
        Invoke("Think", 5); // 초기화 함수 안에 넣어서 실행될 때 마다(최초 1회) nextMove변수가 초기화 되도록함 
        
    }


    // Update is called once per frame
    void FixedUpdate()
    {
       rigid.velocity = new Vector2(nextMove,rigid.velocity.y); //nextMove 에 0:멈춤 -1:왼쪽 1:오른쪽 으로 이동 
    }


    void Think(){//몬스터가 스스로 생각해서 판단 (-1:왼쪽이동 ,1:오른쪽 이동 ,0:멈춤  으로 3가지 행동을 판단)

        //Random.Range : 최소<= 난수 <최대 /범위의 랜덤 수를 생성(최대는 제외이므로 주의해야함)
        nextMove = Random.Range(-1,2);
        
        //Think(); : 재귀함수 : 딜레이를 쓰지 않으면 CPU과부화 되므로 재귀함수쓸 때는 항상 주의 ->Think()를 직접 호출하는 대신 Invoke()사용
        Invoke("Think", 5); //매개변수로 받은 함수를 5초의 딜레이를 부여하여 재실행 
    }

}

그런데 이렇게 되면 만약에 5초가 지난 이후에도 오른쪽 방향으로 계속 이동하게되면 결국 

맵 밖으로 탈출하고 바닥밑으로 떨어지게 될 수도 있으니 이를 막아주어야 합니다.

지능 높이기

RayHit를 이동하여 자신의 앞이 낭떨어지인지 지형을 체크해보도록 만들어봅시다 

낭떨어지를 만나면 Debug.Log로 경고문구를 출력하는 코드를 FixedUpdate()에 작성하여 움직일때마다 측정하도록 합니다.

    void FixedUpdate()
    {
        //Move
       rigid.velocity = new Vector2(nextMove,rigid.velocity.y); //nextMove 에 0:멈춤 -1:왼쪽 1:오른쪽 으로 이동 


       //Platform check(맵 앞이 낭떨어지면 뒤돌기 위해서 지형을 탐색)


       //자신의 한 칸 앞 지형을 탐색해야하므로 position.x + nextMove(-1,1,0이므로 적절함)
        Vector2 frontVec = new Vector2(rigid.position.x + nextMove, rigid.position.y);

        //한칸 앞 부분아래 쪽으로 ray를 쏨
        Debug.DrawRay(frontVec, Vector3.down, new Color(0,1,0));

        //레이를 쏴서 맞은 오브젝트를 탐지 
        RaycastHit2D raycast = Physics2D.Raycast(frontVec, Vector3.down,1,LayerMask.GetMask("Platform"));

        //탐지된 오브젝트가 null : 그 앞에 지형이 없음
        if(raycast.collider == null){
            Debug.Log("경고! 이 앞은 낭떨어지");
        }

    }

이렇게 Ray를 이용하여 탐지되면 경고 문구가 출력되었네요 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

이제 경고문구 출력대신 우리가 직접 방향을 바꾸어 떨어지지 않도록 해봅시다

-1을 곱하면 되겠네요 

        //탐지된 오브젝트가 null : 그 앞에 지형이 없음
        if(raycast.collider == null){
            nextMove= nextMove*(-1); //우리가 직접 방향을 바꾸어 주었으니 Think는 잠시 멈추어야함
            CancelInvoke(); //think를 잠시 멈춘 후 재실행
            Invoke("Think",5); 
        }

직접 방향을 멈추었으니 잠시 스스로 생각하여 방향을 정하는 것을 멈춰주어야 할 것입니다.

캔슬을 통해 잠시 중지해 준 후 다시시작하면 됩니다

낭떠러지 탐지 거리 줄이기 

또한 ray가 1칸앞을 내다보는 것이 너무 멀다고 생각될 경우 

       //자신의 한 칸 앞 지형을 탐색해야하므로 position.x + nextMove(-1,1,0이므로 적절함)
        Vector2 frontVec = new Vector2(rigid.position.x + nextMove*0.4f, rigid.position.y);

한칸앞이 아닌 0.4정도 앞을 탐지하도록 적절한 수를 곱해주시면 해결됩니다. 

1보단 훨씬 가까이를 탐지하게 되네요 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

추가적으로

5초 마다 생각하기 보다는 생각하는 시간 또한 랜덤으로 부여하면 더욱 더 똑똑해보이는 인공지능이 될 것입니다.

    void Think(){//몬스터가 스스로 생각해서 판단 (-1:왼쪽이동 ,1:오른쪽 이동 ,0:멈춤  으로 3가지 행동을 판단)

        //Random.Range : 최소<= 난수 <최대 /범위의 랜덤 수를 생성(최대는 제외이므로 주의해야함)
        nextMove = Random.Range(-1,2);
        

        float time = Random.Range(2f, 5f); //생각하는 시간을 랜덤으로 부여 
        //Think(); : 재귀함수 : 딜레이를 쓰지 않으면 CPU과부화 되므로 재귀함수쓸 때는 항상 주의 ->Think()를 직접 호출하는 대신 Invoke()사용
        Invoke("Think", time); //매개변수로 받은 함수를 time초의 딜레이를 부여하여 재실행 
    }

이런식으로 변경하면 됩니다. 

전체 코드를 한번 쓰고 이제 애니메이션으로 넘어가도록 하겠습니다.

MonsterMove.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MonsterMove : MonoBehaviour
{

    Rigidbody2D rigid;
    public int nextMove;//다음 행동지표를 결정할 변수

    // Start is called before the first frame update
    private void Awake() {
        rigid = GetComponent<Rigidbody2D>();
        Invoke("Think", 5); // 초기화 함수 안에 넣어서 실행될 때 마다(최초 1회) nextMove변수가 초기화 되도록함 
        
    }


    // Update is called once per frame
    void FixedUpdate()
    {
        //Move
       rigid.velocity = new Vector2(nextMove,rigid.velocity.y); //nextMove 에 0:멈춤 -1:왼쪽 1:오른쪽 으로 이동 


       //Platform check(맵 앞이 낭떨어지면 뒤돌기 위해서 지형을 탐색)


       //자신의 한 칸 앞 지형을 탐색해야하므로 position.x + nextMove(-1,1,0이므로 적절함)
        Vector2 frontVec = new Vector2(rigid.position.x + nextMove*0.4f, rigid.position.y);

        //한칸 앞 부분아래 쪽으로 ray를 쏨
        Debug.DrawRay(frontVec, Vector3.down, new Color(0,1,0));

        //레이를 쏴서 맞은 오브젝트를 탐지 
        RaycastHit2D raycast = Physics2D.Raycast(frontVec, Vector3.down,1,LayerMask.GetMask("Platform"));

        //탐지된 오브젝트가 null : 그 앞에 지형이 없음
        if(raycast.collider == null){
            nextMove= nextMove*(-1); //우리가 직접 방향을 바꾸어 주었으니 Think는 잠시 멈추어야함
            CancelInvoke(); //think를 잠시 멈춘 후 재실행
            Invoke("Think",5); 
        }

    }


    void Think(){//몬스터가 스스로 생각해서 판단 (-1:왼쪽이동 ,1:오른쪽 이동 ,0:멈춤  으로 3가지 행동을 판단)

        //Random.Range : 최소<= 난수 <최대 /범위의 랜덤 수를 생성(최대는 제외이므로 주의해야함)
        nextMove = Random.Range(-1,2);
        

        float time = Random.Range(2f, 5f); //생각하는 시간을 랜덤으로 부여 
        //Think(); : 재귀함수 : 딜레이를 쓰지 않으면 CPU과부화 되므로 재귀함수쓸 때는 항상 주의 ->Think()를 직접 호출하는 대신 Invoke()사용
        Invoke("Think", time); //매개변수로 받은 함수를 time초의 딜레이를 부여하여 재실행 
    }

}
애니메이션

이제 애니메이션을 변경해 줄 차례입니다. 

변수생성 / 초기화 

애니메이션 넣을때 Animator / sprite Renderer(방향전환flipX) 를 사용한 것이 기억이 나실겁니다

일단 두개의 변수를 만들어 초기화해줍니다.

    //변수 생성 
    Animator animator;
    SpriteRenderer spriteRenderer;

    // Start is called before the first frame update
    private void Awake() {
    
    	//초기화
        animator = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        
    }

애니메이터 설정 

걷고있는지 판정에 Bool 형이 아닌 WalkSpeed(int형) 구조를 사용해서 판단해보도록 합시다

원래 만들었던 isWalking을 제거한 후 

2개의 Transition에 전부 Condition을 다시 추가해주셔야합니다. 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

WalkSpeed가 0이면(Equal) ,  Walk -> Idle로

WalkSpeed가 0이 아니면(NotEqual) ,  Idle -> Walk로  변경해줍시다 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

스크립트 

이제 애니메이션 상태는 완료했으니 코드로 WalkSpeed에 따른 방향전환을 

걷기 

Think함수 내에서 애니메이터 안의 WalkSpeed변수를 NextMove와 동일하게 set해주는 코드를 작성합니다. 

nextMove는 어쩌피 0,-1,1이므로 0인 정지상태가 아니면 Walk애니메이션이 실행됩니다.

방향전환 

nextMove==0일때는 굳이 방향전환의 필요가 없으므로 제외하고

nextMove가 1일때만(그림은 왼쪽으로 가는 그림이므로 오른쪽으로 전환하면 flip임) flipX를 true로 변경하는 코드를 작성합니다 

void Think(){//몬스터가 스스로 생각해서 판단 (-1:왼쪽이동 ,1:오른쪽 이동 ,0:멈춤  으로 3가지 행동을 판단)

        //Set Next Active
        //Random.Range : 최소<= 난수 <최대 /범위의 랜덤 수를 생성(최대는 제외이므로 주의해야함)
        nextMove = Random.Range(-1,2);

        //Sprite Animation
        //WalkSpeed변수를 nextMove로 초기화 
        animator.SetInteger("WalkSpeed",nextMove);


        //Flip Sprite
        if(nextMove != 0) //서있을 때 굳이 방향을 바꿀 필요가 없음 
            spriteRenderer.flipX = nextMove == 1; //nextmove 가 1이면 방향을 반대로 변경  


        //Recursive (재귀함수는 가장 아래에 쓰는게 기본적) 
        float time = Random.Range(2f, 5f); //생각하는 시간을 랜덤으로 부여 
        //Think(); : 재귀함수 : 딜레이를 쓰지 않으면 CPU과부화 되므로 재귀함수쓸 때는 항상 주의 ->Think()를 직접 호출하는 대신 Invoke()사용
        Invoke("Think", time); //매개변수로 받은 함수를 time초의 딜레이를 부여하여 재실행 
    }

다음 코드를 실행하면 하나의 오류가 발생합니다.

바로 이 부분에서는 Think함수가 Invoke되는 것이 Cancle되어 방향전환이 이루어지지 않는 버그입니다.

        //탐지된 오브젝트가 null : 그 앞에 지형이 없음
        if(raycast.collider == null){
            nextMove= nextMove*(-1); //우리가 직접 방향을 바꾸어 주었으니 Think는 잠시 멈추어야함
            CancelInvoke(); //think를 잠시 멈춘 후 재실행
            Invoke("Think",5); 
        }

이런 경우 if문 내부에 방향전환을 해주는 것보다는

앞에 지형이 없는 특수한경우에 해당하는 방향전환을 위한 Turn함수를 작성해 주는 것이 좋습니다. 

    void Turn(){

        nextMove= nextMove*(-1); //우리가 직접 방향을 바꾸어 주었으니 Think는 잠시 멈추어야함
        spriteRenderer.flipX = nextMove == 1;

        CancelInvoke(); //think를 잠시 멈춘 후 재실행
        Invoke("Think",2);//  

    }

이를 작성 한 후 if문 내부에서 실행시키면 

      //탐지된 오브젝트가 null : 그 앞에 지형이 없음
        if(raycast.collider == null){
            Turn();
        }

오류가 해결됩니다.

마지막으로 코드 전문

MonsterMove.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MonsterMove : MonoBehaviour
{

    Rigidbody2D rigid;
    public int nextMove;//다음 행동지표를 결정할 변수
    Animator animator;
    SpriteRenderer spriteRenderer;

    // Start is called before the first frame update
    private void Awake() {
        animator = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        rigid = GetComponent<Rigidbody2D>();
        Invoke("Think", 5); // 초기화 함수 안에 넣어서 실행될 때 마다(최초 1회) nextMove변수가 초기화 되도록함 
        
    }


    // Update is called once per frame
    void FixedUpdate()
    {
        //Move
       rigid.velocity = new Vector2(nextMove,rigid.velocity.y); //nextMove 에 0:멈춤 -1:왼쪽 1:오른쪽 으로 이동 


       //Platform check(맵 앞이 낭떨어지면 뒤돌기 위해서 지형을 탐색)


       //자신의 한 칸 앞 지형을 탐색해야하므로 position.x + nextMove(-1,1,0이므로 적절함)
        Vector2 frontVec = new Vector2(rigid.position.x + nextMove*0.4f, rigid.position.y);

        //한칸 앞 부분아래 쪽으로 ray를 쏨
        Debug.DrawRay(frontVec, Vector3.down, new Color(0,1,0));

        //레이를 쏴서 맞은 오브젝트를 탐지 
        RaycastHit2D raycast = Physics2D.Raycast(frontVec, Vector3.down,1,LayerMask.GetMask("Platform"));

        //탐지된 오브젝트가 null : 그 앞에 지형이 없음
        if(raycast.collider == null){
            Turn();
        }

    }


    void Think(){//몬스터가 스스로 생각해서 판단 (-1:왼쪽이동 ,1:오른쪽 이동 ,0:멈춤  으로 3가지 행동을 판단)

        //Set Next Active
        //Random.Range : 최소<= 난수 <최대 /범위의 랜덤 수를 생성(최대는 제외이므로 주의해야함)
        nextMove = Random.Range(-1,2);

        //Sprite Animation
        //WalkSpeed변수를 nextMove로 초기화 
        animator.SetInteger("WalkSpeed",nextMove);


        //Flip Sprite
        if(nextMove != 0) //서있을 때 굳이 방향을 바꿀 필요가 없음 
            spriteRenderer.flipX = nextMove == 1; //nextmove 가 1이면 방향을 반대로 변경  


        //Recursive (재귀함수는 가장 아래에 쓰는게 기본적) 
        float time = Random.Range(2f, 5f); //생각하는 시간을 랜덤으로 부여 
        //Think(); : 재귀함수 : 딜레이를 쓰지 않으면 CPU과부화 되므로 재귀함수쓸 때는 항상 주의 ->Think()를 직접 호출하는 대신 Invoke()사용
        Invoke("Think", time); //매개변수로 받은 함수를 time초의 딜레이를 부여하여 재실행 
    }

    void Turn(){

        nextMove= nextMove*(-1); //우리가 직접 방향을 바꾸어 주었으니 Think는 잠시 멈추어야함
        spriteRenderer.flipX = nextMove == 1;

        CancelInvoke(); //think를 잠시 멈춘 후 재실행
        Invoke("Think",2);//  

    }

}

여기까지가 몬스터 AI구현하기였습니다. 

이제 몬스터가 플레이어를 공격하도록 만들어봅시다

사실 공격은 아니고.. 닿으면 플레이어가 피해를 입는 것으로.. 

플레이어 피격 이벤트 구현하기

함정추가

몬스터와 더불어 플레이어에게 타격을 입힐 수 있는 함정 맵을 추가해봅시다

골드메탈님이 가시를 만들어 놓으셨네요

다시 타일맵으로 돌아가서 타일팔레트에 가시를 추가 한 후 그려주면됩니다(생략)

또한 가시도 콜라이더 물리모양Physics Shape도 수정해주셔야 합니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

이 때 주의할 점은 지형Platform과 가시Spike가 동일한 취급을 받아서는 안되므로 

Tilemap을 하나 더 추가하여 분리해주어야합니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

Spike로 하나 더 추가하여 가시는 여기에 그려주었습니다.

물론 새로 추가한 Spike 타일맵에도 TileMap Collider 2D 컴포넌트를 추가해야합니다.

기본 설정(레이어/태그)

둘이 닿으면 발생하기 때문에 누구랑 닿았는지를 구분하기위해서는

Tag / Layer를 사용할탠데 

일단 Enamy라는 태그와 레이어를 둘다 만들어서 적용시켜 주도록 합시다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI
Tag
유니티 3D 몬스터 AI - yuniti 3D monseuteo AI
Layer

가시가 들어간 Tilemap에 적용해주는데 Monster에도 적용해주세요

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

그리고 플레이어에도 레이어를 추가합시다  

Player / PlayerDamaged라는 두개의 Layer를 만들어줍니다 

플레이어는 레이어를 2개 사용할 예정입니다. 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI
레이어 설정 

 - 몬스터 끼리의 물리충돌 해제

보통 몬스터는 함정(가시)장애물 같은 것을 그냥 지나칩니다 

물리 효과를 받지 않는데, 지금은 실행해보면 몬스터가 가시를 지나갈때 열심히 기어올라가서 지나가는 모습입니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

몬스터 Enamy레이어 끼리는 서로를 무시하도록 만들어봅시다

Edit -> Project Setting ->Physics2D 

맨아래 보시면 물리 충돌을 관리하는 Matrix가 있는데 Enamy끼리의 충돌을 꺼줍시다

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

이제 장애물을 그냥 통과하게 되는데

몬스터가 더 앞쪽으로 지나가게 하려면 몬스터의 z축을 앞쪽(-1로 변경)으로 변경하면됩니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

이제 Enamy끼리는 물리 충돌이 발생하지 않고, 몬스터가 장애물보다 앞쪽을 지나가는 것을 볼 수 있습니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

- 플레이어가 피격을 받았을 때 1~2초의 무적타임을 부여

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

그러기 위하여 PlayedDamaged 레이어는 플레이어와 닿지 않게 체크를 해제해줍니다

(이렇게 해제되면 OncollisionEnter도 먹지않음) 

스크립트 

" 충돌이 발생하면 플레이어가 튕겨나가짐 + 무적상태에 돌입 " 하는 코드를 작성해 봅시다

충돌이 발생하는 것을 감지하는 함수는 OnCollsionEnter2D이고, 그 대상이 Enamy여야 합니다

해당 충돌이 발생하면 여러가지 이벤트가 발생되므로 충돌시 호출할 함수 OnDamaged()를 만들어줍니다.

- 무적상태에 돌입한 레이어로 변경 

- 무적 상태에 돌입했음을 알려줄 시각효과(투명해짐)

- 튕겨져 나가기 (addForce) -> 튕겨져 나가는 방향을 계산해야함 

private void OnCollisionEnter2D(Collision2D collision) {
        
        if(collision.gameObject.tag == "Enamy"){
            OnDamaged(collision.transform.position); //현재 충돌한 오브젝트의 위치값을 넘겨줌  
        }

    }


    void OnDamaged(Vector2 tartgetPos){
        gameObject.layer = 11; //playerDamaged Layer number가 11로 지정되어있음 

        spriteRenderer.color = new Color(1,1,1,0.4f); //투명도를 0.4로 부여하여 지금이 무적시간으로 변경되었음을 보여줌

        //맞으면 튕겨나가는 모션
        int dirc = transform.position.x-tartgetPos.x > 0 ? 1 : -1; 
        //튕겨나가는 방향지정 -> 플레이어 위치(x) - 충돌한 오브젝트위치(x) > 0: 플레이어가 오브젝트를 기준으로 어디에 있었는지 판별
        //> 0이면 1(오른쪽으로 튕김) , <=0 이면 -1 (왼쪽으로 튕김)
        rigid.AddForce(new Vector2(dirc,1)*7, ForceMode2D.Impulse); // *7은 튕겨나가는 강도를 의미 
    }

일정시간이 지나면 무적상태가 해제되도록 수정해봅시다 

무적상태 해제를 해주는 함수를 OffDamaged()로 제작하여 Invoke함수를 통해 일정 시간이 지나면 실행되도록 만듭시다

    void OnDamaged(Vector2 tartgetPos){
        gameObject.layer = 11; //playerDamaged Layer number가 11로 지정되어있음 

        spriteRenderer.color = new Color(1,1,1,0.4f); //투명도를 0.4로 부여하여 지금이 무적시간으로 변경되었음을 보여줌

        //맞으면 튕겨나가는 모션
        int dirc = transform.position.x-tartgetPos.x > 0 ? 1 : -1; 
        //튕겨나가는 방향지정 -> 플레이어 위치(x) - 충돌한 오브젝트위치(x) > 0: 플레이어가 오브젝트를 기준으로 어디에 있었는지 판별
        //> 0이면 1(오른쪽으로 튕김) , <=0 이면 -1 (왼쪽으로 튕김)
        rigid.AddForce(new Vector2(dirc,1)*7, ForceMode2D.Impulse); // *7은 튕겨나가는 강도를 의미 


        Invoke("OffDamaged",2); //2초의 딜레이 (무적시간 2초)
    }

    void OffDamaged(){ //무적해제함수 
        gameObject.layer = 10; //플레이어 레이어로 복귀함

        spriteRenderer.color = new Color(1,1,1,1); //투명도를 1로 다시 되돌림 

    }
애니메이션 

튕겨나가는 모션에 대한 스프라이트가 따로 없으므로 점프를 재활용합니다.

Damaged 라고 이름을 지어서 만들었습니다.

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

일단 loop를 해제해주세요

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

애니메이션을 열어서 

앞에꺼는 하나 지우고 뒤에 꺼 하나를 늘려서 0.3초 정도로 설정해주고

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

애니메이터에 들어가서 다음과 같이 연결해 준 후

AnyState -> ? -> Exit: 는 현재 상태에 상관없이 실행 후 복귀되는 애니메이션입니다

충돌이벤트는 다른 walk / idle / jump등과 상관없이 발생할 수 있고 후에 복귀되므로 다음과 같이 연결합니다.

Trigger라는 타입의 변수로 doDamaged를 만들어줍니다.

Trigger : 방아쇠 역할의 매개변수, 값이 없음 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

AnyState -> Damaged로 가는 Transition

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

다음과 같이 설정해주시고

Damaged->Exit 는 건들이지 않으셔도됩니다.

이제 Enamy와 충돌 시 Trigger변수를 스크립트에서 조작해주어야합니다.

 
    void OnDamaged(Vector2 tartgetPos){    
     
     	//Animation
        animator.SetTrigger("doDamaged");
        
     }

다음과 같이 한 줄 추가해주시면됩니다. 

완성되었습니다. 

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

흠..근데 강의랑 똑같이했는데도 애니메이션에 오류가 좀 나는 것 같네요.. 뭐지 어디가 틀렸나...

되긴되는데 가끔 먹혀요 

<참고영상>

youtu.be/7MYUOzgZTf8

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI

youtu.be/epZFE5Hpbdc

유니티 3D 몬스터 AI - yuniti 3D monseuteo AI