Unity - 2D 플랫포머 : 몬스터 AI 구현하기

TXMAY·2023년 8월 2일
0

Unity 튜토리얼

목록 보기
18/33
post-thumbnail

이번 강좌는 몬스터 AI 구현에 관한 강좌이다.


준비하기

Player 애니메이션과 같은 방식으로 이동 애니메이션을 추가한다.
그리고 'EnemyMove'라는 스크립트 파일을 생성 후 추가한다.

기본 이동

다음과 같은 코드를 작성한다.

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);
    }
}

실행하면 몬스터가 왼쪽으로 이동한다.

행동 결정 로직

몬스터의 움직임을 세 가지(정지, 좌/우 이동)로 늘려보겠다.
다음과 같이 코드를 수정한다.

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

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

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

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

    void Think()
    {
        nextMove = Random.Range(-1, 2);
    }
}

Random : 난수를 생성하는 로직 관련 클래스
Range(min, max) : min ~ max - 1 범위의 난수 생성
실행할 때마다 몬스터가 세 가지 행동 중 하나를 수행한다.
게임을 진행하는 중에 행동을 계속 바꿔야 하므로 코드를 다음과 같이 수정한다.

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

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

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

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

    void Think()
    {
        nextMove = Random.Range(-1, 2);

        Invoke("Think", 5);
    }
}

Invoke(Function Name, Time(sec)) : 주어진 시간이 지난 뒤, 지정된 함수를 실행하는 함수
실행하면 5초 간격으로 몬스터의 행동이 바뀐다. (같은 행동이 나올 수도 있다)

지능 높이기

몬스터가 절벽 끝으로 가면 그대로 떨어진다.
이를 해결해 보겠다.
다음과 같은 코드를 추가한다.

...

void FixedUpdate()
{
	...

    Vector2 frontVec = new Vector2(rigid.position.x + nextMove * 0.5f, rigid.position.y);
    RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform"));
    if (rayHit.collider == null)
    {
        nextMove *= -1;
        CancelInvoke();
        Invoke("Think", 5);
    }
}
...

CancelInvoke() : 현재 작동 중인 모든 Invoke 함수를 멈추는 함수
몬스터가 추락하려 하면 반대 방향으로 이동한다.
다음 행동을 하는 시간도 무작위로 바꿔 보겠다.
다음과 같이 코드를 수정한다.

...

void Think()
{
	...
    
    float nextThinkTime = Random.Range(2f, 5f);
    Invoke("Think", nextThinkTime);
}
...

2~5초 뒤에 다음 행동을 한다.

애니메이션

기존에 생성한 애니메이션 파라미터를 삭제하고 int형의 'WalkSpeed' 변수를 생성한다.
다음과 같이 코드를 수정한다.

...

Animator anim;
...

void Awake()
{
    ...
    
    anim = GetComponent<Animator>();
    spriteRenderer = GetComponent<SpriteRenderer>();
    ...
    
}
...

void Think()
{
    ...
    
    anim.SetInteger("WalkSpeed", nextMove);
    
	if (nextMove != 0)
    {
        spriteRenderer.flipX = nextMove == 1;
    }
}

제자리 -> 이동의 Conditions는 'WalkSpeed - NotEqual - 0', 이동 -> 제자리의 Conditions는 'WalkSpeed - Equal - 0'으로 설정한다.
실행하면 이동 방향에 맞춰 애니메이션과 방향이 바뀌는데 떨어지려 할 때 방향을 바꾸는 것은 애니메이션이 적용되지 않는다.
다음과 같이 코드를 수정한다.

...

void FixedUpdate()
{
    ...
    
    if (rayHit.collider == null)
    {
        Turn();
    }
}
...

void Turn()
{
    nextMove *= -1;
    spriteRenderer.flipX = nextMove == 1;
    CancelInvoke();
    Invoke("Think", 5);
}

Ray Cast를 사용하니 이런 동작도 쉽게 할 수 있어서 좋았다.

profile
게임 개발 공부하는 고양이

1개의 댓글

comment-user-thumbnail
2023년 8월 2일

잘 읽었습니다. 좋은 정보 감사드립니다.

답글 달기