[Unity] 2D 게임 및 기타 팁

윤주원·2025년 6월 17일

유니티

목록 보기
2/5
post-thumbnail

기본 세팅

  • New Scene → Basic 2D
  • Main Camera 설정
    • orthgraphic : 거리에 상관없이 똑같이 보임.(2d 게임에서 좋음)
  • Tips : 움직이는 물체는 rigidbody와 collider는 넣는게 좋다.
  • RigidBody 설정

  • Discrete : 충돌 감지를 일정시간 끊어서 실행
    - 빠르지만 놓칠 수 있음
    - 매우 빠른 속도로 이동하다가 사이에 벽을 통과해서 지나간다면 충돌 감지가 안됨.
    • Continuous : 움직임 이전 위치와 다음 위치에서 예상되는 충돌가지 함께 감지
      • 계산량 : Discrete < Continuous
  • Animation과 Ainmator차이
    • Animation : 2d(Sprite)에서 함
    • Animator : 상태 전이 그래프를 볼 수 있으며 2d,3d 둘 다 사용

Sprite 애니메이션 만들기

  1. 애니메이션 sprite 구하고 자르기
  2. player 선택 후 animation창 들어가기
  3. create 후 sprite 넣고 samples 조정하기

유한 상태 머신

어떤 상황에서 어떤 Anim를 실행할지

한번에 하나의 상태가 되도록 해야됨

애니메이터 컨트롤러와 애니메이터

  • 애니메이터 컨트롤러는 애니메이션을 결정하는 상태도를 표현하는 에셋(지도)
    • Run을 만들면 Player 애니메어터 컨트롤러가 자동 생성
    • 애니메이터 컴포넌트도 자동 추가
  • 애니메이터는 컴포넌트(부품)

주요 상태 노드

  • Entry: 시작 애니메이션 상태
  • AnyState: 어떤 상태에서든 즉시 전이 가능 (ex. 피격, 죽음 등)
  • Exit: 상태 머신 종료 지점 (잘 쓰이지 않음)

Transition Duration

  • 3D 모델에서는 걷고 뛰는 동작 사이에 텀을 둬서 블렌딩으로 자연스럽게 함
  • but 2d Sprite에서는 중간 위치를 계산하는 관절이 없어서 못함
    • 0으로 설정한다.

Layer 할당

  • Layer 숫자가 클수록 앞에 렌더링됨

배경 돌리기

  • Awake는 Start보다 1 프레임 더 빠르게 진행됨.

    • 초기화 용도로 적합

    TextMeshPro

  • 기존의 text보다 더 활용도 높은 ui

  • 기존의 폰트를 사용하고 싶다면?

- Font Asset Creator하고 선택 후 Generate
  • 그림자 설정

  • 텍스트 크기에 맞게 상자 조절
    • 아래 컴포넌트 추가

싱글톤 패턴의 필요성

  • 역할 : 게임의 관리자(점수, UI, 게임 상태, 몬스터 플레이어 매니저 등)
  • 두가지 조건을 만족해야됨.
    1. 게임 내에서 단일 오브젝트만 존재해야됨.
    2. 어디서나 접근 가능(static)
  • GameManager.cs
    using UnityEngine;
    using UnityEngine.SceneManagement;
    using TMPro;
    
    // 게임 오버 상태를 표현하고, 게임 점수와 UI를 관리하는 게임 매니저
    // 씬에는 단 하나의 게임 매니저만 존재할 수 있다.
    public class GameManager : MonoBehaviour {
        public static GameManager instance; // 싱글톤을 할당할 전역 변수
    
        public bool isGameover = false; // 게임 오버 상태
        public TextMeshProUGUI scoreText; // 점수를 출력할 UI 텍스트
        public GameObject gameoverUI; // 게임 오버시 활성화 할 UI 게임 오브젝트
    
        private int score = 0; // 게임 점수
    
        // 게임 시작과 동시에 싱글톤을 구성
        void Awake() {
            // 싱글톤 변수 instance가 비어있는가?
            if (instance == null)
            {
                // instance가 비어있다면(null) 그곳에 자기 자신을 할당
                instance = this;
            }
            else
            {
                // instance에 이미 다른 GameManager 오브젝트가 할당되어 있는 경우
    
                // 씬에 두개 이상의 GameManager 오브젝트가 존재한다는 의미.
                // 싱글톤 오브젝트는 하나만 존재해야 하므로 자신의 게임 오브젝트를 파괴
                Debug.LogWarning("씬에 두개 이상의 게임 매니저가 존재합니다!");
                Destroy(gameObject);
            }
        }
    
        void Update() {
            // 게임 오버 상태에서 게임을 재시작할 수 있게 하는 처리
            if (isGameover && Input.GetMouseButtonDown(0))
            {
                // 현재 씬 가져옴
                SceneManager.LoadScene(SceneManager.GetActiveScene().name);
            }
        }
    
        // 점수를 증가시키는 메서드
        public void AddScore(int newScore) {
            if (!isGameover)
            {
                score += newScore;
                scoreText.text = "Score: " + score;
            }
        }
    
        // 플레이어 캐릭터가 사망시 게임 오버를 실행하는 메서드
        public void OnPlayerDead()
        {
            isGameover = true;
            gameoverUI.SetActive(true);
            
        }
    }
  • OnEnable() : 컴포넌트가 활성화될 때마다 실행.
  • 너무 많이 사용했을 때의 문제점!!!!
    • 코드끼리의 결합도가 높아져서 단위 테스트가 힘들어짐.

오브젝트 풀링(Object Pooling)

  • 필요한 만큼의 오브젝트를 미리 만들어 풀(웅덩이)에 쌓아두는 방식
    • 매번 Instantiate와 destroy를 하면 많은 계산이 발생.

    • 너무 자주 생성하거나 파괴하면 끊긴다.

      → 필요할 때 Pool에서 가져오고, 아니면 반납한다.

  • PlatformSpawner.cs
    using UnityEngine;
    
    // 발판을 생성하고 주기적으로 재배치하는 스크립트
    public class PlatformSpawner : MonoBehaviour {
        public GameObject platformPrefab; // 생성할 발판의 원본 프리팹
        public int count = 3; // 생성할 발판의 개수
    
        public float timeBetSpawnMin = 1.25f; // 다음 배치까지의 시간 간격 최솟값
        public float timeBetSpawnMax = 2.25f; // 다음 배치까지의 시간 간격 최댓값
        private float timeBetSpawn; // 다음 배치까지의 시간 간격
    
        public float yMin = -3.5f; // 배치할 위치의 최소 y값
        public float yMax = 1.5f; // 배치할 위치의 최대 y값
        private float xPos = 20f; // 배치할 위치의 x 값
    
        private GameObject[] platforms; // 미리 생성한 발판들
        private int currentIndex = 0; // 사용할 현재 순번의 발판
    
        private Vector2 poolPosition = new Vector2(0, -25); // 초반에 생성된 발판들을 화면 밖에 숨겨둘 위치
        private float lastSpawnTime; // 마지막 배치 시점
    
        void Start() {
            // 변수들을 초기화하고 사용할 발판들을 미리 생성
            platforms = new GameObject[count];
    
            for (int i = 0; i < count; i++)
            {
                platforms[i] = Instantiate(platformPrefab, poolPosition, Quaternion.identity);
            }
    
            lastSpawnTime = 0f;
            timeBetSpawn = 0;
        }
    
        void Update() {
            // 순서를 돌아가며 주기적으로 발판을 배치
            if (GameManager.instance.isGameover) return;
    
            if (Time.time >= lastSpawnTime + timeBetSpawn)
            {
                lastSpawnTime = Time.time;
                timeBetSpawn = Random.Range(timeBetSpawnMin, timeBetSpawnMax);
                
                float yPos = Random.Range(yMin, yMax);
                
                platforms[currentIndex].SetActive(false);
                platforms[currentIndex].SetActive(true);
                
                platforms[currentIndex].transform.position = new Vector2(xPos, yPos);
                currentIndex++;
    
                if (currentIndex >= count)
                {
                    currentIndex = 0;
                }
            }
        }
    }
profile
안녕하세요

0개의 댓글