HTML5 отлично подходит для разработки игр — конечно, есть много скептиков, но возможность создать игру, которая работает на всех платформах с минимальными изменениями, не имеет аналогов. Это первая часть серии из трех частей, где я поделюсь своими советами по разработке кроссплатформенной игры HTML5.
Часть 1: заставить игру выглядеть великолепно и хорошо работать на всех платформах
Часть 2: Обработка различных типов ввода каждой платформы
Часть 3: Обеспечение безопасности вашей игры.
Часть 1 охватывает:
- CSS3 медиа-запросы для пользовательского интерфейса
- Масштабирование игры
- Retina отображает
- «Полный экран» на мобильном телефоне
- «Установка» игры на мобильный
- Улучшение FPS за счет использования субканваз.
CSS3 Media Queries для пользовательского интерфейса
К настоящему времени вы, вероятно, хорошо знакомы с медиа-запросами CSS — это отличный способ адаптировать свой сайт или игру к экранам разных размеров. Они будут просто полезны для пользовательского интерфейса ваших игр, так как сама игра, скорее всего, будет работать внутри элемента canvas.
Для наших игр и сайта мы обычно просто настраиваем макеты шириной менее 800 пикселей. Вот как это делается:
@media screen and (max-width: 800px) { /* Whatever CSS you want here */ }
Мы используем медиазапросы, чтобы скрыть или переместить элементы пользовательского интерфейса, которые не важны для мобильных устройств.
Еще одна вещь, с которой вам придется иметь дело, это то, как ваша игра выглядит в разных направлениях. Для настольных компьютеров мы привыкли к пейзажу, но на мобильном устройстве вы можете захотеть, чтобы ваша игра выглядела хорошо в портретном режиме.
В Word Wars у нас есть некоторый JavaScript, который определяет, является ли представление пейзажным или портретным (если ширина окна меньше высоты окна, это портрет), и мы добавляем класс .portrait к элементу <html>. Затем в нашем CSS у нас есть некоторые специфические свойства для дочерних элементов html.portrait (например, скрытие определенных элементов, их масштабирование по-разному). Если вы посмотрите на
http://wordwars.clay.ioна мобильном устройстве (или просто уменьшите размер браузера) вы увидите, как мы показываем информационную панель и таймер вверх для портрета и влево для ландшафта.
Вот немного кода для определения портрета и добавления класса портрета:
window.onresize = function() // this needs to be called onorientationchange as well { var htmlTag = document.getElementsByTagName('html')[0]; if(window.innerWidth < window.innerHeight) htmlTag.className += ' portrait'; else htmlTag.className = htmlTag.className.replace(' portrait', ''); }
Масштабирование игры
Достаточно легко масштабировать элемент canvas до полной ширины окна. Трудная часть — масштабирование всего содержимого внутри.
Чтобы масштабировать холст до полной ширины и высоты, просто убедитесь, что ваши <html> и <body> не имеют отступов или полей, и немного JavaScript, вы можете установить ширину и высоту холста на полную и обновлять их, когда Изменение размера окна (обратите внимание, что изменение ориентации на мобильном устройстве не вызывает изменение размера, только onorientationchange):
window.onresize = function() // You can alternatively add an event listener for onresize { var canvas = document.getElementById(‘canvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; } window.onload = function() { window.onresize(); // Call onload to set the width & height initially }
Когда вы добавляете элементы в контекст холста, вы указываете высоту и ширину, например: <code> ctx.fillRect (0, 0, 100, 100); </ code> рисует прямоугольник размером 100 на 100 пикселей, который совсем не жидкий
Чтобы сделать эту жидкость, вам нужно масштабировать в соответствии с высотой и / или шириной окна. Например, если вы хотите, чтобы элемент занимал 10% экрана по вертикали и 10% по горизонтали, вы должны использовать:
var rectangle = { // Be sure to update this onresize width: window.innerWidth / 10, height: window.innerHeight / 10 };
Как заставить игру хорошо выглядеть на экранах Retina
Другая проблема, с которой приходится сталкиваться, — заставить игру хорошо выглядеть на экранах сетчатки. Для этого мы делаем две вещи. Сначала мы устанавливаем ширину CSS и высоту элемента canvas как window.innerWidth и window.innerHeight. Кроме того, мы устанавливаем атрибуты width и height элемента canvas, чтобы они были значениями, умноженными на devicePixelRatio. Во-вторых, мы умножаем размер каждого элемента, нарисованного на холсте, на отношение пикселей устройства (обычно это 1, но для таких устройств, как iPhone 4, это 2). Следуя приведенному выше примеру, мы получим:
var devicePixelRatio = window.devicePixelRatio || 1; // > 1 for retina displays var canvas = document.getElementById(‘canvas'); /* Be sure to update all these values onresize */ // Width & height attributes (scaled according to pixel ratio) canvas.width = window.innerWidth * devicePixelRatio; canvas.height = window.innerHeight * devicePixelRatio; // CSS width & height canvas.style.width = window.innerWidth; canvas.style.height = window.innerHeight; var rectangle = { width: devicePixelRatio * window.innerWidth / 10, height: devicePixelRatio * window.innerHeight / 10 };
«Полный экран» на мобильном телефоне
Адресная строка на мобильном устройстве может быть довольно раздражающей, когда дело доходит до игр, но с этим битом кода довольно легко пролистать .
Еще одна вещь, которую стоит рассмотреть, чтобы попытаться удержать больше мобильных пользователей, — это предложить пользователям «установить» вашу игру как закладку на iOS (которая добавляет значок на домашний экран). Другое преимущество этих закладок заключается в том, что когда ваша игра открывается с главного экрана, нижняя панель в Safari не отображается, поэтому появляется больше ресурсов.
Для этого вам нужно указать значок 57×57 (в <head>):
<!-- Works for iOS and Android --> <link rel="apple-touch-icon-precomposed" href="images/apple-touch-icon.png" />
Этот фрагмент кода JavaScript, написанный моим партнером Джо, покажет подсказку для установки. Не стесняйтесь использовать этот фрагмент в своей игре!
Конечно, если вы разместите свою игру на
clay.io , мы позаботимся обо всем этом для вас.
Улучшение FPS с помощью субканваз
В нашей самой последней игре Word Wars FPS в мобильном Safari поначалу был ужасным.
Поскольку каждая плитка в нашей игре имеет градиенты и что-то, что не должно выглядеть хорошо, перерисовка этих градиентов 60 раз в секунду действительно сильно загружает процессор. Word Wars имеет четыре типа плиток: серый (невыбранный), синий (выбранный), зеленый (правильный), красный (неправильный). Решением нашей низкой частоты кадров было то, что для каждого типа, когда мы изначально загружаем страницу, мы рисуем их и сохраняем их в подканале и используем эту подкану в drawImage (). Вот посмотрите, как мы это делаем:
var insetCircle = document.createElement('canvas'); var redCircle = document.createElement('canvas'); var blueCircle = document.createElement('canvas'); var greenCircle = document.createElement('canvas'); var renderTile = function(circle, color1, color2, outglow1, outglow2, strokeColor) { circle.width = size; // size is something we set elsewhere for fluid width & height circle.height = size; var innerGlow = ctx.createRadialGradient(0, 0, size/2-4, 0, 0, size/2); // Gradient for looks innerGlow.addColorStop(0, 'rgba(255,255,255,0)'); innerGlow.addColorStop(1, 'rgba(255,255,255,.2)'); var cctx = circle.getContext('2d'); cctx.translate(size/2, size/2); // center of circle var grdRedLin = cctx.createLinearGradient(0, -size/2, 0, size/2); grdRedLin.addColorStop(0, color1); grdRedLin.addColorStop(1, color2); cctx.save(); outerGlow(outglow1, outglow2, cctx); cctx.beginPath(); cctx.arc(0, 0, size/2, 0, 2*Math.PI); cctx.strokeStyle = strokeColor; cctx.lineWidth = 4; cctx.stroke(); cctx.fillStyle = grdRedLin; cctx.fill(); cctx.fillStyle = innerGlow; cctx.fill(); } var renderAllTiles = function() { // Render each tile in its own canvas renderTile(redCircle, '#a52222', '#7e0101', '#a52222', 'rgba(165, 34, 34, 0)', '#520101'); renderTile(blueCircle, '#1d97c9', '#0a7cab', '#7bb5c5', 'rgba(123, 181, 197, 0)', '#105a78'); renderTile(greenCircle, '#1dc924', '#0faf15', '#1dc924', 'rgba(29, 201, 36, 0)', '#107814'); insetCircle.width = size; insetCircle.height = size; var cctx = insetCircle.getContext('2d'); cctx.translate(size/2+50, size/2+50); cctx.lineWidth = Math.floor(size * 0.1); cctx.strokeStyle = 'rgba(255,255,255,.25)'; // draw the rest of the tile cctx.beginPath(); cctx.arc(0, 0, size/2, 0, 2*Math.PI); cctx.stroke(); var grd = ctx.createRadialGradient(0, 0, 10, 0, 0, size/2); grd.addColorStop(0, 'rgba(255,255,255,.1)'); grd.addColorStop(1, '#a1aaae'); cctx.fillStyle = grd; cctx.fill(); } renderAllTiles(); // Actually rendering them (render() is called with requestAnimationFrame()) var circles = [insetCircle, blueCircle, greenCircle, redCircle]; // These correspond to tile states var render = function() { // ... ctx.drawImage(circles[tiles[i].state], Math.floor(-size/2), Math.floor(-size/2), size, size); // ... }
Приведенный выше код визуализирует каждый из 4 типов плиток на своем собственном холсте, и когда нам нужно отобразить его на главном холсте, мы ссылаемся на субканву в ctx.drawImage (). Затем мы добавляем что-нибудь динамическое поверх плиток (в нашем случае это буквы).
Вот и все к части 1. Часто проверяйте части 2 и 3, где я расскажу об обработке различных типов ввода, а также о безопасности для игр с JavaScript и бэкэндом.
Если вы заинтересованы в разработке игр на HTML5, посмотрите API разработчика clay.io
где мы заботимся о достижениях, списках лидеров, обработке платежей, социальной интеграции, входе пользователей в систему, многопользовательских комнатах, скриншотах и нескольких вещах, которые я упомянул в этом посте. Если у вас есть какие-либо отзывы об API или идеи о том, как мы можем сделать вашу жизнь проще, дайте мне знать в комментариях!