게임 개발/유니티(Unity)

[Unity] 2D 캐릭터 위치 변경

마달랭 2024. 12. 27. 20:41
반응형

개요

게임을 플레이 하다 보면 플레이어가 특정 지역에 도달했을 경우 혹은 도달 후에 특정 키를 통해 인터렉션 시 다른 맵으로 이동하는 로직이 존재한다.

이는 특정 오브젝트 간 충돌 시 트리거를 발생시키는 Is Trigger 기능을 통해 구현할 수 있다.

 

예를 들어 Player에 물리 적용이 적용되어 있으며 충돌 반경을 정해놓은 상태에서

충돌 반경 및 Is Trigger 기능이 활성화된 특정 오브젝트에 접근 시 이벤트를 발생시킬 수 있다.

 

player가 특정 타일에 접근 시 player의 좌표를 특정 위치로 이동시키는 기능을 구현해 보자

 

 

준비

2D 맵 상에서 특정 지점에 도달 시 player를 다음 레벨로 이동시키고, 이전 레벨로 이동하는 기능을 구현한다.

 

 

다음과 같은 레벨이 있을 때 이전 레벨에서 현재 레벨로 이동 시 player는 노란색 타일로 이동하게 된다.

마찬가지로 청록색 타일에 player가 접근한다면 다음 레벨의 노란색 타일로 이동하게 된다.

만약 연두색 타일에 player가 접근한다면 현재 레벨에서 이전 레벨의 주황색 타일로 이동하게 된다.

 

이를 구현하기 위해선 총 4개의 게임 오브젝트가 필요하다.

 

오브젝트의 이름에서 알 수 있듯

PlayerSpawn_Entry는 하위 레벨에서 현재 레벨로 진입 시 플레이어가 소환되는 위치이다.

LevelTransition_Previous는 이전 레벨로 이동하기 위한 위치, Next는 다음 레벨로 이동하기 위한 위치이다.

PlayerSpwan_Exit은 당연하게도 다음 레벨에서 현재 레벨로 내려올 경우 플레이어가 소환되는 위치이다.

 

네 가지 오브젝트 모두 Collider 2D 컴포넌트를 추가해 준 뒤 Is Trigger에 체크를 해준다.

 

이제 기본적인 준비는 끝이 났다.

 

 

스크립트 작성

스크립트 작성 후 4개의 오브젝트 각각 스크립트를 할당하여 이동하기 위한 오브젝트를 등록해 주는 방법이 있다.

하지만 레벨이 무수히 많을 경우 각 레벨에 대한 오브젝트를 매번 할당해 주기는 힘들다.

따라서 우리는 작업자가 항상 편할 수 있는 방법을 고안해야 한다.

 

[Unity] 오브젝트 네이밍 자동화

 

[Unity] 오브젝트 네이밍 자동화

개요유니티에서 오브젝트를 관리할 때 이전에 사용했던 오브젝트를 복제하여 재활용 하는일이 많다.예를 들어 유사한 구조로 이루어 진 레벨을 관리할 때 Level1 오브젝트를 복사하여 Level2 오브

zzzz955.tistory.com

 

위 글에서 레벨 오브젝트 복사 시 child 오브젝트에 대한 네이밍을 자동으로 하는 에디터 기능을 구현했었다.

이를 활용한다면 레벨간 이동 기능을 스크립트 단 한개로, 그것도 매우 짧게 구현할 수 있게 된다.

 

using UnityEngine;

public class PlayerTeleport : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D other)
{
    // LevelTransition_Next에 닿았을 경우
    if (other.gameObject.name.Contains("LevelTransition_Next"))
    {
        Debug.Log($"{other.gameObject.name}에 닿았습니다.");
        TeleportToSpawn("PlayerSpawn_Entry", 1, other.gameObject.name);
    }
    // LevelTransition_Previous에 닿았을 경우
    else if (other.gameObject.name.Contains("LevelTransition_Previous"))
    {
        Debug.Log($"{other.gameObject.name}에 닿았습니다.");
        TeleportToSpawn("PlayerSpawn_Exit", -1, other.gameObject.name);
    }
}


    // 플레이어를 해당 인덱스의 스폰 포인트로 이동
    void TeleportToSpawn(string spawnType, int offset, string transitionName)
    {
        // 현재 트리거의 인덱스를 추출
        int currentIndex = ExtractIndex(transitionName);

        // 이동할 타겟 인덱스 설정
        int targetIndex = currentIndex + offset;

        // 이동할 스폰 포인트 이름 구성
        string targetSpawnName = $"{spawnType}_{targetIndex}";

        // 타겟 스폰 포인트 찾기
        GameObject targetSpawn = GameObject.Find(targetSpawnName);

        // 타겟 위치로 플레이어 이동
        if (targetSpawn != null)
        {
            transform.position = targetSpawn.transform.position;
            Debug.Log($"{targetSpawnName}로 이동!");
        }
        else
        {
            Debug.LogWarning($"{targetSpawnName}를 찾을 수 없습니다.");
        }
    }

    // 오브젝트 이름에서 숫자 인덱스를 추출하는 메서드
    int ExtractIndex(string name)
    {
        // 이름에서 마지막 "_" 이후의 숫자 부분 추출
        int index = 1;  // 기본값
        string[] splitName = name.Split('_');

        if (splitName.Length > 1 && int.TryParse(splitName[splitName.Length - 1], out int result))
        {
            index = result;
        }
        return index;
    }
}

 

각 레벨의 child 오브젝트 명에는 모두 규칙이 있다.

Level이 1이라면 모든 child 오브젝트의 이름 뒤에는 언더바 이후 레벨 인덱스가 붙게 설정했었다.

이를 활용하여 플레이어가 특정 인덱스 오브젝트에 접근하여 Trigger가 작동한다면 이전 또는 이후의 오브젝트로 이동하게 끔 구현하기가 매우 쉽다.

 

우선 이벤트 발생 조건은 OnTriggerEnter2D 이벤트로 구현할 수 있다.

player가 특정 오브젝트가 위치한 타일에 접근한다면 해당 이벤트가 자동으로 실행된다.

만약 오브젝트 이름이 LevelTransition_Next인 오브젝트에 접근했다면 TeleportToSpawn 메서드를 실행하게 된다.

마찬가지로 PlayerSpawn_Exit에 접근했다면 TeleportToSpawn 메서드를 실행하게 된다.

 

두 케이스의 다른 점은 두번째로 전달하는 매개변수가 1, -1인 점을 확인할 수 있다.

눈치가 빠른 사람들은 알아챘겠지만 이로써 다음 레벨 혹은 이전 레벨로 이동하는 기능을 구현할 수 있다.

 

우선 TeleportToSpawn 함수를 확인해 보자, 매개변수로 spawnType, offset, transitionName를 받고 있다.

spawnType은 플레이어가 이동할 오브젝트의 이름이다.

따라서 해당 함수를 호출할 때는 LevelTransition_Next에 접근했다면 PlayerSpawn_Entry를, LevelTransition_Previous에 접근했다면 PlayerSpawn_Exit를 매개변수로 전달해 주어야 한다.

마찬가지로 두번째 인자는 다음 레벨로 이동이라면 1을, 이전 레벨로 이동이라면 -1이다.

세번째 인자는 모두 동일하다, 이는 아래서 설명할 ExtractIndex함수를 통해 현재 레벨 인덱스를 파싱하기 위해 쓰인다.

 

함수 호출과 동시에 ExtractIndex함수에  transitionName를 매개변수로 전달하여 currentIndex에 현재 레벨을 받아준다.

ExtractIndex함수는 현재 오브젝트 이름을 name이라는 매개변수로 전달받는다.

index변수를 기본값이 1인 상태로 초기화를 해준다.

이후 splitName이라는 문자열 배열에 Split메서드를 통해 name에서  '_'를 기준으로 문자열을 분할해 준다.

만약 splitName의 크기가 1보다 크면, splitName의 마지막 인자를 정수형으로 변환 후 index에 저장해 준다.

splitName의 크기가 1보다 작거나 같으면 name이 제대로 되어있지 않은 경우이므로 예외를 막는 용도이다.

 

예를 들어보자, 만약 player가 LevelTransition_Next_1에 접근하였다면, OnTriggerEnter2D 이벤트가 발생하여 첫번째 if문에 걸리게 되어 TeleportToSpawn함수가 실행된다, 따라서 ExtractIndex함수에 LevelTransition_Next_1라는 문자열이 매개변수로 전달된다.

LevelTransition_Next_1를 _를 기준으로 Split을 한다면 splitName엔 ["LevelTransition", "Next", "1"]과 같이 데이터가 들어가 있는 상태일 것이다.

이 배열의 길이는 3으로 1보다 크다, 또한 마지막 요소는 "1"로 이를 정수형으로 변환하면 1이 된다.

따라서 result는 1이 되며 이를 index에 저장하면 index는 1이 된다, ExtractIndex함수는 1을 리턴하게 된다.

 

이제 다시 TeleportToSpawn함수로 돌아와 currentIndex + offset을 하게 되어 1 + 1 = 2, 2라는 값을 변수 targetIndex에 전달한다.

문자열 targetSpawnName엔 "PlayerSpawn_Entry" + "_" + "2"의 값이 저장된다.

따라서 targetSpawnName는 "PlayerSpawn_Entry_2"가 된다.

게임 오브젝트 중 PlayerSpawn_Entry_2의 이름을 갖는 오브젝트를 찾아 targetSpawn에 전달해 준다.

만약 targetSpawn가 존재한다면 player를 targetSpawn으로 이동하게 된다.

 

이 로직은 굉장히 직관적이고 쉬우면서 Level이 아무리 많아져도 추가적인 작업을 해줄 필요가 없다.

 

 

테스트

 

성공적으로 player가 청록색 타일에 접근하니 다음 레벨의 시작 포인트로 이동한 것을 확인할 수 있다.

 

728x90
반응형

'게임 개발 > 유니티(Unity)' 카테고리의 다른 글

[Unity] 오브젝트 네이밍 자동화  (0) 2024.12.27
[Unity] 2D 타일 충돌 처리  (0) 2024.12.26
[Unity] 2D 캐릭터 이동  (0) 2024.12.26
[Unity] 2D 타일맵 배치  (1) 2024.12.26