Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

인텔리킴의 Unity Studying & Game Analysis

13. 유니티 2D 게임 제작 - 몬스터 AI 구현 본문

게임 개발 일지/유니티 엔진 공부

13. 유니티 2D 게임 제작 - 몬스터 AI 구현

인텔리킴 2024. 4. 19. 19:08

기본 세팅

적 애니메이터

먼저 애니메이터를 편집해 기본적인 매개변수 및 트랜지션을 설정함

 

기본적인 이동 구현

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

public class EnemyMove : MonoBehaviour
{
    Rigidbody2D rigid;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()
    {
        rigid.velocity = new Vector2(-1, rigid.velocity.y);
        
    }
}

 

플레이어의 방향키 없이 움직여도 되기 때문에 

rigid.velocity를 이용해 간단하게 구현

 

행동 결정 로직

public class EnemyMove : MonoBehaviour
{
    Rigidbody2D rigid;
    public int nextMove;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        Invoke("Think", 5);
    }

    void FixedUpdate()
    {
        rigid.velocity = new Vector2(-1, rigid.velocity.y);
        
    }

    //재귀함수
    void Think()
    {
        nextMove = Random.Range(-1, 2);
        Debug.Log(nextMove);
        Invoke("Think", 5);
    }
}

 

Random.Range(int , int) : 앞 숫자 이상 뒷 숫자 미만의 랜덤한 숫자 하나를 불러옴

Invoke() : 주어진 시간이 지난 뒤에 지정된 함수를 실행하는 함수

재귀함수 : 자기 자신을 불러오는 함수

일정시간마다 랜덤한 숫자를 불러오고 자기자신을 불러오는 새로운 Think() 재귀함수를 작성

 

낭떠러지 앞의 에너미

레이캐스트가 오브젝트의 앞에서 아래를 향해 발사되야함

이 값은 nextMove 변수가 가지고 있음

 

Vector2 frontVector = new Vector2(nextMove + rigid.position.x, rigid.position.y);

 

자기의 앞을 확인하는 벡터값을 하나 제작

 

//이동
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);

//지형 체크
Vector2 frontVector = new Vector2(nextMove + rigid.position.x, rigid.position.y);
RaycastHit2D rayHit = Physics2D.Raycast(frontVector, Vector2.down, 1, LayerMask.GetMask("Platform"));

if (rayHit.collider == null)
{
    Debug.Log("이 앞은 낭떠러지");
}

 

오브젝트의 앞에서 레이가 발사되어 

레이가 바닥의 플랫폼과 닿지 않으면 디버그 메세지를 출력하는 코드를 작성

 

-1이 나오면 자신의 x축 -1 지점에서 아래로 레이발사

 

    void FixedUpdate()
    {
        //이동
        rigid.velocity = new Vector2(nextMove, rigid.velocity.y);

        //지형 체크
        Vector2 frontVector = new Vector2(nextMove + rigid.position.x, rigid.position.y);
        RaycastHit2D rayHit = Physics2D.Raycast(frontVector, Vector2.down, 1, LayerMask.GetMask("Platform"));
        Debug.DrawRay(frontVector,Vector3.down ,new Color(0, 1, 0));
        if (rayHit.collider == null)
        {
            Debug.Log("이 앞은 낭떠러지");
            nextMove = nextMove * -1;
            CancelInvoke();
            Invoke("Think", 5);
        }

    }

 

CancelInvoke() : 지금 진행중인 Invoke()를 취소하는 함수

만약 앞으로 진행하는 방향에 낭떠러지가 있으면 반대 방향으로 이동

 

이동 애니메이션 설정

 

애니메이터 매개변수에 WalkSpeed int형 매개변수 작성

 

인스펙터의 int 매개변수

인스펙터의 int 매개변수는 

Greater, Equal, NotEqual, Less 의 4가지로 나누어짐

 

WalkSpeed 매개변수가 0일때 가만히 있는 애니메이션이 나오도록 설정

만약 0이 아니라면 움직이는 애니메이션 재생

 

void Think()
{
    //다음 움직임 생각
    thinkTime = Random.Range(2, 5);
    nextMove = Random.Range(-1, 1);
    Debug.Log(nextMove);
    
    //애니메이션 재생
    animator.SetInteger("WalkSpeed", nextMove);
    
    //애니메이션 방향 전환
    if (nextMove != 0)
    {
        spriteRenderer.flipX = nextMove == 1;
    }

    //반복
    Invoke("Think", thinkTime);
}

Think 함수 다시 작성

 

spriteRenderer.flipX = nextMove == 1; : nextMove를 1이면 전환(true면 반전)

하지만 그대로 코드를 실행하면 앞에 낭떠러지가 있을경우 애니메이션이 반전되지 않는 문제가 발생함

 

if (nextMove != 0)
{
	spriteRenderer.flipX = nextMove == 1;
}

 

이 구문이 한번더 낭떠러지 확인 조건문 안에 들어가야하지만 그러면 번거로우니 따로 함수로 한번더 작성

 

     void Flip(int decideFlip)
    {
        if (decideFlip != 0)
        {
            spriteRenderer.flipX = decideFlip == 1;
        }
    }

 

Flip 함수로 작성

 

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

public class EnemyMove : MonoBehaviour
{
    Rigidbody2D rigid;
    int nextMove;
    int thinkTime;
    Animator animator;
    SpriteRenderer spriteRenderer;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        Invoke("Think", 5);
        animator = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();
    }

    void FixedUpdate()
    {
        //이동
        rigid.velocity = new Vector2(nextMove, rigid.velocity.y);

        //지형 체크
        Vector2 frontVector = new Vector2(nextMove * 0.25f + rigid.position.x, rigid.position.y);
        RaycastHit2D rayHit = Physics2D.Raycast(frontVector, Vector2.down, 1, LayerMask.GetMask("Platform"));
        Debug.DrawRay(frontVector,Vector3.down ,new Color(0, 1, 0));
        if (rayHit.collider == null)
        {
            Debug.Log("이 앞은 낭떠러지");
            nextMove = nextMove * -1;

            Flip(nextMove);

            CancelInvoke();
            Invoke("Think", 5);
        }

    }

    //재귀함수
    void Think()
    {
        //다음 움직임 생각
        thinkTime = Random.Range(2, 5);
        nextMove = Random.Range(-1, 2);
        Debug.Log(nextMove);
        
        //애니메이션 재생
        animator.SetInteger("WalkSpeed", nextMove);

        //애니메이션 방향 전환
        Flip(nextMove);

        //반복
        Invoke("Think", thinkTime);
    }

     void Flip(int decideFlip)
    {
        if (decideFlip != 0)
        {
            spriteRenderer.flipX = decideFlip == 1;
        }
    }
}

 

최종적인 몬스터 이동 코드

 

추가 설명

spriteRenderer.flipX = nextMove == 1; : nextMove가 1이라면 전환(true면 전환)

 

이부분이 이해가 처음에 되지않아 추가설명을 함

몬스터 스프라이트가 기본적으로 왼쪽(-1)방향을 바라보고 있는데위 구문은 1일 경우(오른쪽으로 이동할 경우) 스프라이트를 반전시킴하지만 -1일 경우(왼쪽으로 이동할 경우) 스프라이트를 반전시키지 않음
한마디로스프라이트가 왼쪽을 바라보고 있을 때왼쪽으로 이동할 경우(-1일 때) 플립을 꺼서 스프라이트 그대로 왼쪽을 바라보게 하고오른쪽으로 이동할 경우(1일 때) 플립을 켜서 오른쪽을 바라보게함또한 0이 나온다면 아예 bool 값에서 오류가 날 수 있음그렇기 때문에 0이 아닐 경우의 조건문 또한 추가함