Jasontreks Blog

DM 보내기

메세지는 텔레그램 챗봇에 의해 익명으로 전달됩니다. 답장을 받으려면 이메일을 입력하세요.
Send

UI 디자인 - 플레이어 체력바 및 대시보드

프로토타입이 완성된 이후 화면에 UI를 어떻게 구성할지 다른 팀원과 아이디어를 나눴다.

체력바 아이디어

플레이어가 입는 피해의 범위가 다양하지 않기 때문에, 젤다의 전설 게임처럼 체력바를 칸 수를 정해놓고 체력을 표시하는 쪽이 나을 것이라 생각했다.

체력 칸은 대부분의 게임에서 하트 모양 아이콘을 사용하고 있는데, 팀원은 삼각형을 위, 아래 방향으로 번갈아가며 배치해 체력바를 구성하는 아이디어를 제안했고 팀은 이를 받아들였다.

그리고 체력바 사이에 대시보드 UI를 하나 배치해 게임 진행 상황, 각 플레이어의 잔여 시간 등을 표시하는 쪽으로 아이디어를 구상했다. 하지만 이 구상은 나중에 구현해본 후 가시성도 떨어지고 스타일이 전체적으로 구닥다리 같은 느낌이 나서 다 갈아엎긴 했다.

HP 셀 구현

가장 먼저 체력 칸 하나에 대해서 체력이 빈 상태, 있는 상태, 각 상태가 바뀔 때 전환되는 효과를 구현하였다. 플레이어가 포탄에 맞아 체력을 잃을 때 단순히 여러 삼각형이 색깔만 바뀌거나 사라지기만 한다면 시각적으로 볼품없기도 하고 본인이 타격을 입었다는 사실을 인지하기도 힘들다. 유저 경험 측면에서도 적당한 임팩트를 연출해 위급 상황을 강조시키는 것은 중요하다.

우선 스프라이트 객체에서 쉐이더로 삼각형을 하나 그렸다. 이 삼각형은 미리 그려진 텍스처가 아니라 쉐이더 코드에 의해 GPU에서 동적으로 생성되고 있는 삼각형이다. 이렇게 한 이유는 색상 변화, 크기 변화 등을 유연하게 부여할 수 있고 단순 도형의 경우는 쉐이더 코드로 그리는 것이 리소스 절약 측면에서 유리하다.

삼각형은 체력이 없을 때 어두운 붉은색, 체력이 높을 때 채도가 높은 빨간색을 띈다. 그리고 두 색상 변화는 한 번에 변하는 것이 아니라, 채도가 높은 삼각형이 내부에서부터 커지며 채워지도록 했다.

inner_ratio 값에 따라 내부 삼각형이 외부 삼각형을 채우는 원리는 아래와 같다.

void fragment() {
	float dist = UV.y / 2.0;
    // 삼각형이 그려지는 원리. y가  0에서 1로 변화함에 따라 dist는 0에서 0.5로 변화.
    // x좌표의 중앙(0.5)으로부터 dist만큼 떨어진 픽셀만 채운다.
	if ((UV.x > 0.5 - dist) && (UV.x < 0.5 + dist)){
        // 내부 삼각형이 그려지기 시작할 위치를 결정짓는다.
        // 중앙으로부터 위, 아래로 꼭짓점과 밑변의 y좌표를 구한다.
		float inner_y_start = 0.5 - 0.5 * inner_ratio;
		float inner_y_end = 0.5 + 0.5 * inner_ratio;
        // 내부 삼각형을 그리기 위해 현재 픽셀 좌표를
        // 내부 삼각형의 시작 y값과 끝 y값을 기준으로 0과 1 사이의 값으로 구한 것.
		float t = (UV.y - inner_y_start) / (inner_y_end - inner_y_start);
		if ((t < 0.0) || (t > 1.0))
			t = 0.0;
		else
			t = clamp(t, 0, 1.0);
        // 앞서 dist을 구한 것과 같은 원리로 inner_dist를 구한다.
		float inner_dist = t / 2.0 * inner_ratio;
        // 내부 삼각형 픽셀이면 inner_color, 외부 삼각형 픽셀이면 outter_color로 채운다.
		if ((UV.x > 0.5 - inner_dist) && (UV.x < 0.5 + inner_dist)) {
			COLOR = inner_color;
		}
		else {
			COLOR = outter_color;
		}
	}
    // 외부 삼각형 픽셀 밖이면 투명한 색으로 채운다.
	else {
		COLOR = vec4(0, 0, 0, 0);
	}
}

그리고 키프레임 애니메이션으로 inner_ratio 값과 Scale 값을 조작해 역동적인 채우기 효과를 연출하였다.

게임이 시작될 때 이 HP 셀들의 채우기 효과를 차례대로 재생하면 나름 괜찮은 연출이 완성된다. 현재 게임에는 없지만 추후 체력 회복과 같은 액션이 추가되면 이 채우기 효과를 활용할 수 있다.

플레이어가 포탄에 맞은 후 그 임팩트를 유저가 좀 더 실감할 수 있도록, 화면 흔들림 효과와 더불어 손실된 HP 셀들도 흔들리도록 구현했다.

오브젝트를 무작위로 흔드는 것에는 Godot 커뮤니티 애드온으로 구할 수 있는 ShakerComponent2D 노드를 활용했다. 화면 흔들림 효과도 이로 구현했으며 위치값의 변화뿐만 아니라 빛의 밝기가 떨리는 효과 등도 이 유틸리티 노드로 구현할 수 있다.

대시보드 및 탄환 셀렉터

턴 전환, 제한 시간 등을 표시할 대시보드와 세 가지 탄환을 선택할 수 있는 셀렉터 UI를 구현해야 했다.

가장 먼저 떠올린 형태는 아래와 같이 양쪽 플레이어의 체력바 사이의 육각형 모양의 대시보드와 뷰포트 한쪽에 자리 잡고 있는 원형 셀렉터이다.

처음엔 이대로 개발을 이어나갔다. 하지만 UI 스타일이 좀 더 일관성 있고 전환 애니메이션도 적용이 되면 좋을 거 같아서 개발 거의 마지막 단계에 이 두 UI 디자인은 폐기되었다.

다음 포스트

스팀 멀티플레이