사이드뷰 게임답게  배경화면을 옆으로 스크롤되게 만들어 봅시다.

 

새로운 씬 만들기

Stage1과 마찬가지고 Prefab 폴더의 배경및 지면과 플레이어 캐릭터를 배치하고 가로방향으로 화면이 스크롤 될 수 있도록 화면의 두배 크기로 스테이지를 배치합니다. Stage1을 복사하거나 다른이름으로 변경하면 쉽게 만들 수 있습니다. 이름은 BaseScene으로 합니다.

설정시 UI가 있어 편집이 어려우면 하이라키의 숨김기능이나 인스펙터의 비활성화를 하면 됩니다.

먼저 블록과 구멍을 없애고 지면을 여섯 개 나열합니다. 목표지점은 화면 오른쪽 끝에 배치하고 Collider의 EditCollider를 재 설정해 Wall Object은 좌우끝에 DeadZone은 아래쪽에 길게 배치합니다. 

Ctrl-S로 저장합니다. 이름이 BaseScene이 아니면 이름도 바꿔줍니다. 게임에 직접사용되지는 않고 앞으로 새로운 씬을 만들때 템플릿으로 사용할겁니다.

 

카메라를 관리하는 스크립트 추가하기

게임을 하면 화면이 캐릭터에 따라가는 장면을 보셨을겁니다. 

가장 간단한 방법은 Main Camera를 player의 자식오브젝트로 만들면 됩니다. 

여기서는 스크립트를 짜서 구현해 보겠습니다.. CameraManager.cs를 만듭니다.

변수 : 카메라의 상하좌우 리미트를 정하는 변수 입니다.

실제 카메라의 위치를 캐릭터에 고정해주는 법은 캐릭터의 x,y좌표를 카메라에 복사해주면 됩니다.  여기에 조금더 처리를 추가해 줍니다. 

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

public class CameraManager : MonoBehaviour
{
    // Update is called once per frame
    void Update() {
        GameObject player = GameObject.FindGameObjectWithTag("Player"); // 플레이어 찾기
        if(player != null) {
            float x = player.transform.position.x;
            float y = player.transform.position.y;
            float z = transform.position.z;  // z값은 카메라 자신을 사용합니다.
            Vector3 v3 = new Vector3(x, y, z); // 카메라 위치의 Vector3 만들기
            transform.position = v3;
        }
}

Update() : 

FindGameObjectWithTag("Player")로 Player를 찾습니다. 매번 찾는 이유는 Player가 Null인지 체크해주기 위해서입니다.

하이라키의 player를 선택하고 인스펙터뷰에서 Tag를 Player로 선택해줍니다.

Player가 Null이 아니라면 Player위치를 위치제한검사및 스크롤 처리를 해줍니다. 처리를 마치면 (x,y,z)를 카메라의 위치로 갱신합니다.

Vector3 v3 = new Vector3(x, y, z); // 카메라 위치의 Vector3 만들기
            transform.position = v3;

CameraManager.cs
0.00MB

 

스크립트를 MainCamera에 끌어다 어태치합니다.

이제 스크립트의 RightLimit값을 알아보겠습니다. 플레이를 시키고 Scene뷰탭을 클릭해서 씬뷰모드로 변경합니다.

하이라키에서 MainCamera를 선택하고 이동툴을 이용해 X축으로 끌어 화면 오른쪽끝이 포함되게 이동시켜봅니다. 전 약 18정도가 나왔습니다. 이 값을 스크립트의 RightLimit값으로 설정합니다. 플레이를 멈추면 다시 원상태로 됩니다.

플레이후 카메라위치를 바꾼이유는 플레이를 정지하면 변경한값들을 다시 원래 상태로 돌려주기 때문입니다. 게임 개발시 상당히 유용합니다.

하이라키의 back(배경화면)을 끌어다 카메라에 붙여줍니다.

게임을 실행해보면 캐릭터가 중간쯤 오면 화면이 스크롤하기 시작해서  Goal이 보이는 오른쪽 끝이 보이면 스크롤을 멈춥니다.  이때 배경은 카메라를 따라 다니기 때문에 항상 멈춰 있는 것 처럼 보입니다.  잘 생각해보면 캐릭터>카메라>배경 3종류가 같이 움직이고 Ground만 스크롤 되는 것입니다. 좀더 생각해보면 실제 Ground가 스크롤 되는게 아니라 카메라가 오른쪽으로 움직일 뿐입니다. Ground가 스크롤 되는 시점의 씬뷰를 보면 카메라의 위치가 가운데로 이동해서 찍고 있죠.

그리고 신기한건 캐릭터가 움직이다가 화면 중간쯤 오면 스크롤이 시작됩니다. 이건 이동제한 로직 때문입니다.

// 양 끝에 이동 제한 적용
if (x < leftLimit) {
    x = leftLimit;
} else if (x > rightLimit)  {
    x = rightLimit;
}

게임 시작시 캐릭터의 위치는 저의 경우 -7.44입니다. leftLimit는0이므로 캐릭터의 위치가 0이 될때까지는 x는 0으로 고정되죠. 따라서 카메라의 위치도 0으로 고정되서 안움직이는 겁니다. RigitLimit는 아까 오른쪽 골이 보이는 위치로 지정해 주었으니 오른쪽 골이 보이면 RightLimit에 고정되는거죠. 

 

배경에 다중 스크롤 적용하기

현재 배경이 카메라에 고정되어 움직이지 않는것 처럼 보입니다. 그 위에 화면보다 조금 큰 배경 이미지를 표시하고 조금 느리게 스크롤 되도록 만들어 봅시다. 다중 스크롤 되면 게임 화면에 깊이감이 더해집니다.

하이라키에 빈게임오브젝트를 만들고 이름을 SubScreen으로 합니다. SubScreen의 Transform>Position의 X,Y,Z값은 0으로 리셋합니다. 프로젝트뷰에서 배경이미지 back2를 두번 끌어다 놓고 자식으로 만듭니다. back2중 하나를 옆으로 붙여 길게 만들어 줍니다. Back2 두개를 다 선택하고  Sprite Rendere의 Order in Layer를 1로 만들어 배경보다 위쪽으로 해줍니다.

CameraManager.cs 맨 밑에 subScreen 처리가 있습니다. x속도를 반으로 해서 효과를 줍니다. 또 하이라키의  SubScreen을 끌어다 MainCamera의 CameraManager스크립터 컴포넌트의 subScreen 참조에 연결해줍니다.

if (subScreen != null) {
    y = subScreen.transform.position.y;
    z = subScreen.transform.position.z;
    Vector3 v = new Vector3(x / 2.0f, y, z);
    subScreen.transform.position = v;
}

플레이 해보면 subScreen이 플레이어보다 느린 속도로 스크롤 되네요.

 

강제스크롤 게임오버 처리

게임의 긴박감을 주기 위해 자동으로 횡스크롤을 하게 만들겠습니다. 캐릭터의 위치를 참조하지 않습니다.

하이라키의 DeadZone과 Wall을 카메라의 자식으로 만듭니다. Wall의 오른쪽 충돌 위치를 화면의 오른쪽으로 바꿔줍니다. 화면 오른쪽으로 가면 캐릭터가 스크롤을 못하게 하기 위해서입니다. MainCamera의 CameraManager 스크립트 컴포넌트의 IsForceScrollX만 체크해 줍니다. 

그럼 스크립트의 다음 부분이 처리 되어 자동으로 횡스크롤 합니다.

// 가로 방향 동기
if (isForceScrollX)  {
    // 가로 강제 스크롤
    x = transform.position.x + (forceScrollSpeedX * Time.deltaTime);
}

오른쪽 화면 끝으로 이동하면 더이상 못 나가는건 스크립트가 아닌 아까 Wall의 Collider위치를 화면 오른쪽으로 배치해서 그럽니다.

동작을 확인하셨으면 File>SaveAs를 선 BaseForcedScrollStage라는 이름으로 씬을 저장합니다. 나중에 강제 스크롤 스테이지용 템플릿으로 사용합니다

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

public class CameraManager : MonoBehaviour
{
    public float leftLimit = 0.0f;      // 왼쪽 스크롤 제한
    public float rightLimit = 0.0f;     // 오른쪽 스크롤 제한 
    public float topLimit = 0.0f;       // 위 스크롤 제한 
    public float bottomLimit = 0.0f;    // 아래 스크롤 제한

    public GameObject subScreen;        // 서브 스크린

    public bool isForceScrollX = false;     // X 축 강제 스크롤 플래그
    public float forceScrollSpeedX = 0.5f;  // 1초간 움직일 X의 거리
    public bool isForceScrollY = false;     // Y 축 강제 스크롤 플래그
    public float forceScrollSpeedY = 0.5f;  // 1초간 움직일 Y의 거리

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

    // Update is called once per frame
    void Update() {
        GameObject player = GameObject.FindGameObjectWithTag("Player"); // 플레이어 찾기
        if(player != null) {
            // 카메라의 좌표 갱신
            float x = player.transform.position.x;
            float y = player.transform.position.y;
            float z = transform.position.z;
            // 가로 방향 동기
            if (isForceScrollX)  {
                // 가로 강제 스크롤
                x = transform.position.x + (forceScrollSpeedX * Time.deltaTime);
            }
            // 양 끝에 이동 제한 적용
            if (x < leftLimit) {
                x = leftLimit;
            } else if (x > rightLimit)  {
                x = rightLimit;
            }
            // 세로 방향 동기
            if (isForceScrollY) {
                // 세로 강제 스크롤
                y = transform.position.y + (forceScrollSpeedY * Time.deltaTime);
            }
            // 위 아래에 이동 제한 적용
            if (y < bottomLimit) {
                y = bottomLimit;
            }
            else if (y > topLimit){
                y = topLimit;
            }
            Vector3 v3 = new Vector3(x, y, z); // 카메라 위치의 Vector3 만들기
            transform.position = v3;

            // 서브 스크린 스크롤
            if (subScreen != null) {
                y = subScreen.transform.position.y;
                z = subScreen.transform.position.z;
                Vector3 v = new Vector3(x / 2.0f, y, z);
                subScreen.transform.position = v;
            }
        }
    }
}

 

+ Recent posts