카테고리 없음

2025.05.27 8주차 화요일

mochoa 2025. 5. 27. 22:09

주요 구현 내용 요약

1. Unity 시작 연출: Cinemachine + Timeline으로 카메라 투어 만들기

2. Unity 저장 시스템 SaveManager 통합 및 초기 데이터 처리

3. Unity에서 Tenkoku - Dynamic Sky를 활용한 게임 내 시간 시스템 구성

 

1. Unity 시작 연출: Cinemachine + Timeline으로 카메라 투어 만들기

 


목표

게임이 시작되기 전에 맵 전체를 보여주는 카메라 투어 연출을 만들어보자.
여러 포인트를 전환하면서 부드러운 시네마틱 효과를 주고,
Timeline이 끝난 뒤에는 플레이어 조작이 가능하도록 만들었다.


사용한 구성 요소

요소 설명
CinemachineVirtualCamera 가상 카메라. 각각 다른 시점을 제안
Main Camera 실제 렌더링용 카메라. CinemachineBrain 포함
Timeline 카메라 전환 연출 제어
PlayableDirector Timeline 실행 컨트롤
CameraDirector Timeline 실행용 빈 오브젝트
Player 플레이어 오브젝트 (초기 비활성화)
UI Root UI 전체 루트 오브젝트 (초기 비활성화)

 


구현 흐름

1. Virtual Camera 배치

  • GameObject > Cinemachine > Virtual Camera 를 통해 CM_vcam1 ~ CM_vcam3 생성
  • 각각의 위치는 미리 만들어 둔 CameraPath의 포인트와 일치시킴
  • 각 Virtual Camera의 LookAt은 LookTargets[i]로 설정

2. Timeline 생성

  • CameraDirector 오브젝트에 PlayableDirector 추가
  • Timeline 생성 후 Cinemachine Track 추가
  • Main Camera를 바인딩 후, CM_vcam들을 순서대로 배치 (Cinemachine Shot)
  • Shot 간 간격으로 Blend 시간 조절

3. Timeline 끝나면 플레이어 조작 전환

using UnityEngine;
using UnityEngine.Playables;

public class CameraIntroController : MonoBehaviour
{
    public PlayableDirector director;
    public GameObject player;
    public GameObject uiRoot;

    void Start()
    {
        player.SetActive(false);
        uiRoot.SetActive(false);

        director.stopped += OnFinished;
        director.Play();
    }

    void OnFinished(PlayableDirector d)
    {
        player.SetActive(true);
        uiRoot.SetActive(true);
    }
}

 

CameraDirector에 이 스크립트를 붙이고, 필요한 오브젝트 연결

 


문제 상황 & 해결

문제 1: Timeline이 끝나도 카메라가 계속 시네마틱 상태 유지

원인: 마지막 Virtual Camera가 계속 활성화되어 있었음
해결: Timeline의 마지막 CM_vcam에 Activation Track을 추가하여 Timeline 종료 시 비활성화 처리

 


배운 점

  • Cinemachine은 실제 카메라가 아니라 시점 제안자라는 개념
  • Timeline과 함께 쓰면 연출을 아주 쉽게 구성할 수 있음
  • Cinemachine Track과 Activation Track 조합이 매우 강력함
  • Timeline이 끝나면 반드시 카메라 제어권을 플레이어에 넘겨야 한다

전체 구조 정리

Main Camera (CinemachineBrain)
CameraDirector (PlayableDirector + CameraIntroController.cs)
CM_vcam1 ~ CM_vcam3 (Virtual Camera)
LookTargets (Look_0 ~ Look_3)
Player (초기 비활성화)
UI Root (초기 비활성화)

 


2. Unity 저장 시스템 SaveManager 통합 및 초기 데이터 처리


오늘 한 일

  • SaveManager를 실제 저장 구조(SaveData)와 일치하도록 수정
  • 임시 저장용 스크립트인 SaveTester를 자동 실행이 아닌 수동 호출 방식으로 변경
  • TitleUIManager에서 NewGame 버튼을 클릭했을 때만 SaveTester가 실행되도록 연결
  • 저장 경로(Application.persistentDataPath) 확인 및 save.json 생성 여부 디버깅
  • Unity Hierarchy 상에서 SaveTester 오브젝트는 비활성화 상태로 두고, 필요 시만 실행

주요 배운 점

SaveData 구조와 일치하지 않으면 저장 불가

  • JsonUtility.ToJson()은 [System.Serializable] 구조만 저장 가능
  • 클래스가 불일치할 경우 저장은 정상적으로 호출되어도 save.json이 생성되지 않음

Start() 자동 실행 대신, public 메서드로 분리하여 명시적 실행

public void CreateInitialSaveData()
{
    // SaveData 생성 및 SaveManager 저장 호출
}

 

TitleUIManager에서 수동으로 SaveTester 호출

saveTesterObject.SetActive(true);
saveTesterObject.GetComponent<SaveTester>().CreateInitialSaveData();
saveTesterObject.SetActive(false);

트러블슈팅

문제해결 방법
save.json이 안 생김 저장 클래스(MyGameData) → 실제 구조(SaveData)로 교체
SaveTester가 실행되자마자 저장됨 Start() 제거하고, public 메서드로 분리
탐색기에서 저장 파일이 안 보임 Application.persistentDataPath를 로그로 출력하고 직접 탐색기 경로 접근

참고 구조

SaveData 클래스 요약

public class SaveData {
    public int hp, gold, level, exp, stamina;
    public List<SaveItem> inventory;
    public SaveEquipment equipped;
    public List<string> completedQuests;
}

 

SaveManager 사용 예

SaveManager.Instance.SaveData(mySaveData);
var loaded = SaveManager.Instance.LoadData();

 


느낀점

Unity 저장 시스템에서 클래스 구조가 일치하지 않으면 저장 자체가 실패한다는 점을 명확히 알게 되었고,
Start()에 의존하지 않고 **게임 내 이벤트(NewGame 클릭)**로 저장 로직을 분리하는 게 더 유연하다는 걸 체감했다.
또한 Application.persistentDataPath를 이용한 경로 확인은 저장 디버깅 시 매우 유용했다.

 


3. Unity에서 Tenkoku - Dynamic Sky를 활용한 게임 내 시간 시스템 구성

오늘의 목표

  • 기존 GameClock 시스템을 유지하면서,
  • Tenkoku - Dynamic Sky 에셋을 연동해
  • 자연스러운 낮/밤 변화와 하늘 표현을 구현하고자 했다.

진행 내용

1. Tenkoku - Dynamic Sky 설치 및 기본 구성

  • Tenkoku DynamicSky 프리팹을 GameScene에 배치 (Position (0,0,0))
  • Main Camera에 다음 효과 추가:
    • Tenkoku Fog
    • Tenkoku Sun Shafts (선택)
  • Lighting 설정:
    • Skybox Material: 비워두거나 무시
    • Sun Source: Tenkoku Sun으로 지정
    • Ambient Source: Gradient 또는 Color 사용

2. GameClock 시스템과 Tenkoku 연동

// GameClock → Tenkoku 시간 연동
tenkokuModule.useAutoTime = false;
tenkokuModule.autoTimeSync = false;

tenkokuModule.currentHour = currentHour;
tenkokuModule.currentMinute = currentMinute;
tenkokuModule.currentSecond = calculatedSecond;

tenkokuModule.RecalculateTime();

 

  • GameClock은 매 1초마다 가상 시간 1분이 흐르도록 구성
  • OnTimeChanged 이벤트를 다시 추가하여 외부에서 수면 이벤트 등 감지 가능

3. SleepManager 개선

  • 새벽 2시가 되면 5초간 수면 처리 후 오전 6시로 시간 이동
  • OnTimeChanged 이벤트를 통해 반응하도록 구성

4. Skybox 관련 이해 확장

  • CubeMap Skybox는 시간/태양 변화 반영이 불가능
  • Tenkoku는 자체적으로 하늘을 렌더링하므로 Skybox 설정을 직접 하지 않음
  • 기존 SkyboxManager, DayNightCycle 시스템은 제거해도 무방

배운 점

  • Tenkoku는 Skybox가 아닌 자체 Sky Sphere 렌더링을 사용하므로 기존 Skybox 시스템과는 구조적으로 다름
  • GameClock을 수동으로 제어하면서도 Tenkoku에 시간 값을 직접 반영하면 매우 유연하게 시간 시스템을 만들 수 있음
  • Unity의 Flare Layer는 렌즈 플레어 전용이며, Tenkoku의 Sun Shafts와는 별개
  • OnTimeChanged 이벤트는 수면, 랜덤 이벤트 등 다양한 게임 시스템과 느슨하게 연결할 수 있는 확장 포인트가 됨

결과

  • 자연스럽게 하늘이 변하며 시간 흐름이 시각적으로 표현됨
  • 새벽 2시가 되면 자동으로 캐릭터가 잠에 들고, 아침 6시에 일어남
  • 하늘뿐만 아니라 빛, 색상, 분위기까지 함께 바뀌어 몰입도 향상