개발 기간: 2026.07.27 ~ 2026.08.03
담당 역할: Pause 인터페이스, 상태창, 인벤토리 및 종료 메뉴 기반 제작
3주차에는 기존 HUD를 사이버펑크 스타일로 리메이크하고, Heat 상태 효과와 Skill 아이콘, 사망 연출을 추가했다.
4주차에는 플레이 중 ESC 키를 눌렀을 때 나타나는 Pause 인터페이스 를 제작하기 시작했다.
최종적으로 구상한 Pause 화면은 다음과 같은 구조였다.
- 좌측: Resume, Setting, Exit 등의 메뉴 버튼
- 우측: 플레이어 상태 정보
- 우측: 현재 보유 Core 및 인벤토리
- 배경: 『ERROR-CODE-H』의 분위기에 맞는 동적 사이버펑크 효과
다만 이번 주에는 이 모든 기능을 한 번에 완성하기보다, ESC 입력을 받아 게임을 일시정지하고 Pause Panel을 표시하는 기본 구조와 동적인 배경 효과 를 먼저 구현하는 데 집중했다.
상태창, 인벤토리, 설정, 종료 버튼의 실제 기능은 이후 Pause 화면 위에 추가할 수 있도록 남겨두고, 우선 일시정지 상태 자체가 안정적으로 동작하는 기반부터 만드는 것을 목표로 했다.
4주차 목표
이번 주에 계획한 작업은 다음과 같았다.
- ESC 키를 이용한 일시정지
- Pause Panel 표시
- Resume 기능을 연결할 수 있는 구조
- 마우스를 사용할 수 있도록 커서 활성화
- 게임 플레이 입력과 UI 입력 분리
- 상태창과 인벤토리를 배치할 Pause 화면 레이아웃
- UI 배경용 범용 셰이더 제작
- Pause 중에도 움직이는 동적 배경
- 마우스 위치에 반응하는 배경 변화
- Core 1 인터페이스 디자인 개선
- 이후 Core 2·3에 적용할 디자인 기준 마련
이번 주 역시 계획한 모든 항목을 억지로 한 번에 끝내기보다는, 완료한 기능과 후속 작업을 구분해 진행 했다.
ESC 입력을 Input System에 추가하기
가장 먼저 Unity Input System의 InputSystem_Actions에 Pause 입력을 추가했다.
게임 플레이 중 ESC 키를 눌렀을 때 사용할 Pause Action을 Player Action Map에 추가하고, Pause 화면으로 진입한 이후에도 같은 ESC 키로 게임을 재개할 수 있도록 UI Action Map의 Cancel에도 ESC를 연결했다.
입력 구조는 다음과 같이 구성했다.
PlayerAction Map의Pause→ ESCUIAction Map의Cancel→ ESC- 두 입력 모두
PauseManager.OnPause()호출 - Pause 진입 시 Action Map을
Player에서UI로 변경 - Resume 시 다시
UI에서Player로 복귀
Pause 입력을 받는 메서드에서는 입력이 실제 수행된 시점인지 먼저 확인했다.
public void OnPause(InputAction.CallbackContext context)
{
if (!context.performed)
return;
TogglePause();
}
InputAction.CallbackContext는 Action의 시작, 수행, 취소 등 여러 단계에서 호출될 수 있기 때문에, context.performed를 확인해 ESC 입력이 실제로 처리된 순간에만 TogglePause()가 실행되도록 했다.
현재 Pause 상태에 따라 게임을 일시정지하거나 재개한다.
public void TogglePause()
{
if (isPaused)
Resume();
else
Pause();
}
게임 플레이 중 ESC를 누르면 Pause 상태로 진입하고, Pause 화면에서 ESC를 다시 누르면 Resume 상태로 돌아가도록 구성했다.
[캡처 1 삽입 — Input Actions의 Pause와 UI Cancel 설정]
Player와UIAction Map 양쪽에 ESC 입력을 연결했다. Pause 진입 후 Action Map이 UI로 전환된 상태에서도 동일한 ESC 키로 게임을 재개할 수 있도록 구성했다.
게임 일시정지 처리
Pause 상태에 진입할 때에는 단순히 Pause Panel만 화면에 띄우지 않았다.
게임 시간, 플레이어 입력, 마우스 커서, Input Action Map을 함께 변경하여 플레이 상태 자체를 UI 조작 상태로 전환 하도록 만들었다.
private void Pause()
{
if (isPaused)
return;
isPaused = true;
Time.timeScale = 0f;
playerController.SetInputEnabled(false);
pausePanel.SetActive(true);
Cursor.visible = true;
playerInput.SwitchCurrentActionMap("UI");
}
Pause 상태에서는 다음과 같은 처리가 이루어진다.
- 중복 Pause 실행을 방지한다.
isPaused를true로 변경한다.Time.timeScale을0으로 설정한다.- 플레이어 조작 입력을 비활성화한다.
- Pause Panel을 활성화한다.
- 마우스 커서를 표시한다.
- Input Action Map을
Player에서UI로 전환한다.
Time.timeScale = 0으로 설정하면 Time.deltaTime을 기준으로 동작하는 이동이나 애니메이션, 그리고 Scaled Time을 사용하는 처리들이 정지한다.
여기에 플레이어 컨트롤러의 입력까지 비활성화함으로써, Pause 상태에서 이동이나 공격이 발생하지 않도록 했다.
또한 이후 Resume, Setting, Exit와 같은 버튼을 마우스로 조작할 수 있도록 커서를 화면에 표시했다.
[캡처 2 삽입 — 일반 플레이 화면과 ESC Pause 화면 비교]
ESC 키를 누르면 게임의 시간과 플레이어 입력이 정지하고, 마우스 커서와 Pause 화면이 활성화된다.
게임 재개 처리
Resume 상태에서는 Pause 과정에서 변경했던 값들을 다시 원래 상태로 복구한다.
private void Resume()
{
if (!isPaused)
return;
isPaused = false;
Time.timeScale = 1f;
playerController.SetInputEnabled(true);
pausePanel.SetActive(false);
Cursor.visible = false;
playerInput.SwitchCurrentActionMap("Player");
}
Resume 시에는 다음과 같은 순서로 상태를 되돌린다.
isPaused를false로 변경한다.Time.timeScale을1로 복구한다.- 플레이어 입력을 다시 활성화한다.
- Pause Panel을 숨긴다.
- 마우스 커서를 숨긴다.
- Input Action Map을
UI에서Player로 되돌린다.
Pause Panel의 Resume 버튼에서도 사용할 수 있도록 공개 메서드 역시 준비했다.
public void ResumeGame()
{
Resume();
}
다만 이번 주 커밋에는 Button_Resume 오브젝트와 ResumeGame() 메서드까지는 존재하지만, 버튼의 OnClick에 해당 메서드를 실제로 연결한 기록은 남아 있지 않았다.
따라서 이번 주에 확실하게 동작하는 재개 방식은 ESC 키를 다시 누르는 방식 이고, Resume 버튼은 추후 실제 기능을 연결할 수 있도록 준비한 단계였다.
추가로 오브젝트가 파괴될 때 Time.timeScale이 0으로 남는 상황을 막기 위한 방어 코드도 추가했다.
private void OnDestroy()
{
Time.timeScale = 1f;
}
씬을 종료하거나 PauseManager가 파괴되는 과정에서 Time.timeScale이 0인 상태로 남으면, 다음 씬에서도 게임이 멈춘 것처럼 보일 수 있다.
이를 방지하기 위해 오브젝트가 파괴될 때 기본값인 1로 복구하도록 했다.
Pause 화면 레이아웃 계획
Pause 화면은 최종적으로 좌우 영역을 나누어 사용하는 형태로 계획했다.
좌측에는 플레이어가 직접 선택할 수 있는 메뉴를 배치하고, 우측에는 게임의 상태와 인벤토리를 보여주는 구조다.
좌측 메뉴 계획
- Resume
- Setting
- Exit
- 필요에 따라 추가되는 메뉴 버튼
우측 정보 계획
- 플레이어 현재 상태
- 현재 HP와 Heat
- 장착 중인 Core
- 보유 Core 또는 아이템
- 인벤토리
- 기타 상태 정보
이번 주에는 Pause Panel과 배경, 그리고 각종 버튼을 배치할 수 있는 기본 영역부터 만들었다.
테스트 씬에는 Button_Resume과 여러 테스트용 버튼 오브젝트가 추가되었지만, 실제 Setting·Exit·Inventory 기능은 아직 연결하지 않았다.
즉, 이번 주의 결과물은 완성된 상태창이나 인벤토리가 아니라 이후 상태창과 인벤토리를 담아낼 Pause 인터페이스의 기반 이었다.
[캡처 3 삽입 — Pause 화면 전체 레이아웃과 향후 영역 표시]
Pause 화면의 기본 레이아웃. 좌측에는 Resume·Setting·Exit 버튼을, 우측에는 플레이어 상태와 인벤토리를 배치하는 방향으로 계획했다.
아직 구현되지 않은 영역은 블로그 이미지에서도 “예정”, “구상 중” 등으로 표시해 실제로 완성된 기능과 구분하기로 했다.
Pause 중에도 Core가 전환되는 문제
Pause 상태에서는 플레이어 컨트롤러의 입력을 비활성화하고, Action Map도UI로 전환했다.
하지만 테스트 과정에서 Pause 상태에서도 숫자 키를 이용해 Core를 전환할 수 있는 문제 가 남아 있다는 것을 확인했다.
당시 작업 기록에도 다음과 같이 남겼다.
현재 Pause 상태에서도 Core 전환이 가능한 문제가 있습니다. 다만 이 부분은 제가 해결할 수 있는 부분은 아닌 듯해 일단 건드리지 않았습니다. 현재 Pause는 TimeScale을 0으로 하는 방식으로 구현해둔 상태입니다.
원인은 Core 전환 입력이 PlayerController2D나 Input System의 Action Map을 통해 처리되지 않고, CoreManager.Update()에서 기존 Input API로 직접 키 입력을 확인하고 있었기 때문이다.
private void Update()
{
for (int i = 0; i < coreSlots.Count; i++)
{
CoreSlot slot = coreSlots[i];
if (slot == null || slot.SelectKey == KeyCode.None)
continue;
if (Input.GetKeyDown(slot.SelectKey))
SetCurrentCore(i);
}
}
Time.timeScale을 0으로 설정하더라도 Unity의Update() 호출이나 키 입력 확인 자체가 완전히 중단되는 것은 아니다.
또한 CoreManager는 현재 PlayerInput의 Action Map이 Player인지 UI인지 확인하지 않고 직접 숫자 키 입력을 읽고 있었다.
따라서 PauseManager에서 다음과 같은 처리를 모두 수행하더라도 Core 전환은 별도로 차단되지 않았다.
- 플레이어 컨트롤러 입력 비활성화
PlayerAction Map에서UIAction Map으로 전환Time.timeScale = 0
이를 해결하려면 CoreManager가 Pause 상태나 공용 입력 잠금 상태를 확인하도록 만들거나, Core 전환 입력 자체를 Input System Action으로 통합할 필요가 있었다.
하지만 이는 단순한 Pause UI 수정이 아니라 Core 시스템의 입력 처리 방식까지 변경해야 하는 문제 였다.
따라서 이번 주에는 다른 팀원이 담당한 Core 시스템을 임의로 변경하지 않고, 원인과 해결 방향만 확인한 상태로 남겨두었다.
단순히 “Pause에서 Core가 바뀌는 버그가 있다”로 끝나는 문제가 아니라, 서로 다른 입력 방식이 하나의 프로젝트 안에서 섞여 있을 때 Pause 같은 전역 상태 제어가 어려워질 수 있다는 점을 확인한 사례 였다.
[선택 캡처 4 삽입 — Pause 중 Core가 전환되는 알려진 문제]
Pause 상태에서도
CoreManager가 숫자 키를 직접 확인하고 있어 Core 전환이 가능했다. Pause UI만으로 해결할 수 있는 문제가 아니라 Core 입력 구조와 함께 수정해야 하는 항목으로 분리했다.
UI 에셋 및 폴더 구조 정리
4주차에는 UI 관련 에셋의 위치도 정리했다.
기존에는 Assets/jhkim 아래에 UI 관련 파일들이 위치해 있었지만, 이를 Assets/WorkSpace/jhkim으로 이동하여 팀 프로젝트의 작업 폴더 구조에 맞췄다.
이동된 항목에는 다음과 같은 파일들이 포함되어 있었다.
- HUD 프리팹
- HP·Heat·Core Drawer
- Skill UI 관련 코드
- Death UI 관련 코드
- UI 테스트 씬
- 폰트
- Material 및 Shader Graph
- 임시 UI 이미지
파일을 이동할 때에는 .meta 파일 역시 함께 유지하여 Unity 에셋의 GUID 참조가 끊어지지 않도록 했다.
또한 사용하지 않게 된 임시 에셋들은 바로 삭제하지 않고 TrashCan 폴더로 옮겼다.
팀 프로젝트에서는 내가 사용하지 않는다고 생각한 에셋이 다른 씬이나 프리팹에서 참조되고 있을 가능성이 있기 때문에, 바로 제거하기보다 먼저 분리해서 보관하는 쪽을 선택했다.
Core 1 디자인 개선
Pause 화면 작업과 함께 Core 1의 UI 디자인도 일부 개선했다.
기존 Core 1 아이콘을 새로운 사이버펑크 스타일의 이미지로 교체하고, 해당 이미지를 Core_01의 ScriptableObject 데이터에 다시 연결했다.
이 변경은 코드가 아니라 Core_01.asset의 coreIcon 참조를 새로운 이미지로 변경하는 방식으로 이루어졌다.
3주차에는 Core 이름과 아이콘을 화면에 표시하는 기능을 만들었다면, 4주차에는 실제로 화면에 표시되는 Core 1 아이콘 자체의 디자인을 개선한 것 이다.
이번 주에는 우선 Core 1을 먼저 수정하고, 이후 동일한 디자인 규칙을 Core 2와 Core 3에도 적용할 계획을 세웠다.
Heat 게이지 역시 추후 Core UI와 디자인적인 통일감을 가질 수 있도록 다시 조정할 예정이었다.
다만 Core 2·3의 신규 디자인과 Heat 게이지의 추가 리메이크는 이번 주의 완료 범위에는 포함하지 않았다.
[캡처 5 삽입 — Core 1 아이콘 디자인 전후 비교]
기존 Core 1 아이콘을 새로운 사이버펑크 디자인으로 교체했다. 이후 Core 2·3과 Heat 게이지에도 같은 디자인 규칙을 적용할 계획을 세웠다.
범용 UI Shader Graph 실험
Pause 화면의 배경에는 단순한 정적 이미지 대신, 『ERROR-CODE-H』의 사이버펑크 분위기에 어울리는 동적 셰이더를 적용하고자 했다.
처음에는 Pause 화면에만 사용하는 셰이더를 만드는 대신, 여러 UI에서 공통으로 재사용할 수 있는 범용 UI Shader Graph 를 제작하는 방향으로 시도했다.
구성한 기능은 다음과 같다.
- Appearance
- Distortion
- Glow
- Gradient
- Mask
- Noise
- Pixelize
- Reveal
- Scan
각각의 기능은 Sub Graph로 나누고, 이를 하나의 SG_UIFramework에서 조합하는 구조를 만들었다.
목표는 Material에서 필요한 효과만 선택해 사용할 수 있도록 만드는 것이었다.
예를 들어 UI 종류에 따라 다음과 같은 방식으로 활용하는 것을 생각했다.
- Pause 배경 → Noise, Distortion, Scan
- Core 아이콘 → Glow, Reveal
- 일반 패널 → Gradient, Mask
- 강조 UI → Pixelize, Scan
다만 실제로 테스트하면서 UI Mask를 비롯한 일부 기능이 정상적으로 동작하지 않는 문제가 발생했다.
8월 2일 커밋에도 다음과 같이 기록했다.
Shader Graph 기반으로 UI 전반에 적용하기 위한 Shader를 제작했습니다. 다만 Mask를 비롯한 일부 기능이 작동하지 않는 점을 확인했습니다. 어디가 문제인지 찾기 어려워 HLSL 기반으로 다시 작성할 생각이며, 현재 버전은 임시로 올립니다.
처음 시도한 구조가 원하는 대로 동작하지 않았지만, 문제를 숨기거나 완성된 기능처럼 처리하지 않고 현재 한계와 다음 구현 방향을 그대로 기록 했다.
[캡처 6 삽입 — 범용 Shader Graph 전체와 Sub Graph 구성]
Glow, Gradient, Noise, Distortion, Mask, Scan 등의 기능을 조합할 수 있는 범용 UI Shader Graph를 실험했다. UI Mask 호환 문제를 확인한 뒤 HLSL 기반 구현으로 방향을 변경했다.
Shader Graph에서 HLSL 기반 셰이더로 전환
Shader Graph 기반의 범용 셰이더에서 UI Mask 문제가 발생하자, Pause 배경용 셰이더는 HLSL 기반의 Shader_Background.shader로 다시 제작했다.
셰이더에는 다음과 같은 속성을 마련했다.
_TintColor("TintColor", Color) = (1, 1, 1, 1)
_Opacity("Opacity", Range(0, 1)) = 1
_Progress("Progress", Range(0, 1)) = 1
_PixelCount("PixelCount", Range(1, 1024)) = 128
_GlowIntensity("GlowIntensity", Range(0, 5)) = 1
_ScanSpeed("ScanSpeed", Range(-5, 5)) = 1
_NoiseStrength("NoiseStrength", Range(0, 1)) = 0.2
_DistortionStrength("DistortionStrength", Range(0, 0.1)) = 0
이러한 값을 통해 하나의 Material 안에서 다음과 같은 표현을 조정할 수 있도록 했다.
- UI 색조
- 투명도
- 좌우 진행도
- 픽셀화 정도
- Glow 강도
- Scan 속도
- Noise 강도
- Distortion 강도
Shader Graph에서 문제가 되었던 UI Mask와의 호환을 위해 Stencil 관련 속성도 직접 추가했다.
_Stencil("Stencil Ref", Float) = 0
_StencilComp("Stencil Comp", Float) = 8
_StencilOp("Stencil Op", Float) = 0
_StencilReadMask("Stencil Read Mask", Float) = 255
_StencilWriteMask("Stencil Write Mask", Float) = 255
_ColorMask("Color Mask", Float) = 15
처음 만든 Shader Graph 구조를 그대로 고집하기보다, 실제 UI에서 필요했던 Mask와 Stencil 처리를 직접 다룰 수 있도록 HLSL 기반으로 방향을 바꾼 것이다.
이번 작업을 통해 구현하기 편한 방식과 실제 요구사항을 만족하는 방식이 항상 같지는 않다는 점 을 다시 느낄 수 있었다.
Pause 중에도 셰이더가 움직이도록 만들기
Pause 상태에서는 Time.timeScale이 0이 된다.
그런데 Pause 화면의 Noise, Scan, Distortion 효과까지 함께 멈춰버리면, 화면 전체가 단순한 정지 이미지처럼 보이게 된다.
이번 Pause 화면은 게임 자체는 멈추더라도 인터페이스는 계속 살아 움직이는, 일종의 시스템 화면처럼 보이기를 원했다.
그래서 셰이더의 시간 값을 게임 시간과 분리할 필요가 있었다.
이를 위해 Time.unscaledTime을 전역 셰이더 값으로 전달하는 DontStopShader를 추가했다.
private void UpdateTimeParams()
{
float t = Time.unscaledTime;
Shader.SetGlobalVector("_UnscaledTimeParams", new Vector4(t * xScale, t * yScale, t * zScale, t * wScale));
}
Time.unscaledTime은 Time.timeScale의 영향을 받지 않는다.
따라서 게임 시간이 멈춰 있어도 _UnscaledTimeParams는 계속 증가하고, Pause 배경 셰이더의 애니메이션을 유지할 수 있다.
셰이더에서는 Unity의 기본 Time Parameter 대신 전달받은 Unscaled Time을 사용하도록 했다.
float4 _UnscaledTimeParams;
이를 통해 게임 플레이는 완전히 정지해 있지만 Pause 화면의 Scan과 Noise는 계속 움직이는 상태 를 만들 수 있었다.
[캡처 7 삽입 — Pause 중에도 움직이는 Scan 또는 Noise 효과]
Time.timeScale이0인 Pause 상태에서도 UI 셰이더가 움직이도록Time.unscaledTime을 전역 셰이더 값으로 전달했다.
이 기능은 정적인 이미지보다 2~3초 정도의 GIF나 짧은 영상으로 보여주는 편이 훨씬 잘 드러날 것 같다.
마우스 위치에 반응하는 Pause 배경
Pause 화면에서는 마우스를 움직일 때 배경 표현도 함께 변화하도록 PauseMouseController를 추가했다.
먼저 Pause Panel을 기준으로 마우스의 로컬 좌표를 계산했다.
RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRect, Input.mousePosition, cam, out Vector2 localPoint);
Rect rect = panelRect.rect;
float mouseX = Mathf.InverseLerp(rect.xMin, rect.xMax, localPoint.x);
mouseX = Mathf.Clamp01(mouseX);
마우스의 X좌표를 0~1 사이의 값으로 정규화한 뒤, 이를 다시 좌우 방향을 나타내는 값으로 변환했다.
float signed = (0.5f - mouseX) * 2f;
float amplified = Mathf.Clamp(signed * amplify, -1f, 1f);
float target = center + amplified * moveRange;
마우스가 화면 중앙에 있을 때는 기준값인 center를 사용하고, 좌우로 이동할수록 moveRange 범위 안에서 목표값이 달라지도록 했다.
값이 갑자기 튀지 않도록 Lerp()를 사용해 부드럽게 이동시켰다.
이 과정에서도 Pause 상태의 영향을 받지 않도록 Time.unscaledDeltaTime을 사용했다.
float t = Mathf.Clamp01(lerpSpeed * Time.unscaledDeltaTime);
progress = Mathf.Lerp(progress, target, t);
계산된 값은 Material과 전역 Shader 변수 양쪽에 전달했다.
materialInstance.SetFloat("_Progress", progress);
Shader.SetGlobalFloat("_GlobalProgress", progress);
전체 흐름을 정리하면 다음과 같다.
마우스 좌우 이동
→ Pause Panel 기준 로컬 좌표 계산
→ X좌표를 0~1로 정규화
→ 목표 Progress 계산
→ Unscaled Delta Time으로 부드럽게 보간
→ Material의 _Progress 변경
→ Pause 배경 표현 변화
이 기능을 통해 Pause 화면은 단순히 움직이는 배경에서 한 단계 더 나아가, 플레이어의 마우스 움직임에 반응하는 인터페이스 가 되었다.
[캡처 8 삽입 — 마우스 좌·중앙·우 위치에 따른 Pause 배경 비교]
Pause 화면에서 마우스를 좌우로 움직이면 셰이더의
_Progress값이 달라지고, 그에 따라 배경 표현이 유동적으로 변화한다.
이 기능 역시 정적인 비교 이미지보다 GIF로 보여주는 편이 실제 느낌을 전달하기에 더 적합할 것 같다.
PlayerController2D 클래스명 오타 수정
Pause 기능을 구현하는 과정에서는 기존 플레이어 컨트롤러의 클래스명 오타도 수정했다.
변경 전:
public class PlayerCotroller2D : MonoBehaviour
변경 후:
public class PlayerController2D : MonoBehaviour
기존 클래스명에는 Controller의 n이 빠져 있었다.
PauseManager에서 플레이어 컨트롤러를 참조해야 하는 상황에서 잘못된 클래스명을 그대로 확장해서 사용하는 것보다, 이 시점에서 올바른 이름으로 정리하는 편이 낫다고 판단했다.
이에 맞춰 다음과 같은 참조 역시 함께 수정했다.
PlayerHealthSwordCoreSkillPlayerController2D스크립트- Player Prefab
- Player 테스트 씬
클래스명 변경은 여러 코드와 Prefab 참조에 영향을 줄 수 있는 작업이었기 때문에, 이후 다른 팀원이 남아 있던 일부 타입 참조를 추가로 정리했다.
GitHub 작업 기록
4주차에 내가 작업한 주요 커밋은 다음과 같다.
037656f — UI 에셋 수정 및 Pause 구현을 위한 Input System 수정
작업일: 2026.07.29 오전 6시 54분
PlayerAction Map에 Pause Action 추가- ESC 키와 Pause Action 연결
UIAction Map의 Cancel에 ESC 추가PauseManager기본 구조 추가- 초기 Pause·Resume 상태 전환 구현
- UI 에셋 수정
- Core 1과 Skill 관련 신규 이미지 추가
- UI 관련 폴더를
Assets/WorkSpace/jhkim으로 이동 - HUD 프리팹 구조 수정
dd4e92d — PlayerCotroller2D 클래스명 오타 수정 및 Pause 기능 임시 구현
작업일: 2026.07.30 오전 4시 17분
PlayerCotroller2D를PlayerController2D로 수정- 관련 코드 참조 수정
- Pause 입력 Callback 추가
Time.timeScale을 이용한 일시정지- 플레이어 입력 비활성화
- Pause Panel 활성화
- 마우스 커서 표시
Player/UIAction Map 전환- Resume 처리
- Pause 테스트 씬 연결
b79f329 — Pause 기능 작업 병합
작업일: 2026.07.30 오전 4시 19분
- Pause System 작업을 메인 작업 흐름에 병합
13b346b — Shader Graph 기반 UI용 범용 Shader 제작
작업일: 2026.08.02 오전 4시 16분
- 범용 UI Shader Graph 제작
- Appearance Sub Graph
- Distortion Sub Graph
- Glow Sub Graph
- Gradient Sub Graph
- Mask Sub Graph
- Noise Sub Graph
- Pixelize Sub Graph
- Reveal Sub Graph
- Scan Sub Graph
- 테스트 Material 및 Panel 이미지 추가
- UI Mask 등 일부 기능의 문제 확인
- HLSL 전환 계획 공유
f5fb363 — Pause 화면 추가 및 Shader 개선
작업일: 2026.08.03 오전 1시 48분
- Pause 화면 추가
- Pause Panel 및 버튼 배치 기반 구성
- HLSL 기반
Shader_Background추가 - UI Stencil 및 Mask 대응 속성 추가
DontStopShader추가- Pause 상태에서도 Shader 시간 진행
PauseMouseController추가- 마우스 위치에 따른
_Progress변화 - Core 1 신규 디자인 적용
- Pause용 신규 이미지 에셋 추가
- 사용하지 않는 임시 에셋 분리
- Heat 텍스트 표시 일부 수정
ba2f675 — Pause 화면 및 Shader 작업 병합
작업일: 2026.08.03 오전 1시 49분
- Pause 화면과 Shader 개선 작업을 메인 작업 흐름에 병합
4주차 작업 흐름 정리
이번 주의 작업 흐름을 정리하면 다음과 같다.
- Pause 화면의 최종 레이아웃을 구상했다.
PlayerAction Map에 Pause 입력을 추가했다.UIAction Map에도 ESC 입력을 추가했다.- ESC 입력을
PauseManager에 연결했다. Time.timeScale을 이용한 정지 기능을 구현했다.- Pause 상태에서 플레이어 입력을 비활성화했다.
Player와UIAction Map을 전환했다.- 마우스 커서를 표시하고 Pause Panel을 활성화했다.
- Resume 처리를 위한 메서드를 준비했다.
- Pause 중 Core 전환 문제가 남아 있음을 확인했다.
- UI 에셋과 폴더 구조를 정리했다.
- Core 1의 신규 디자인을 적용했다.
- 범용 UI Shader Graph를 제작했다.
- UI Mask 등 일부 기능의 문제를 확인했다.
- Pause 배경을 HLSL 기반 Shader로 전환했다.
- Unscaled Time을 이용해 Pause 중에도 Shader가 움직이도록 했다.
- 마우스 위치에 따라 배경이 변화하도록 구현했다.
- 상태창, 인벤토리, Setting, Exit 기능은 후속 작업으로 남겼다.
이번 주의 작업은 단순한 Pause 버튼 하나를 만드는 것에서 끝나지 않았다.
입력 전환 → 게임 상태 정지 → UI 조작 환경 구성 → Pause 전용 시각 효과 → 다른 시스템과의 입력 충돌 확인 까지, 하나의 Pause 화면이 실제 게임 안에서 동작하기 위해 필요한 여러 요소를 함께 다루게 되었다.
4주차 결과
이번 주에 완료한 기능은 다음과 같다.
- ESC 키 Pause 입력
- ESC 키를 이용한 Pause·Resume 상태 전환
Player/UIAction Map 전환Time.timeScale = 0을 이용한 게임 정지- 플레이어 이동·공격 입력 비활성화
- Pause Panel 활성화
- Pause 상태에서 마우스 커서 표시
- Pause 화면 레이아웃 기반
- 범용 UI Shader Graph 실험
- Shader Graph의 UI Mask 문제 확인
- HLSL 기반 Pause 배경 Shader
- Pause 상태에서도 계속 움직이는 Shader
- 마우스 위치에 반응하는 배경 효과
- Core 1 아이콘 디자인 개선
- UI 에셋과 폴더 구조 정리
- 플레이어 컨트롤러 클래스명 오타 수정
반면 다음 기능들은 후속 작업으로 남겨두었다.
- 실제 Resume 버튼 연결
- Setting 버튼 및 설정 화면
- Exit 버튼 및 종료 처리
- 플레이어 상태창
- 인벤토리 데이터 및 UI
- Pause 중 Core 전환 차단
- Core 2·3 디자인 개선
- Core 디자인에 맞춘 Heat 게이지 추가 수정
4주차를 마치며
4주차에는 상태창이나 인벤토리부터 곧바로 제작하기보다, 그 기능들을 담아낼 Pause 화면의 기반부터 만드는 것 에 집중했다.
ESC 입력을 Input System에 추가하고, Pause 상태에서는 게임 시간과 플레이어 입력을 멈추도록 했다. UI를 조작할 수 있도록 마우스 커서를 표시하고, Input Action Map 역시 Player에서 UI로 전환했다.
여기까지만 보면 비교적 평범한 Pause 기능이지만, 배경은 단순한 반투명 이미지로 끝내고 싶지 않았다.
『ERROR-CODE-H』의 분위기에 맞춰 Scan, Noise, Distortion, Glow 등을 조합할 수 있는 범용 UI Shader Graph를 먼저 시도했고, UI Mask가 정상적으로 동작하지 않는 문제가 발생하자 HLSL 기반 구현으로 방향을 바꿨다.
처음 만든 방식이 실패했지만, 그대로 억지로 끌고 가기보다 문제를 기록하고 다른 구현 방식으로 전환했다.
또한 Time.timeScale이 0인 상태에서도 Pause 화면만큼은 계속 움직일 수 있도록 Unscaled Time을 사용했다.
여기에 마우스의 위치를 셰이더의 _Progress 값과 연결하면서, 단순히 움직이는 배경을 넘어 마우스 움직임에 반응하는 인터페이스 까지 만들어 볼 수 있었다.
테스트 과정에서는 Pause 상태에서도 Core 전환이 가능하다는 문제도 발견했다.
PauseManager에서 플레이어 입력을 비활성화하고 Action Map을 UI로 바꾸더라도, CoreManager가 별도로 기존 Input API를 사용해 숫자 키를 직접 읽고 있었기 때문이다.
결국 Pause 같은 전역 상태를 제대로 제어하려면, 단순히 하나의 스크립트만 보는 것이 아니라 프로젝트 전체에서 입력이 어떤 방식으로 처리되고 있는지까지 함께 봐야 한다는 점 을 알게 되었다.
이번 주에 인벤토리나 설정 화면까지 완성하지는 못했다.
대신 앞으로 해당 기능을 추가할 수 있는 Pause 화면과 입력 전환 구조, 그리고 『ERROR-CODE-H』의 분위기를 살릴 수 있는 동적 배경의 기반을 만들었다.
1~3주차가 플레이 중 항상 보이는 HUD를 만들어가는 과정이었다면, 4주차부터는 플레이어가 게임과 상호작용하는 또 하나의 인터페이스 계층 을 만들기 시작한 셈이다.
開発期間: 2026.07.27 ~ 2026.08.03
担当役割: Pauseインターフェース、ステータス画面、インベントリおよび終了メニューの基盤制作
3週目には既存HUDをサイバーパンクスタイルへリメイクし、Heat状態エフェクトやSkillアイコン、死亡演出を追加した。
4週目には、プレイ中にESCキーを押した際に表示される Pauseインターフェース の制作を始めた。
最終的に構想したPause画面は、以下のような構造であった。
- 左側: Resume、Setting、Exitなどのメニューボタン
- 右側: プレイヤーのステータス情報
- 右側: 現在所持しているCoreおよびインベントリ
- 背景: 『ERROR-CODE-H』の雰囲気に合わせた動的なサイバーパンクエフェクト
ただし今週は、これらすべての機能を一度に完成させるのではなく、ESC入力を受けてゲームを一時停止し、Pause Panelを表示する基本構造と動的な背景エフェクト を先に実装することへ集中した。
ステータス画面、インベントリ、設定、終了ボタンの実際の機能については、その後Pause画面上へ追加できるよう残しておき、まずは一時停止状態そのものが安定して動作する基盤を作ることを目標とした。
4週目の目標
今週予定していた作業は、以下の通りであった。
- ESCキーを利用した一時停止
- Pause Panelの表示
- Resume機能を接続できる構造
- マウスを使用できるようカーソルを有効化
- ゲームプレイ入力とUI入力の分離
- ステータス画面とインベントリを配置するPause画面レイアウト
- UI背景用の汎用Shader制作
- Pause中でも動き続ける動的背景
- マウス位置に反応する背景変化
- Core 1インターフェースデザインの改善
- 後にCore 2・3へ適用するデザイン基準の策定
今週も、予定していたすべての項目を無理に一度で終わらせるのではなく、完成した機能と後続作業を分けながら進めた。
ESC入力をInput Systemに追加する
まずUnity Input SystemのInputSystem_ActionsへPause入力を追加した。
ゲームプレイ中にESCキーを押した際に使用するPause ActionをPlayer Action Mapへ追加し、Pause画面へ入った後も同じESCキーでゲームを再開できるよう、UI Action MapのCancelにもESCを接続した。
入力構造は以下のように構成した。
PlayerAction MapのPause→ ESCUIAction MapのCancel→ ESC- どちらの入力も
PauseManager.OnPause()を呼び出す - Pause進入時にAction Mapを
PlayerからUIへ変更 - Resume時には再び
UIからPlayerへ戻す
Pause入力を受け取るメソッドでは、入力が実際に実行されたタイミングであるかを先に確認した。
public void OnPause(InputAction.CallbackContext context)
{
if (!context.performed)
return;
TogglePause();
}
InputAction.CallbackContextはActionの開始、実行、キャンセルなど複数の段階で呼び出されるため、context.performedを確認し、ESC入力が実際に処理された瞬間だけTogglePause()が実行されるようにした。
現在のPause状態に応じて、ゲームを一時停止または再開する。
public void TogglePause()
{
if (isPaused)
Resume();
else
Pause();
}
ゲームプレイ中にESCを押すとPause状態へ入り、Pause画面で再びESCを押すとResume状態へ戻るよう構成した。
[キャプチャ1を挿入 — Input ActionsのPauseとUI Cancel設定]
PlayerとUIAction Mapの両方へESC入力を接続した。Pause進入後にAction MapがUIへ切り替わった状態でも、同じESCキーでゲームを再開できるよう構成した。
ゲームの一時停止処理
Pause状態へ入る際には、単純にPause Panelを画面へ表示するだけにはしなかった。
ゲーム時間、プレイヤー入力、マウスカーソル、Input Action Mapを同時に変更し、プレイ状態そのものをUI操作状態へ切り替える ようにした。
private void Pause()
{
if (isPaused)
return;
isPaused = true;
Time.timeScale = 0f;
playerController.SetInputEnabled(false);
pausePanel.SetActive(true);
Cursor.visible = true;
playerInput.SwitchCurrentActionMap("UI");
}
Pause状態では、以下の処理が行われる。
- Pause処理の重複実行を防ぐ。
isPausedをtrueへ変更する。Time.timeScaleを0へ設定する。- プレイヤー操作入力を無効化する。
- Pause Panelを有効化する。
- マウスカーソルを表示する。
- Input Action Mapを
PlayerからUIへ切り替える。
Time.timeScale = 0へ設定すると、Time.deltaTimeを基準として動作する移動やアニメーション、Scaled Timeを使用する処理が停止する。
さらにプレイヤーコントローラーの入力も無効化することで、Pause状態で移動や攻撃が発生しないようにした。
また、その後Resume、Setting、Exitなどのボタンをマウスで操作できるよう、カーソルを画面上へ表示した。
[キャプチャ2を挿入 — 通常プレイ画面とESC Pause画面の比較]
ESCキーを押すとゲーム時間とプレイヤー入力が停止し、マウスカーソルとPause画面が有効化される。
ゲーム再開処理
Resume状態では、Pause処理の中で変更した値を元の状態へ戻す。
private void Resume()
{
if (!isPaused)
return;
isPaused = false;
Time.timeScale = 1f;
playerController.SetInputEnabled(true);
pausePanel.SetActive(false);
Cursor.visible = false;
playerInput.SwitchCurrentActionMap("Player");
}
Resume時には、以下の順序で状態を元に戻す。
isPausedをfalseへ変更する。Time.timeScaleを1へ戻す。- プレイヤー入力を再び有効化する。
- Pause Panelを非表示にする。
- マウスカーソルを非表示にする。
- Input Action Mapを
UIからPlayerへ戻す。
Pause PanelのResumeボタンからも使用できるよう、公開メソッドも用意した。
public void ResumeGame()
{
Resume();
}
ただし、今週のコミットにはButton_ResumeオブジェクトとResumeGame()メソッドまでは存在しているものの、ボタンのOnClickへこのメソッドを実際に接続した記録は残っていなかった。
そのため、今週の時点で確実に動作する再開方法は ESCキーを再び押す方法 であり、Resumeボタンについては後から実際の機能を接続できるよう準備した段階であった。
さらに、オブジェクトが破棄された際にTime.timeScaleが0のまま残る状況を防ぐための防御コードも追加した。
private void OnDestroy()
{
Time.timeScale = 1f;
}
シーンを終了したりPauseManagerが破棄されたりする過程で、Time.timeScaleが0のまま残ってしまうと、次のシーンでもゲームが停止しているように見える可能性がある。
これを防ぐため、オブジェクトが破棄された際には初期値である1へ戻すようにした。
Pause画面のレイアウト計画
Pause画面は最終的に、左右の領域を分けて使用する形で計画した。
左側にはプレイヤーが直接選択できるメニューを配置し、右側にはゲームの状態とインベントリを表示する構造である。
左側メニューの計画
- Resume
- Setting
- Exit
- 必要に応じて追加するメニューボタン
右側情報の計画
- プレイヤーの現在状態
- 現在のHPとHeat
- 装備中のCore
- 所持しているCoreまたはアイテム
- インベントリ
- その他のステータス情報
今週はPause Panelと背景、そして各種ボタンを配置できる基本領域から制作した。
テストシーンにはButton_Resumeと複数のテスト用ボタンオブジェクトを追加したものの、実際のSetting・Exit・Inventory機能はまだ接続していなかった。
つまり、今週の成果物は完成したステータス画面やインベントリではなく、今後ステータス画面とインベントリを収めるためのPauseインターフェースの基盤 であった。
[キャプチャ3を挿入 — Pause画面全体のレイアウトと今後の領域表示]
Pause画面の基本レイアウト。左側にはResume・Setting・Exitボタンを、右側にはプレイヤーの状態とインベントリを配置する方向で計画した。
まだ実装されていない領域については、ブログの画像上でも「予定」「構想中」などと表記し、実際に完成している機能と区別することにした。
Pause中でもCoreが切り替わる問題
Pause状態ではプレイヤーコントローラーの入力を無効化し、Action MapもUIへ切り替えた。
しかしテスト中、Pause状態でも数字キーを使ってCoreを切り替えられる問題 が残っていることを確認した。
当時の作業記録にも、以下のように残している。
現在Pause状態でもCoreの切り替えが可能な問題があります。ただし、この部分は私の担当範囲だけでは解決できないと思われるため、ひとまず変更していません。現在のPauseはTimeScaleを0にする方式で実装しています。
原因は、Core切り替え入力がPlayerController2DやInput SystemのAction Mapを通して処理されておらず、CoreManager.Update()内で従来のInput APIを使い、直接キー入力を確認していたためである。
private void Update()
{
for (int i = 0; i < coreSlots.Count; i++)
{
CoreSlot slot = coreSlots[i];
if (slot == null || slot.SelectKey == KeyCode.None)
continue;
if (Input.GetKeyDown(slot.SelectKey))
SetCurrentCore(i);
}
}
Time.timeScaleを0へ設定しても、UnityのUpdate()呼び出しやキー入力の確認自体が完全に停止するわけではない。
また、CoreManagerは現在のPlayerInput Action MapがPlayerなのかUIなのかを確認せず、直接数字キーの入力を読み取っていた。
そのためPauseManager側で以下の処理をすべて行っても、Core切り替えだけは別途遮断されなかった。
- プレイヤーコントローラー入力の無効化
PlayerAction MapからUIAction Mapへの切り替えTime.timeScale = 0
これを解決するには、CoreManager側でPause状態や共通の入力ロック状態を確認するよう変更するか、Core切り替え入力そのものをInput System Actionへ統合する必要があった。
しかし、これは単純なPause UIの修正ではなく、Coreシステムの入力処理方式まで変更する必要がある問題 であった。
そのため今週は、ほかのチームメンバーが担当していたCoreシステムを勝手に変更せず、原因と解決方針だけを確認した状態で残した。
単純に「Pause中にCoreが切り替わるバグがある」というだけではなく、異なる入力方式が一つのプロジェクト内に混在している場合、Pauseのようなグローバル状態の制御が難しくなることを確認した事例 であった。
[任意キャプチャ4を挿入 — Pause中にCoreが切り替わる既知の問題]
Pause状態でも
CoreManagerが数字キーを直接確認しているため、Coreの切り替えが可能であった。Pause UIだけで解決できる問題ではなく、Core入力構造と合わせて修正する必要がある項目として分離した。
UIアセットおよびフォルダ構造の整理
4週目には、UI関連アセットの配置も整理した。
これまではAssets/jhkim以下にUI関連ファイルが配置されていたが、これをAssets/WorkSpace/jhkimへ移動し、チームプロジェクトの作業フォルダ構造に合わせた。
移動した項目には、以下のようなファイルが含まれていた。
- HUDプレハブ
- HP・Heat・Core Drawer
- Skill UI関連コード
- Death UI関連コード
- UIテストシーン
- フォント
- MaterialおよびShader Graph
- 仮UI画像
ファイルを移動する際には.metaファイルも一緒に維持し、UnityアセットのGUID参照が切れないようにした。
また、使用しなくなった仮アセットについてもすぐに削除せず、TrashCanフォルダへ移動した。
チームプロジェクトでは、自分では使用していないと思っているアセットが、ほかのシーンやプレハブから参照されている可能性もある。そのため、すぐに削除するのではなく、まず分離して保管する方法を選んだ。
Core 1デザインの改善
Pause画面の作業と並行して、Core 1のUIデザインも一部改善した。
既存のCore 1アイコンを新しいサイバーパンクスタイルの画像へ変更し、その画像をCore_01のScriptableObjectデータへ再接続した。
この変更はコードではなく、Core_01.assetのcoreIcon参照を新しい画像へ変更する形で行った。
3週目にはCore名とアイコンを画面へ表示する機能を作ったが、4週目には実際に画面へ表示される Core 1アイコンそのもののデザインを改善した ことになる。
今週はまずCore 1から修正し、その後同じデザインルールをCore 2とCore 3にも適用する計画を立てた。
Heatゲージについても、今後Core UIとデザイン上の統一感を持たせられるよう、再調整する予定であった。
ただしCore 2・3の新しいデザインとHeatゲージの追加リメイクについては、今週の完了範囲には含めなかった。
[キャプチャ5を挿入 — Core 1アイコンデザインの変更前後]
既存のCore 1アイコンを新しいサイバーパンクデザインへ変更した。その後Core 2・3とHeatゲージにも同じデザインルールを適用する計画を立てた。
汎用UI Shader Graphの実験
Pause画面の背景には単純な静止画像ではなく、『ERROR-CODE-H』のサイバーパンクな雰囲気に合う動的Shaderを適用したいと考えた。
最初はPause画面専用のShaderを作るのではなく、複数のUIで共通して再利用できる 汎用UI Shader Graph を制作する方向で試みた。
構成した機能は以下の通りである。
- Appearance
- Distortion
- Glow
- Gradient
- Mask
- Noise
- Pixelize
- Reveal
- Scan
それぞれの機能をSub Graphへ分け、それらを一つのSG_UIFramework内で組み合わせる構造を作った。
目標は、Material側で必要なエフェクトだけを選択して使用できるようにすることであった。
例えばUIの種類に応じて、以下のように利用することを想定した。
- Pause背景 → Noise、Distortion、Scan
- Coreアイコン → Glow、Reveal
- 通常パネル → Gradient、Mask
- 強調UI → Pixelize、Scan
しかし実際にテストしたところ、UI Maskを含む一部機能が正常に動作しない問題が発生した。
8月2日のコミットにも以下のように記録している。
Shader GraphをベースにUI全般へ適用するためのShaderを制作しました。ただしMaskを含む一部機能が動作しないことを確認しました。原因を特定するのが難しいため、HLSLベースで再実装する予定であり、現在のバージョンは暫定版としてアップロードします。
最初に試した構造が意図した通りに動作しなかったものの、問題を隠したり完成済みの機能として扱ったりせず、現在の限界と次の実装方針をそのまま記録 した。
[キャプチャ6を挿入 — 汎用Shader Graph全体とSub Graph構成]
Glow、Gradient、Noise、Distortion、Mask、Scanなどの機能を組み合わせられる汎用UI Shader Graphを実験した。UI Maskとの互換性問題を確認した後、HLSLベースの実装へ方向を変更した。
Shader GraphからHLSLベースのShaderへ移行する
Shader Graphベースの汎用ShaderでUI Maskの問題が発生したため、Pause背景用ShaderはHLSLベースのShader_Background.shaderとして作り直した。
Shaderには以下のようなプロパティを用意した。
_TintColor("TintColor", Color) = (1, 1, 1, 1)
_Opacity("Opacity", Range(0, 1)) = 1
_Progress("Progress", Range(0, 1)) = 1
_PixelCount("PixelCount", Range(1, 1024)) = 128
_GlowIntensity("GlowIntensity", Range(0, 5)) = 1
_ScanSpeed("ScanSpeed", Range(-5, 5)) = 1
_NoiseStrength("NoiseStrength", Range(0, 1)) = 0.2
_DistortionStrength("DistortionStrength", Range(0, 0.1)) = 0
これらの値によって、一つのMaterial内で以下の表現を調整できるようにした。
- UIの色調
- 透明度
- 左右方向の進行度
- ピクセル化の程度
- Glowの強度
- Scanの速度
- Noiseの強度
- Distortionの強度
Shader Graphで問題になったUI Maskとの互換性に対応するため、Stencil関連のプロパティも直接追加した。
_Stencil("Stencil Ref", Float) = 0
_StencilComp("Stencil Comp", Float) = 8
_StencilOp("Stencil Op", Float) = 0
_StencilReadMask("Stencil Read Mask", Float) = 255
_StencilWriteMask("Stencil Write Mask", Float) = 255
_ColorMask("Color Mask", Float) = 15
最初に作ったShader Graphの構造へ固執するのではなく、実際のUIで必要であったMaskやStencil処理を直接扱えるよう、HLSLベースへ方向を変更したのである。
今回の作業を通して、実装しやすい方法と、実際の要求を満たせる方法が必ずしも同じではないということ を改めて感じることができた。
Pause中でもShaderを動かす
Pause状態ではTime.timeScaleが0になる。
しかしPause画面のNoise、Scan、Distortionエフェクトまで一緒に停止してしまうと、画面全体が単純な静止画像のように見えてしまう。
今回のPause画面は、ゲームそのものは停止していてもインターフェースだけは動き続ける、一種のシステム画面のように見せたいと考えた。
そのため、Shaderで使用する時間値をゲーム時間から分離する必要があった。
そこで、Time.unscaledTimeをグローバルShader値として渡すDontStopShaderを追加した。
private void UpdateTimeParams()
{
float t = Time.unscaledTime;
Shader.SetGlobalVector("_UnscaledTimeParams", new Vector4(t * xScale, t * yScale, t * zScale, t * wScale));
}
Time.unscaledTimeはTime.timeScaleの影響を受けない。
そのためゲーム時間が停止していても_UnscaledTimeParamsは増加し続け、Pause背景Shaderのアニメーションを維持できる。
Shader側ではUnity標準のTime Parameterではなく、渡されたUnscaled Timeを使用するようにした。
float4 _UnscaledTimeParams;
これによって、ゲームプレイ自体は完全に停止していても、Pause画面のScanとNoiseは動き続ける状態 を作ることができた。
[キャプチャ7を挿入 — Pause中でも動くScanまたはNoiseエフェクト]
Time.timeScaleが0のPause状態でもUI Shaderが動くよう、Time.unscaledTimeをグローバルShader値として渡した。
この機能は静止画像よりも、2〜3秒程度のGIFや短い動画で見せる方がはるかに分かりやすいと思う。
マウス位置に反応するPause背景
Pause画面では、マウスを動かした際に背景表現も一緒に変化するようPauseMouseControllerを追加した。
まずPause Panelを基準として、マウスのローカル座標を計算した。
RectTransformUtility.ScreenPointToLocalPointInRectangle(panelRect, Input.mousePosition, cam, out Vector2 localPoint);
Rect rect = panelRect.rect;
float mouseX = Mathf.InverseLerp(rect.xMin, rect.xMax, localPoint.x);
mouseX = Mathf.Clamp01(mouseX);
マウスのX座標を0~1の範囲へ正規化した後、これを左右方向を示す値へ変換した。
float signed = (0.5f - mouseX) * 2f;
float amplified = Mathf.Clamp(signed * amplify, -1f, 1f);
float target = center + amplified * moveRange;
マウスが画面中央にある場合は基準値であるcenterを使用し、左右へ移動するほどmoveRangeの範囲内で目標値が変化するようにした。
値が急激に変化しないようLerp()を使い、滑らかに移動させた。
この処理でもPause状態の影響を受けないよう、Time.unscaledDeltaTimeを使用した。
float t = Mathf.Clamp01(lerpSpeed * Time.unscaledDeltaTime);
progress = Mathf.Lerp(progress, target, t);
計算した値はMaterialとグローバルShader変数の両方へ渡した。
materialInstance.SetFloat("_Progress", progress);
Shader.SetGlobalFloat("_GlobalProgress", progress);
全体の流れを整理すると、以下の通りである。
マウスを左右へ移動
→ Pause Panel基準のローカル座標を計算
→ X座標を0~1へ正規化
→ 目標Progressを計算
→ Unscaled Delta Timeを使って滑らかに補間
→ Materialの_Progressを変更
→ Pause背景表現が変化
この機能によって、Pause画面は単純に動く背景からさらに一歩進み、プレイヤーのマウス操作に反応するインターフェース となった。
[キャプチャ8を挿入 — マウスの左・中央・右位置によるPause背景の比較]
Pause画面でマウスを左右へ動かすとShaderの
_Progress値が変化し、それに応じて背景表現も動的に変化する。
この機能についても、静止した比較画像よりGIFで見せる方が実際の感覚を伝えるには適していると思う。
PlayerController2Dのクラス名の誤字を修正
Pause機能を実装する過程では、既存プレイヤーコントローラーのクラス名にあった誤字も修正した。
変更前:
public class PlayerCotroller2D : MonoBehaviour
変更後:
public class PlayerController2D : MonoBehaviour
既存のクラス名では、Controllerのnが抜けていた。
PauseManagerからプレイヤーコントローラーを参照する必要が出てきた際、誤ったクラス名をそのまま使い続けるよりも、この段階で正しい名前へ整理した方がよいと判断した。
これに合わせ、以下の参照も同時に修正した。
PlayerHealthSwordCoreSkillPlayerController2Dスクリプト- Player Prefab
- Playerテストシーン
クラス名の変更は複数のコードやPrefab参照へ影響する作業であったため、その後ほかのチームメンバーが残っていた一部の型参照も追加で整理した。
GitHub作業記録
4週目に私が作業した主なコミットは以下の通りである。
037656f — UI 에셋 수정 및 Pause 구현을 위한 Input System 수정
作業日: 2026.07.29 午前6時54分
PlayerAction MapにPause Actionを追加- ESCキーとPause Actionを接続
UIAction MapのCancelへESCを追加PauseManagerの基本構造を追加- 初期Pause・Resume状態切り替えを実装
- UIアセットを修正
- Core 1とSkill関連の新規画像を追加
- UI関連フォルダを
Assets/WorkSpace/jhkimへ移動 - HUDプレハブ構造を修正
dd4e92d — PlayerCotroller2D 클래스명 오타 수정 및 Pause 기능 임시 구현
作業日: 2026.07.30 午前4時17分
PlayerCotroller2DをPlayerController2Dへ修正- 関連コード参照を修正
- Pause入力Callbackを追加
Time.timeScaleを利用した一時停止- プレイヤー入力を無効化
- Pause Panelを有効化
- マウスカーソルを表示
Player/UIAction Mapを切り替え- Resume処理
- Pauseテストシーンを接続
b79f329 — Pause機能作業をマージ
作業日: 2026.07.30 午前4時19分
- Pause System作業をメインの作業フローへマージ
13b346b — Shader Graph 기반 UI용 범용 Shader 제작
作業日: 2026.08.02 午前4時16分
- 汎用UI Shader Graphを制作
- Appearance Sub Graph
- Distortion Sub Graph
- Glow Sub Graph
- Gradient Sub Graph
- Mask Sub Graph
- Noise Sub Graph
- Pixelize Sub Graph
- Reveal Sub Graph
- Scan Sub Graph
- テスト用MaterialおよびPanel画像を追加
- UI Maskなど一部機能の問題を確認
- HLSLへの移行計画を共有
f5fb363 — Pause 화면 추가 및 Shader 개선
作業日: 2026.08.03 午前1時48分
- Pause画面を追加
- Pause Panelおよびボタン配置の基盤を構成
- HLSLベースの
Shader_Backgroundを追加 - UI StencilおよびMask対応プロパティを追加
DontStopShaderを追加- Pause状態でもShader時間を進行
PauseMouseControllerを追加- マウス位置に応じた
_Progress変化 - Core 1の新規デザインを適用
- Pause用の新規画像アセットを追加
- 使用しない仮アセットを分離
- Heatテキスト表示を一部修正
ba2f675 — Pause画面およびShader作業をマージ
作業日: 2026.08.03 午前1時49分
- Pause画面とShader改善作業をメインの作業フローへマージ
4週目の作業フロー整理
今週の作業フローを整理すると、以下の通りである。
- Pause画面の最終レイアウトを構想した。
PlayerAction MapへPause入力を追加した。UIAction MapにもESC入力を追加した。- ESC入力を
PauseManagerへ接続した。 Time.timeScaleを利用した停止機能を実装した。- Pause状態でプレイヤー入力を無効化した。
PlayerとUIAction Mapを切り替えた。- マウスカーソルを表示し、Pause Panelを有効化した。
- Resume処理用のメソッドを準備した。
- Pause中にCore切り替え問題が残っていることを確認した。
- UIアセットとフォルダ構造を整理した。
- Core 1の新規デザインを適用した。
- 汎用UI Shader Graphを制作した。
- UI Maskなど一部機能の問題を確認した。
- Pause背景をHLSLベースのShaderへ変更した。
- Unscaled Timeを利用し、Pause中もShaderが動くようにした。
- マウス位置に応じて背景が変化するよう実装した。
- ステータス画面、インベントリ、Setting、Exit機能は後続作業として残した。
今週の作業は、単純にPauseボタンを一つ作るだけでは終わらなかった。
入力切り替え → ゲーム状態の停止 → UI操作環境の構築 → Pause専用の視覚エフェクト → ほかのシステムとの入力競合の確認 まで、一つのPause画面を実際のゲーム内で動作させるために必要な複数の要素をまとめて扱うことになった。
4週目の結果
今週完成した機能は以下の通りである。
- ESCキーによるPause入力
- ESCキーを利用したPause・Resume状態切り替え
Player/UIAction Mapの切り替えTime.timeScale = 0を利用したゲーム停止- プレイヤーの移動・攻撃入力を無効化
- Pause Panelを有効化
- Pause状態でマウスカーソルを表示
- Pause画面レイアウトの基盤
- 汎用UI Shader Graphの実験
- Shader GraphのUI Mask問題を確認
- HLSLベースのPause背景Shader
- Pause状態でも動き続けるShader
- マウス位置に反応する背景エフェクト
- Core 1アイコンデザインの改善
- UIアセットとフォルダ構造の整理
- プレイヤーコントローラーのクラス名の誤字修正
一方、以下の機能は後続作業として残した。
- 実際のResumeボタン接続
- Settingボタンおよび設定画面
- Exitボタンおよび終了処理
- プレイヤーステータス画面
- インベントリデータおよびUI
- Pause中のCore切り替え防止
- Core 2・3のデザイン改善
- Coreデザインに合わせたHeatゲージの追加修正
4週目を終えて
4週目には、ステータス画面やインベントリからすぐに制作を始めるのではなく、それらの機能を収めるPause画面の基盤から作ること に集中した。
ESC入力をInput Systemへ追加し、Pause状態ではゲーム時間とプレイヤー入力を停止するようにした。UIを操作できるようマウスカーソルを表示し、Input Action MapもPlayerからUIへ切り替えた。
ここまでだけを見れば比較的一般的なPause機能であるが、背景を単純な半透明画像だけで終わらせたくはなかった。
『ERROR-CODE-H』の雰囲気に合わせ、Scan、Noise、Distortion、Glowなどを組み合わせられる汎用UI Shader Graphをまず試し、UI Maskが正常に動作しない問題が発生したため、HLSLベースの実装へ方向を変更した。
最初に作った方式はうまくいかなかったが、そのまま無理に使い続けるのではなく、問題を記録した上で別の実装方式へ切り替えた。
また、Time.timeScaleが0の状態でもPause画面だけは動き続けられるよう、Unscaled Timeを使用した。
さらにマウス位置をShaderの_Progress値へ接続することで、単純に動く背景を超え、マウスの動きに反応するインターフェース まで作ることができた。
テスト中には、Pause状態でもCoreを切り替えられる問題も発見した。
PauseManager側でプレイヤー入力を無効化しAction MapをUIへ切り替えても、CoreManagerが別途従来のInput APIを使い、数字キーを直接読み取っていたためである。
結局、Pauseのようなグローバル状態を正しく制御するためには、一つのスクリプトだけを見るのではなく、プロジェクト全体で入力がどのような方式で処理されているのかまで確認する必要があること が分かった。
今週はインベントリや設定画面まで完成させることはできなかった。
その代わり、今後それらの機能を追加できるPause画面と入力切り替え構造、そして『ERROR-CODE-H』の雰囲気を活かせる動的背景の基盤を作った。
1〜3週目がプレイ中に常に表示されるHUDを作っていく過程であったとすれば、4週目からはプレイヤーがゲームとやり取りするための もう一つのインターフェース層 を作り始めたと言える。