YunDev

Unity - 2D 적 A.I 구현하기 본문

Programming/Unity

Unity - 2D 적 A.I 구현하기

S준 2020. 3. 9. 21:57

 

https://www.youtube.com/watch?v=7MYUOzgZTf8

 

 

오늘은 A.I를 구현해보도록 하겠습니다!

유니티에서는 C#을 사용합니다.

 

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

public class EnemyMove : MonoBehaviour
{
    Rigidbody2D rigid;
    public int nextMove;
    Animator anim;
    SpriteRenderer spriteRenderer;
    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        anim = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        Think();

        Invoke("Think", 2);
    }

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

        //Platform Check
        Vector2 frontVec = new Vector2(rigid.position.x + nextMove*0.2f, rigid.position.y);
        Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0));
        RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform"));
        if (rayHit.collider == null)
        {
            Turn();
        }
    }

    //재귀 함수
    void Think()
    {
        nextMove = Random.Range(-1, 2);

        //Sprite Animation
        anim.SetInteger("WalkSpeed", nextMove);
        //Flip Sprite
        if(nextMove != 0)
        spriteRenderer.flipX = nextMove == 1;

        //Recursive
        float nextThinkTime = Random.Range(2f, 5f);
        Invoke("Think", nextThinkTime);

    }

    void Turn()
    {
        nextMove *= -1;
        spriteRenderer.flipX = nextMove == 1;

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

이건 A.I 구현 코드이고요, 이제 각 함수마다 기능을 알아보도록 하겠습니다.

 

 

public class EnemyMove : MonoBehaviour
{
	//기본적으로 가져와야하는 컴포넌트들.
    Rigidbody2D rigid;
    public int nextMove;
    Animator anim;
    SpriteRenderer spriteRenderer;
    
    void Awake() //게임 실행시 처음으로 실행되는 함수
    {
        rigid = GetComponent<Rigidbody2D>(); //Rigidbody2D 컴포넌트를 가져와서 rigid에다 담겠다는 뜻.
        anim = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        Think();

        Invoke("Think", 2);
    }

여기서 제일 중요한건 Invoke 입니다. Invoke는 실행할 함수와 시간을 설정하여 "몇초 뒤 이 함수를 실행하겠다"가 됩니다.

 

 

//재귀 함수
    void Think()
    {
        nextMove = Random.Range(-1, 2);

        //Sprite Animation
        anim.SetInteger("WalkSpeed", nextMove);
        //Flip Sprite
        if(nextMove != 0) 
        spriteRenderer.flipX = nextMove == 1;

        //Recursive
        float nextThinkTime = Random.Range(2f, 5f); //2초, 5초를 랜덤으로 값을 정함.
        Invoke("Think", nextThinkTime); //Think 함수를 2초 아니면 5초 후 실행.

    }

재귀 함수 : 어떤 함수안에서 자신의 함수를 다시 호출하는 함수를 뜻합니다. Random.Range()는 정해진 값을 랜덤으로 출력합니다.( 값 기준 -1 ~ 0, 2 까지) flipX는 bool(true, false) 값이고 nextMove가 1이라는 건 x축 기준으로 앞을 말하니

-1일 땐 왼쪽을 바라보고 1일 땐 오른쪽을 바라보게 됩니다.

flipX가 true일 때 : X 체크                    

flipX가 false 일 때 : X체크가 없음

 

 

 

 void FixedUpdate() //물리 연산을 하기 전 실행되는 함수, 50프레임
    {
        //Move
        rigid.velocity = new Vector2(nextMove, rigid.velocity.y);

        //Platform Check
        Vector2 frontVec = new Vector2(rigid.position.x + nextMove*0.2f, rigid.position.y);
        Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0)); //초록색
        RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform"));
        if (rayHit.collider == null)
        {
            Turn();
        }
    }

DrawRay는 빔을 쏘지만 게임에선 보이지 않습니다. 땅에 떨어지는 것을 막기 위해 nextMove*0.2를 하여 A.I 바로 앞에 빔 위치를 두고 RaycastHit으로 닿은 물체의 Layer가 "Platform"인 것만 rayHit에 값을 담았습니다.

LayerMask로 물리 충돌(효과)를 구분하는 역할입니다.

저는 지형에다 Platform을 넣었습니다.

그러면 rayHit의 값이 null이면 A.I가 움직이면서 앞이 낭떠러지 인걸 확인한거죠. 그래서 Turn 함수를 실행하게 됩니다.!

 

 void Turn()
    {
        nextMove *= -1;
        spriteRenderer.flipX = nextMove == 1;

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

낭떠러지를 확인했을 때 실행되면 움직이고 있는 값에 *-1을 하여 -1 * -1 = 1, 1 * -1 = 1 이 되어 떨어지는 것을 막을 수 있게 된거죠. 여기서 CancelInvoke()는 현재 Invoke() 함수를 멈추게 합니다.

(그닥 필요는 없지만 방향을 바꿨으니 다시 시간 설정을 준거죠.)

 

 

 

이렇게 해서 함수마다 기능을 알아보았습니다. 유니티를 많이 안해보신 분들은 아예 이해가 안가실 수도 있습니다.

그래서 앞으로는 유니티 기초글도 많이 올리도록 하겠습니다~!!