Unity

[Unity 2D] 장애물과 겹치지 않게 랜덤하게 움직이기

(ꐦ •᷄ࡇ•᷅) 2025. 3. 5. 22:53

기능 설명

NPC가 배경에서 랜덤하게 움직이는 기능을 필요로 하였다.

이때, NPC가 움직일 때, 장애물과 겹치면 안 된다.

 

구현 알고리즘

  1. 랜덤한 위치를 선정한다.
  2. 그 위치에서 Physics2D.OverlapCircle을 사용하여 장애물의 반경에 속해 있는지 확인한다. (이때 장애물 레이어를 따로 설정해 준다.)
  3. 만약, 그 위치가 장애물 위면 다시 랜덤한 위치를 선택한다. 
  4. 만약, 그 위치가 정상 위치라면 목표 경로에 장애물이 있는지 한 번 더 체크한다.
  5. 그래도 정상 위치라면 그 좌표로 이동하고, 장애물이 있는 경로라면 다시 랜덤한 위치를 선택한다.

AnimalController.cs

더보기
using System.Collections;
using UnityEngine;

public class AnimalController : MonoBehaviour
{
    public int id;
    public float moveSpeed = 2f;
    public float waitTime = 2f;
    public float checkRadius = 0.1f;

    private bool isWaiting = false;
    private LayerMask obstacleLayer;
    private Vector2 targetPos;
    private Vector2 minPos;
    private Vector2 maxPos;

    private void Start()
    {
        minPos = Managers.Game.Confiner.bounds.min;
        maxPos = Managers.Game.Confiner.bounds.max;
        obstacleLayer = LayerMask.GetMask("Obstacle");

        ChooseNewPos();
    }

    private void Update()
    {
        if (isWaiting)
            return;

        Vector2 currentPos = transform.position;
        float distance = Vector2.Distance(currentPos, targetPos);

        if(distance < 0.1f)
        {
            StartCoroutine(WaitAndChooseNewTarget());
        }
        else
        {
            // 목표 경로에 장애물이 있는지 체크
            // 동적으로 확인하여 움직이는 장애물도 피하게끔 함. (NPC끼리의 충돌)
            Vector2 direction = (targetPos - currentPos).normalized;
            RaycastHit2D hit = Physics2D.Raycast(currentPos, direction, distance, obstacleLayer);

            #region 디버깅
            //if (hit.collider != null)
            //    Debug.Log(hit.collider.gameObject.name);
            //Debug.DrawRay(currentPos, direction * distance, Color.red);
            #endregion

            if (hit.collider == null)
            {
                transform.position = Vector2.MoveTowards(currentPos, targetPos, moveSpeed * Time.deltaTime);
            }
            else
            {
                ChooseNewPos();
            }
        }
    }

    void ChooseNewPos()
    {
        float x = Random.Range(minPos.x, maxPos.x);
        float y = Random.Range(minPos.y, maxPos.y);
        
        targetPos = new Vector2(x, y);

        // 만약 새로 선택한 위치가 장애물 위면 다시 선택
        if(Physics2D.OverlapCircle(targetPos, checkRadius, obstacleLayer))
        {
            ChooseNewPos();
        }
    }

    IEnumerator WaitAndChooseNewTarget()
    {
        isWaiting = true;
        yield return new WaitForSeconds(waitTime);
        ChooseNewPos();
        isWaiting = false;
    }
}

 

결과

 

경로에 장애물이 있어 다시 경로를 설정하여 이동하는 모습이다.