Studium przypadku – automatyczna zmiana rozmiaru gier na HTML5

Derek Detweiler
Derek Detweiler

Wstęp

Latem 2010 roku stworzyliśmy Sand Trap, grę, do której wzięliśmy udział w konkursie o gry HTML5 na telefony komórkowe. Jednak większość telefonów komórkowych wyświetlała tylko część gry lub sprawiała, że gra była zbyt mała. Wzięliśmy więc udział w płynnym dostosowywaniu gry do dowolnej rozdzielczości. Po przeprogramowaniu i wykorzystaniu pomysłów przedstawionych w tym artykule stworzyliśmy grę, która sprawdzi się w każdej nowoczesnej przeglądarce – zarówno na komputerze, jak i na urządzeniu mobilnym.

Zrzut ekranu z thwack na pełnym ekranie
zrzut ekranu z plikiem thwack mniejszą w oknie przeglądarki

Ta metoda sprawdziła się w przypadku Sand Trap, więc zastosowaliśmy ją w naszej najnowszej grze Thwack!. Gra automatycznie dostosowuje rozdzielczość ekranu, tak aby pasowały do pełnego ekranu i okna o niestandardowych rozmiarach, jak widać na poniższych zrzutach ekranu.

Wdrożenie tych rozwiązań wymagało użycia zarówno CSS, jak i JavaScriptu. Wypełnienie całego ekranu za pomocą CSS jest banalne, ale CSS nie umożliwia utrzymania takiego samego współczynnika szerokości do wysokości, co zapobiega rozciągnięciu obszaru roboczego i obszaru gry. Właśnie tu do akcji wkracza JavaScript. Możesz przeskalować elementy dokumentu za pomocą JavaScriptu i wywołać zmianę rozmiaru w przypadku zdarzeń okna.

Przygotowywanie strony

Pierwszym krokiem jest wyznaczenie na stronie obszaru, w którym toczy się rozgrywka. Jeśli dodasz ten blok, możesz w nim umieścić inne tagi lub element canvas. Po prawidłowym skonfigurowaniu te elementy podrzędne odziedziczą skalowanie z nadrzędnego bloku div.

Jeśli obszar gry składa się z 2 części: obszaru zabawy i strefy na bieżąco, może on wyglądać tak:

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

Po uzyskaniu podstawowej struktury dokumentu możesz dodać tym elementom kilka właściwości CSS, aby przygotować je do zmiany rozmiaru. Wiele właściwości CSS związanych z „gameArea” jest manipulowanych bezpośrednio przez JavaScript, ale aby działały, skonfiguruj kilka innych właściwości CSS, zaczynając od nadrzędnego bloku div gameArea:

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

W ten sposób lewy górny róg obszaru roboczego zostanie umieszczony na środku ekranu. Opisana w następnej sekcji funkcja automatycznego zmiany rozmiaru JavaScriptu modyfikuje dodatkowe właściwości CSS, aby zmienić rozmiar obszaru gry i wyśrodkować go w oknie.

Rozmiar obszaru gry jest automatycznie zmieniany zgodnie z wymiarami okna, więc wymiary elementów podrzędnych bloku div gameArea nie powinny być wyrażone w pikselach. Zależy Ci na wyrażonej w procentach wartościach procentowych. Wartości pikseli nie pozwalają na skalowanie elementów wewnętrznych wraz z nadrzędnym elementem div po zmianie. Warto jednak zacząć od pikseli, a potem przekonwertować je na wartości procentowe, gdy już uzyskasz układ, który Ci odpowiada.

W tym przykładzie obszar gry ma 300 pikseli wysokości i 400 pikseli szerokości. Obszar roboczy zajmuje cały obszar gry, a półprzezroczysty panel ze statystykami biegnie wzdłuż dolnej krawędzi i ma wysokość 24 pikseli, jak widać na Rysunku 1.

Wymiary elementów podrzędnych gameArea w pikselach
Rysunek 1. Wymiary elementów podrzędnych gameArea w pikselach

Przełożenie tych wartości na wartości procentowe sprawia, że obszar roboczy ma 100% szerokości i 100% wysokości (obszaru gameArea, a nie okna). Po podzieleniu 24 przez 300 panel statystyk będzie miał wysokość 8%. Ponieważ zakryje on dolną część obszaru gry, będzie on mieć 100%, jak widać na Rysunku 2.

Wymiary elementów podrzędnych gameArea w procentach
Rys. 2. Wymiary elementów podrzędnych gameArea w procentach

Po określeniu wymiarów obszaru gry i jego elementów podrzędnych możesz przygotować właściwości CSS obu elementów wewnętrznych w ten sposób:

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

Zmiana rozmiaru gry

Teraz możesz utworzyć funkcję do obsługi zmiany rozmiaru okna. Najpierw pobierz odniesienie do nadrzędnego elementu dokumentu gameArea.

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

Nie zależy Ci na dokładnej szerokości ani wysokości, więc kolejną informację, jaką musisz podać, jest stosunek szerokości do wysokości. Jak już wspominaliśmy o obszarze gry o szerokości 400 pikseli i wysokości 300 pikseli, wiesz, że zamierzasz ustawić współczynnik proporcji na 4 jednostki szerokości i 3 jednostki wysokości.

var widthToHeight = 4 / 3;

Ponieważ funkcja ta jest wywoływana przy każdym zmianie rozmiaru okna, trzeba też zadbać o nowe wymiary okna, aby dopasować do niego wymiary gry. Aby go znaleźć, użyj właściwości innerWidth i innerHeight okna.

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

Tak jak w przypadku danego współczynnika szerokości do wysokości, możesz teraz określić bieżący stosunek szerokości do wysokości okna:

var newWidthToHeight = newWidth / newHeight;

Pozwala to określić, czy gra ma wypełnić ekran w pionie czy w poziomie, tak jak pokazano na Rysunku 3.

Dopasowanie elementu gameArea do okna z zachowaniem współczynnika proporcji
Rys. 3: Dopasowanie elementu gameArea do okna z zachowaniem współczynnika proporcji

Jeśli wybrany kształt obszaru gry jest szerszy niż okno (a wysokość jest krótsza), musisz wypełnić okno w poziomie i zostawić marginesy u góry i dołu. Jeśli wybrany kształt obszaru gry jest większy niż kształt okna (a szerokość jest węższa), musisz wypełnić okno w pionie i pozostawić marginesy po lewej i prawej stronie.

Aby to zrobić, przetestuj odpowiedni współczynnik szerokości do wysokości z uwzględnieniem stosunku szerokości do wysokości bieżącego okna i wprowadź odpowiednie zmiany w ten sposób:

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';
}

Po dostosowaniu szerokości i wysokości obszaru gry musisz wyśrodkować elementy, umieszczając ujemny margines u góry, czyli u góry o połowę wysokości, a po lewej – do połowy szerokości. Pamiętaj, że CSS już umieszcza lewy górny róg elementu div gameArea dokładnie pośrodku okna, dlatego obszar gry jest wyśrodkowany na oknie:

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

Warto też automatycznie dostosować rozmiar czcionki. Jeśli masz wszystkie elementy podrzędne korzystające z takich elementów, możesz po prostu ustawić wartość właściwości CSS fontSize bloku div gameArea na wartość określoną przez jego rozmiar.

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

Na koniec chcesz, aby wymiary rysunków na płótnie były zgodne z jej nową szerokością i wysokością. Pamiętaj, że reszta kodu gry musi oddzielić wymiary silnika gry od wymiarów obszaru roboczego, aby uzyskać zgodność z dynamiczną rozdzielczością obszaru roboczego.

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

Ukończona funkcja zmiany rozmiaru może więc wyglądać tak:

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;
}

Teraz chcesz, aby te korekty były wykonywane automatycznie po każdej zmianie rozmiaru okna lub, w przypadku urządzeń mobilnych, zmianie orientacji ekranu. Aby zareagować na te zdarzenia, wywołaj funkcję changeGame() w ten sposób:

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

Jeśli rozmiar okna jest zbyt duży lub orientacja ekranu jest pionowa, ustawiasz szerokość 100% okna, a jeśli rozmiar okna jest za szeroki lub orientacja ekranu jest pozioma, wysokość okna jest ustawiana na 100%. Rozmiar pozostałego wymiaru jest dopasowywany zgodnie z wcześniej określonym współczynnikiem proporcji szerokości do wysokości.

Podsumowanie

Studio Gopherwood Studios używa tej struktury we wszystkich naszych grach w formacie HTML5. Okazało się, że jest on bardzo przydatny w obsłudze różnych rozdzielczości ekranu i urządzeń mobilnych. Co więcej, dzięki przeglądarce pełnoekranowej nasze gry internetowe są bardziej wciągające niż w wielu innych grach na komputery. Czekamy na kolejne innowacje w grach internetowych, ponieważ HTML5 i technologie internetowe wciąż ewoluują.