[🖥️Project: HoP] #1 투명 윈도우와 위젯 시스템 구축

2025. 12. 4. 21:41·도토리도굴단/Project: HoP 개발일지

🚀 오늘의 작업 요약


  1. Windows API 연동: 배경 없는 완전 투명 윈도우 및 'Always On Top' 구현
  2. 스마트 클릭 시스템: 위젯 위에서는 클릭을 받고, 빈 공간은 데스크탑으로 클릭을 흘려보내는 Raycast 시스템 구축
  3. 확장형 아키텍처: BaseWidget과 DraggableWidget 분리로 유지보수가 쉬운 위젯 구조 설계

예전 PokeIdle 기획이랑 투명 창까지만 구현해놓고 독감걸려서 못 만들었는데, 12월 미니 프로젝트로 햄초버전 데스크탑 위젯을 만드는 데에 기반으로 쓸 수 있었다. PokeIdle은 이번 프로젝트를 기반 삼아서 나중에 시간 남으면 만들어보는걸로 ...

 

🛠 구현 상세


오늘은 Project HoP(Hamcho on PC)가 데스크탑 앱으로서 기능하기 위한 기반 공사를 진행했다.

 

1. 투명 윈도우와 오버레이 (OverlayController)

Unity는 기본적으로 사각 윈도우를 렌더링하기 때문에, 캐릭터가 화면에 둥둥 떠 있는 효과를 내려면 Windows API(User32.dll, DwmApi.dll)를 직접 건드려야 했다. DwmExtendFrameIntoClientArea를 사용해 클라이언트 영역을 투명하게 확장하고, SetWindowPos로 최상위 레이어(TopMost) 속성을 부여했다. 결과적으로 윈도우 크롬(테두리) 없이 깔끔하게 햄초만 화면에 남기는 데 성공했다.

 

2. Raycast 기반 동적 Click-through

가장 고민했던 부분이다. "투명한 부분은 클릭이 통과되어야 하고, 캐릭터는 클릭할 수 있어야 한다." 매 프레임 Physics2D.Raycast를 쏘아 마우스 아래에 위젯이 있는지 감지하는 방식을 택했다.

  • 감지됨: 윈도우의 투명 속성(WS_EX_TRANSPARENT)을 해제 → 앱이 클릭 수신.
  • 감지 안 됨: 투명 속성 활성화 → 클릭이 뒤쪽(바탕화면)으로 통과.

성능 이슈를 막기 위해 상태가 변하는 순간에만 API를 호출하도록 최적화했다. 

 

3. 위젯 아키텍처

나중에 위젯 종류가 늘어날 것을 대비해 구조를 잡았다.

  • BaseWidget (추상 클래스): 모든 위젯의 공통 로직(ID, 저장/로드 인터페이스) 담당.
  • DraggableWidget (컴포넌트): 드래그 기능만 전담하여 어떤 위젯이든 붙이기만 하면 움직이게 만듦.
  • HamchoWidget: 실제 구현체. 현재 클릭 시 점프 반응과 스택 리셋 로직까지 테스트 완료.

 

🔥 트러블슈팅: 계속 투명 창이 아닌 검은 창으로 나옴


 

오늘 개발 중 가장 당황스러웠던 순간이다. 프로젝트 시작 전에 Cursor 개발 환경 통합을 위해서 프로젝트를 이사했더니 투명창 설정 관련 코드가 전부 날아가서, 예전에 써놓았던 글을 바탕으로 그대로 다시 복원했는데 계속 검은 화면이 나왔었다.

로그를 찍어보니까 Windows API 호출 결과는 분명 성공인데, 화면이 투명해지는 대신 새까만 배경이 유지되었다. API 문제가 아니라면 렌더링 파이프라인 문제라고 판단하고 하나씩 파고들었다.

원인 분석: 범인은 Unity의 렌더링 설정과 창 핸들(Handle) 획득 방식의 불안정함, 그리고 Project Settings의 숨겨진 옵션이었다. 특히 HDR과 Post Processing이 투명 처리를 방해하고 있었다. 예전 트러블 슈팅에는 D3 뭐시기 설정만 써놨는데 HDR이랑 포스트 프로세싱도 꺼야했다.

 

해결 과정:

1. 렌더링 파이프라인 강제 조정 일단 코드 레벨에서 카메라 설정을 강제했다. 투명 윈도우에서 HDR 출력이 되면 안 되는 거였다. 

private void SetupCamera()
{
    var mainCamera = Camera.main;
    
    // 1. 기본 설정: 배경을 완전 투명(Alpha 0)으로
    mainCamera.clearFlags = CameraClearFlags.SolidColor;
    mainCamera.backgroundColor = new Color(0f, 0f, 0f, 0f); 
    mainCamera.allowHDR = false;  // HDR이 켜져 있으면 투명 처리 불가
    mainCamera.allowMSAA = false;
    
    // 2. URP 설정: 포스트 프로세싱과 HDR 출력 차단
    var cameraData = mainCamera.GetUniversalAdditionalCameraData();
    if (cameraData != null)
    {
        cameraData.renderPostProcessing = false; // 글로우 효과 등을 포기해야 함
        cameraData.allowHDROutput = false;
    }
}

투명 윈도우에서는 RGBA에서 A값을 0으로 만들어야 하는데 HDR 윈도우는 더 넓은 색상과 밝기를 위해 10비트나 16비트 포맷을 사용한다고 한다. 그래서 HDR이 켜져있으면 알파값이 무시된다고 한다. 더 자세히는 톤 매핑 왜곡, 블렌딩 방식의 차이..라고 하는데 거기까지 알 필요 없을 것 같다.

 

2. 창 핸들 획득 로직 강화 GetActiveWindow()만 쓰면 Unity가 백그라운드에 있을 때 엉뚱한 핸들을 가져와 기능이 먹통이 된다고 한다. 3단계 폴백(Fallback) 시스템을 도입했다.

  • 1단계: 현재 포커스된 창의 프로세스 ID 확인
  • 2단계: FindWindow로 'Project HoP' 이름 검색
  • 3단계: 최후의 수단으로 전경 창 핸들 반환

 

3. Project Settings 설정 Project Settings > Player > Resolution and Presentation에서 Use DXGI Flip Model Swapchain for D3D11 체크를 해제해야만 한다. 이 최적화 옵션이 켜져 있으면 투명 레이어와 충돌한다.

결과: 위 세 가지를 적용하고 초기화 대기 시간(0.5초)을 주니 투명창이 잘 나왔다

 

🔥 트러블슈팅: 우클릭 드래그


 

문제 상황: 기획상 위젯 이동은 Right-Click + Drag로 정의했다. 하지만 코드를 짰는데 전혀 작동하지 않았다. 알고 보니 Unity의 OnMouseDrag 자체가 오직 좌클릭(Mouse Button 0)에만 반응하도록 하드코딩 되어 있었다.

해결: 결국 OnMouseDrag를 버리고 Update 문에서 직접 입력을 처리하는 방식으로 선회했다.

C#
 
// DraggableWidget.cs 로직 일부
void Update() {
    // 마우스가 위에 있고 우클릭 시작 시 드래그 모드 진입
    if (isMouseOver && Input.GetMouseButtonDown(1)) {
        StartDragging();
    }
    
    // 드래그 중 우클릭 떼면 해제
    if (isDragging && !Input.GetMouseButton(1)) {
        StopDragging();
    }
    
    // ...위치 업데이트 로직
}

다행히 isMouseOver 플래그를 활용해 불필요한 연산을 줄였고, 우클릭 드래그가 부드럽게 작동한다.

 

 

📝 문서화와 정리


혼자 개발하더라도 나중에 왜 이렇게 짰는지 묻지 않기 위해 기록을 남겼다.

  • ADR (Architecture Decision Records): 왜 인터페이스가 아닌 추상 클래스를 썼는지, 위치 저장은 왜 JSON으로 할 것인지에 대한 의사결정 기록.

 

🔮 다음 계획


테스트를 하다 보니 앱을 껐다 킬 때마다 햄초 위치가 초기화되는 게 너무 불편하다. 다음 작업은 무조건 데이터 지속성이다.

  1. Position Save System: JSON 직렬화를 통해 위젯 위치 저장/로드 구현 (최우선).
  2. Monitor Selection: 듀얼 모니터 환경에서 원하는 화면에 띄우는 기능.

 

💡 회고


오늘은 코딩 자체보다 구조 잡기와 기반 기술 검증에 시간을 많이 썼다. Windows API를 다루는 게 낯설어 초반에 헤맸지만, 덕분에 Unity가 렌더링하는 윈도우의 본질을 조금 더 이해하게 되었다. 또 ADR과 로드맵 필요성을 크게 느꼈기 때문에 이번 프로젝트에서는 좀 더 체계적으로 진행하려고 한다.

'도토리도굴단 > Project: HoP 개발일지' 카테고리의 다른 글

[🖥️Project: HoP] #5 무한 점프 시스템  (0) 2025.12.13
[🖥️Project: HoP] #4 Windows Low-Level Hook과 전역 입력 감지  (0) 2025.12.11
[🖥️Project: HoP] #3 애니메이션 시스템과 O(1) 최적화  (0) 2025.12.08
[🖥️Project: HoP] #2 위치 저장과 멀티 모니터  (0) 2025.12.08
[👾Project: PokeIdle] #1 투명 창 만들기  (0) 2025.08.19
'도토리도굴단/Project: HoP 개발일지' 카테고리의 다른 글
  • [🖥️Project: HoP] #4 Windows Low-Level Hook과 전역 입력 감지
  • [🖥️Project: HoP] #3 애니메이션 시스템과 O(1) 최적화
  • [🖥️Project: HoP] #2 위치 저장과 멀티 모니터
  • [👾Project: PokeIdle] #1 투명 창 만들기
happy124219
happy124219
happylaboratory 님의 블로그 입니다.
  • happy124219
    윤아 실험실
    happy124219
  • 전체
    오늘
    어제
    • 분류 전체보기 (116)
      • 공부방 (103)
        • Unity UI (2)
        • C# 알고리즘 (3)
        • C# 에러발생 로그 (7)
        • 내일배움캠프 Unity 9기 TIL (91)
      • 도토리도굴단 (9)
        • Project: HSR 개발일지 (3)
        • Project: HoP 개발일지 (6)
      • 개인 프로젝트 (1)
        • Project: BloomFolk (1)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    스파르타내일배움캠프
    도망개발일지
    Unity게임개발
    유니티
    내일배움캠프
    도망공부일지
    유니티트랙후기
    c#문법
    게임개발
    내일배움캠프후기
    스파르타내일배움캠프TIL
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
happy124219
[🖥️Project: HoP] #1 투명 윈도우와 위젯 시스템 구축
상단으로

티스토리툴바