레트로의 유니티 게임 프로그래밍 에센스 - 8.1

Cosmos·2023년 4월 12일
0

학습 매체 : 책

책이름 : 레트로의 유니티 게임 프로그래밍 에센스

저자 : 이제민


본 내용은 해당 강의 내용을 공부하면서 정리한 글입니다.


  • 앞 장까지 게임에 필요한 개별 요소인 플레이어, 적, 적 생성기 등을 완성했다.

  • 이 장에서는 UI를 만들고, 게임의 규칙을 관리하고 게임오버 상태를 표현하는 게임 매니저를 만든다.

이 장에서 다루는 내용

1. 게임 매니저와 UI 만들기

2. 씬 관리자로 씬을 로드하는 방법

3. PlayerPrefs를 사용해 데이터를 저장하는 방법

4. 게임을 빌드하는 방법


8.1 바닥 회전

  • 바닥 회전을 진행하기 전에 프로젝트 창에 폴더를 만들어 에셋과 스크립트를 정돈하자.

  • Assets 폴더 내부에 또 다른 폴더를 만들고 에셋의 경로를 수정하는 것은 프로젝트와 씬에 영향을 미치지 않는다.

  • 따라서 안심하고 언제든지 개발 도중에 에셋의 경로를 바꾸고 정돈해도 된다.

  • 프로젝트 창에서 + > Folder 클릭 > 폴더명을 Scripts로 변경

  • 프로젝트 창에서 + > Folder 클릭 > 폴더명을 Materials로 변경

  • 프로젝트 창에서 + > Folder 클릭 > 폴더명을 Prefabs로 변경

  • 에셋을 종류별로 폴더에 정리하기

  • 이제 플레이어 아래의 바닥을 일정 속도로 회전시키는 기능을 구현할 것이다. 바닥 회전 기능을 스크립트로 구현하여 Level 게임 오브젝트에 적용해보자.


8.1.1 Rotator 스크립트 준비

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

public class Rotator : MonoBehaviour
{
    public float rotationSpeed = 60f;
    
    void Update()
    {
        transform.Rotate(0f, rotationSpeed, 0f);
    }
}
  • 선언된 변수 rotationSpeed는 게임 오브젝트가 1초에 Y축 기준으로 몇 도 회전할지 타나낸다. 초깃값으로 60을 할당했다.

  • 게임 오브젝트를 회전시키는 데 트랜스폼 컴포넌트에 내장된 Rotate( ) 메서드를 사용했다.

Rotate(float xAngle, float yAngle, float zAngle)
  • Transform 타입의 Rotate( ) 메서드는 입력값으로 X, Y, Z축에 대한 회전값을 받고, 현재 회전 상태에서 입력된 값만큼 상대적으로 더 회전한다.

  • transform.Rotate(0f, rotationSpeed, 0f);는 한 번에 Y축을 기준으로 자신의 게임 오브젝트를 rotationSpeed(60도)만큼 회전한다.

  • 이러면 매 프레임마다 60도 회전을 하게 된다.

  • 이것은 '1초에 60도'가 아니라 '한 번에 60도'를 회전하는 문제가 있는 코드이다.

  • 스크립트를 Level에 적용시키고 플레이 버튼을 눌러보자.

  • 비정상적으로 빠른 속도로 Level 게임 오브젝트가 회전한다. 이것은 우리가 의도한 동작이 아니다.

  • 작성한 코드의 문제는 transform.Rotate(0f, rotationSpeed, 0f);가 1초에 60도 회전하는 코드가 아니라 한 번(한 프레임)에 60도 회전한다는 거다.


8.1.2 속도와 시간 간격

  • Update( ) 메서드는 게임 화면(프레임)이 한 번 갱신될 때 1회 실행한다.

  • 따라서 Update( ) 메서드에서 초당 이동 속도나 회전 속도 등 시간과 관련된 수치를 다룰 때는 시간 간격도 고려해야 한다. 그렇지 않으면 컴퓨터 성능에 따라 같은 코드가 다르게 동작할 수 있다.

  • 이 문제와 관련된 흔한 현상으로 PC 게이머들이 '프레임 제한'을 해제했을 때 발생하는 문제가 있다.


프레임 제한 해제

  • 엑스박스, 플레이스테이션 같은 콘솔 게임기는 기기의 사양이 정확하게 정해져 있어서 PC보다 초당 프레임을 상대적으로 정확하게 추측할 수 있다.

  • 많은 콘솔 게임이 초당 30프레임이나 초당 60프레임으로 제한된 고정 프레임을 주로 사용한다.

  • 그런데 몇몇 콘솔 게임은 PC로 출시될 때 프레임 제한을 그대로 유지하는 경우가 있다. 그러면 고성능 PC라 해도 프레임 제한 이상으로 초당 프레임을 높일 수 없어 고사양 PC 게이머들은 불만이 생긴다.

  • 몇몇 PC 게이머들은 게임 파일을 수정하거나 해킹 프로그램을 사용하여 프레임 제한을 강제로 해제하기도 한다.

  • 여기서 일부 게임은 프레임 제한을 해제하면 게임 속 물체들의 속도가 몇 배 빨라지거나 물리 상호작용이 비현실적으로 변경되어 게임을 정상적으로 진행하기 힘들어지기도 한다.

  • 이것은 해당 게임이 고정 프레임을 기준으로 만들어졌기 때문이다.

  • 고정 프레임이 60인 게임을 가정해보자.

  • 어떤 게임 오브젝트가 1초 동안 1미터를 이동해야 한다면 Update( ) 메서드에서 한 번에 1/60 미터를 이동하도록 작성한다.

  • Update( ) 메서드가 1초에 60번 실행된다. 그래서 한 번에 1/60 미터를 이동하는 코드가 1초 동안 60번 누적 실행되면 총 1 미터를 이동한다.

1/60미터 X 60번 = 1미터

  • 여기서 강제로 프레임 제한을 해제하여 1초에 120번 게임 화면을 갱신한다고 가정해보자. 1초에 1미터 이동하던 게임 오브젝트가 1초에 2미터 이동한다.

1/60미터 X 120번 = 2미터


초당 프레임이 서로 다른 컴퓨터

  • 결론적으로 Update( ) 메서드 안에 작성된 코드는 초당 프레임이 다른 경우를 고려해야 한다.

  • 정답부터 이야기하자면 이 문제를 해결하는 방법은 '시간 간격으로 쪼개서 누적하는 것'이다.

  • 즉, 초당 프레임의 역수회전값에 곱하면 된다.

  • 1초에 코드가 실행되는 횟수 60에 역수를 취하면 1/60이다. 이렇게 구한 1/60은 직전 프레임과 현재 프레임 사이의 시간 간격이 된다.

  • 그리고 해당 값을 회전 속도인 60도에 곱한다. 그러면 1초 동안 원래 의도했던 초당 회전값이 나온다.

(60도 회전) X (1/60) X (60번 실행) = 총 60도 회전

  • 결론은 초당 프레임의 역수를 취한 값(프레임의 주기)을 곱하면 '한 번에 x만큼 이동'에서 '1초 동안 x만큼 이동'을 구현할 수 있다.

  • 7장 'Time.deltaTime'에서 이전 프레임과 현재 프레임 사이의 시간 간격은 Time.deltaTime으로 제공된다는 사실을 배웠다.

  • Time.deltaTime 값은 프레임의 주기이자 초당 프레임에 역수를 취한 값이다. Time.deltaTime을 사용하여 지금까지 설명한 해결 방법을 구현할 수 있다.


8.1.3 Rotator 스크립트 수정

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

public class Rotator : MonoBehaviour
{
    public float rotationSpeed = 60f;
    
    void Update()
    {
        transform.Rotate(0f, rotationSpeed * Time.deltaTime, 0f);
    }
}

다음 강의에서 계속~

profile
게임 개발을 목적으로 공부하고 있는 대학생입니다.

0개의 댓글