개발 기간: 2026.07.05 ~ 2026.07.13
담당 역할: 인게임 HUD 기획 및 Heat·현재 Core 표시 기능 구현
공모전 팀 프로젝트 『ERROR CODE: H』 의 개발이 시작되었다.
우리 팀은 매주 회의를 통해 그 주에 필요한 기능을 정하고, 각자의 역할을 나누어 개발을 진행하기로 했다. 1주차에는 플레이어 조작, 적의 피격 및 공격 판정, Core 전환, Heat 시스템 등 게임의 기본적인 전투 구조를 만드는 작업이 동시에 진행되었다.
나는 그중 플레이어가 전투 상황을 빠르게 파악할 수 있도록 인게임 UI를 기획하고, 현재 Heat와 장착 중인 Core를 표시하는 기능을 맡았다.
이번 주의 목표는 단순히 화면 위에 게이지를 배치하는 것이 아니었다. 팀원이 구현한 Heat와 Core 시스템에서 상태가 변경되었을 때, 그 결과가 실제 HUD에 자연스럽게 반영되도록 연결하고, 이후 다른 전투 정보도 추가할 수 있는 UI의 기본 구조를 만드는 것이었다.
인게임 UI 기획안 제작
가장 먼저 전투 중 플레이어가 어떤 정보를 확인해야 하는지 정리하고, 각 정보를 어느 위치에 배치할지 구상했다.
『ERROR CODE: H』에서는 검·체인·총 Core를 전환하며 공격과 스킬을 연계한다. 공격하거나 Core를 교체하면 Heat가 증가하기 때문에, 플레이어는 전투 중에도 현재 장착 중인 Core와 누적된 Heat를 지속적으로 확인할 필요가 있었다.
이러한 게임의 특징을 바탕으로 UI 기획안에는 다음과 같은 요소를 배치했다.
- 현재 장착 중인 Core
- 교체 가능한 Core 목록
- 현재 Heat를 나타내는 원형 게이지
- 플레이어의 HP 바
- 공격 및 스킬 상태를 보여주는 하단 UI
초기 UI 기획안
1주차에 제작한 인게임 UI 기획안. 전투 중 시선을 크게 분산시키지 않으면서 현재 Core와 Heat 상태를 확인할 수 있도록 좌측 상단에 핵심 정보를 모아 배치했다.
현재 장착 중인 Core는 좌측 상단에 크게 표시하고, 교체 가능한 Core는 그 아래에 작은 아이콘으로 배치하는 방향으로 구성했다. Heat 게이지와 HP 바 역시 같은 영역에 묶어, 플레이어가 화면 여러 곳으로 시선을 옮기지 않고도 주요 전투 정보를 한 번에 파악할 수 있도록 했다.
하단에는 기본 공격, 가드, 스킬 등 전투 행동을 보여주는 영역을 구상했다.
다만 이번 기획안은 최종 UI 디자인을 확정하기 위한 시안이라기보다, 게임에 필요한 정보의 우선순위와 각 요소의 배치 관계를 정리하기 위한 초기 설계안에 가까웠다.
따라서 1주차의 실제 구현 범위는 기획안에 포함된 요소 가운데 Heat와 현재 Core 표시 기능에 집중했다.
별도의 UI 테스트 환경 만들기
처음부터 개발 중인 플레이어 씬에 UI를 직접 추가할 경우, 다른 시스템에서 발생한 문제와 UI 자체의 문제를 구분하기 어려울 수 있다고 판단했다.
그래서 먼저 UITestScene이라는 별도의 테스트 씬을 제작했다. 임시 이미지와 텍스트를 이용해 HUD를 구성하고, 실제 전투 시스템과 연결하기 전에 게이지의 기본적인 동작부터 독립적으로 확인할 수 있도록 했다.
초기 테스트 씬에는 다음과 같은 요소를 추가했다.
- Heat 게이지 프레임과 채움 이미지
- Heat 수치를 표시하는 텍스트
- 테스트용 HP 게이지
- 임시 Core 이미지
- 키 입력으로 수치를 변경하는 테스트 스크립트
테스트 단계에서는 UITestManager가 임시 Heat 값을 가지고 있고, 키를 누르면 해당 값이 증가하거나 감소하도록 만들었다.
private float currentHeat = 0f;
private void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
currentHeat = Mathf.Clamp(currentHeat + 10, 0, 100);
_heatUI.SetHeat(currentHeat);
}
if (Input.GetKeyDown(KeyCode.R))
{
currentHeat = Mathf.Clamp(currentHeat - 10, 0, 100);
_heatUI.SetHeat(currentHeat);
}
}
E 키를 누르면 Heat가 10씩 증가하고, R 키를 누르면 10씩 감소한다. 변경된 값은 SetHeat()를 통해 곧바로 Heat UI에 전달된다.
물론 이 코드는 실제 게임의 Heat 시스템이 아니다. 팀원이 구현한 HeatManager와 연결하기 전에, UI가 전달받은 값에 따라 정상적으로 변화하는지만 확인하기 위한 테스트 코드였다.
실제 시스템과의 연동을 시작하기 전에 UI 자체의 문제를 먼저 분리해 검증하고자 한 것이다.
Heat 값을 게이지와 퍼센트로 표시하기
Heat UI에서는 전달받은 수치를 그대로 사용하는 것이 아니라, Unity UI 이미지에서 사용할 수 있는 값으로 변환할 필요가 있었다.
Unity UI 이미지의 fillAmount는 0부터 1 사이의 값을 사용한다. 따라서 0부터 100까지의 Heat 값을 100으로 나누어 게이지의 채움 비율로 변환했다.
public void SetHeat(float current)
{
current = Mathf.Clamp(current, 0f, 100f);
heatFill.fillAmount = current / 100f;
if (heatText != null)
heatText.text = $"{current:0}%";
}
먼저 Mathf.Clamp()를 사용해 Heat 값이 0보다 작아지거나 100을 초과하지 않도록 제한했다.
그다음 현재 Heat를 100으로 나눈 값을 fillAmount에 전달했다. 예를 들어 Heat가 50이라면 fillAmount에는 0.5가 들어가고, 게이지는 절반만 채워진다.
동시에 같은 값을 정수 형태의 퍼센트 텍스트로 표시했다.
- Heat 0 → 게이지 0%, 텍스트
0% - Heat 50 → 게이지 50%, 텍스트
50% - Heat 100 → 게이지 100%, 텍스트
100%
1주차 당시 Heat 시스템의 최대값이 100을 기준으로 만들어져 있었기 때문에, UI 역시 동일하게 100을 기준으로 계산하도록 구성했다.
구조 자체는 단순했지만, 이후 실제 Heat 시스템과 연결할 때도 같은 표시 로직을 그대로 사용할 수 있도록 먼저 분리해 둔 것이 중요했다.
HP UI는 연결할 자리만 남겨두기
UI 기획안과 초기 테스트 씬에는 HP 바도 포함되어 있었다. 테스트 씬에서는 임시 HP 값을 증가시키거나 감소시키는 구조를 만들어 게이지의 표시 방식까지 시험했다. 그러나 7월 13일 당시에는 실제 HP 시스템이 UI와 연결할 수 있는 형태로 아직 준비되지 않은 상태였다.
따라서 이번 주에는 HP 연동까지 임의로 확장하지 않고, 향후 연결할 수 있는 자리만 남겨둔 채 Heat와 Core UI를 먼저 완성하기로 했다.
또한 팀 디스코드에도 다음과 같이 작업 결과를 공유했다.
UI 통합 작업 완료했습니다. 아직 HP 관련된 부분은 없는 것 같아서 그 부분은 남겨두고 Heat, Core UI만 먼저 작업했습니다.
실제 UIManager에서도 HP UI 참조는 준비해 두었지만, HP 데이터를 관리하는 시스템은 아직 연결하지 않은 상태로 남겨두었다.
[Header("UI Drawer References")]
[SerializeField] private HPDrawer _hpDrawer;
[SerializeField] private HeatDrawer _heatDrawer;
[SerializeField] private CoreDrawer _coreDrawer;
[Header("Variables References")]
//[SerializeField] private PlayerHealth _playerHealth;
[SerializeField] private HeatManager _heatManager;
[SerializeField] private CoreManager _coreManager;
HPDrawer가 들어갈 자리는 만들어두었지만, 실제 HP 데이터를 관리할 PlayerHealth 참조는 주석 처리되어 있다. 반면 이번 주의 구현 범위였던 HeatManager와 CoreManager는 실제 참조를 연결했다.
초기 상태를 표시하는 부분에서도 마찬가지였다.
private void Start()
{
//_hpUI.SetHP(currentHP, maxHP);
_heatDrawer.SetHeat(_heatManager.CurrentHeat);
_coreDrawer.SetCore(_coreManager.CurrentCoreData);
}
이 코드를 보면 당시의 작업 범위를 비교적 명확하게 확인할 수 있다.
- HP UI: 향후 연결을 위한 참조와 자리만 마련
- Heat UI: 실제
HeatManager의 현재 값과 연결 - Core UI: 실제
CoreManager의 현재 Core와 연결
아직 준비되지 않은 HP 시스템을 임시로 가정하여 구현하기보다, 현재 실제로 연결할 수 있는 Heat와 Core부터 먼저 통합하고 HP는 후속 작업으로 남겨두었다.
팀 프로젝트에서는 단순히 내가 맡은 기능만 빠르게 구현하는 것뿐 아니라, 다른 팀원의 작업 영역을 임의로 가정하지 않는 것 역시 중요하다고 생각했다.
동일한 클래스 이름으로 발생한 충돌 해결
UI 테스트 기능을 팀 프로젝트에 합치는 과정에서는 예상하지 못한 클래스 이름 충돌도 발생했다.
팀원이 Core와 Heat 시스템을 구현하면서 이미 HeatUI라는 이름의 스크립트를 추가한 상태였는데, 내가 독립 테스트용으로 만든 스크립트 역시 같은 이름을 사용하고 있었다.
별도의 네임스페이스 없이 동일한 HeatUI 클래스가 두 개 존재하면 Unity 프로젝트에서 충돌이 발생할 수 있었다.
이를 해결하기 위해 테스트용 스크립트의 이름을 HeatUITest로 변경했다.
이후 실제 게임 시스템과 연결할 UI는 역할에 따라 다음과 같이 다시 나누어 구성했다.
HeatManager: 실제 Heat 수치 관리CoreManager: 현재 장착 중인 Core 관리HeatDrawer: 전달받은 Heat 값을 게이지와 텍스트로 표시CoreDrawer: 현재 Core 정보를 UI에 표시UIManager: Heat·Core 시스템의 변화를 받아 각 UI로 전달
초기 테스트용 스크립트는 TestScripts(Old) 폴더로 옮겨 보관하고, 실제 게임에서 사용할 UI 표시 코드는 별도로 분리했다.
처음에는 단순한 이름 충돌 문제였지만, 이를 해결하는 과정에서 테스트 코드와 실제 통합 코드의 역할을 다시 나눌 수 있었다. 결과적으로 클래스 이름의 충돌을 해결하는 동시에, 각 스크립트가 무엇을 담당하는지 보다 명확하게 정리할 수 있었다.
Heat·Core 시스템과 UI 연결
팀원이 구현한 HeatManager와 CoreManager에는 각각의 상태가 변경될 때 알림을 보내는 이벤트가 존재했다.
- Heat가 변경되면
OnHeatChanged이벤트 발생 - 현재 Core가 변경되면
OnCoreChanged이벤트 발생
나는 UIManager가 이 두 이벤트를 구독하도록 구성했다.
private void OnEnable()
{
_heatManager.OnHeatChanged += HandleHeatChanged;
_coreManager.OnCoreChanged += HandleCoreChanged;
}
private void OnDisable()
{
_heatManager.OnHeatChanged -= HandleHeatChanged;
_coreManager.OnCoreChanged -= HandleCoreChanged;
}
OnEnable()에서는 Heat와 Core의 변경 이벤트에 각각의 처리 함수를 등록했다.
반대로 OnDisable()에서는 앞서 등록한 이벤트를 해제했다. UI 오브젝트가 비활성화되거나 제거된 이후에도 이벤트가 남아 불필요한 호출이 발생하는 것을 방지하기 위해, 이벤트의 등록과 해제를 한 쌍으로 구성했다.
이 구조를 사용하면 UI 전체가 매 프레임 Heat와 Core의 상태를 확인할 필요가 없다.
실제 값이 변경되었을 때만 이벤트를 통해 필요한 UI를 갱신할 수 있고, 전투 시스템과 UI 사이의 역할 역시 보다 명확하게 나눌 수 있었다.
Heat 변경 이벤트 처리
Heat 값이 변경되면 HeatManager는 현재 Heat와 최대 Heat를 함께 전달한다.
UIManager는 해당 이벤트를 받아 현재 Heat를 HeatDrawer에 넘기도록 했다.
private void HandleHeatChanged(float currentHeat, float maxHeat)
{
_heatDrawer.SetHeat(currentHeat);
}
이후 HeatDrawer.SetHeat()가 전달받은 값을 게이지의 채움 비율과 퍼센트 텍스트로 변환한다.
전체 흐름은 다음과 같다.
플레이어의 공격 또는 Core 전환
→ HeatManager의 Heat 수치 변경
→ OnHeatChanged 이벤트 발생
→ UIManager.HandleHeatChanged() 호출
→ HeatDrawer.SetHeat() 호출
→ 게이지와 퍼센트 텍스트 갱신
씬이 처음 시작될 때에는 HeatManager.CurrentHeat를 직접 읽어 초기 UI를 표시하고, 이후부터는 Heat가 실제로 변경될 때마다 이벤트를 통해 UI가 갱신된다.
단순히 현재 값을 화면에 표시하는 것뿐 아니라, Heat 값을 관리하는 영역과 이를 표현하는 영역을 분리했다는 점이 이번 구조에서 중요한 부분이었다.
현재 Core 변경 이벤트 처리
현재 장착 중인 Core 역시 같은 방식으로 연결했다.
플레이어가 Core를 교체하면 CoreManager가 OnCoreChanged 이벤트를 발생시킨다. UIManager는 새로운 CoreData를 받아 CoreDrawer에 전달한다.
private void HandleCoreChanged(CoreData coreData)
{
_coreDrawer.SetCore(coreData);
}
CoreDrawer는 전달받은 CoreData에서 현재 Core의 이름을 읽어 화면에 표시한다.
public void SetCore(CoreData coreData)
{
coreText.text = coreData != null
? coreData.CoreName
: "None";
}
정상적인 CoreData가 전달된 경우에는 현재 Core의 이름을 표시하고, Core가 지정되지 않은 경우에는 null 참조 오류가 발생하지 않도록 None을 표시했다.
전체 흐름은 다음과 같다.
플레이어가 Core 교체
→ CoreManager의 현재 Core 변경
→ OnCoreChanged 이벤트 발생
→ UIManager.HandleCoreChanged() 호출
→ CoreDrawer.SetCore() 호출
→ 현재 Core 텍스트 갱신
초기 기획안에서는 현재 Core와 교체 가능한 Core들을 이미지 아이콘으로 표시하는 것을 목표로 했다.
하지만 1주차에서는 먼저 디자인을 완성하기보다, 실제 Core 변경 정보가 UI까지 정상적으로 전달되는 구조를 완성하는 것에 집중했다. 아이콘이나 최종 디자인은 이후에도 얼마든지 변경할 수 있지만, 시스템에서 UI까지 데이터가 전달되는 구조는 이후 작업의 기반이 되기 때문이다.
UI의 역할을 나누어 관리하기
통합 작업에서는 하나의 스크립트가 모든 UI 기능을 처리하지 않도록 역할을 분리했다.
UIManager는 게임 시스템과 UI 사이에서 데이터를 전달하고, HeatDrawer와 CoreDrawer는 전달받은 데이터를 실제 화면에 표시한다.
public class UIManager : MonoBehaviour
{
[Header("UI Drawer References")]
[SerializeField] private HPDrawer _hpDrawer;
[SerializeField] private HeatDrawer _heatDrawer;
[SerializeField] private CoreDrawer _coreDrawer;
[Header("Variables References")]
[SerializeField] private HeatManager _heatManager;
[SerializeField] private CoreManager _coreManager;
}
각 클래스의 역할은 다음과 같이 구분했다.
HeatManager
- 현재 Heat 보관
- Heat 증가 및 감소 처리
- Heat 값 제한
- Heat가 변경되면 이벤트 발생
CoreManager
- 현재 장착 중인 Core 관리
- Core 교체 처리
- 현재
CoreData제공 - Core가 변경되면 이벤트 발생
UIManager
HeatManager와CoreManager참조- Heat·Core 이벤트 구독
- 시스템에서 받은 데이터를 각 Drawer로 전달
- 씬 시작 시 초기 UI 상태 설정
HeatDrawer
- Heat 값을 0~100 범위로 제한
- Heat 값을 이미지의 채움 비율로 변환
- Heat 퍼센트 텍스트 표시
CoreDrawer
- 현재
CoreData수신 - Core 이름 표시
- Core 데이터가 없을 경우
None표시
이렇게 역할을 분리하면서 Heat 계산 방식이 변경되더라도 게이지 표시 코드를 별도로 관리할 수 있고, 반대로 UI 디자인이 변경되더라도 Core 전환 로직을 직접 수정할 필요가 없는 구조를 만들 수 있었다.
작은 규모의 UI였지만, 이후 기능이 늘어날 것을 생각하면 처음부터 책임을 나누어두는 편이 관리하기 쉬울 것이라 판단했다.
테스트 UI에서 실제 게임 UI로 확장하기
초기 테스트 단계에서는 UITestManager가 임시 Heat 값을 직접 가지고 있었다.
키를 누르면 임시 값이 증가하거나 감소하고, 그 결과가 Heat 게이지에 반영되는 방식이었다.
이 구조는 UI 자체를 빠르게 확인하기에는 편리했지만, 당연히 실제 게임의 상태를 표시하는 구조는 아니었다.
통합 단계에서는 이러한 임시 값 기반의 테스트 구조에서 벗어나, UI가 실제 HeatManager와 CoreManager의 데이터를 전달받도록 변경했다.
변경 전후를 비교하면 다음과 같다.
테스트 단계
키 입력
→ UITestManager의 임시 Heat 값 변경
→ 테스트용 Heat UI 갱신
실제 통합 단계
공격 또는 Core 전환
→ HeatManager·CoreManager의 실제 상태 변경
→ 변경 이벤트 발생
→ UIManager가 이벤트 수신
→ HeatDrawer·CoreDrawer 갱신
UI 요소는 Canvas 프리팹으로 구성한 뒤 플레이어 테스트 씬에 배치했다.
이를 통해 UITestScene에서만 사용할 수 있는 임시 UI가 아니라, 이후 다른 씬에서도 재사용할 수 있는 HUD의 기본 구조를 마련했다.
처음에는 키 하나를 눌러 움직이는 테스트용 게이지에 불과했지만, 이 구조를 실제 전투 시스템과 연결하면서 점차 게임에서 사용할 수 있는 HUD로 확장해 나간 셈이다.
팀원과의 협업
이번 작업은 UI를 독립적으로 만드는 것만으로 끝나는 작업이 아니었다.
UI가 표시해야 할 정보는 결국 다른 팀원이 구현한 시스템에서 가져와야 했기 때문에, Core와 Heat 시스템의 구조를 이해하고 해당 시스템과 연결하는 과정이 필요했다.
Core와 Heat 시스템을 담당한 팀원이 CoreManager와 HeatManager를 구현했고, 각 시스템에서 상태가 변경될 때 이벤트가 발생하도록 구성했다. 나는 해당 이벤트를 UI에서 받아 화면에 표시하는 구조를 만들었다.
역할을 정리하면 다음과 같다.
- 시스템 담당: Core와 Heat의 실제 상태 및 변경 규칙 구현
- UI 담당: 상태 변경 이벤트를 받아 HUD에 표시
- 통합 과정: 테스트 씬에서 만든 UI를 플레이어 테스트 씬과 실제 시스템에 연결
이벤트를 기준으로 시스템과 UI를 연결하면서, UI 코드가 직접 전투 로직을 수정하거나 관리하지 않도록 역할을 나눌 수 있었다.
또한 HP 시스템이 아직 연결 가능한 상태가 아니라는 점을 확인한 뒤, 임시 로직을 추가하여 완성된 것처럼 처리하지 않았다.
현재 준비되어 있는 Heat와 Core부터 먼저 통합하고, HP 시스템이 구현된 이후 남겨둔 부분을 이어서 연결하는 방향으로 작업 범위를 조정했다.
이번 작업을 진행하면서 팀 프로젝트에서는 단순히 내가 필요한 기능을 모두 직접 만들어버리는 것보다, 다른 팀원이 만든 시스템과 역할을 존중하면서 서로의 코드를 연결하는 과정 자체가 중요하다는 점을 조금씩 느낄 수 있었다.
GitHub 작업 기록
1주차의 관련 커밋은 다음과 같다.
fcb4195 — feat: add UI test
작업일: 2026.07.12
UITestScene추가- Heat 및 HP 테스트 UI 구현
- UI 기획안과 임시 Core 이미지 추가
- 테스트용 한글 폰트 리소스 추가
- 키 입력을 통한 Heat 및 HP 변화 테스트 구성
- 실제 시스템과 연결하기 전의 독립 UI 테스트 환경 마련
d170153 — Fix Conflict
작업일: 2026.07.12
- 중복된
HeatUI클래스 이름 수정 - 테스트용 클래스를
HeatUITest로 변경 - 변경된 클래스 이름에 맞춰 씬 참조 수정
- 충돌 과정에서 변경된 UI 리소스 정리
1aba666 — feat: integrate Heat and Core UI
작업일: 2026.07.13
UIManager추가HeatDrawer추가CoreDrawer추가- Heat 변경 이벤트와 UI 연결
- Core 변경 이벤트와 UI 연결
Canvas프리팹 제작- 플레이어 테스트 씬에 HUD 통합
- 테스트용 스크립트를 기존 코드와 분리
- HP 연동 부분은 이후 구현을 위해 남겨둠
1주차 작업 흐름 정리
이번 주의 작업 흐름을 정리하면 다음과 같다.
- 인게임 HUD에 필요한 정보와 배치 방향을 기획했다.
- 별도의 UI 테스트 씬을 제작했다.
- 임시 키 입력을 이용해 Heat 게이지의 값 표시 구조를 만들었다.
- 기존 코드와 겹친
HeatUI클래스 이름을 정리했다. - 테스트 코드와 실제 통합 코드를 분리했다.
HeatManager의 변경 이벤트를 Heat UI와 연결했다.CoreManager의 변경 이벤트를 현재 Core UI와 연결했다.Canvas프리팹을 제작하고 플레이어 테스트 씬에 통합했다.- 아직 준비되지 않은 HP 연동 부분은 이후 작업을 위해 남겨두었다.
- 7월 13일 팀 디스코드에 Heat·Core UI 통합 완료 내용을 공유했다.
처음부터 실제 시스템을 한 번에 연결한 것이 아니라, UI 단독 테스트 → 문제 해결 → 실제 시스템 통합이라는 순서로 하나씩 범위를 넓혀나갔다.
1주차 결과
1주차에는 인게임 HUD의 초안을 만들고, Heat와 현재 Core를 실제 게임 시스템의 데이터와 연결하는 작업까지 완료했다.
최종적으로 구현한 기능은 다음과 같다.
- 현재 Heat를 게이지와 퍼센트로 표시
- Heat 값이 변경되면 HUD 자동 갱신
- 현재 장착 중인 Core 정보 표시
- Core를 교체하면 HUD 자동 갱신
- 씬 시작 시 현재 Heat와 Core 상태 초기화
- 별도의 UI 테스트 씬 제작
- 실제 플레이어 테스트 씬에 HUD 통합
Canvas프리팹을 통한 UI 재사용 기반 마련- 향후 HP 시스템을 연결할 수 있는 확장 지점 확보
기획안에 포함된 모든 UI를 한 번에 완성한 것은 아니다.
1주차에는 실제 시스템이 준비되어 있던 Heat와 Core를 우선 연결했고, HP는 테스트용 표시 구조와 향후 연결할 부분만 남겨두었다.
하지만 최종 디자인을 완성하는 것보다 먼저, 전투 시스템의 데이터가 HUD까지 전달되는 기본적인 흐름을 실제 게임 안에 만들어냈다는 점에서 의미 있는 첫 단계였다고 생각한다.
1주차를 마치며
이번 주에는 UI 초안을 만드는 것부터 독립적인 테스트 환경 구축, 클래스 이름 충돌 해결, 그리고 팀원이 만든 시스템과의 실제 연동까지 경험했다.
처음에는 임시 데이터를 이용해 Heat 게이지가 어떻게 표시되는지부터 확인했다. 이후 실제 통합 단계에서는 해당 임시 데이터를 걷어내고, HeatManager와 CoreManager에서 발생하는 이벤트를 받아 HUD가 갱신되도록 구조를 변경했다.
특히 같은 이름의 클래스가 서로 충돌했던 문제를 해결하면서, 팀 프로젝트에서는 단순히 내가 작성한 코드만 잘 동작한다고 끝나는 것이 아니라는 점을 느꼈다.
다른 팀원이 작성한 코드의 이름과 구조, 그리고 서로가 담당하고 있는 영역까지 함께 고려해야 하나의 프로젝트 안에서 문제없이 동작할 수 있었다.
또한 아직 준비되지 않은 HP 시스템을 임의로 구현하지 않고, 현재 연결 가능한 Heat와 Core부터 먼저 완성했다. 덕분에 이번 주의 작업 범위를 명확하게 유지하면서도, 이후 HP 기능을 추가할 수 있는 구조를 남겨둘 수 있었다.
아직 화면에 표시되는 요소들은 대부분 임시 이미지와 텍스트에 가깝다.
그래도 1주차의 작업을 통해 플레이어가 전투 중 현재 Heat와 사용 중인 Core를 확인할 수 있는 HUD의 기본적인 뼈대가 만들어졌다.
다음 단계에서는 이 구조를 바탕으로 임시 텍스트와 이미지를 실제 게임의 분위기에 맞게 개선하고, HP 시스템이 준비되는 대로 남겨둔 UI 연결 부분도 이어서 구현할 예정이다.
開発期間: 2026.07.05 ~ 2026.07.13
担当役割: ゲーム内HUDの企画およびHeat・現在のCore表示機能の実装
コンテストのチームプロジェクト 『ERROR CODE: H』 の開発が始まった。
私たちのチームでは、毎週のミーティングを通してその週に必要な機能を決め、それぞれの役割を分担しながら開発を進めることにした。1週目には、プレイヤー操作、敵の被弾・攻撃判定、Core切り替え、Heatシステムなど、ゲームの基本的な戦闘構造を作る作業が同時に進められた。
私はその中で、プレイヤーが戦闘状況を素早く把握できるように ゲーム内UIを企画し、現在のHeatと装備中のCoreを表示する機能 を担当した。
今週の目標は、単純に画面上へゲージを配置することではなかった。チームメンバーが実装したHeatとCoreシステムの状態が変更された際、その結果が実際のHUDへ自然に反映されるよう接続し、その後ほかの戦闘情報も追加できるUIの基本構造を作ることであった。
ゲーム内UI企画案の制作
まず、戦闘中にプレイヤーがどのような情報を確認する必要があるのかを整理し、それぞれの情報をどこに配置するのかを考えた。
『ERROR CODE: H』では、剣・チェーン・銃のCoreを切り替えながら攻撃とスキルを連携させる。攻撃したりCoreを切り替えたりするとHeatが増加するため、プレイヤーは戦闘中でも現在装備しているCoreと蓄積されたHeatを継続的に確認する必要があった。
こうしたゲームの特徴をもとに、UI企画案には以下の要素を配置した。
- 現在装備中のCore
- 切り替え可能なCore一覧
- 現在のHeatを示す円形ゲージ
- プレイヤーのHPバー
- 攻撃・スキル状態を表示する下部UI
初期UI企画案
1週目に制作したゲーム内UI企画案。戦闘中に視線を大きく分散させず、現在のCoreとHeatの状態を確認できるよう、左上に主要な情報をまとめて配置した。
現在装備中のCoreは左上に大きく表示し、切り替え可能なCoreはその下に小さなアイコンとして配置する方向で構成した。HeatゲージとHPバーも同じ領域にまとめ、プレイヤーが画面のさまざまな場所へ視線を移さなくても、主要な戦闘情報を一度に把握できるようにした。
下部には、基本攻撃、ガード、スキルなどの戦闘アクションを表示する領域を考えた。
ただし、今回の企画案は最終的なUIデザインを確定するためのものというより、ゲームに必要な情報の優先順位と、それぞれの要素の配置関係を整理するための初期設計案 に近いものであった。
そのため、1週目の実際の実装範囲は、企画案に含まれていた要素のうち Heatと現在のCore表示機能 に集中した。
独立したUIテスト環境を作る
最初から開発中のプレイヤーシーンへ直接UIを追加した場合、ほかのシステムで発生した問題とUIそのものの問題を区別しにくくなる可能性があると判断した。
そこで、まずUITestSceneという別のテストシーンを制作した。仮の画像とテキストを利用してHUDを構成し、実際の戦闘システムと接続する前に、ゲージの基本的な動作を独立して確認できるようにした。
初期のテストシーンには、以下の要素を追加した。
- HeatゲージのフレームとFill画像
- Heatの数値を表示するテキスト
- テスト用HPゲージ
- 仮のCore画像
- キー入力によって数値を変更するテストスクリプト
テスト段階では、UITestManagerが仮のHeat値を持ち、キーを押すとその値が増加・減少するようにした。
private float currentHeat = 0f;
private void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
currentHeat = Mathf.Clamp(currentHeat + 10, 0, 100);
_heatUI.SetHeat(currentHeat);
}
if (Input.GetKeyDown(KeyCode.R))
{
currentHeat = Mathf.Clamp(currentHeat - 10, 0, 100);
_heatUI.SetHeat(currentHeat);
}
}
Eキーを押すとHeatが10ずつ増加し、Rキーを押すと10ずつ減少する。変更された値はSetHeat()を通して、すぐにHeat UIへ渡される。
もちろん、このコードは実際のゲームで使用するHeatシステムではない。チームメンバーが実装したHeatManagerと接続する前に、UIが渡された値に応じて正常に変化するかだけを確認するためのテストコード であった。
実際のシステムとの連携を始める前に、まずUIそのものの問題を分離して検証しようとしたのである。
Heat値をゲージとパーセントで表示する
Heat UIでは、渡された数値をそのまま使用するのではなく、Unity UIのImageで使用できる値へ変換する必要があった。
Unity UI ImageのfillAmountは0から1までの値を使用する。そのため、0から100までのHeat値を100で割り、ゲージのFill量へ変換した。
public void SetHeat(float current)
{
current = Mathf.Clamp(current, 0f, 100f);
heatFill.fillAmount = current / 100f;
if (heatText != null)
heatText.text = $"{current:0}%";
}
まずMathf.Clamp()を使用し、Heat値が0未満になったり100を超えたりしないよう制限した。
その後、現在のHeatを100で割った値をfillAmountへ渡した。例えばHeatが50であれば、fillAmountには0.5が入り、ゲージは半分まで満たされる。
同時に、同じ値を整数形式のパーセントテキストとして表示した。
- Heat 0 → ゲージ 0%、テキスト
0% - Heat 50 → ゲージ 50%、テキスト
50% - Heat 100 → ゲージ 100%、テキスト
100%
1週目当時のHeatシステムは最大値100を基準として作られていたため、UI側も同じく100を基準に計算するよう構成した。
構造そのものは単純であったが、その後実際のHeatシステムと接続する際にも同じ表示ロジックをそのまま使用できるよう、先に分離しておいたことが重要であった。
HP UIは接続するための場所だけを残す
UI企画案と初期テストシーンには、HPバーも含まれていた。 テストシーンでは仮のHP値を増減させる構造を作り、ゲージの表示方法までテストした。しかし、7月13日時点では、実際のHPシステムがUIと接続できる形でまだ準備されていなかった。
そのため、今週はHP連携まで無理に拡張せず、今後接続できるための場所だけを残し、HeatとCore UIを先に完成させる ことにした。
また、チームのDiscordにも以下のように作業結果を共有した。
UI統合作業が完了しました。まだHP関連の部分はないようなので、その部分は残してHeat、Core UIだけ先に作業しました。
実際のUIManagerでもHP UIの参照自体は用意していたが、HPデータを管理するシステムはまだ接続していない状態のまま残した。
[Header("UI Drawer References")]
[SerializeField] private HPDrawer _hpDrawer;
[SerializeField] private HeatDrawer _heatDrawer;
[SerializeField] private CoreDrawer _coreDrawer;
[Header("Variables References")]
//[SerializeField] private PlayerHealth _playerHealth;
[SerializeField] private HeatManager _heatManager;
[SerializeField] private CoreManager _coreManager;
HPDrawerを入れるための場所は作ってあるが、実際のHPデータを管理するPlayerHealthの参照はコメントアウトされている。一方、今週の実装範囲であったHeatManagerとCoreManagerには実際の参照を接続した。
初期状態を表示する部分でも同様であった。
private void Start()
{
//_hpUI.SetHP(currentHP, maxHP);
_heatDrawer.SetHeat(_heatManager.CurrentHeat);
_coreDrawer.SetCore(_coreManager.CurrentCoreData);
}
このコードを見ると、当時の作業範囲を比較的明確に確認できる。
- HP UI: 今後接続するための参照と場所のみ用意
- Heat UI: 実際の
HeatManagerの現在値と接続 - Core UI: 実際の
CoreManagerの現在のCoreと接続
まだ準備されていないHPシステムを仮定して実装するのではなく、現在実際に接続できるHeatとCoreから先に統合し、HPは後続の作業として残した。
チームプロジェクトでは、単純に自分が担当した機能を素早く実装するだけでなく、ほかのチームメンバーの作業領域を勝手に仮定しないこと も重要だと考えた。
同じクラス名によって発生した競合を解決する
UIテスト機能をチームプロジェクトへ統合する過程では、予想していなかったクラス名の競合も発生した。
チームメンバーがCoreとHeatシステムを実装する際、すでにHeatUIという名前のスクリプトを追加していたが、私が独立テスト用として作ったスクリプトも同じ名前を使用していた。
別のNamespaceを使用せず、同じHeatUIクラスが二つ存在すると、Unityプロジェクト上で競合が発生する可能性があった。
これを解決するため、テスト用スクリプトの名前をHeatUITestへ変更した。
その後、実際のゲームシステムと接続するUIについては、役割ごとに以下のように分けて構成し直した。
HeatManager: 実際のHeat数値を管理CoreManager: 現在装備中のCoreを管理HeatDrawer: 渡されたHeat値をゲージとテキストとして表示CoreDrawer: 現在のCore情報をUIに表示UIManager: Heat・Coreシステムの変化を受け取り、それぞれのUIへ渡す
初期テスト用スクリプトはTestScripts(Old)フォルダへ移して保管し、実際のゲームで使用するUI表示コードは別に分離した。
最初は単純な名前の競合問題であったが、それを解決する過程で、テストコードと実際の統合コードの役割を改めて分けることができた。 結果としてクラス名の競合を解決すると同時に、それぞれのスクリプトが何を担当するのかを、より明確に整理することができた。
Heat・CoreシステムとUIを接続する
チームメンバーが実装したHeatManagerとCoreManagerには、それぞれの状態が変更された際に通知を送るイベントが存在した。
- Heatが変更されると
OnHeatChangedイベントが発生 - 現在のCoreが変更されると
OnCoreChangedイベントが発生
私はUIManagerがこの二つのイベントを購読するよう構成した。
private void OnEnable()
{
_heatManager.OnHeatChanged += HandleHeatChanged;
_coreManager.OnCoreChanged += HandleCoreChanged;
}
private void OnDisable()
{
_heatManager.OnHeatChanged -= HandleHeatChanged;
_coreManager.OnCoreChanged -= HandleCoreChanged;
}
OnEnable()では、HeatとCoreの変更イベントにそれぞれの処理関数を登録した。
反対にOnDisable()では、それまで登録していたイベントを解除した。UIオブジェクトが非アクティブになったり削除されたりした後にもイベントが残り、不要な呼び出しが発生することを防ぐため、イベントの登録と解除を一組で構成した。
この構造を使用すると、UI全体が毎フレームHeatとCoreの状態を確認する必要がなくなる。
実際に値が変更された場合だけ、イベントを通して必要なUIを更新でき、戦闘システムとUIの役割もより明確に分けることができた。
Heat変更イベントを処理する
Heat値が変更されると、HeatManagerは現在のHeatと最大Heatを一緒に渡す。
UIManagerはそのイベントを受け取り、現在のHeatをHeatDrawerへ渡すようにした。
private void HandleHeatChanged(float currentHeat, float maxHeat)
{
_heatDrawer.SetHeat(currentHeat);
}
その後、HeatDrawer.SetHeat()が渡された値をゲージのFill量とパーセントテキストへ変換する。
全体の流れは以下の通りである。
プレイヤーの攻撃またはCore切り替え
→ HeatManagerのHeat値が変更
→ OnHeatChangedイベントが発生
→ UIManager.HandleHeatChanged()を呼び出す
→ HeatDrawer.SetHeat()を呼び出す
→ ゲージとパーセントテキストを更新
シーンが最初に開始された際にはHeatManager.CurrentHeatを直接読み込み、初期UIを表示する。それ以降は、実際にHeatが変更されるたびにイベントを通してUIが更新される。
単純に現在値を画面へ表示するだけでなく、Heat値を管理する領域と、それを表示する領域を分離したこと が今回の構造において重要な部分であった。
現在のCore変更イベントを処理する
現在装備中のCoreも同じ方法で接続した。
プレイヤーがCoreを切り替えると、CoreManagerがOnCoreChangedイベントを発生させる。UIManagerは新しいCoreDataを受け取り、CoreDrawerへ渡す。
private void HandleCoreChanged(CoreData coreData)
{
_coreDrawer.SetCore(coreData);
}
CoreDrawerは渡されたCoreDataから、現在のCoreの名前を読み取って画面に表示する。
public void SetCore(CoreData coreData)
{
coreText.text = coreData != null
? coreData.CoreName
: "None";
}
正常なCoreDataが渡された場合は現在のCore名を表示し、Coreが指定されていない場合にはnull参照エラーが発生しないようNoneを表示した。
全体の流れは以下の通りである。
プレイヤーがCoreを切り替える
→ CoreManagerの現在のCoreが変更
→ OnCoreChangedイベントが発生
→ UIManager.HandleCoreChanged()を呼び出す
→ CoreDrawer.SetCore()を呼び出す
→ 現在のCoreテキストを更新
初期企画案では、現在のCoreと切り替え可能なCoreを画像アイコンとして表示することを目標としていた。
しかし1週目では、先にデザインを完成させることより、実際のCore変更情報がUIまで正常に伝わる構造を完成させること に集中した。 アイコンや最終デザインはその後でもいくらでも変更できるが、システムからUIまでデータが伝わる構造は、以降の作業の基盤となるためである。
UIの役割を分けて管理する
統合作業では、一つのスクリプトがすべてのUI機能を処理しないよう、役割を分けた。
UIManagerはゲームシステムとUIの間でデータを受け渡し、HeatDrawerとCoreDrawerは渡されたデータを実際の画面へ表示する。
public class UIManager : MonoBehaviour
{
[Header("UI Drawer References")]
[SerializeField] private HPDrawer _hpDrawer;
[SerializeField] private HeatDrawer _heatDrawer;
[SerializeField] private CoreDrawer _coreDrawer;
[Header("Variables References")]
[SerializeField] private HeatManager _heatManager;
[SerializeField] private CoreManager _coreManager;
}
各クラスの役割は、以下のように分けた。
HeatManager
- 現在のHeatを保持
- Heatの増加・減少処理
- Heat値の制限
- Heatが変更されるとイベントを発生
CoreManager
- 現在装備中のCoreを管理
- Core切り替え処理
- 現在の
CoreDataを提供 - Coreが変更されるとイベントを発生
UIManager
HeatManagerとCoreManagerを参照- Heat・Coreイベントを購読
- システムから受け取ったデータを各Drawerへ渡す
- シーン開始時に初期UI状態を設定
HeatDrawer
- Heat値を0~100の範囲に制限
- Heat値をImageのFill量へ変換
- Heatのパーセントテキストを表示
CoreDrawer
- 現在の
CoreDataを受信 - Core名を表示
- Coreデータが存在しない場合は
Noneを表示
このように役割を分けることで、Heatの計算方法が変わったとしてもゲージ表示用コードを別に管理でき、反対にUIデザインが変わったとしてもCore切り替えロジックを直接修正する必要がない構造を作ることができた。
小規模なUIではあったが、その後機能が増えていくことを考えると、最初から責任を分けておく方が管理しやすいと判断した。
テストUIから実際のゲームUIへ拡張する
初期テスト段階では、UITestManagerが仮のHeat値を直接持っていた。
キーを押すと仮の値が増加・減少し、その結果がHeatゲージへ反映される方式であった。
この構造はUIそのものを素早く確認するには便利であったが、当然ながら実際のゲーム状態を表示する構造ではなかった。
統合段階では、この仮の値を利用したテスト構造から離れ、UIが実際のHeatManagerとCoreManagerからデータを受け取るよう変更した。
変更前後を比較すると以下のようになる。
テスト段階
キー入力
→ UITestManagerの仮Heat値を変更
→ テスト用Heat UIを更新
実際の統合段階
攻撃またはCore切り替え
→ HeatManager・CoreManagerの実際の状態が変更
→ 変更イベントが発生
→ UIManagerがイベントを受信
→ HeatDrawer・CoreDrawerを更新
UI要素はCanvasプレハブとして構成した後、プレイヤーテストシーンへ配置した。
これにより、UITestSceneでしか使用できない仮のUIではなく、その後ほかのシーンでも再利用できるHUDの基本構造を用意することができた。
最初はキー一つを押して動かすだけのテスト用ゲージに過ぎなかったが、この構造を実際の戦闘システムと接続しながら、少しずつゲーム内で使用できるHUDへ拡張していった形である。
チームメンバーとの協業
今回の作業は、UIを独立して作るだけで完結するものではなかった。
UIが表示する情報は、結局ほかのチームメンバーが実装したシステムから取得する必要があるため、CoreとHeatシステムの構造を理解し、それらと接続する過程が必要であった。
CoreとHeatシステムを担当したチームメンバーがCoreManagerとHeatManagerを実装し、それぞれのシステムで状態が変更された際にイベントが発生するよう構成した。私はそのイベントをUI側で受け取り、画面へ表示する構造を作った。
役割を整理すると以下の通りである。
- システム担当: CoreとHeatの実際の状態・変更ルールを実装
- UI担当: 状態変更イベントを受け取りHUDへ表示
- 統合過程: テストシーンで作ったUIをプレイヤーテストシーンと実際のシステムへ接続
イベントを基準としてシステムとUIを接続することで、UIコードが直接戦闘ロジックを修正したり管理したりしないよう、役割を分けることができた。
また、HPシステムがまだ接続可能な状態ではないことを確認した後、仮のロジックを追加して完成したように処理することもしなかった。
現在準備されているHeatとCoreから先に統合し、HPシステムが実装された後に、残しておいた部分を続けて接続する方向へ作業範囲を調整した。
今回の作業を進めながら、チームプロジェクトでは単純に自分に必要な機能をすべて直接作ってしまうことよりも、ほかのチームメンバーが作ったシステムと役割を尊重しながら、互いのコードを接続していく過程そのものが重要である ということを少しずつ感じることができた。
GitHub作業記録
1週目の関連コミットは以下の通りである。
fcb4195 — feat: add UI test
作業日: 2026.07.12
UITestSceneを追加- Heat・HPテストUIを実装
- UI企画案と仮のCore画像を追加
- テスト用韓国語フォントリソースを追加
- キー入力によるHeat・HP変化テストを構成
- 実際のシステムへ接続する前の独立したUIテスト環境を用意
d170153 — Fix Conflict
作業日: 2026.07.12
- 重複していた
HeatUIクラス名を修正 - テスト用クラスを
HeatUITestへ変更 - 変更したクラス名に合わせてシーンの参照を修正
- 競合の過程で変更されたUIリソースを整理
1aba666 — feat: integrate Heat and Core UI
作業日: 2026.07.13
UIManagerを追加HeatDrawerを追加CoreDrawerを追加- Heat変更イベントとUIを接続
- Core変更イベントとUIを接続
Canvasプレハブを制作- プレイヤーテストシーンへHUDを統合
- テスト用スクリプトを既存コードから分離
- HP連携部分は今後の実装のために残す
1週目の作業フロー整理
今週の作業フローを整理すると、以下の通りである。
- ゲーム内HUDに必要な情報と配置方針を企画した。
- 別のUIテストシーンを制作した。
- 仮のキー入力を利用して、Heatゲージの値表示構造を作った。
- 既存コードと重複していた
HeatUIクラス名を整理した。 - テストコードと実際の統合コードを分離した。
HeatManagerの変更イベントをHeat UIへ接続した。CoreManagerの変更イベントを現在のCore UIへ接続した。Canvasプレハブを制作し、プレイヤーテストシーンへ統合した。- まだ準備されていないHP連携部分は今後の作業のために残した。
- 7月13日、チームのDiscordへHeat・Core UIの統合完了を共有した。
最初から実際のシステムを一度に接続したのではなく、UI単独テスト → 問題解決 → 実際のシステム統合 という順序で、一つずつ範囲を広げていった。
1週目の結果
1週目にはゲーム内HUDの初稿を作り、Heatと現在のCoreを実際のゲームシステムのデータへ接続するところまで完了した。
最終的に実装した機能は以下の通りである。
- 現在のHeatをゲージとパーセントで表示
- Heat値が変更されるとHUDを自動更新
- 現在装備中のCore情報を表示
- Coreを切り替えるとHUDを自動更新
- シーン開始時に現在のHeatとCore状態を初期化
- 別のUIテストシーンを制作
- 実際のプレイヤーテストシーンへHUDを統合
CanvasプレハブによるUI再利用の基盤を用意- 今後HPシステムを接続できる拡張ポイントを確保
企画案に含まれていたすべてのUIを一度に完成させたわけではない。
1週目では、実際のシステムが準備されていたHeatとCoreを優先的に接続し、HPについてはテスト用の表示構造と、今後接続する部分だけを残した。
しかし、最終デザインを完成させることよりも先に、戦闘システムのデータがHUDまで伝わる基本的な流れを実際のゲーム内に作り上げたこと に、意味のある最初の一歩があったと思う。
1週目を終えて
今週は、UIの初稿を作るところから、独立したテスト環境の構築、クラス名の競合解決、そしてチームメンバーが作ったシステムとの実際の連携まで経験した。
最初は仮のデータを利用し、Heatゲージがどのように表示されるのかから確認した。その後、実際の統合段階ではその仮データを取り除き、HeatManagerとCoreManagerから発生するイベントを受け取ってHUDが更新される構造へ変更した。
特に、同じ名前のクラス同士が競合した問題を解決しながら、チームプロジェクトでは単純に自分の書いたコードだけが正常に動作すれば終わりではない、ということを感じた。
ほかのチームメンバーが書いたコードの名前や構造、そしてそれぞれが担当している領域まで一緒に考慮してこそ、一つのプロジェクトの中で問題なく動かすことができた。
また、まだ準備されていないHPシステムを勝手に実装するのではなく、現在接続できるHeatとCoreから先に完成させた。そのおかげで、今週の作業範囲を明確に維持しながらも、その後HP機能を追加できる構造を残すことができた。
まだ画面に表示される要素のほとんどは、仮の画像とテキストに近い。
それでも、1週目の作業を通して、プレイヤーが戦闘中に 現在のHeatと使用中のCoreを確認できるHUDの基本的な骨格 が作られた。
次の段階では、この構造をもとに仮のテキストや画像を実際のゲームの雰囲気に合わせて改善し、HPシステムが準備され次第、残しておいたUIの接続部分についても続けて実装する予定である。