어제 밀크티를 마셨더니 잠이 안와서 새벽 5시까지 개발을 했는데요,, 사실 다 개발했습니다. 우유 게임은 90퍼센트 완성되었고 서버와 통신과 리워드를 어떻게 할지 기획만 하면 됩니다. 그래서 블로그에 무슨 내용을 써야할까 고민을 하다가 게임의 간략한 아키텍처와 게임 일시정지를 위해 구현했던 Time.scaleTime에 대해서 다뤄보고자 합니다.
아키텍처
MilkDrop, MilkFill과 같은 스크립트는 서로를 호출하지 않고 MilkGameManager를 통해 호출합니다. 물론 MilkGameManager와 MilkUIManager는 싱글턴 패턴을 적용하여 인스턴스를 가지고 있습니다.
UI를 관리하는 매니저를 둔 이유는 MilkGameManager 부피가 커져서 관리하기 힘들어짐, 이벤트 함수 등록을 용이하게 하기 위해서 분리했습니다.
** 현재는 튜토리얼이 포함되어 있지만 모든 게임 시작 전 튜토리얼을 진행 할 예정이기 때문에, 분리 및 UI는 프리팹으로 만들어서 재사용 할 예정입니다.
게임 일시정지를 구현하는 방법
일시정지를 구현하려는 이유는 튜토리얼 때문입니다. 튜토리얼이 있으면 게임이 멈추고 확인 버튼을 누르면 게임이 시작됩니다. 이는 Time.TimeSacle로 구현해도 되고 게임 시작을 관리하는 스크립트에서 bool값으로 게임 상태를 판별하도록 로직을 수정해도 됩니다.
유니티 홈페이지의 설명을 보면, 슬로우 모션 효과, 빠르게 하는 효과, 정지하는 효과를 제공한다고 합니다.
timeScale이 1.0이면 real time으로 움직인다고 하니 이는 평상시 속도를 말하는 거 같습니다. 그리고 0이 되면 정지된다고
하네요.
위 내용을 읽다보면 궁금증이 듭니다.
"시간을 멈추면 UI도 호출이 안되는거 아냐?"
이럴때 공부를 해야하는게 유니티의 생명주기입니다.
유니티의 생명주기 중 Update(), FixedUpdate()가 있습니다. Update()는 Time.timeScale이 0이 되더라도, 즉 정지하더라도 호출됩니다. 하지만 FixedUpdate()는 Time.timeScale이 0이 되면 멈춥니다.
왜냐하면 Update()는 스크립트가 enabled 상태이면 매 프레임마다 호출됩니다.
FixedUpdate()는 Fixed Timesetp에 설정된 값에 따라 일정한 간격으로 호출됩니다. 그래서 물리 효과가 적용된 오브젝트를 적용할 때 사용됩니다. Update()는 호출이 불규칙해서 물리엔진의 충돌 검사가 유효하지 않을 수 있기 때문입니다.
결론적으로 Update()문에서 Time.timeScale 값을 변경해도 호출엔 문제가 없다!가 됩니다. 또한, Time.deltaTime으로도 제어가 가능합니다.
더 나아가서 코루틴이라는 함수가 적용됐을 때는 어떻게 될까요? 코루틴 또한 Time.timeScale에 의존합니다. 그렇기 때문에 0이 되면 코루틴이 멈춥니다. 만약 코루틴을 Tiem.timeScale에 의존하지 않게 하려면 return값을 변경해주면 됩니다.
저희는 yeid return new waitForSeconds();를 보통 사용하죠? 이는 Time.timeScale에 의존적입니다. 그러니 WaitForSecondsRealtime();을 사용하면 Time.timeScale 값에 상관없이 코루틴이 실행됩니다.
구현하기
그런데 저는 Time.timeScale을 사용하지 않았습니다. isPlay = true, false로 게임을 시작하고 정지했습니다.. 사실상 정지가 아니라 isPlay = true가 되기 전까진 게임시작하는 메서드를 실행하지 않는거지만요.. 그럼에도 불구하고 Time.timeScale에 대한 내용을 작성한 이유는 공부, 정보공유 용입니다!! 만약 Tiem.timeScale로 코드를 수정하게 된다면 다시 포스팅 하겠습니다.
구현 결과를 .gif로 첨부했습니다.
보시면 시작에 튜토리얼 -> 3, 2 ,1 시작! UI노출 -> 게임 시작입니다.
튜토리얼 UI는 프리팹을 언팩하여 사용했습니다.
먼저 UIManager에서 OnEnable에서 tutorial을 활성화 해줍니다. 그리고 다음 버튼에 이벤트를 등록합니다. 이벤트는 다음 버튼을 클릭하면 튜토리얼 UI가 사라지고 게임이 시작되는 함수를 담고있습니다.
OnEnable()에서 UI를 활성화하는 이유?
OnEnable()은 스크립트가 활성화될 때마다 호출되므로 UI 요소를 초기화하기 적합하기 때문입니다. 스크립트가 활성화될 때마다 이벤트를 등록하여 UI와의 상호작용을 설정할 수 있습니다. 또한 OnDisable()이 호출되면서 이벤트를 해지하는 방식으로 성능을 최적화 합니다. 그래서 일반적으로 OnEnable()에서 UI요소를 다룹니다.
다음 버튼을 누르면 GameManager에서 게임 상태를 변경하고 카운트다운 UI가 코루틴을 통해 호출됩니다.
UI는 UIManager에서 게임은 GameManager에서 관리하기 위해 다음과 같은 복잡한 이동경로가 나왔습니다.
다음 버튼을 누르면 UIManager에서 튜토리얼 비활성화 -> GamaManager에서 게임 시작하고 -> UIManager에 카운트다운 UI 시작
이것이 스파게티 코드인가 싶지만 각 클래스별로 관리해야 할 객체가 있기때문에 더 좋은 방안이 생각나지 않는다면 현상태로 유지할 거 같습니다.
마무리
개발하면서 이슈가 발생했습니다. 모두 해결했으나 며칠이 지나버리니 기억이 나질 않군요.. 깃허브 커밋 내역을 뒤적거리면서 작성할만한 내용이 있으면 작성해보겠습니다. 오늘부턴 빵을 만드는 로직을 개발 할 예정입니다. 레시피 부분에서 고민이 많지만 어릴적 저를 화나게 만들었던 `고향만두` 스타일을 따라갈 것 같습니다. 그럼 다음 개발일지에서 보아요!
'Project > 빵빵빵 타이쿤' 카테고리의 다른 글
[빵빵빵 타이쿤 개발일지] 유니티 Transform과 RectTransform 차이점 (0) | 2024.10.18 |
---|---|
[빵빵빵 타이쿤 개발일지] 좌우로 이동하는 무한 레일 제작. 근데 배경 스크롤링(background scrolling)을 곁들인... (1) | 2024.10.07 |
[빵빵빵 타이쿤 개발일지] OnTriggerEnter가 인식이 안돼요;; (1) | 2024.10.07 |
[빵빵빵 타이쿤 개발일지] 2D 게임에서 UI와 배경 오브젝트의 차이와 유니티 2D 해상도 맞추기 (2) | 2024.10.02 |