2D RPG – 6. 맵 이동하기

맵 이동을 해보겠습니다.
우선 Tile 프로그램으로 다른형태의 맵을 하나 제작합니다.

1.Scene 추가

[File] -> [New Scene]에서 새로운 Scene을 만들고 이름을 지정합니다.
만들어진 Scene 안에 새로만든 맵을 추가합니다.
Scene 저장하고, 카메라 객체는 삭제합니다.

2.Script 추가

기존의 Scene으로 돌아와서 스크립트 파일을 추가합니다.
추가로 빈 오브젝트를 추가하고 맵이동이 일어날 지점을 만들어 줍니다.
이 빈 오브젝트에 새로 만든 스크립트와 박스 콜라이더 컴포넌트를 추가합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; //Scene 매니저 라이브러리 추가

public class TransferMap : MonoBehaviour
{
    public string transferMapName; // 이동할 맵이름

    // Start is called before the first frame update
    void Start()
    {

    }

    // 박스 콜라이더에 닿는 순간 이벤트 발생
    private void OnTriggerEnter2D(Collider2D collision)
    {

        if (collision.gameObject.name == "Player")
        {
            SceneManager.LoadScene(transferMapName);
        }
    }    
}

스크립트를 작성한 뒤 빈 오브젝트의 [Is Trigger] 옵션이 체크되어야 제대로 작동합니다.
스크립트에 선언된 변수인 transferMapName도 새로운 Scene 이름으로 해줍니다.
그리고 캐릭터 객체의 이름도 “Player”로 변경해 줍니다.

이제 게임을 실행해서 포인터로 이동하지만 맵이동이 작동하지 않고 오류가 납니다.
이유는 새로운 Scene을 빌드 리스트에 등록하지 않았기 때문입니다.
[File] -> [Build Settings]에서 새로 만든 Scene을 추가합니다.
이후 실행해보면 맵이동은 정상적으로 작동하지만 캐릭터와 카메라 객체가 사라집니다.

3.객체의 유지

객체를 유지하기 위해서는 객체 파괴 소스를 넣어주어야 합니다.
캐릭터에 들어가 있는 스크립트 파일을 열고 아래와 같이 구문을 추가합니다.

    void Start()
    {
        DontDestroyOnLoad(this.gameObject); // 게임 오브젝트 파괴금지

        // 애니메이터 컴포넌트 가져오기
        boxCollider = GetComponent<BoxCollider2D>();
        animator = GetComponent<Animator>();
    }

추가로 카메라도 같이 이동해야 하므로 카메라 스크립트 파일에도 추가합니다.

    void Start()
    {
        DontDestroyOnLoad(this.gameObject); // 게임 오브젝트 파괴금지
    }

게임을 실행해보면 [DontDestoryOnLoad] 그룹이 추가되고 해당되는 객체가 들어가 있는걸 확인할 수 있습니다.

4.원하는 위치에서 시작하기

이제 이동한 맵에서 원하는 위치에서 시작하도록 해보겠습니다.
이동할 맵을 열어 빈 객체를 만들고, 시작할 곳에 위치시킵니다.
새로운 스크립트 파일도 추가합니다.

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

public class StartPoint : MonoBehaviour
{
    public string startPoint; // 이동되어온 맵이름을 체크하기 위한 변수
    private MovingObject thePlayer; // 캐릭터 객체 가져오기 위한 변수
    private CamaraManager theCamera; // 자연스러운 카메라 이동을 위해 가져온 카메라 변수

    // Start is called before the first frame update
    void Start()
    {
        theCamera = FindObjectOfType<CamaraManager>(); // 카메라 변수에 카메라 객체를 할당
        thePlayer = FindObjectOfType<MovingObject>(); // 캐릭터 변수에 현재 캐릭터 객체를 할당


        if(startPoint == thePlayer.currentMapName)
        {
            // 카메라 이동
            theCamera.transform.position = new Vector3(this.transform.position.x, this.transform.position.y, theCamera.transform.position.z);
            // 캐릭터 이동
            thePlayer.transform.position = this.transform.position;
        }
    }

    // Update is called once per frame
    void Update()
    {

    }
}

[FindObjectOfType] 메소드가 쓰였는데 이는 [GetComponent]와는 달리 Hierachy에 존재하는 모든 객체에 접근할 수 있습니다.

스크립트 파일에서 선언한 Start Point 변수에는 이동할 맵(Scene) 이름을 넣어줍니다.

Moving Object.cs

캐럭터 객체에 추가된 스크립트에도 currentMapName 변수를 추가해줍니다.
이 변수는

    public string currentMapName; // 이동할 맵 이름 저장

    // BoxCollider 컴포넌트를 가져오기 위해 선언
    private BoxCollider2D boxCollider;

    // 통과불가능한 레이어를 설정해주기 위해 선언
    public LayerMask layerMask;

    public float speed; // 움직이는 속도 정의
    private Vector3 vector; // 움직이는 방향 정의

    public float runSpeed; // Shift키 입력시 증가하는 속도
    private float applyRunSpeed; // Shift키 입력시 연산되는 증가 속도
    private bool applyRunFlag = false; // Shift키 입력여부

    public int walkCount; // 방향키 입력시 이동값을 정하기 위한 값
    private int currentWalkCount; // 이동값 리셋을 위한 값

    private bool canMove = true; // 방향키 이동 반복실행 방지를 위한 값

    private Animator animator;

TransferMap.cs

캐릭터의 이동시에 충돌을 체크하는 스크립트에도 캐릭터 객체를 가져와서 currentMapName을 할당해 줍니다.

public class TransferMap : MonoBehaviour
{
    public string transferMapName; //이동할 맵이름       

    private MovingObject thePlayer;

    // Start is called before the first frame update
    void Start()
    {
        thePlayer = FindObjectOfType<MovingObject>();
    }

    // 박스 콜라이더에 닿는 순간 이벤트 발생
    private void OnTriggerEnter2D(Collider2D collision)
    {

        if (collision.gameObject.name == "Player")
        {
            thePlayer.currentMapName = transferMapName;
            SceneManager.LoadScene(transferMapName);
        }
    }    
}

1.캐릭터에 빈 변수 하나 생성 (변수 currentMap)
2.이동지역 객체에 변수를 만들고, 이동할 맵 이름으로 할당 (변수 transferMap)
3.캐릭터가 이동지역에 충돌할 시 변수 currentMap에 변수 transferMap 값을 할당
4.새로운 맵에 시작지점을 만들고 변수 하나 생성 후 맵 이름으로 할당 (변수 startPoint)
5.1번,4번에서 선언한 변수의 값을 비교 (currentMap == startPoint)
6.변수가 같을시에 카메라와 캐릭터 이동

5.맵간의 서로 이동

맵간의 서로 이동처리를 위해 기존 맵 두곳의 포인트를 만들어줍니다.

기존맵에는 시작할 포인트를 만들고 StartPoint 스크립트 추가 및 변수 할당
이동맵에는 이동할 포인트를 만들고 Transfermap 스크립트 추가 및 변수 할당

이 상태로만 만들고 게임을 실행해보면 처음 맵이동시에는 문제가 없지만 기존맵으로 넘어올때 캐릭터가 +1되서 나타나 집니다.
기존에 있던 캐릭터 객체가 파괴되지 않고 남아있기 때문입니다.
이를 수정하기 위해 스크립트를 수정하겠습니다.

캐릭터 스크립트 수정

    static public MovingObject instance; // instance의 값을 공유

    public string currentMapName; // 이동할 맵 이름 저장

    // BoxCollider 컴포넌트를 가져오기 위해 선언
    private BoxCollider2D boxCollider;

    // 통과불가능한 레이어를 설정해주기 위해 선언
    public LayerMask layerMask;

    public float speed; // 움직이는 속도 정의
    private Vector3 vector; // 움직이는 방향 정의

    public float runSpeed; // Shift키 입력시 증가하는 속도
    private float applyRunSpeed; // Shift키 입력시 연산되는 증가 속도
    private bool applyRunFlag = false; // Shift키 입력여부

    public int walkCount; // 방향키 입력시 이동값을 정하기 위한 값
    private int currentWalkCount; // 이동값 리셋을 위한 값

    private bool canMove = true; // 방향키 이동 반복실행 방지를 위한 값

    private Animator animator;


    // Start is called before the first frame update
    void Start()
    {
        if (instance == null)
        {
            DontDestroyOnLoad(this.gameObject); // 게임 오브젝트 파괴금지

            // 애니메이터 컴포넌트 가져오기
            boxCollider = GetComponent<BoxCollider2D>();
            animator = GetComponent<Animator>();
            instance = this;
        } else
        {
            Destroy(this.gameObject);

static으로 선언된 instance 변수는 스크립트가 적용된 모든 객체들과 공유합니다.

instance가 처음 실행시 값이 this로 할당됩니다.
맵이동으로 해당 객체가 또 생성될 경우, 이밎 this값을 가지고 있으므로 객체는 파괴됩니다

6.같은 Scene내의 맵이동

Scene 전환이 아닌 같은 Scene내에서 맵이동을 해보겠습니다.
기존의 맵에 타일을 추가합니다.
추가한 타일에 캐릭터가 이동할 위치에 빈 객체를 만들어 줍니다.
이동을 담당했던 스크립트 파일을 수정합니다.

TrasnferMap.cs

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

public class TransferMap : MonoBehaviour
{
    public string transferMapName; //이동할 맵이름       
    public Transform target; // 이동할 타겟 설정

    private MovingObject thePlayer; 
    private CamaraManager theCamera;

    // Start is called before the first frame update
    void Start()
    {
        thePlayer = FindObjectOfType<MovingObject>();
        theCamera = FindObjectOfType<CamaraManager>();
    }

    // 박스 콜라이더에 닿는 순간 이벤트 발생
    private void OnTriggerEnter2D(Collider2D collision)
    {

        if (collision.gameObject.name == "Player")
        {
            thePlayer.currentMapName = transferMapName;
            //SceneManager.LoadScene(transferMapName);
            theCamera.transform.position = new Vector3(target.transform.position.x, target.transform.position.y, theCamera.transform.position.z);
            thePlayer.transform.position = target.transform.position;

        }
    }


}

이동할 위치 지정을 위한 target 변수 선언과 캐릭터와 카메라를 가져오는 변수도 추가합니다.
스크립트가 임포트된 객체의 target 변수의 위치에 이동할 위치 객체를 드래그하여 설정합니다.

완성화면