개발 기간: 2026.08.10 ~ 2026.08.14
담당 역할: 공통 UI 통합, Pause 상태창, Restart 및 실제 플레이 씬 적용
지난 5주 동안에는 HUD와 Pause 화면을 각각 만들고, 필요한 기능을 하나씩 추가하는 작업을 진행했다.
Heat와 Core를 화면에 표시하는 것부터 시작해 HP, Skill, 사망 연출, Pause 화면, 볼륨 설정까지 조금씩 기능을 확장해 왔다.
그리고 마지막 6주차에는 지금까지 만든 UI를 게임 전반에서 공통으로 사용할 수 있는 하나의 인터페이스로 정리하고 통합하는 작업 을 진행했다.
이번 주의 주요 목표는 다음과 같았다.
- Heat UI의 형태와 Shader 최종 조정
- Core별 Skill 아이콘 리메이크 및 데이터 연결
- HP UI를 Heat UI와 통일된 디자인으로 수정
- Pause 화면에 HP·Heat·Core·Skill 상태 표시
- UI 전체를 공통
Canvas.prefab으로 정리 - 공통 UI Prefab을 실제 플레이 씬에 일괄 적용
- Pause와 OverClock 사이의 시간 제어 충돌 수정
- 중복 EventSystem 및 입력 잠금 충돌 방지
- Pause 화면의 Quit 버튼을 제거하고 Restart 기능 추가
- 사망 상태에서도 현재 씬을 다시 시작할 수 있도록 연결
이번 주는 새로운 UI 하나를 추가했다기보다는, 지금까지 개별적으로 만들어온 기능을 실제 게임 구조에 맞게 묶고 안정적으로 동작하도록 만드는 통합 단계 에 가까웠다.
실제 플레이 씬 통합을 위한 UI 최종 정리
UI를 실제 플레이 씬에 연결하기 전에, 기존 UI 에셋과 데이터 구조를 한 차례 정리했다.
먼저 Core별 Skill 아이콘을 새롭게 제작하고, 각 Core 데이터에서 해당 아이콘과 설명을 직접 가져올 수 있도록 CoreData를 확장했다.
기존에는 Core 자체의 아이콘만 가지고 있었지만, 수정 후에는 Core마다 두 개의 Skill 아이콘과 UI 설명을 별도로 지정할 수 있도록 했다.
[SerializeField] private Sprite skill1Icon;
[SerializeField] private Sprite skill2Icon;
[Header("UI Descriptions")]
[SerializeField, TextArea] private string coreHoverDescription;
[SerializeField, TextArea] private string skill1HoverDescription;
[SerializeField, TextArea] private string skill2HoverDescription;
public Sprite Skill1Icon => skill1Icon;
public Sprite Skill2Icon => skill2Icon;
public string CoreHoverDescription => coreHoverDescription;
public string Skill1HoverDescription => skill1HoverDescription;
public string Skill2HoverDescription => skill2HoverDescription;
이렇게 데이터를 CoreData에 모아두면 HUD나 Pause 화면에서 이미지와 설명을 각각 직접 지정할 필요가 없다.
현재 장착 중인 Core의 데이터만 받아오면, 해당 Core가 사용할 아이콘과 설명까지 함께 가져올 수 있다.
Skill HUD를 담당하는 SkillDrawer에서도 전달받은 CoreData를 기준으로 각 슬롯의 이미지를 갱신하도록 수정했다.
public void SetCore(CoreData coreData)
{
if (skillSlots == null || skillSlots.Length == 0)
return;
for (int i = 0; i < skillSlots.Length; i++)
{
SkillSlotUI slot = skillSlots[i];
if (slot == null || slot.skillImage == null)
continue;
slot.skillImage.sprite = GetSkillIcon(coreData, i);
}
}
private Sprite GetSkillIcon(CoreData coreData, int slotIndex)
{
if (coreData == null)
return null;
switch (slotIndex)
{
case 0:
return coreData.Skill1Icon != null ? coreData.Skill1Icon : coreData.CoreIcon;
case 1:
return coreData.Skill2Icon != null ? coreData.Skill2Icon : coreData.CoreIcon;
default:
return coreData.CoreIcon;
}
}
Skill 아이콘이 아직 지정되지 않은 Core가 있더라도 빈 이미지가 표시되지 않도록, 해당 Core의 기본 아이콘을 대체 이미지로 사용하도록 했다.
이제 Core가 변경되면 Core HUD뿐 아니라 Skill HUD도 같은 데이터를 기준으로 함께 갱신된다.
[캡처 1 삽입 — Core 1·2·3의 최종 Skill 아이콘]
각 Core 데이터에 전용 Skill 아이콘과 설명을 추가해 HUD와 Pause 화면에서 동일한 정보를 사용할 수 있도록 구성했다.
Pause 화면에 현재 Core와 Skill 정보 표시
4~5주차에 제작한 Pause 화면은 단순히 게임을 멈추는 메뉴에서 조금씩 상태창의 형태로 발전하고 있었다.
6주차에는 우측 영역에 현재 플레이어의 상태를 실제로 표시하도록 구성했다.
Pause 화면에는 다음 정보가 표시된다.
- 현재 HP
- 현재 Heat
- 장착 중인 Core
- 현재 Core의 Skill 1
- 현재 Core의 Skill 2
현재 Core가 변경되면 Pause 화면의 Core와 Skill 이미지 역시 함께 변경되도록 CoreManager의 변경 이벤트와 연결했다.
private void HandleCoreChanged(CoreData coreData)
{
UpdateIcons(coreData);
}
private void UpdateIcons(CoreData coreData)
{
coreImage.sprite = coreData != null ? coreData.CoreIcon : null;
skill1Image.sprite = coreData != null
? (coreData.Skill1Icon != null ? coreData.Skill1Icon : coreData.CoreIcon)
: null;
skill2Image.sprite = coreData != null
? (coreData.Skill2Icon != null ? coreData.Skill2Icon : coreData.CoreIcon)
: null;
}
UI가 매 프레임 현재 Core를 직접 확인하는 대신, 실제 Core가 변경된 순간에만 필요한 이미지를 갱신하도록 했다.
Core와 Skill 아이콘에는 Hover 기능도 추가했다.
마우스를 올리면 해당 아이콘이 확대되고, 별도의 설명 영역에 CoreData에 저장해둔 설명이 표시된다.
Pause 상태에서는 Time.timeScale이 0이기 때문에 일반적인 Time.deltaTime을 사용하면 UI 애니메이션도 함께 멈출 수 있다.
따라서 Hover 애니메이션에서는 Time.unscaledDeltaTime을 사용해, 게임 시간이 멈춰 있어도 UI는 계속 움직일 수 있도록 했다.
[캡처 2 삽입 — Pause 화면 전체 상태창]
Pause 화면 우측에 현재 HP·Heat·Core·Skill 정보를 표시해 플레이 도중 캐릭터의 상태를 한눈에 확인할 수 있도록 했다.
[캡처 3 삽입 — Core 및 Skill Hover 전후]
Core 또는 Skill 아이콘에 마우스를 올리면 아이콘이 확대되고, 해당 기능에 대한 설명이 표시된다.
이 부분은 정적인 이미지보다 실제 Hover 움직임과 설명이 나타나는 모습을 GIF로 보여주는 편이 더 적합할 것 같다.
Pause 화면에서도 현재 HP와 Heat를 확인할 수 있도록 연결
Pause 화면을 실제 상태창으로 사용하려면, 게임을 멈춘 순간의 HP와 Heat가 정확하게 표시되어야 했다.
이를 위해 Pause 화면을 활성화할 때 현재 플레이어의 데이터를 다시 읽어오는 RefreshPauseStatusUI()를 구성했다.
private void RefreshPauseStatusUI()
{
if (playerHealth != null && pauseHPDrawer != null)
{
int currentHP = playerHealth.CurrentHP;
int maxHP = playerHealth.MaxHP;
pauseHPDrawer.SetHP(currentHP, maxHP);
bool isLowHP = maxHP > 0 && (float)currentHP / maxHP <= lowHPThreshold;
pauseHPDrawer.SetLowHP(isLowHP);
}
if (heatManager != null && pauseHeatDrawer != null)
{
float currentHeat = heatManager.CurrentHeat;
float maxHeat = heatManager.MaxHeat;
bool isHighHeat = maxHeat > 0f && currentHeat / maxHeat >= highHeatThreshold;
bool isOverHeat = maxHeat > 0f && currentHeat / maxHeat >= overHeatThreshold;
pauseHeatDrawer.SetHeat(currentHeat, isHighHeat);
pauseHeatDrawer.SetOverHeat(isOverHeat);
}
}
현재 수치만 복사하는 것이 아니라 Low HP, High Heat, OverHeat 같은 상태 역시 동일하게 계산했다.
따라서 Pause 화면에서도 평상시 HUD와 같은 기준으로 다음 상태를 확인할 수 있다.
- 일반 HP
- Low HP
- 일반 Heat
- High Heat
- OverHeat
Pause 화면을 여는 순간 현재 값을 새롭게 읽기 때문에, 플레이 도중 변경된 수치가 그대로 Pause 상태창에 반영된다.
[캡처 4 삽입 — 인게임 HUD와 Pause 상태창 비교]
Pause 화면을 여는 시점에 현재 HP와 Heat를 다시 읽어, 인게임 HUD와 동일한 상태를 표시하도록 했다.
[캡처 5 삽입 — Low HP 및 OverHeat 상태]
Pause 상태창에서도 Low HP, High Heat, OverHeat와 같은 위험 상태를 동일하게 구분할 수 있도록 구성했다.
HP UI를 Heat UI와 통일된 방식으로 수정
이번 주에는 HP UI 역시 Heat UI와 비슷한 시각적인 규칙을 사용하도록 수정했다.
기존에는 일반적인 Unity UI Image의 fillAmount를 이용했다면, 이번에는 Heat UI처럼 Material에 현재 HP 비율을 직접 전달해 Shader 내부에서 Fill 영역을 계산하도록 했다.
float normalizedHP = (float)current / max;
if (materialInstance != null)
materialInstance.SetFloat("_Fill", normalizedHP);
현재 HP를 최대 HP로 나누어 0~1 사이의 값으로 정규화한 뒤, Shader의 _Fill 값으로 전달한다.
예를 들어 최대 HP가 100이고 현재 HP가 30이라면 다음과 같다.
normalizedHP = 30 / 100 = 0.3
Shader는 전달받은 0.3을 기준으로 HP 영역의 30%만 표시한다.
이 방식을 사용한 이유는 단순한 직사각형 게이지에 한정되지 않고, 사이버펑크 스타일의 비정형 프레임이나 발광 효과를 그대로 유지하면서 게이지를 표현하기 위해서였다.
또한 HP가 감소하는 경계 부분에 Particle 효과가 계속 따라갈 수 있도록 좌표 계산 역시 보완했다.
Particle System이 Local 좌표를 사용하는지 World 좌표를 사용하는지에 따라 위치를 구분해서 처리하여, Canvas 구조가 변경되더라도 피해 효과가 최대한 HP Fill의 끝부분을 따라가도록 했다.
[캡처 6 삽입 — HP·Heat UI 최종 디자인]
HP UI에도 Shader 기반 Fill 표현을 적용해 Heat·Core HUD와의 시각적 통일성을 높였다.
[캡처 7 삽입 — HP 감소 과정]
현재 HP 비율을 Shader에 전달하고, HP가 감소하는 경계에 Particle 효과를 배치해 피해 상황을 직관적으로 표현했다.
모든 UI를 공통 Canvas Prefab으로 정리
지금까지는 HUD, Pause, Death UI를 조금씩 확장하며 사용해 왔다.
하지만 이를 각 플레이 씬에 개별적으로 배치한다면 이후 UI를 수정할 때마다 모든 씬을 직접 열어 같은 변경을 반복해야 한다.
씬마다 UI 설정이 조금씩 달라질 가능성도 있고, 어느 한 씬에서만 수정 사항이 빠질 수도 있다.
그래서 6주차에는 지금까지 만든 UI 전체를 하나의 공통 Canvas.prefab으로 정리했다.
공통 Canvas Prefab에는 다음 요소들이 포함되었다.
- HP HUD
- Heat HUD
- Core HUD
- Skill HUD
- Skill Reference Manager
- Death UI
- Pause Panel
- Pause 전용 HP·Heat·Core·Skill 상태창
- Resume 및 Restart 버튼
- Master·BGM·SFX Volume Slider
- Pause 배경 Shader
- Pause Manager
- Volume Controller
- Hover Info UI
각 UI를 씬마다 독립적인 오브젝트로 관리하는 대신, 하나의 Prefab 안에서 관리하도록 변경했다.
덕분에 이후 디자인이나 구조를 수정할 때 원본 Prefab을 기준으로 여러 씬에 동일한 변경을 반영할 수 있다.
이번 통합에서 중요했던 것은 단순히 UI를 한곳에 모으는 것만은 아니었다.
HUD, Pause, Death UI가 동일한 Player·Health·Heat·Core 등의 상태 데이터를 공유할 수 있도록 구조를 다시 정리하는 것 이 핵심이었다.
[캡처 8 삽입 — Canvas.prefab 전체 Hierarchy]
개별적으로 제작했던 HUD, Pause, Death UI를 하나의 공통 Canvas Prefab으로 정리해 전체 플레이 씬에서 동일한 구조를 사용하도록 했다.
[캡처 9 삽입 — 실제 플레이 씬의 Prefab Instance]
실제 플레이 씬에서는 공통 Canvas Prefab을 사용하므로, 원본 Prefab의 수정 사항을 여러 씬에 공통으로 반영할 수 있다.
공통 UI를 실제 플레이 씬에 적용
정리한 Canvas.prefab을 실제 플레이에 사용되는 씬에 연결했다.
이번 작업에서 UI가 적용된 주요 씬은 다음과 같다.
1HRoom2CoreRoom3street4top15top26top37top4
이전까지는 테스트 씬에서 HUD와 Pause 기능을 확인하는 경우가 많았다.
이번 주부터는 게임의 시작 구간부터 후반부 옥상 구간까지 동일한 UI를 사용할 수 있도록 실제 플레이 흐름에 적용했다.
씬마다 Canvas를 복사하는 것이 아니라 동일한 Prefab을 참조하기 때문에, 이후 UI의 위치나 디자인을 변경하더라도 공통 Prefab을 기준으로 수정할 수 있다.
물론 단순히 Prefab을 씬에 넣는 것만으로 끝나는 작업은 아니었다.
각 씬마다 Player, Camera, Health, Heat Manager 등의 위치가 다를 수 있기 때문에 다음과 같은 참조를 함께 확인해야 했다.
- 현재 씬의 Player와 HP UI 연결
HeatManager와 Heat UI 연결CoreManager와 Core·Skill UI 연결- Main Camera와 Canvas Render Camera 연결
PauseManager와PlayerController연결- Pause Panel 버튼 이벤트 연결
- 씬별 EventSystem 중복 여부
테스트 씬에서 정상적으로 동작하던 기능이 실제 씬에서도 그대로 동작하도록 만드는 것이 이번 통합 작업의 핵심이었다.
[캡처 10 삽입 — 대표 플레이 씬 3종 UI 비교]
공통 Canvas Prefab을 실내, 거리, 옥상 등 서로 다른 실제 플레이 씬에 적용해 모든 구간에서 동일한 HUD를 사용할 수 있도록 했다.
대표적으로 1HRoom, 3street, 7top4 정도를 비교하면 실내·야외·후반 구간이라는 차이가 잘 보여서 적합할 것 같다.
Pause와 OverClock의 시간 제어 충돌 수정
Pause 기능은 Time.timeScale = 0으로 설정해 게임 시간을 멈추는 방식으로 구현했다.
그런데 프로젝트에는 OverClock처럼 Time.timeScale과 Time.fixedDeltaTime을 변경하는 또 다른 기능도 존재했다.
이 상태에서 Pause를 해제할 때 항상 다음과 같이 처리한다면 문제가 발생할 수 있다.
Time.timeScale = 1f;
Pause 직전에 OverClock 등의 영향으로 시간 배율이 변경되어 있었다면, Resume하는 순간 해당 상태가 사라져버리기 때문이다.
이를 해결하기 위해 Pause 직전의 시간 상태를 저장하도록 구조를 변경했다.
private void Pause()
{
if (isPaused)
return;
isPaused = true;
timeScaleBeforePause = Time.timeScale;
fixedDeltaTimeBeforePause = Time.fixedDeltaTime;
hasTimeStateSnapshot = true;
heatStateController?.SetPauseMenuStatePaused(true);
Time.timeScale = 0f;
playerController.SetInputEnabled(false);
pauseInputLockApplied = true;
DisableDuplicatePausePanelEventSystems();
pausePanel.SetActive(true);
RefreshPauseStatusUI();
Cursor.visible = true;
}
Pause에 진입하기 직전의 Time.timeScale과 Time.fixedDeltaTime을 저장한다.
Resume할 때는 기본값을 강제로 넣는 대신, 저장해둔 값으로 복원한다.
private void RestoreTimeStateBeforePause()
{
if (!hasTimeStateSnapshot)
return;
Time.timeScale = timeScaleBeforePause;
Time.fixedDeltaTime = fixedDeltaTimeBeforePause;
hasTimeStateSnapshot = false;
}
이제 일반 상태에서 Pause했다면 일반 속도로 돌아가고, OverClock처럼 다른 시스템에서 시간 배율을 변경한 상태였다면 Resume 이후에도 해당 상태로 돌아갈 수 있다.
Pause가 적용한 입력 잠금 역시 다른 시스템의 입력 제한과 구분할 수 있도록 pauseInputLockApplied를 별도로 기록했다.
이를 통해 Pause가 직접 변경한 상태만 Pause가 복원하도록 구조를 조정했다.
이번 수정은 Pause를 독립적인 하나의 기능으로 보는 것이 아니라, 기존의 OverClock과 Heat, Player Input 시스템과 함께 동작하도록 만드는 과정이었다.
[캡처 11 삽입 — OverClock 중 Pause 테스트]
Pause 직전의
timeScale과fixedDeltaTime을 저장해, Resume 이후 기존 시간 상태로 복구되도록 수정했다.
Prefab 통합 과정에서 EventSystem 충돌 대응
공통 Canvas Prefab을 여러 씬에 적용하면서 또 하나 확인해야 했던 부분이 EventSystem이었다.
일부 씬에는 기존 EventSystem이 이미 존재할 수 있고, Prefab 내부에도 EventSystem이 포함되어 있다면 하나의 씬 안에 두 개 이상의 EventSystem이 활성화될 수 있다.
Unity에서 EventSystem이 중복되면 버튼 선택이나 마우스 입력이 두 번 처리되는 등 예상하지 못한 문제가 발생할 수 있다.
이를 줄이기 위해 Pause Panel 내부의 EventSystem을 확인하고, 현재 사용하는 EventSystem을 제외한 중복 인스턴스를 비활성화하도록 처리했다.
EventSystem[] pausePanelEventSystems = pausePanel.GetComponentsInChildren<EventSystem>(true);
foreach (EventSystem eventSystem in pausePanelEventSystems)
{
if (eventSystem == null || eventSystem == EventSystem.current)
continue;
eventSystem.enabled = false;
}
공통 Prefab을 여러 씬에 적용하면서 생길 수 있는 구조적인 충돌을 코드에서도 한 번 더 방어한 것이다.
통합 과정에서는 폰트 에셋도 함께 정리했다.
개인 작업 폴더나 테스트 폴더에 동일한 폰트 파일이 여러 개 존재하면 .meta 파일이나 TextMesh Pro Font Asset 변경 사항이 Merge 과정에서 충돌할 가능성이 있다.
따라서 실제 UI에서 사용하는 폰트를 공통 TextMesh Pro/Fonts 경로에서 관리하도록 정리했다.
[캡처 12 삽입 — 중복 EventSystem 구조 또는 폰트 정리 결과]
공통 UI Prefab을 여러 씬에 적용하면서 EventSystem과 폰트 리소스가 중복되지 않도록 구조를 정리했다.
Quit 버튼 대신 Restart 기능 추가
초기 Pause 화면에서는 Resume, Setting, Quit 등의 메뉴를 배치하는 것을 생각했다.
하지만 실제 공모전 빌드의 플레이 흐름을 고려했을 때, 게임을 완전히 종료하는 기능보다는 현재 구간을 다시 시작하는 기능이 더 유용하다 고 판단했다.
따라서 Pause 화면의 Quit 항목을 제거하고 Restart 버튼을 추가했다.
Restart 기능은 현재 활성화되어 있는 씬의 Build Index를 가져와 같은 씬을 다시 불러오는 방식으로 구현했다.
public void RestartScene()
{
Time.timeScale = 1f;
Time.fixedDeltaTime = fixedDeltaTimeBeforePause;
isPaused = false;
hasTimeStateSnapshot = false;
pauseInputLockApplied = false;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
Pause 상태에서는 Time.timeScale이 0이므로, 씬을 다시 불러오기 전에 시간 상태와 Pause 상태를 먼저 초기화한다.
그다음 현재 활성화된 씬을 다시 로드한다.
씬 이름을 문자열로 직접 지정하는 대신 현재 씬의 Build Index를 사용했기 때문에, 각 플레이 씬마다 별도의 Restart 로직을 만들 필요 없이 같은 기능을 재사용할 수 있다.
Restart 버튼은 Pause 화면뿐 아니라 Death UI에도 연결했다.
이제 플레이어가 어느 구간에서 사망하더라도 별도의 씬 이름을 지정하지 않고 현재 플레이 중인 씬을 바로 다시 시작 할 수 있다.
[캡처 13 삽입 — Quit 제거 및 Restart 버튼]
공모전 빌드의 실제 플레이 흐름에 맞춰 Quit을 제거하고 현재 씬을 다시 불러오는 Restart 버튼을 추가했다.
[캡처 14 삽입 — 사망 상태 Restart]
Restart 기능을 사망 화면에도 연결해, 플레이어가 사망한 현재 플레이 구간을 바로 다시 시작할 수 있도록 했다.
Pause → Restart와 Death → Restart 과정은 짧은 GIF로 만들어도 좋을 것 같다.
6주차 Git 작업 흐름
이번 주 작업은 다음과 같은 순서로 진행되었다.
8월 10일 — Volume 컨트롤 및 Heat UI 개편
- Master·BGM·SFX Volume 컨트롤 정리
- Heat UI 위치와 형태 조정
- 변경된 UI 구조에 맞춰 Heat Shader 수정
8월 12일 — Skill 아이콘 및 텍스트 문제 수정
- Core별 Skill 아이콘 리메이크
CoreData에 Skill 아이콘 및 설명 데이터 추가- HUD와 Pause 화면에서 Core별 Skill 아이콘 표시
- 한글 텍스트 깨짐 문제 대응을 위한 폰트 추가 및 정리
- Pause 화면 Hover 설명 기능 추가
8월 12일 — Pause 기능 및 HP UI 완성
- Pause 우측 상태창 구성
- Pause 화면에 HP·Heat·Core·Skill 정보 표시
- HP UI를 Heat UI와 통일된 Shader 기반 구조로 수정
- 공통
Canvas.prefab제작 - Resume 및 Volume UI 연결
8월 12일 — Merge 충돌 정리
- 중복 폰트 에셋 정리
- 폰트 및 UI 참조 구조 조정
- 개인 작업 폴더와 공통 폴더 사이의 중복 파일 정리
8월 13일 — 실제 플레이 씬 UI 통합
- 공통 Canvas Prefab을 실제 플레이 씬에 연결
1HRoom부터7top4까지 총 7개 플레이 씬에 UI 적용- Pause와 OverClock의 시간 상태 충돌 수정
- Pause 입력 잠금 및 EventSystem 중복 문제 대응
8월 13일 — Restart 기능 추가
- Pause 화면에서 Quit 제거
- Restart 버튼 추가
- 현재 활성화된 씬을 다시 불러오는 기능 구현
- Death UI에서도 Restart를 사용할 수 있도록 연결
[캡처 15 삽입 — 6주차 GitHub 커밋 기록]
개별 UI 기능을 마무리한 뒤 충돌을 정리하고, 공통 Prefab을 실제 플레이 씬에 통합하는 순서로 작업을 진행했다.
6주차 결과
이번 주에는 지금까지 제작한 UI를 실제 게임 전반에서 사용할 수 있는 형태로 통합했다.
최종적으로 완료한 작업은 다음과 같다.
- Heat UI 형태 및 Shader 수정
- HP UI를 Heat UI와 통일된 디자인으로 개선
- Core별 Skill 아이콘 및 설명 데이터 추가
- 현재 Core에 맞춰 HUD와 Pause Skill 아이콘 갱신
- Pause 화면에 HP·Heat·Core·Skill 상태 표시
- Pause 화면의 Core·Skill Hover 및 설명 기능 구현
- HUD·Pause·Death UI를 공통 Canvas Prefab으로 정리
- 공통 Canvas Prefab을 7개의 실제 플레이 씬에 적용
- Pause와 OverClock 사이의 시간 제어 충돌 수정
- Pause 입력 잠금과 중복 EventSystem 처리
- 폰트 에셋 및 UI 참조 구조 정리
- Quit 제거 및 Restart 기능 추가
- Pause 및 사망 상태에서 현재 씬 Restart 기능 연결
이번 작업을 통해 UI를 단순히 화면 위에 표시하는 기능에서 끝내지 않고, 여러 플레이 씬에서 공통으로 사용할 수 있는 실제 게임 인터페이스 구조 로 정리할 수 있었다.
특히 Prefab을 중심으로 UI를 관리하도록 바꾸면서, 이후 디자인이나 기능을 수정할 때 모든 씬을 개별적으로 수정할 필요가 크게 줄어들었다.
또한 Pause와 OverClock처럼 서로 다른 시스템이 동시에 Time.timeScale 같은 전역 상태를 변경할 수 있는 상황에서는, 각 기능이 단순히 값을 덮어쓰는 방식만으로는 안정적으로 동작하기 어렵다는 점도 확인했다.
결국 중요한 것은 기존 상태를 저장하고, 자신이 변경한 부분만 다시 복원하는 구조 였다.
6주차를 마치며
이번 주에 가장 많은 시간이 필요했던 것은 새로운 UI 에셋을 만드는 작업이 아니었다.
이미 만들어둔 UI를 실제 게임 구조 안에 넣고, 서로 다른 씬과 시스템 속에서도 같은 방식으로 동작하도록 정리하는 과정이었다.
테스트 씬에서는 멀쩡하게 동작하던 UI도 실제 플레이 씬에 넣자 새로운 문제들이 하나씩 나타났다.
- 씬마다 Player와 Manager의 위치가 다른 문제
- Canvas의 Render Camera 연결
- Prefab 내부 Reference와 씬별 Reference의 차이
- EventSystem 중복
- 폰트 에셋 및
.meta파일 충돌 - Pause와 OverClock이 동시에 시간 배율을 변경하는 문제
- Pause 입력 잠금과 다른 입력 제한 기능의 충돌
- 사망 상태에서 Restart 기능을 호출할 수 있는지 여부
결국 공통 Prefab을 만든다는 것은 단순히 오브젝트를 한 번만 만들어 복사하지 않는다는 의미는 아니었다.
Prefab이 실제 유지보수에 도움이 되려면, 데이터 참조와 이벤트 연결, 각 씬에서 달라지는 Override의 범위까지 함께 설계해야 했다.
Pause 기능도 마찬가지였다.
처음에는 단순히Time.timeScale을0과1 사이에서 바꾸면 충분하다고 생각했지만, OverClock처럼 같은 전역 상태를 사용하는 기능이 생기면서 그렇게 단순한 문제는 아니라는 것을 알게 되었다.
Pause 이전의 상태를 저장하고 다시 복구하는 방식으로 수정하면서, 여러 시스템이 하나의 전역 상태를 공유할 때에는 각 시스템이 무엇을 바꾸었고 어디까지 되돌려야 하는지를 분명하게 구분해야 한다는 점을 배웠다.
그리고 이번 6주차를 마지막으로, 1주차부터 만들어온 UI 작업도 하나의 흐름으로 이어졌다.
1주차에는 Heat와 Core라는 두 개의 값을 화면에 표시하는 것에서 시작했다.
2주차에는 HP와 피해 피드백을 추가했고,
3주차에는 Shader와 Skill, 사망 연출을 통해 『ERROR-CODE-H』만의 분위기를 입혔다.
4주차에는 Pause 화면과 동적인 인터페이스를 만들었고,
5주차에는 Resume와 Volume 등 실제 메뉴 기능을 추가했다.
그리고 마지막 6주차에는 그동안 만들어온 모든 요소를 하나의 Canvas.prefab으로 묶어 실제 플레이 씬 전체에 적용했다.
처음에는 테스트 씬 한쪽에 놓여 있던 작은 Heat 게이지 하나에 불과했다.
그 작은 UI가 6주 동안 HP와 Core, Skill, Pause, Death UI로 확장되고, Shader와 데이터, 다른 팀원이 만든 시스템들과 연결되면서 마지막에는 게임 전반에서 사용하는 하나의 인터페이스가 되었다.
이번 프로젝트를 통해 UI는 단순히 정보를 예쁘게 보여주는 요소가 아니라는 것을 배울 수 있었다.
플레이어의 입력, 게임의 시간, 다른 시스템의 상태, 데이터 구조, 씬 구성까지 모두 연결되어야 비로소 하나의 UI가 실제 게임 안에서 제대로 동작한다.
그리고 6주 동안 이어진 『ERROR-CODE-H』의 UI 작업은, 그렇게 실제 게임 속 하나의 인터페이스로 마무리되었다.
開発期間: 2026.08.10 ~ 2026.08.14
担当役割: 共通UIの統合、Pauseステータス画面、Restartおよび実際のプレイシーンへの適用
これまでの5週間では、HUDとPause画面をそれぞれ制作し、必要な機能を一つずつ追加してきた。
HeatとCoreを画面へ表示するところから始まり、HP、Skill、死亡演出、Pause画面、音量設定まで、少しずつ機能を拡張してきた。
そして最後の6週目には、これまで制作してきたUIを ゲーム全体で共通して使用できる一つのインターフェースとして整理し、統合する作業 を進めた。
今週の主な目標は以下の通りであった。
- Heat UIの形状とShaderの最終調整
- CoreごとのSkillアイコンのリメイクおよびデータ接続
- HP UIをHeat UIと統一されたデザインへ修正
- Pause画面にHP・Heat・Core・Skillの状態を表示
- UI全体を共通の
Canvas.prefabとして整理 - 共通UI Prefabを実際のプレイシーンへ一括適用
- PauseとOverClock間の時間制御の競合を修正
- 重複EventSystemおよび入力ロックの競合を防止
- Pause画面のQuitボタンを削除し、Restart機能を追加
- 死亡状態からでも現在のシーンを再開できるよう接続
今週は新しいUIを一つ追加するというよりも、これまで個別に制作してきた機能を実際のゲーム構造に合わせてまとめ、安定して動作させるための 統合段階 に近かった。
実際のプレイシーンへの統合に向けたUIの最終整理
UIを実際のプレイシーンへ接続する前に、既存のUIアセットとデータ構造を一度整理した。
まずCoreごとのSkillアイコンを新しく制作し、それぞれのCoreデータから該当するアイコンと説明を直接取得できるようCoreDataを拡張した。
これまではCore自体のアイコンだけを持っていたが、修正後はCoreごとに二つのSkillアイコンとUI説明を個別に指定できるようにした。
[SerializeField] private Sprite skill1Icon;
[SerializeField] private Sprite skill2Icon;
[Header("UI Descriptions")]
[SerializeField, TextArea] private string coreHoverDescription;
[SerializeField, TextArea] private string skill1HoverDescription;
[SerializeField, TextArea] private string skill2HoverDescription;
public Sprite Skill1Icon => skill1Icon;
public Sprite Skill2Icon => skill2Icon;
public string CoreHoverDescription => coreHoverDescription;
public string Skill1HoverDescription => skill1HoverDescription;
public string Skill2HoverDescription => skill2HoverDescription;
このようにデータをCoreDataへまとめておけば、HUDやPause画面側で画像と説明をそれぞれ直接指定する必要がなくなる。
現在装備しているCoreのデータさえ受け取れば、そのCoreで使用するアイコンと説明も一緒に取得できる。
Skill HUDを担当するSkillDrawerでも、受け取ったCoreDataを基準として各スロットの画像を更新するよう修正した。
public void SetCore(CoreData coreData)
{
if (skillSlots == null || skillSlots.Length == 0)
return;
for (int i = 0; i < skillSlots.Length; i++)
{
SkillSlotUI slot = skillSlots[i];
if (slot == null || slot.skillImage == null)
continue;
slot.skillImage.sprite = GetSkillIcon(coreData, i);
}
}
private Sprite GetSkillIcon(CoreData coreData, int slotIndex)
{
if (coreData == null)
return null;
switch (slotIndex)
{
case 0:
return coreData.Skill1Icon != null ? coreData.Skill1Icon : coreData.CoreIcon;
case 1:
return coreData.Skill2Icon != null ? coreData.Skill2Icon : coreData.CoreIcon;
default:
return coreData.CoreIcon;
}
}
Skillアイコンがまだ指定されていないCoreであっても空の画像が表示されないよう、そのCoreの基本アイコンを代替画像として使用するようにした。
これによってCoreが変更されると、Core HUDだけでなくSkill HUDも同じデータを基準として同時に更新される。
[キャプチャ1を挿入 — Core 1・2・3の最終Skillアイコン]
各Coreデータに専用のSkillアイコンと説明を追加し、HUDとPause画面で同じ情報を使用できるよう構成した。
Pause画面に現在のCoreとSkill情報を表示する
4〜5週目に制作したPause画面は、単純にゲームを停止するためのメニューから、少しずつステータス画面に近い形へ発展していた。
6週目には右側の領域へ、現在のプレイヤー状態を実際に表示するよう構成した。
Pause画面には以下の情報が表示される。
- 現在のHP
- 現在のHeat
- 装備中のCore
- 現在のCoreのSkill 1
- 現在のCoreのSkill 2
現在のCoreが変更されると、Pause画面のCoreとSkill画像も同時に変更されるよう、CoreManagerの変更イベントへ接続した。
private void HandleCoreChanged(CoreData coreData)
{
UpdateIcons(coreData);
}
private void UpdateIcons(CoreData coreData)
{
coreImage.sprite = coreData != null ? coreData.CoreIcon : null;
skill1Image.sprite = coreData != null
? (coreData.Skill1Icon != null ? coreData.Skill1Icon : coreData.CoreIcon)
: null;
skill2Image.sprite = coreData != null
? (coreData.Skill2Icon != null ? coreData.Skill2Icon : coreData.CoreIcon)
: null;
}
UI側で毎フレーム現在のCoreを直接確認するのではなく、実際にCoreが変更された瞬間だけ必要な画像を更新するようにした。
CoreとSkillアイコンにはHover機能も追加した。
マウスを重ねると該当アイコンが拡大し、別の説明領域にCoreDataへ保存しておいた説明が表示される。
Pause状態ではTime.timeScaleが0になるため、通常のTime.deltaTimeを使用するとUIアニメーションまで停止する可能性がある。
そのためHoverアニメーションにはTime.unscaledDeltaTimeを使用し、ゲーム時間が停止していてもUIは動き続けるようにした。
[キャプチャ2を挿入 — Pause画面全体のステータス画面]
Pause画面右側に現在のHP・Heat・Core・Skill情報を表示し、プレイ中のキャラクター状態を一目で確認できるようにした。
[キャプチャ3を挿入 — CoreおよびSkillのHover前後]
CoreまたはSkillアイコンへマウスを重ねるとアイコンが拡大し、その機能についての説明が表示される。
この部分は静止画像よりも、実際のHoverの動きと説明が表示される様子をGIFで見せる方が適していると思う。
Pause画面でも現在のHPとHeatを確認できるよう接続する
Pause画面を実際のステータス画面として使用するには、ゲームを停止した瞬間のHPとHeatが正確に表示されなければならなかった。
そのため、Pause画面を有効化する際に現在のプレイヤーデータを改めて読み込むRefreshPauseStatusUI()を構成した。
private void RefreshPauseStatusUI()
{
if (playerHealth != null && pauseHPDrawer != null)
{
int currentHP = playerHealth.CurrentHP;
int maxHP = playerHealth.MaxHP;
pauseHPDrawer.SetHP(currentHP, maxHP);
bool isLowHP = maxHP > 0 && (float)currentHP / maxHP <= lowHPThreshold;
pauseHPDrawer.SetLowHP(isLowHP);
}
if (heatManager != null && pauseHeatDrawer != null)
{
float currentHeat = heatManager.CurrentHeat;
float maxHeat = heatManager.MaxHeat;
bool isHighHeat = maxHeat > 0f && currentHeat / maxHeat >= highHeatThreshold;
bool isOverHeat = maxHeat > 0f && currentHeat / maxHeat >= overHeatThreshold;
pauseHeatDrawer.SetHeat(currentHeat, isHighHeat);
pauseHeatDrawer.SetOverHeat(isOverHeat);
}
}
現在の数値だけをコピーするのではなく、Low HP、High Heat、OverHeatといった状態も同じ基準で計算した。
そのためPause画面でも、通常のHUDと同じ基準で以下の状態を確認できる。
- 通常HP
- Low HP
- 通常Heat
- High Heat
- OverHeat
Pause画面を開いた瞬間に現在値を改めて読み込むため、プレイ中に変化した数値がそのままPauseステータス画面へ反映される。
[キャプチャ4を挿入 — ゲーム内HUDとPauseステータス画面の比較]
Pause画面を開く時点で現在のHPとHeatを再度読み込み、ゲーム内HUDと同じ状態を表示するようにした。
[キャプチャ5を挿入 — Low HPおよびOverHeat状態]
Pauseステータス画面でもLow HP、High Heat、OverHeatといった危険状態を同じ基準で区別できるよう構成した。
HP UIをHeat UIと統一された方式へ修正する
今週はHP UIについても、Heat UIと似た視覚的なルールを使用するよう修正した。
従来は一般的なUnity UI ImageのfillAmountを利用していたが、今回はHeat UIと同様にMaterialへ現在のHP割合を直接渡し、Shader内部でFill領域を計算する方式へ変更した。
float normalizedHP = (float)current / max;
if (materialInstance != null)
materialInstance.SetFloat("_Fill", normalizedHP);
現在HPを最大HPで割り、0~1の値へ正規化した後、Shaderの_Fill値として渡す。
例えば最大HPが100で、現在HPが30であれば以下のようになる。
normalizedHP = 30 / 100 = 0.3
Shaderは渡された0.3を基準に、HP領域の30%だけを表示する。
この方式を使用した理由は、単純な長方形ゲージだけに限定されず、サイバーパンクスタイルの不規則なフレームや発光エフェクトを維持したままゲージを表現するためであった。
また、HPが減少する境界部分へParticleエフェクトが引き続き追従できるよう、座標計算も補完した。
Particle SystemがLocal座標を使用するかWorld座標を使用するかに応じて位置を分けて処理し、Canvas構造が変更された場合でも、ダメージエフェクトができる限りHP Fillの端へ追従するようにした。
[キャプチャ6を挿入 — HP・Heat UIの最終デザイン]
HP UIにもShaderベースのFill表現を適用し、Heat・Core HUDとの視覚的な統一感を高めた。
[キャプチャ7を挿入 — HP減少過程]
現在のHP割合をShaderへ渡し、HPが減少する境界にParticleエフェクトを配置することで、ダメージ状態を直感的に表現した。
すべてのUIを共通Canvas Prefabとして整理する
これまではHUD、Pause、Death UIを少しずつ拡張しながら使用してきた。
しかし、それらを各プレイシーンへ個別に配置すると、今後UIを修正するたびにすべてのシーンを直接開き、同じ変更を繰り返す必要がある。
シーンごとにUI設定が少しずつ異なる可能性もあり、あるシーンだけ修正が反映されないことも考えられた。
そこで6週目には、これまで制作したUI全体を一つの共通Canvas.prefabとして整理した。
共通Canvas Prefabには以下の要素を含めた。
- HP HUD
- Heat HUD
- Core HUD
- Skill HUD
- Skill Reference Manager
- Death UI
- Pause Panel
- Pause専用HP・Heat・Core・Skillステータス画面
- ResumeおよびRestartボタン
- Master・BGM・SFX Volume Slider
- Pause背景Shader
- Pause Manager
- Volume Controller
- Hover Info UI
各UIをシーンごとの独立したオブジェクトとして管理するのではなく、一つのPrefab内で管理するよう変更した。
これによって、今後デザインや構造を変更する際には、元のPrefabを基準として複数のシーンへ同じ変更を反映できる。
今回の統合で重要であったのは、単純にUIを一か所へ集めることだけではなかった。
HUD、Pause、Death UIが同じPlayer・Health・Heat・Coreなどの状態データを共有できるよう、構造を改めて整理すること が核心であった。
[キャプチャ8を挿入 — Canvas.prefab全体のHierarchy]
個別に制作していたHUD、Pause、Death UIを一つの共通Canvas Prefabとして整理し、すべてのプレイシーンで同じ構造を使用できるようにした。
[キャプチャ9を挿入 — 実際のプレイシーンのPrefab Instance]
実際のプレイシーンでは共通Canvas Prefabを使用するため、元Prefabの修正内容を複数のシーンへ共通して反映できる。
共通UIを実際のプレイシーンへ適用する
整理したCanvas.prefabを、実際のプレイで使用するシーンへ接続した。
今回の作業でUIを適用した主なシーンは以下の通りである。
1HRoom2CoreRoom3street4top15top26top37top4
それまではテストシーンでHUDやPause機能を確認することが多かった。
今週からは、ゲーム開始区間から後半の屋上区間まで同じUIを使用できるよう、実際のプレイフローへ適用した。
シーンごとにCanvasをコピーするのではなく、同じPrefabを参照するため、今後UIの位置やデザインを変更した場合でも共通Prefabを基準として修正できる。
もちろん、単純にPrefabをシーンへ置くだけで完了する作業ではなかった。
シーンごとにPlayer、Camera、Health、Heat Managerなどの位置が異なる可能性があるため、以下の参照も合わせて確認する必要があった。
- 現在のシーンのPlayerとHP UIを接続
HeatManagerとHeat UIを接続CoreManagerとCore・Skill UIを接続- Main CameraとCanvas Render Cameraを接続
PauseManagerとPlayerControllerを接続- Pause Panelのボタンイベントを接続
- シーンごとのEventSystem重複有無を確認
テストシーンで正常に動作していた機能を、実際のシーンでも同じように動作させることが今回の統合作業の核心であった。
[キャプチャ10を挿入 — 代表的なプレイシーン3種類のUI比較]
共通Canvas Prefabを室内、街路、屋上など異なる実際のプレイシーンへ適用し、すべての区間で同じHUDを使用できるようにした。
代表例として1HRoom、3street、7top4を比較すると、室内・屋外・後半区間という違いが分かりやすく、適していると思う。
PauseとOverClockの時間制御競合を修正する
Pause機能はTime.timeScale = 0へ設定することでゲーム時間を停止する方式で実装していた。
しかし、プロジェクトにはOverClockのようにTime.timeScaleとTime.fixedDeltaTimeを変更する別の機能も存在していた。
この状態でPauseを解除する際、常に以下のように処理すると問題が発生する可能性がある。
Time.timeScale = 1f;
Pause直前にOverClockなどの影響で時間倍率が変更されていた場合、Resumeした瞬間にその状態が失われてしまうためである。
これを解決するため、Pause直前の時間状態を保存するよう構造を変更した。
private void Pause()
{
if (isPaused)
return;
isPaused = true;
timeScaleBeforePause = Time.timeScale;
fixedDeltaTimeBeforePause = Time.fixedDeltaTime;
hasTimeStateSnapshot = true;
heatStateController?.SetPauseMenuStatePaused(true);
Time.timeScale = 0f;
playerController.SetInputEnabled(false);
pauseInputLockApplied = true;
DisableDuplicatePausePanelEventSystems();
pausePanel.SetActive(true);
RefreshPauseStatusUI();
Cursor.visible = true;
}
Pauseへ入る直前のTime.timeScaleとTime.fixedDeltaTimeを保存する。
Resume時には基本値を強制的に設定するのではなく、保存しておいた値へ復元する。
private void RestoreTimeStateBeforePause()
{
if (!hasTimeStateSnapshot)
return;
Time.timeScale = timeScaleBeforePause;
Time.fixedDeltaTime = fixedDeltaTimeBeforePause;
hasTimeStateSnapshot = false;
}
これによって通常状態でPauseした場合は通常速度へ戻り、OverClockのように別のシステムが時間倍率を変更していた状態であれば、Resume後もその状態へ戻ることができる。
Pauseが適用した入力ロックも、ほかのシステムによる入力制限と区別できるようpauseInputLockAppliedを別途記録した。
これによって Pause自身が変更した状態だけをPauseが復元する よう構造を調整した。
今回の修正はPauseを独立した一つの機能として見るのではなく、既存のOverClock、Heat、Player Inputシステムと共に動作させるための過程であった。
[キャプチャ11を挿入 — OverClock中のPauseテスト]
Pause直前の
timeScaleとfixedDeltaTimeを保存し、Resume後にそれまでの時間状態へ復元されるよう修正した。
Prefab統合過程でEventSystemの競合に対応する
共通Canvas Prefabを複数のシーンへ適用する中で、もう一つ確認する必要があったのがEventSystemであった。
一部のシーンには既存のEventSystemがすでに存在している可能性があり、Prefab内部にもEventSystemが含まれている場合、一つのシーン内で二つ以上のEventSystemが同時に有効になることがある。
UnityでEventSystemが重複すると、ボタン選択やマウス入力が二重に処理されるなど、予期しない問題が発生する可能性がある。
これを減らすため、Pause Panel内部のEventSystemを確認し、現在使用しているEventSystemを除く重複インスタンスを無効化するよう処理した。
EventSystem[] pausePanelEventSystems = pausePanel.GetComponentsInChildren<EventSystem>(true);
foreach (EventSystem eventSystem in pausePanelEventSystems)
{
if (eventSystem == null || eventSystem == EventSystem.current)
continue;
eventSystem.enabled = false;
}
共通Prefabを複数のシーンへ適用することで発生し得る構造的な競合を、コード側でももう一度防御した形である。
統合過程ではフォントアセットも合わせて整理した。
個人作業フォルダやテストフォルダに同じフォントファイルが複数存在すると、.metaファイルやTextMesh Pro Font Assetの変更内容がMerge時に競合する可能性がある。
そのため実際のUIで使用するフォントは、共通のTextMesh Pro/Fontsパスで管理するよう整理した。
[キャプチャ12を挿入 — 重複EventSystem構造またはフォント整理結果]
共通UI Prefabを複数のシーンへ適用する過程で、EventSystemとフォントリソースが重複しないよう構造を整理した。
Quitボタンの代わりにRestart機能を追加する
初期のPause画面ではResume、Setting、Quitなどのメニューを配置することを想定していた。
しかし実際のコンテストビルドのプレイフローを考えた場合、ゲームを完全に終了する機能よりも 現在の区間をやり直す機能の方が有用である と判断した。
そのためPause画面からQuit項目を削除し、Restartボタンを追加した。
Restart機能は、現在有効になっているシーンのBuild Indexを取得し、同じシーンを再度読み込む方式で実装した。
public void RestartScene()
{
Time.timeScale = 1f;
Time.fixedDeltaTime = fixedDeltaTimeBeforePause;
isPaused = false;
hasTimeStateSnapshot = false;
pauseInputLockApplied = false;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
Pause状態ではTime.timeScaleが0であるため、シーンを再読み込みする前に時間状態とPause状態を先に初期化する。
その後、現在有効になっているシーンを再び読み込む。
シーン名を文字列として直接指定するのではなく、現在のシーンのBuild Indexを使用したため、各プレイシーンごとに別のRestartロジックを用意する必要なく、同じ機能を再利用できる。
RestartボタンはPause画面だけでなくDeath UIにも接続した。
これによってプレイヤーがどの区間で死亡しても、別途シーン名を指定することなく 現在プレイ中のシーンをそのまま再開 できる。
[キャプチャ13を挿入 — Quit削除およびRestartボタン]
コンテストビルドの実際のプレイフローに合わせてQuitを削除し、現在のシーンを再読み込みするRestartボタンを追加した。
[キャプチャ14を挿入 — 死亡状態からのRestart]
Restart機能を死亡画面にも接続し、プレイヤーが死亡した現在のプレイ区間をすぐにやり直せるようにした。
Pause → RestartとDeath → Restartの流れは、短いGIFにしてもよさそうである。
6週目のGit作業フロー
今週の作業は以下の順序で進めた。
8月10日 — Volume ControlおよびHeat UI改修
- Master・BGM・SFX Volume Controlを整理
- Heat UIの位置と形状を調整
- 変更されたUI構造に合わせてHeat Shaderを修正
8月12日 — Skillアイコンおよびテキスト問題を修正
- CoreごとのSkillアイコンをリメイク
CoreDataにSkillアイコンおよび説明データを追加- HUDとPause画面でCoreごとのSkillアイコンを表示
- 韓国語テキストの文字化け問題へ対応するためフォントを追加・整理
- Pause画面にHover説明機能を追加
8月12日 — Pause機能およびHP UIを完成
- Pause右側のステータス画面を構成
- Pause画面にHP・Heat・Core・Skill情報を表示
- HP UIをHeat UIと統一されたShaderベースの構造へ修正
- 共通
Canvas.prefabを制作 - ResumeおよびVolume UIを接続
8月12日 — Merge競合を整理
- 重複フォントアセットを整理
- フォントおよびUI参照構造を調整
- 個人作業フォルダと共通フォルダ間の重複ファイルを整理
8月13日 — 実際のプレイシーンへUIを統合
- 共通Canvas Prefabを実際のプレイシーンへ接続
1HRoomから7top4まで、計7つのプレイシーンへUIを適用- PauseとOverClock間の時間状態の競合を修正
- Pause入力ロックおよびEventSystem重複問題へ対応
8月13日 — Restart機能を追加
- Pause画面からQuitを削除
- Restartボタンを追加
- 現在有効なシーンを再読み込みする機能を実装
- Death UIからもRestartを使用できるよう接続
[キャプチャ15を挿入 — 6週目のGitHubコミット記録]
個別のUI機能を仕上げた後に競合を整理し、共通Prefabを実際のプレイシーンへ統合する順序で作業を進めた。
6週目の結果
今週は、これまで制作してきたUIを実際のゲーム全体で使用できる形へ統合した。
最終的に完了した作業は以下の通りである。
- Heat UIの形状およびShaderを修正
- HP UIをHeat UIと統一されたデザインへ改善
- CoreごとのSkillアイコンおよび説明データを追加
- 現在のCoreに合わせてHUDとPauseのSkillアイコンを更新
- Pause画面へHP・Heat・Core・Skill状態を表示
- Pause画面のCore・Skill Hoverおよび説明機能を実装
- HUD・Pause・Death UIを共通Canvas Prefabとして整理
- 共通Canvas Prefabを7つの実際のプレイシーンへ適用
- PauseとOverClock間の時間制御競合を修正
- Pause入力ロックと重複EventSystemを処理
- フォントアセットおよびUI参照構造を整理
- Quitを削除しRestart機能を追加
- Pauseおよび死亡状態から現在のシーンをRestartできるよう接続
今回の作業によって、UIを単純に画面上へ表示する機能だけで終わらせず、複数のプレイシーンで共通して使用できる実際のゲームインターフェース構造 へ整理することができた。
特にPrefabを中心としてUIを管理する形へ変更したことで、今後デザインや機能を修正する際に、すべてのシーンを個別に修正する必要が大きく減った。
また、PauseとOverClockのように異なるシステムが同時にTime.timeScaleのようなグローバル状態を変更する場合、それぞれの機能が単純に値を上書きする方式だけでは安定して動作しにくいことも確認した。
最終的に重要であったのは、元の状態を保存し、自分自身が変更した部分だけを再び復元する構造 であった。
6週目を終えて
今週、最も多くの時間を必要としたのは、新しいUIアセットを制作する作業ではなかった。
すでに制作していたUIを実際のゲーム構造へ組み込み、異なるシーンやシステムの中でも同じ方法で動作するよう整理する過程であった。
テストシーンでは問題なく動いていたUIも、実際のプレイシーンへ入れると新しい問題が一つずつ現れた。
- シーンごとにPlayerとManagerの位置が異なる問題
- CanvasのRender Camera接続
- Prefab内部ReferenceとシーンごとのReferenceの違い
- EventSystemの重複
- フォントアセットおよび
.metaファイルの競合 - PauseとOverClockが同時に時間倍率を変更する問題
- Pause入力ロックとほかの入力制限機能の競合
- 死亡状態でRestart機能を呼び出せるかどうか
結局、共通Prefabを作るということは、単純にオブジェクトを一度だけ制作し、何度もコピーしなくて済むという意味ではなかった。
Prefabが実際の保守に役立つものになるためには、データ参照とイベント接続、各シーンで異なるOverrideの範囲まで一緒に設計する必要があった。
Pause機能についても同様であった。
最初は単純に Time.timeScaleを 0と 1の間で切り替えれば十分だと考えていたが、OverClockのように同じグローバル状態を使用する機能が加わることで、それほど単純な問題ではないことが分かった。
Pause前の状態を保存し、再び復元する方式へ修正する中で、複数のシステムが一つのグローバル状態を共有する場合、それぞれのシステムが何を変更し、どこまで元へ戻すべきなのかを明確に区別する必要があることを学んだ。
そして今回の6週目を最後に、1週目から続いてきたUI作業も、一つの流れとして繋がった。
1週目には、HeatとCoreという二つの値を画面へ表示するところから始めた。
2週目にはHPとダメージフィードバックを追加し、
3週目にはShader、Skill、死亡演出を通して『ERROR-CODE-H』ならではの雰囲気を加えた。
4週目にはPause画面と動的なインターフェースを制作し、
5週目にはResumeやVolumeなど、実際のメニュー機能を追加した。
そして最後の6週目には、それまで制作してきたすべての要素を一つのCanvas.prefabへまとめ、実際のプレイシーン全体へ適用した。
最初はテストシーンの片隅に置かれた、小さなHeatゲージ一つに過ぎなかった。
その小さなUIが6週間を通してHP、Core、Skill、Pause、Death UIへと拡張され、Shaderやデータ、ほかのチームメンバーが制作したシステムと接続されながら、最後にはゲーム全体で使用する一つのインターフェースとなった。
今回のプロジェクトを通して、UIは単純に情報を綺麗に見せるための要素ではないということを学ぶことができた。
プレイヤーの入力、ゲーム時間、ほかのシステムの状態、データ構造、シーン構成まで、すべてが繋がって初めて、一つのUIが実際のゲーム内で正しく動作する。
そして6週間にわたって続いた『ERROR-CODE-H』のUI作業は、こうして実際のゲームの中で機能する一つのインターフェースとして完成した。