[Unity][2D-Game] Undead Survivor (4)

suhan0304·2023년 11월 4일
0
post-thumbnail

Review

저번 개발 과정에서 어떤 작업을 했는지 리뷰해보자.

  • 셀 애니메이션 작업을 했다.
  • 이동 방향을 바라보도록 Player를 FlipX를 수정해줬다.
  • Player0 의 기본적인 애니메이션을 설정하고 트랜지션을 설정했다.
  • Animator Override Controller를 사용해 Player1, 2에도 모션만 바꿔서 똑같은 형식의 애니메이터를 적용시켜 주었다.

강의영상(4) - 무한 맵 이동하기


개발

타일 그리기

맵을 만들기 위해 타일 형식으로 맵을 만들 것인데 타일 에셋을 만들어주기 위해 Project 창에서 +를 누른 후 2D > Tiles > Rule Tile 으로 타일 에셋을 생성해준다. 이 때 타일의 이름을 Ran Tile로 설정해준다.

  • Rule Tile : 인접한 타일에 따라 이미지가 정해지는 타일
    - Number of Tiling Rules : 타일을 채우는 규칙을 지정해줄 수 있는데 여기서는 Random 룰만 이용해서 타일을 채울 것이기 때문에 1로 설정한다.
    - 위 값을 1로 설정하면 Tiling Rules가 생기는데 이 때 output을 Random으로 선택후 Size를 보다 넉넉하게 10으로 설정한다. 하나씩만 넣으면 모든 종류가 비슷하게 나온다. 꽃 타일은 잔디 타일보다 더 적게 나와야 하므로 Size를 좀 많이 늘려 잔디 타일을 많이 넣고 꽃 타일을 적게 넣어서 타일 등장 확률을 조정한다.
    • Noise : 이 값을 조정해서 랜덤 값을 바꿀 수 있다. 같은 수치면 랜덤으로 설정해도 동일한 결과가 나올 수 있다.

위와 같이 Size를 10으로 설정하고 Sprite를 채워줬다.

인스펙터창의 오른쪽 위의 자물쇠를 잠구면 다른 스프라이트를 누르거나 오브젝트를 눌러도 인스펙터 창이 변하지 않고 그대로 유지된다.
인스펙터 창이 아니어도 다른 창에서도 동일하게 사용 가능하다.

완성된 타일 에셋을 팔레트에 드래그드랍하여 덮어씌운다.

Window > 2D > Tile Palette로 팔레트 창을 열어줄 수 있다.

하이어라키에서 2D Object > TileMap > Rectangular을 선택해서 생성해준다. 이후에 팔레트 아래에서 Default Brush를 Line Brush로 변경해주고 팔레트의 타일을 한 번 클릭하면 타일을 그릴 수 있게 마우스 모양이 변한다.

팔레트 상단의 아이콘이 붓으로 설정된 상태여야 한다.

그 상태에서 게임 씬에서 다음과 같이 플레이어를 중심으로 20x20 사각형을 그려준다. 다 그린 후에는 다시 Default Brush로 변경한다.

이후에 팔레트의 페인트 통 툴을 선택 + 잔디 타일을 선택한 다음 빈 공간을 클릭하면 모든 빈 공간이 선택한 타일로 채워진다.


재배치 이벤트 준비

플레이어가 멀어지면 플레이어의 진행 방향으로 타일을 재배치해서 맵이 끊기지 않도록, 계속해서 맵이 있도록 재배치 이벤트를 추가할 것이다. 이 때 재배치 이벤트를 구현하기 위해선 다음과 같은 과정이 필요하다.

  1. Tilemap 오브젝트에 Tilemap Collider 2D를 추가한다.
    • 한개의 타일마다 쪼개진 것을 확인할 수 있다.
  2. Tilemap 오브젝트에 Composite Collider 2D를 추가한다.
    • 자동으로 Rigidbody 2D가 생성된다.
  3. Tilemap Collider 2D의 Used Bt Composite을 체크해준다.
    • 타일마다 쪼개지던 것이 Tilemap 규모로 크게 하나로 합쳐진 것을 확인할 수 있다.
  4. Composite Collider 2D의 Is Trigger을 체크해준다.
    • Tilemap Collider 2D가 모든 권한을 Composite Collider 2D에게 위임했기 때문에 Composite Collider에서 Is Trigger을 체크해준다.
    • Is Trigger을 체크해야 Player와 충돌을 하지 않는다.
  5. Rigidbody에서 Body Type을 Static으로 설정해서 정적이게 변경해준다.
  6. 인스펙터에서 Tag > Add Tag 를 해서 Ground와 Area를 추가한다. 타일맵 오브젝트의 태그는 Ground로 설정한다.
  7. Player의 Area라는 새로운 자식 오브젝트를 추가해준다.
  8. Area 오브젝트에 Box Collider 2D를 넣어준 후 사이즈를 20 * 20으로, Is Trigger을 체크, 태그는 Area 태그로 변경해준다.


재배치 스크립트 준비

Repostion, GameManager이라는 이름으로 C# 스크립트를 하나씩 생성해준다.

타일맵 오브젝트에 Reposition 스크립트를 컴포넌트로 추가해준다. 이 때 타일맵이 플레이어 정보를 얻어와야하는데 플레이어의 정보같은 핵심 정보는 GameManager에 구현해서 다른 여러 스크립트에서 사용가능하도록 구현하는 것이 좋다.

게임매니저를 담당할 오브젝트를 GameManager 오브젝트를 추가한 후 GameManager 스크립트를 컴포넌트로 추가한다.

public Player player; 

다음과 같이 게임 매니저에 Player를 선언하면 인스펙터의 스크립트 컴포넌트 부분에 플레이어 변수에 Player 오브젝트를 드래그드랍해서 초기화하였다.

그 다음 다른 스크립트에서 게임 매니저를 접근할 수 있도록 메모리에 게임 매니저를 얹는다.

장면이 하나라서 싱글톤 로직을 사용하지는 않는다.

public static GameManager Instance;
void Awake()
{
	//static은 인스턴스에 나오지 않으므로 초기화 해줘야함
	Instance = this;
}

다음과 같이 정적 변수로 Gamamager를 선언하면 다른 스크립트의 클래스에서 부를 수 있다는 편리함이 있다.


재배치 로직

다음과 같이 Reposition 스크립트를 작성했다. 코드가 어떤 기능을 하고 왜 그렇게 작성했는지에 대해 주석을 작성해놓았다.

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

public class Reposition : MonoBehaviour
{
    //Trigger가 check된 collider에서 나갔을때 호출
    void OnTriggerExit2D(Collider2D collision)
    {
        if(!collision.CompareTag("Area")) //나간 Collision의 Tag가 Area가 아니면 바로 return
            return;


        //Player의 위치를 가져옴
        Vector3 playerPos = GameManager.Instance.player.transform.position;
        //내 위치를 가져옴
        Vector3 myPos = transform.position;
        float diffX = Mathf.Abs(playerPos.x - myPos.x); //x축 좌표 차이
        float diffY = Mathf.Abs(playerPos.y - myPos.y); //y축 좌표 차이

        //플레이어의 방향을 파악
        Vector3 playerDir = GameManager.Instance.player.inputVec;
        float dirX = playerDir.x < 0 ? -1 : 1; //player의 inputVec의 x가 음수이다? 진행방이 왼쪽(-1), 아닐경우 오른쪽(1)
        float dirY = playerDir.y < 0 ? -1 : 1; //player의 inputVec의 y가 음수이다? 진행방이 아래쪽(-1), 아닐경우 위쪽(1)

        //collsion이 어떤 태그이냐에 따라 동작이 다름
        //추후에 몬스터도 재배치를 해주기 위해 설정
        switch (transform.tag)
        {
            case "Ground": //collsion의 태그가 ground일 경우
                if ( Mathf.Abs(diffX - diffY) <= 0.1f) { // 모서리 부분에서 그라운드 이동이 정상적이지 않은 오류를 보완하기 위한 코드
                    transform.Translate(Vector3.right * dirX * 40);
                    transform.Translate(Vector3.up * dirY * 40);
                }
                else if (diffX > diffY) //두 오브젝트의 거리 차이에서 X축이 Y축보다 크면 맵을 수평이동
                {
                    //Translate 지정된 값 만큼 현재 위치에서 이동
                    transform.Translate(Vector3.right * dirX * 40);  //오른쪽 단위 벡터(1, 0, 0) * 방향(왼쪽 -1 , 오른쪽 1) * 크기(40) 
                                                                //크기가 40인 이유는 타일맵을 4개를 사용해서 2*2로 설정했기 때문
                }
                else if (diffX < diffY)  //두 오브젝트의 거리 차이에서 Y축이 X축보다 크면 맵을 수평이동
                {
                    //Translate 지정된 값 만큼 현재 위치에서 이동
                    transform.Translate(Vector3.up * dirY * 40); 
                }
                break;

            case "Enemy": //collsion의 태그가 Enemy일 경우

                break;
        }


    } 
}

타일맵 선택한 후 Ctrl+D를 사용해서 3개를 복사해준 후 2x2로 배치해준다.


Scene 창의 Snapping을 10으로 설정한 후 Ctrl + 기즈모 드래그를 하면 10씩 오브젝트가 이동한다.


카메라 설정

메인 카메라에 Pixel Perfect Camera 컴포넌트를 추가

  • Piel Per Unit은 스프라이트에서 사용하는 수치와 맞춘다.
    - 기존에 18로 설정했으므로 해당 수치도 18로 설정해준다.
  • Reference Resolution : 카메라 크기 계산을 위한 참고 해상도
    - x=270, y=150으로 설정했다. 모바일은 x와 y를 반대로 해서 세로로 길게 해상도가 참고 되도록하면 된다.

타일맵 크기, Area 크기, 재배치 거리는 카메라 크기와 비슷하게 설정한다.

패키지 매니저에서 Chinemachine 패키지를 설치해준다. 그런 다음 하이어라키에서 Chinemachine > Virtual Camera를 생성해준다. 이 Virtual Camera가 메인 카메라의 감독 역할을 한다고 생각하면 된다.

Virutal Camera 오브젝터 인스펙터의 Follow에 플레이어를 할당하면 플레이어를 따라다니면서 카메라가 움직인다.

  • 메인 카메라의 Chinemachine Brain의 Update Method를 FixedUpdate로 변경해준다.
    - 현재 프로젝트트 물리 이동을 통해서 움직이기 때문이다.

결과물

profile
Be Honest, Be Harder, Be Stronger

0개의 댓글