유니티 배경 스크롤 - yuniti baegyeong seukeulol

참고자료: www.youtube.com/watch?v=Bi-IK4uTBTg&t=466s&ab_channel=%EA%B3%A0%EB%B0%95%EC%82%AC%EC%9D%98%EC%9C%A0%EB%8B%88%ED%8B%B0%EB%85%B8%ED%8A%B8

고박사님의 유니티 슈팅 2D게임 만들기 실습을 따라하다 종스크롤 맵 만들기 부분은 따로 글을 작성하고 요긴하게 써먹을려고 따로 저장해둔다.

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

public class PositionScroller : MonoBehaviour
{
    [SerializeField]
    private Transform target;
    [SerializeField]
    private float scrollRange = 9.9f;
    [SerializeField]
    private float moveSpeed = 3.0f;
    [SerializeField]
    private Vector3 moveDirection = Vector3.down;

    private void Update()
    {
        // Background move to moveDirection, speed = moveSpeed;
        transform.position += moveDirection * moveSpeed * Time.deltaTime;


        // 배경이 설정된 범위를 벗어나면 위치 재설정
        if(transform.position.y <= -scrollRange)
        {
            transform.position = target.position + Vector3.up * scrollRange;
        }    
    }
}

사용하는 Background 오브젝트는 2개이다. 2개를 사용하여 위 스크립트를 부착한다. 

유니티 배경 스크롤 - yuniti baegyeong seukeulol

위 그림처럼 카메라의 세로 길이는 9.9f에 해당한다. 각각 본인에 카메라 설정에 맞게 맵크기를 조절하고 두 오브젝트를 이어서 연결해준다. scrollRange에는 두번째 오브젝트에 y좌표를 넣어주면된다.

transform.position += moveDirection * moveSpeed * Time.deltaTime;

update 구문에서 위 코드에 의해 두 배경 오브젝트는 아래로 움직이게 된다.

유니티 배경 스크롤 - yuniti baegyeong seukeulol
// 배경이 설정된 범위를 벗어나면 위치 재설정
if(transform.position.y <= -scrollRange)
    { transform.position = target.position + Vector3.up * scrollRange; }

계속 아래로 내려가는 배경 오브젝트는 중심좌표가 -scrollRange(여기서는 (0, -9.9))보다 아래로 내려가게 되면 오브젝트는 target의 Transform(Background1의 경우 Background2가 연결되어 있음)의 y좌표에서 9.9만큼 더해진 좌표로 다시 리셋되어 내려가는 방식으로 player 오브젝트가 앞으로 진행되는 느낌을 주게 된다. 


유니티 배경 스크롤 - yuniti baegyeong seukeulol

1. 게임은 트릭

쿠키런을 비롯한 횡스크롤 달리기 게임들은 유독 '게임은 트릭'이라는 말이 자주 떠오르는 장르이다.

무한히 생성되는 오브젝트, 무릎 관절이 걱정되는 주인공 캐릭터, 끝도 없이 스크롤되며 멀미를 절로 유발하는 배경화면까지.

이 장르의 게임은 만들기도 쉽고, 플레이하기도 간단해서 튜토리얼에서 자주 사용되는 장르인 것 같다.

뭐, 아무튼. 이게 중요한 게 아니고.

지금 말하고자 하는 부분은 배경화면을 무한히 스크롤하게 만드는 부분에 대한 것이다.

그것도 Collider를 이용해서 말이지.

2. Collider의 사이즈를 가져올 수 있다.

유니티 3년 공부하면서 이건 첨 알았다...

GetComponent를 이용해 Collider를 가져온 뒤, .size를 붙여주면 된다.

repeatWidth = (GetComponent<BoxCollider>().size.x / 2);

위의 예시 코드처럼 작성하면, 사용자가 설정한 BoxCollider의 사이즈를 정확히 두 동강 내준다.
(코드를 보면 알겠지만 세로로 잘라준다.)

저게 왜 필요하냐? 그리고 왜 2를 넣어준 것이냐?

유니티 배경 스크롤 - yuniti baegyeong seukeulol

위의 사진을 보면, 배경화면 오브젝트가 2개의 동일한 이미지를 붙여놓은 형태를 띠고 있다.

위의 코드는 저 이미지를 기준으로 작성한 코드여서 2로 나눈 것.

만약 배경화면 오브젝트에 이미지 3개를 붙여둔 상태라면, 당연 3으로 나눠주면 된다.

기초적인(또는 노가다적인) 방법으로는 이미지 1개가 끝나는 지점을 손수 찾아내 그 좌표를 가지고 if문을 돌려야 한다.

그게 싫다면, 배경화면 오브젝트를 Prefab 형식으로 변환해 계속 생성해주던가...
(나는 처음에 이 방법을 사용했다.)

하지만 위의 예시 코드처럼 Collider의 사이즈를 나눠버리는 방식을 사용하면, 위의 두 가지 방법보다 더 간단하면서도 정확하게 배경화면을 반복시킬 수 있다.

유니티 Learn 튜토리얼 문서에서 이 방법 처음 알았을 때 나도 모르게 이마를 치고 있었지...

...역시 머리가 좋아야 해.


2D 게임을 진행하다보면 배경이 무한 스크롤되는 것을 종종 확인할 수 있다. 유니티에서는 다음 방법으로 배경 스크롤을 간단하게 구현할 수 있다.

유니티 배경 스크롤 - yuniti baegyeong seukeulol

배경으로 쓸 2D 소스들을 임포트해주었다. 총 7개의 배경이 간격을 두고 겹쳐보이게 한 후 한쪽 방향으로 흐르게 만들 것이다.

유니티 배경 스크롤 - yuniti baegyeong seukeulol

Material을 7개 생성하고 Shader는 Unlit/Transparent로 변경 후 임포트한 배경 Texture을 매핑해주었다. 그리고 씬 뷰에서 Quad를 7개 만들고 Materials에 각각 연결해 준 뒤 하늘 - 구름 - 산 - 돌 순으로 위치를 조정(Z값 이동)해주어 게임 뷰에서는 겹쳐보이도록 만들었다. 그리고 BGScroller라는 빈 오브젝트를 생성 후 7개의 배경을 자식화한 뒤 BGScroller 스크립트를 생성해주었다.

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

[System.Serializable]
public class BGScrollData
{
    public Renderer RenderForScroll;
    public float Speed;
    public float OffsetX;
}

public class BGScroller : MonoBehaviour
{
    [SerializeField]
    BGScrollData[] ScrollDatas;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        UpdateScroll();
    }
    void UpdateScroll()
    {
        for(int i = 0; i < ScrollDatas.Length; i++)
        {
            SetTextureOffset(ScrollDatas[i]);
        }
    }
    void SetTextureOffset(BGScrollData scrollData)
    {
        scrollData.OffsetX += (float)(scrollData.Speed) * Time.deltaTime;
        if (scrollData.OffsetX > 1)
            scrollData.OffsetX = scrollData.OffsetX%1.0f;

        Vector2 Offset = new Vector2(scrollData.OffsetX, 0);

        scrollData.RenderForScroll.material.SetTextureOffset("_MainTex", Offset);
    }
}
유니티 배경 스크롤 - yuniti baegyeong seukeulol

BGCScrollData라는 직렬화용 Serializable 클래스를 생성하고 프로그램에 사용할 변수들(Renderer/Speed/OffsetX)을 Public으로 선언해주었다. 그리고 스크립트 직렬화(SerializeField를 통해 외부에서는 접근하지 못하되 인스펙터에서는 접근 가능하도록 ScrollDatas 배열을 생성해주었다. 이 때문에 인스펙터 창에 Scroll Datas가 생겼고 7개의 Element를 생성할 수 있게 되었다. 2D 환경이기 때문에 Vector2에서 x값의 이동되도록, Ofloat형식으로 변환한 ScrollData의 속도에 매 프레임 간 이동(Time.deltaTime)을 곱하여 OffsetX 값을 계산해주었다.  

foreach문을 사용해도 되지만 연산량이 많아 효율이 떨어질 수 있다는 단점이 있어 for문을 사용해 background 소스 7개가 전부 UpdateScroll() 되도록 하였다. 약간의 시간차를 두고 각 배경들이 움직이도록 하기 위해 Public으로 선언된 Speed 값을 다음과 같이 설정해주었고, 실행하면 된다.

유니티 배경 스크롤 - yuniti baegyeong seukeulol