공성전 중 길드 마크와 이름이 사라지는 현상 분석
최근 공성전 콘텐츠에서 유저들로부터 버그 제보가 들어왔다.
- "공성전 중 사람이 많아지면 화면이 깨진다."
- "컴퓨터 성능 문제는 아니다. 채팅창을 보면 나만 그런 게 아니다."
- "캐릭터와 배경은 멀쩡한데, 길드 마크와 유저 이름이 네모난 투명 형태로 깨지듯 사라진다."
게임이 치열하게 돌아가는 상황이라, 빠르게 영상 자료를 받아 분석을 시작했다.
1. UI 렌더링 구조 의심
해당 게임은 DirectX9 기반으로 동작하며, UI와 이름표는 ID3DXSprite와 ID3DXFont를 이용해 렌더링하고 있다.
처음엔 Sprite 내부 버퍼 초과 문제를 의심했다.
예를 들어:
m_pSprite->Begin();
m_pSprite->Draw(...);
m_pSprite->End();
위와 같은 방식으로 다수의 이름과 마크를 Draw하면서 중간에 Flush() 없이 렌더링하면, 내부적으로 자동 Flush()가 발생해 일부 드로우가 누락되거나 잘릴 수 있다는 가능성이 있었다.
하지만 코드상에서는 그렇게 심하게 누적되지도 않았고, 매번 Begin-End를 반복하고 있었다. 즉, 이 이슈는 직접적인 원인이 되진 않는 듯했다.
2. Viewport / ScissorRect 설정 의심
다음으로는 ScissorRect나 ViewPort 설정을 의심했다.
특정 구간만 렌더링 되도록 제한된 상태에서 원복하지 않으면, 일부 UI가 잘리는 현상이 발생할 수 있기 때문이다.
하지만 이번 문제는 간헐적으로 발생했고, 특정 상황(공성전, 인원 몰림 등)에서만 보였기 때문에 이것 역시 가능성이 낮았다.
3. 영상 재분석 – 중요한 패턴 발견

영상을 다시 살펴보며 아래 두 가지 중요한 특징을 발견했다.
1. 길드 마크와 이름이 '전부' 사라지는 것이 아니라 '일부'만 잘리는 현상
예: 동그란 마크가 반구처럼 잘리거나, 긴 이름의 앞부분만 보이고 뒷부분은 사라짐
→ 이건 뭔가에 '가려졌거나' '일부만 렌더된 것'임을 의미한다.
2. 플레이어, 펫, 배경, 이펙트들은 전혀 문제없이 잘 나옴
→ 렌더 순서 상 문제가 있는 게 아닐까 싶었다.
현재 게임의 렌더 순서는
" 배경 → 캐릭터 & 오브젝트 → 씬 이펙트 → 스킬 이펙트 → 길드 마크 & 이름 "
즉, 마크와 이름은 가장 마지막에 그려지고 있기에 다른 오브젝트에 '덮이는' 건 아니다.
그렇다면 일부만 그려졌다는 의미는 깊이 테스트(Depth Test) 와 관련이 있을 가능성이 높다.
4. 깊이 버퍼 상태 확인
길드 마크와 이름이 그려지기 전 설정은 다음과 같았다.
SetRenderState(D3DRS_ZENABLE, TRUE); // 깊이 테스트 활성화
SetRenderState(D3DRS_ZWRITEENABLE, TRUE); // 깊이 버퍼에 값 기록
SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
이 설정이라면, Z버퍼 값에 따라 길드 마크나 이름이 가려질 수 있다.
그래서 테스트로 아래와 같이 설정을 변경해보았는데 길드마크와 이름이 깨지지 않고 정상적으로 출력 되었다.
SetRenderState(D3DRS_ZENABLE, FALSE);
SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
하지만 이는 또 원하는 결과가 아니다.
깊이 테스트를 끄면, 나무 뒤에 숨었거나 건물 뒤에 있는 캐릭터의 이름도 그냥 다 보여버리는 현상이 생겨버린다. 숨어 있어도 숨어 있을 수 없는 상태가 되니 PVP게임에서는 이렇게 사용할 수 없다.
5. 특정 이펙트의 ZWrite 설정
그렇다면 길드 마크와 이름이 아닌, 이펙트 쪽이 문제일 가능성은?
일반적으로 이펙트는 이렇게 설정된다.
SetRenderState(D3DRS_ZENABLE, TRUE);
SetRenderState(D3DRS_ZWRITEENABLE, FALSE); // 깊이 테스트는 하지만, 깊이값은 쓰지 않음
즉, ZWrite를 꺼야 이후에 그려질 UI가 영향을 받지 않는다.
하지만 디버깅 도중, 특정 랜더 타입 이펙트가 ZWriteEnable을 다시 TRUE로 설정하는 부분을 발견하게 되었다.
이 이펙트가 Z버퍼에 깊이값을 덮어써버리면, 뒤에 그리는 이름표와 마크가 가려질 수밖에 없다.
이 설정을 다음과 같이 바꿔서 모든 문제를 해결 하였다.
SetRenderState(D3DRS_ZENABLE, TRUE);
SetRenderState(D3DRS_ZWRITEENABLE, FALSE); // 이펙트는 깊이값 기록하지 않음
6. (+추가) ZWRITE쓴다 안 쓴다의 차이
ZENABLE = TRUE : Z Test를 한다. 픽셀이 다른 것보다 앞에 있는지 없는지 비교를 한다.
ZWRITEENABLE = TRUE : Z 버퍼에 Z값을 기록을 한다. 픽셀 깊이가 대상자가 앞에 있으면 Z버퍼에 쓴다.
1. (A) 0.3 깊이에 ZWRITEENABLE = TRUE -> 0.3 Z버퍼 픽셀에 (A)자리
2. (B) 0.5 에 그림을 그리는데 (A)와 같은 픽셀 위치임. -> (A)와 같은 위치인 픽셀에는 (A)가 그려지고 (A)와 겹치지 않는 픽셀이라면 그려진다.

'👨🏻💻 programming > ◽ 에러 해결 메모' 카테고리의 다른 글
[HTTP] Error 416 "The requested range is not satisfiable" (1) | 2024.02.07 |
---|
안 하는 것 보다 낫겠지
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!