본문 바로가기

Unity 공부/Unity_2D

[Day 1] 2D 오브젝트 제작 | 플레이어 이동 구현 | 2D 셀 애니메이션 제작 | 무한 맵 이동

GoldMetal 유튜브의 [Undead Survivor]라는 프로젝트를 따라서 진행! --> 2D 플랫폼의 전반적인 이해와 Unity 사용법 숙달!

이전부터 하던 작업이 있었는데, 까먹고 Tistory에 올리지 못했다....오늘부터라도 차근차근 올려야지

 

ㅡㅡㅡㅡㅡㅡ이전까지 했던 작업ㅡㅡㅡㅡㅡㅡ

 

[2D 오브젝트 제작] --> Undead Survivor에서 직접 조종할 수 있는 플레이어블 캐릭터 만들기

  • Pixel Per Unit : 주 캐릭터의 실제 크기로 설정
  • Filter Mode : Point로 설정
  • Compression : None으로 설정
  • Sprite Mode : Multiple로 변경 후 스프라이트를 열어서 각 모션별로 자르기
  • sprite 하나를 Scene에 드래그&드랍 후 이름 변경
  • Add Component
     - Rigidbody 2D : 오브젝트의 물리적인 상호작용
     - (Shape) Collier 2D : 오브젝트의 충돌 면 관리(Shape 모양으로)
  • Sprite Renderer의 Order in Layer를 높게 설정
    (Order in Layer는 오브젝트가 겹쳐질 때, 어떤 오브젝트가 위로 가야하는지를 관리)

 

[플레이어 이동 구현] --> 플레이어블 캐릭터의 이동 구현

  • C# 스크립트 생성(Player)
    (생성이 완료될 때 까지 잠깐 기다린다. 생성 중에 스크립트를 열어버리면 오류가 난다!!!)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public Vector2 inputVec; // 벡터 변수 선언
    public float speed;

    Rigidbody2D rigid; // Rigidbody2D 변수 선언 <이전에 Add Component에서 봤었다>  


    void Awake() // 시작할 때 한번만 실행
    {
        rigid = GetComponent<Rigidbody2D>(); // 게임 오브젝트 변수는 Awake 함수에서 초기화
    }
       
    void Update() // 매 프레임마다 호출
    {
        inputVec.x = Input.GetAxisRaw("Horizontal"); // Input Manager의 x축 값 가져오기
        inputVec.y = Input.GetAxisRaw("Vertical"); // Input Manager의 y축 값 가져오기 
    }

    void FixedUpdate() // 물리 연산 프레임마다 호출 (물리 효과가 적용된 오브젝트를 조정할 때 사용)
    {
        Vector2 nextVec = inputVec.normalized * speed *Time.fixedDeltaTime; // 플레이어블 캐릭터가 다음 프레임에 움직여야 하는 방향과 속도를 지정
        rigid.MovePosition(rigid.position + nextVec); // 기존의 위치에 움직여야 하는 방향과 속도를 더함
    }
}

 

[2D 셀 애니메이션 제작] --> 캐릭터의 애니메이션 제작

  • 방향 바라보게 하기
    기존의 코드에 아래 코드 추가
    private void LateUpdate()
    {
        if (inputVec.x != 0) // inputVec.x 값이 0이 아니면 if문 실행
        {
            spriter.flipX = inputVec.x < 0; // inputVec.x 값이 음수이면(캐릭터가 왼쪽으로 이동하면) x축 대칭 전환 실행
        }
    }
  • sprite에서 죽을 때, 뛸 때, 서있을 때 등등 모션 별로 Player에 다중선택 드래그&드랍하여 애니메이션 만들기
  • Animator 화면 들어가기
        - Stand 모션을  Default State로 설정
        - Stand 모션 <--> Run 모션 Make Transition
        - Any State --> Dead 모션 Make Transition 
        - Parameters에 float : Speed, trigger : Dead 제작
        - Stand 모션 --> Run 모션 Transition Conditions에 Speed | Greater | 0.01  추가
        - Run 모션 --> Stand 모션 Transition Conditions에 Speed | Less | 0.01 추가
        - Any State --> Dead 모션 Transition Conditions에 Dead 설정
  • Project\Animations\Dead 모션 Loop Time 체크 해제
  • 애니메이션 코드 작성
    아래는 완성된 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public Vector2 inputVec; 
    public float speed; // 상수 변수 선언 (animator parameter에 상수 Speed가 있었으니 코드에도 만드는 것임)

    Rigidbody2D rigid;
    SpriteRenderer spriter; // Scene에 보이게 하는 SpriteRenderer 변수 선언
    Animator anim; // Animator 변수 선언

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        spriter = GetComponent<SpriteRenderer>(); // 게임 오브젝트 변수는 Awake 함수에서 초기화
        anim = GetComponent<Animator>(); // 게임 오브젝트 변수는 Awake 함수에서 초기화
    }

    void Update()
    {
        inputVec.x = Input.GetAxisRaw("Horizontal");
        inputVec.y = Input.GetAxisRaw("Vertical");
    }

    private void FixedUpdate()
    {
        Vector2 nextVec = inputVec.normalized * speed *Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);
    }

    private void LateUpdate()
    {
        anim.SetFloat("Speed", inputVec.magnitude); 
        // InputVec 상수값의 크기만 받아옴 --> condition에서 설정한 대로 0.01보다 크면 Run 모션 실행, 작으면 Stand 모션 실행

        if (inputVec.x != 0)
        {
            spriter.flipX = inputVec.x < 0;
        }
    }
}

 

[무한 맵 이동] --> 기존의 맵을 재사용하여 화면에 끊임없이 맵을 만들기

  • 메뉴 바 Window > 2D > Tile Palette로 팔레트 창 열기
  • Tile 폴더에서 우클릭 후 2D > Tiles > Rule Tile로 타일 에셋 생성
  • Number of Tiling Rules 값을 1로 변경
  • Tiling Rules\Output을 Random으로 변경
  • Tiling Rules\Size 10으로 변경(Size에 들어가는 타일은 비율대로 화면에 나타난다)
  • Tile sprite를 Size 빈 칸에 드래그&드랍
  • Hierachy에서 우클릭 후 2D Object > TileMap > Rectangular 선택 --> 이름 정해주기
  • Hierachy에서 타일맵 선택
        - Tile Palette\Default Brush를 Tile Palette\Line Brush 선택 후 타일맵 그리기(20x20)
  • Tilemap에 Add Component
        - Tilemap Colidar 2D --> Used By Composite 체크
        - Composite Collidar 2D(타일마다 나눠진 형태를 하나로 합쳐준다) --> Is Trigger 체크
        - 자동으로 생성되는 Rigidbody 2D --> Body Type을 Static 설정
  • Tag를 눌러 Add Tag
        - Ground
        - Area
  • Tilemap 태그를 Ground로 변경
  • Hierachy의 Player 우클릭 후 Create Empty --> 이름 Area로 변경
  • Area에 Add Component
        - Box Collidar 2D --> Is Trigger 체크
  • Area 태그를 Area로 변경
  • Codes 폴더에서 C# 스크립트 생성 --> 이름 Reposition로 설정
  • Codes 폴더에서 C# 스크립트 생성 --> 이름 GameManager로 설정
    (GameManager에 중요한 내용을 넣어 깔끔한 코딩)
  • Hierachy에서 Create Empty --> GameManager로 이름 변경
        - GameManager 스크립트 Hierachy\GameManager에 드래그&드랍
  • GameManager 스크립트 작성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager instance; // 정적 변수는 즉시 클래스에서 부를 수 있어 편리하다
    public Player player; // inspector 화면에 나타남 --> hierachy의 Player를 드래그&드랍

    private void Awake()
    {
        instance = this; // 인스턴스 변수를 자기자신 this로 초기화
    }
}
  • Reposition 스크립트 작성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Reposition : MonoBehaviour{
    private void OnTriggerExit2D(Collider2D collision){
        if (!collision.CompareTag("Area")){ // 오브젝트의 Tag가 Area가 아닐 경우, 실행 x
            return;
        }

        Vector3 playerPos = GameManager.instance.player.transform.position;
        Vector3 myPos = transform.position;
        float diffX = Mathf.Abs(playerPos.x - myPos.x);
        float diffY = Mathf.Abs(playerPos.y - myPos.y);

        Vector3 playerDir = GameManager.instance.player.inputVec;
        float dirX = playerDir.x < 0 ? -1 : 1;
        float dirY = playerDir.y < 0 ? -1 : 1;

        switch (transform.tag){ // 태그를 비교하는 함수
            case "Ground":
                if(diffX > diffY){ // 이동거리의 X축이 Y축보다 크면 타일을 X축으로 재활용
                    transform.Translate(Vector3.right * dirX * 40);
                }
                else if (diffX < diffY){ // 이동거리의 Y축이 X축보다 크면 타일을 Y축으로 재활용
                    transform.Translate(Vector3.up * dirY * 40);
                }
                break;
            case "Enemy":

                break;
        }
    }
}
  • 타일 맵 오브젝트를 Ctrl + D 로 3번 복사하여 Player를 중심으로 배치(40x40이 되도록)
  • Hierachy의 Main Camera에 Add Component
        - Pixel Perfect Camera
        - Assets Pixels Per Unit을 스프라이트에서 사용하는 수치와 맞추기
        - Reference Resolution(카메라 크기 계산을 위한 참고 해상도) 적당히 맞춰주기
        (타일맵 크기, Area 크기, 재배치 거리는 카메라 크기와 비슷하게!)
  • Window > Package Manager > Unity Registry > Cinemachine 설치
  • Hirachy에서 우클릭 후 Cinemachine > Virtual Camera 생성
    (Virtual Camera는 Main Camera의 카메라 감독이다)
  • Virtual Camera의 Follow에 Player 오브젝트 드래그&드랍
  • Cinemachine Brain의 Update를 FixedUpdate로 변경
  • Tilemap Renderer의 Order in Layer 낮추기

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

★Slow and Steady 

'Unity 공부 > Unity_2D' 카테고리의 다른 글

[Day 5] 회전하는 근접무기 구현  (0) 2023.07.11
[Day 4] 소환 레벨 적용  (2) 2023.07.10
[Day 3] 오브젝트 풀링으로 소환  (0) 2023.07.09
[Day 2] 몬스터 제작  (0) 2023.07.08
Project_Moon 입사를 꿈꾸며....  (0) 2023.06.23