본문 바로가기
게임 개발

[Unity] 참새의 모험 (1) - 무한 맵 생성

by chobbo 2024. 6. 17.

게임 화면

 

플레이어의 게임 화면에서 맵이 끊기지 않고 무한 맵처럼 보이게 만드려고 한다.

 

위의 사진은 내가 구성한 맵!

 

초록색 부분에서 게임이 시작

빨간색 부분에 도달하면 전투 진행

파란 부분에서 맵을 생성

 

다음과 같이 게임을 기획했고,

FSM (유한 상태 머신) 기법 을 사용했다.

 

초록색 부분  Ground State -> Walk State

빨간색 부분 Attack State -> Base Attack State

파란색 부분  Ground State -> Run State

 

이렇게 구현하려면 맵에서 플레이어가 특정 위치를 지남을 코드에서 판단해주어야 했다.

내가 생각한 방법은 두 가지다.

 

1. Update 문에서 Z좌표를 계산하여 플레이어의 위치 판별

->  동일한 맵이 재생성되므로 맵의 길이는 일정하기 때문에 이 방법을 사용할 수 있었다.

 

protected virtual void CheckPlayerZPos()
{
    posZ = Mathf.Repeat(stateMachine.Player.transform.position.z, 135f);
}
    
protected override void CheckPlayerZPos()
 {
     base.CheckPlayerZPos();
     if (posZ >= 37 && posZ <= 80)
     {
         stateMachine.ChangeState(stateMachine.BaseAttackState);
         stateMachine.Player.Controller.SetAttackState(true);
     }
 }

 

2. Trigger로 판별

-> 특정 위치에 IsTrigger가 켜진 Collider를 두고, OnTriggerEnter() 함수와

   OnTriggerExit() 함수를 이용하여 플레이어의 위치 판별

 

 

처음엔 막연히 Update문에서 계속해서 Z좌표를 계산해주는 것 보단

충돌이 발생할 때 트리거를 사용하여 판별해 주는 방법이 더욱 좋아보였으나,

유니티의 충돌 연산도 많은 비용을 사용한다고 알아 고민이 되었다.

 

지금은 스케일이 작은 게임이니까 상관없지만, 규모가 큰 게임에서는 어떤 방법이 최적화에 도움이 되는지 궁금했다.

 

 

 

결론적으로 나는 Update문을 사용했다.

Trigger을 사용하는 방법이 더 간편하지만 다른 아이디어로 구현하였다.

 

업데이트를 매 프레임 돌리지 않고 시간 간격을 두고 Update문을 호출하여 연산량을 줄였다.

void Update()
{
    if (Time.time >= nextCheckTime)
    {
        CheckPlayerZPos();
        nextCheckTime = Time.time + checkInterval;
    }
}

 

nextCheckTime은 다음 검사 시점 변수이고checkInterval은 업데이트를 체크 할 간격 변수이다.

이 방법을 사용하지 않더라도  코루틴을 사용하여 Zpos를 확인해주는 것도 좋은 방법 같다!


무한 맵 만들기

Railway가 하나의 맵

 

파란색 부분에서 계속 새로운 맵 오브젝트를 Instantiate 해주지 않고

다음과 같이 두 개의 오브젝트만을 사용하여 무한맵을 구현했다.

 

MapManager.cs

public class MapManager : Singleton<MapManager>
{
    [SerializeField] private List<GameObject> map;

    private int index;

    private void Start()
    {
        index = 0;
    }

    public void RespawnMap()
    {
        map[index].transform.position = new Vector3(0, 0,(135 * (GameManager.Instance.Wave-1)));
        index = index == 0 ? 1 : 0;
    }
}

 

코드는 단순하다.

 map[index].transform.position = new Vector3(0, 0,(135 * (GameManager.Instance.Wave-1)));

 

이 코드에서 135는 전체 맵의 길이이다.

즉 맵을 생성하는 위치의 Z값은  현재 게임 웨이브에서 1 뺀 값을 전체 맵의 길이만큼 곱하면 된다.

 

이제 이 메서드를

if(posZ >= 80 && !hasRespawned)
{
    MapManager.Instance.RespawnMap();
    hasRespawned = true;
}

 

Run State 스크립트의 CheckPlayerZPos() 메서드 내에서 위와 같이 호출해주면 

무한맵이 된다!

 


로비 화면

로비도 구성해두었다!

 

 

 

 

사용한 에셋과 구현 사항들을 정리한 노션 링크!

 

Sparrow’s Adventure | Notion

주제 - 3D 방치형 RPG 게임

stitch-wallet-120.notion.site