우수사례 - HTML5 게임 크기 자동 조정

Derek Detweiler
Derek Detweiler

소개

2010년 여름 Google은 휴대전화용 HTML5 게임 경연대회인 Sand Trap을 개발했습니다. 하지만 대부분의 휴대전화에서는 게임의 일부만 표시되거나 게임이 너무 작아서 플레이할 수 없습니다. 그래서 우리는 게임이 모든 해상도에 맞게 유연하게 조정되도록 하기로 했습니다. 이 글에 설명된 아이디어를 약간 재프로그래밍하고 사용한 끝에, 우리는 데스크톱이든 모바일 장치에서 실행되든 어떤 최신 브라우저에서든 확장 가능한 게임을 만들 수 있었습니다.

전체 화면 스크린샷
브라우저 창에서 더 작게 표시되는 스크린샷

이 접근방식은 Sand Trap에도 효과적이었으므로 최신 게임인 Thwack!에도 동일한 방식을 사용했습니다. 게임은 아래 스크린샷과 같이 전체 화면 창과 맞춤 크기 창에 모두 맞도록 화면 해상도를 자동으로 조정합니다.

이를 구현하려면 CSS와 자바스크립트를 모두 활용해야 했습니다. CSS를 사용하여 전체 화면을 채우는 것은 간단하지만 CSS에서는 캔버스와 게임 영역이 늘어나지 않도록 하기 위해 동일한 너비-높이 비율을 유지할 수 없습니다. 이때 필요한 것이 JavaScript입니다. JavaScript로 문서 요소의 크기를 재조정하고 창 이벤트에서 크기 조절을 트리거할 수 있습니다.

페이지 준비

첫 번째 단계는 페이지에서 게임이 진행되는 영역을 지정하는 것입니다. 이 요소를 div 블록으로 포함하면 블록 내에 다른 태그나 캔버스 요소를 배치할 수 있습니다. 올바르게 설정하면 이러한 하위 요소가 상위 div 블록의 배율을 상속합니다.

게임 영역에 놀이 공간과 점수 기록이라는 두 부분이 있다면 다음과 같을 수 있습니다.

<div id="gameArea">
  <canvas id="gameCanvas"></canvas>
  <div id="statsPanel"></div>
</div>

기본 문서 구조가 완성되면 이러한 요소에 몇 가지 CSS 속성을 제공하여 크기 조절을 준비할 수 있습니다. 'gameArea'의 CSS 속성은 대부분 JavaScript에 의해 직접 조작되지만 이 속성이 작동하도록 하려면 상위 gameArea div 블록으로 시작하는 몇 가지 다른 CSS 속성을 설정합니다.

#gameArea {
  position: absolute;
  left:     50%;
  top:      50%;
}

이렇게 하면 캔버스의 왼쪽 상단 모서리가 화면 중앙에 배치됩니다. 다음 섹션에서 설명하는 자바스크립트 자동 크기 조절 기능은 추가 CSS 속성을 조작하여 게임 영역의 크기를 조절하고 창의 중앙에 배치합니다.

게임 영역은 창의 크기에 따라 자동으로 크기가 조절되므로 gameArea div 블록의 하위 요소 크기를 픽셀 단위로 지정하는 대신 백분율로 지정하는 것이 좋습니다. 픽셀 값이 변경될 때 내부 요소가 상위 div에 맞춰 조정되도록 허용하지 않습니다. 그러나 픽셀로 시작한 다음 원하는 레이아웃이 있는 경우 백분율로 변환하는 것이 도움이 될 수 있습니다.

이 예에서는 게임 영역의 높이를 300픽셀, 너비 400픽셀로 설정합니다. 캔버스는 게임 영역 전체를 덮고 있으며, 그림 1과 같이 반투명 통계 패널이 하단을 따라 높이 24픽셀로 표시됩니다.

gameArea 하위 요소 크기(픽셀)
그림 1: 픽셀 단위의 gameArea 하위 요소 크기

이 값을 백분율로 변환하면 캔버스의 너비는 100%, 높이가 100% 가 됩니다 (창이 아닌 gameArea). 24를 300으로 나누면 통계 패널의 높이가 8%가 되며, 그림 2와 같이 게임 영역의 하단을 덮게 되므로 너비도 100%가 됩니다.

gameArea 하위 요소의 크기(백분율)
그림 2: gameArea 하위 요소의 크기(백분율)

이제 게임 영역과 하위 요소의 크기를 확인했으므로 다음과 같이 두 내부 요소의 CSS 속성을 결합할 수 있습니다.

#gameCanvas {
  width: 100%;
  height: 100%;
}
#statsPanel {
  position: absolute;
  width: 100%;
  height: 8%;
  bottom: 0;
  opacity: 0.8;
}

게임 크기 조정

이제 창 크기 조절을 처리하는 함수를 만들 준비가 되었습니다. 먼저 상위 gameArea 문서 요소에 대한 참조를 가져옵니다.

var gameArea = document.getElementById('gameArea');

정확한 너비나 높이는 신경 쓰지 않아도 되므로 다음으로 설정해야 하는 정보는 너비 대 높이의 비율입니다. 너비 400픽셀, 높이 300픽셀인 게임 영역에 대한 이전의 참조를 사용하여 가로세로 비율을 너비 4단위와 높이 3단위로 설정하려고 합니다.

var widthToHeight = 4 / 3;

이 함수는 창 크기가 조절될 때마다 호출되므로 게임의 크기를 일치하도록 조정할 수 있도록 창의 새 크기도 가져오려고 합니다. 창의 innerWidth 및 innerHeight 속성을 사용하여 이를 찾습니다.

var newWidth = window.innerWidth;
var newHeight = window.innerHeight;

원하는 너비 대 높이 비율을 결정한 것처럼 이제 창의 너비 대 높이 비율을 결정할 수 있습니다.

var newWidthToHeight = newWidth / newHeight;

이렇게 하면 그림 3과 같이 게임이 화면을 수직 또는 수평으로 채우도록 할지 결정할 수 있습니다.

가로세로 비율을 유지하면서 gameArea 요소 창에 맞춤
그림 3: 가로세로 비율을 유지하면서 gameArea 요소 창에 맞추기

원하는 게임 영역 도형이 창 모양보다 넓고 높이가 더 짧으면 창을 수평으로 채우고 상단과 하단에 여백을 남겨 두어야 합니다. 마찬가지로, 원하는 게임 영역 도형이 창의 모양보다 높고 너비가 더 좁으면 창을 수직으로 채우고 왼쪽과 오른쪽에 여백을 남겨 두어야 합니다.

이렇게 하려면 현재 창의 너비 대 높이 비율로 원하는 너비 대 높이 비율을 테스트하고 다음과 같이 적절하게 조정합니다.

if (newWidthToHeight > widthToHeight) {
  // window width is too wide relative to desired game width
  newWidth = newHeight * widthToHeight;
  gameArea.style.height = newHeight + 'px';
  gameArea.style.width = newWidth + 'px';
} else { // window height is too high relative to desired game height
  newHeight = newWidth / widthToHeight;
  gameArea.style.width = newWidth + 'px';
  gameArea.style.height = newHeight + 'px';
}

이제 게임 영역의 너비와 높이를 조정했으므로 상단에 높이의 절반인 음수 여백을 배치하고 왼쪽에 너비의 절반인 음수 여백을 배치하여 가운데에 배치해야 합니다. CSS는 이미 gameArea div의 왼쪽 상단 모서리를 창의 정확한 중앙에 배치하고 있으므로 다음과 같이 창의 게임 영역이 중앙에 배치됩니다.

gameArea.style.marginTop = (-newHeight / 2) + 'px';
gameArea.style.marginLeft = (-newWidth / 2) + 'px';

글꼴 크기를 자동으로 조정할 수도 있습니다. em을 사용하는 모든 하위 요소가 있는 경우 gameArea div 블록의 fontSize CSS 속성을 크기에 의해 결정된 값으로 설정하면 됩니다.

gameArea.style.fontSize = (newWidth / 400) + 'em';

마지막으로 캔버스 그리기 크기를 새 너비 및 높이와 일치시키려고 합니다. 게임 코드의 나머지 부분은 동적 캔버스 해상도를 수용하기 위해 게임 엔진 크기를 캔버스 그리기 크기와 별도로 유지해야 합니다.

var gameCanvas = document.getElementById('gameCanvas');
gameCanvas.width = newWidth;
gameCanvas.height = newHeight;

따라서 완성된 크기 조절 함수는 다음과 같습니다.

function resizeGame() {
    var gameArea = document.getElementById('gameArea');
    var widthToHeight = 4 / 3;
    var newWidth = window.innerWidth;
    var newHeight = window.innerHeight;
    var newWidthToHeight = newWidth / newHeight;
    
    if (newWidthToHeight > widthToHeight) {
        newWidth = newHeight * widthToHeight;
        gameArea.style.height = newHeight + 'px';
        gameArea.style.width = newWidth + 'px';
    } else {
        newHeight = newWidth / widthToHeight;
        gameArea.style.width = newWidth + 'px';
        gameArea.style.height = newHeight + 'px';
    }
    
    gameArea.style.marginTop = (-newHeight / 2) + 'px';
    gameArea.style.marginLeft = (-newWidth / 2) + 'px';
    
    var gameCanvas = document.getElementById('gameCanvas');
    gameCanvas.width = newWidth;
    gameCanvas.height = newHeight;
}

이제 창의 크기가 조절될 때마다 또는 휴대기기의 경우 화면 방향이 변경될 때마다 이러한 조정이 자동으로 이루어지도록 하려고 합니다. 이러한 이벤트는 다음과 같이 sizeGame() 함수를 호출하도록 하여 처리합니다.

window.addEventListener('resize', resizeGame, false);
window.addEventListener('orientationchange', resizeGame, false);

창 크기가 너무 높게 조정되거나 화면 방향이 세로이면 창의 너비가 100% 가 되고, 창의 너비가 너무 넓거나 화면 방향이 가로이면 높이가 창의 100% 가 됩니다. 나머지 크기는 미리 정해진 너비-높이 가로세로 비율에 따라 크기가 조절됩니다.

요약

Gopherwood Studios는 당사의 모든 HTML5 게임에 이 구조의 버전을 사용했으며, 다양한 화면 해상도와 다양한 모바일 기기를 수용하는 데 매우 유용한 것으로 입증되었습니다. 또한 전체 화면 브라우저를 사용하므로 웹 게임에 많은 브라우저 기반 게임보다 전통적인 데스크톱 게임과 유사한 몰입형 경험을 선사합니다. HTML5 및 웹 기술이 계속 발전함에 따라 웹 게임에도 더 많은 혁신이 있을 것으로 기대합니다.