Lazy Game Dev

Игрушки!

Lazy gay dev?

Почему это выгодно?

  • 1. Flash
  •  
  •  
  • 1. Flash
  • 2. Приложение для iOS
  •  
  • 1. Flash
  • 2. Приложение для iOS
  • 3. Приложение для Android

HTML5 отлично подходит для создания казуальных игр

Можно заставить работать везде!

Его знают все!Очень низкий порог вхождения

Что-что?!Фигня, говорите?

Да ладно, и где это работает?!

Desktop

  • 1. Webkit (Сhrome, Safari и Yandex) 45.69%
  •  
  •  
  •  
  •  
  •  
  •  
  • 1. Webkit (Сhrome, Safari и Yandex) 45.69%
  • 2. Gecko (FireFox) 18.17%
  •  
  •  
  •  
  •  
  •  
  • 1. Webkit (Сhrome, Safari и Yandex) 45.69%
  • 2. Gecko (FireFox) 18.17%
  • 3. Trident (IE 10+) 1.49%
  •  
  •  
  •  
  •  
  • 1. Webkit (Сhrome, Safari и Yandex) 45.69%
  • 2. Gecko (FireFox) 18.17%
  • 3. Trident (IE 10+) 1.49%
  •  
  • Trident? (IE 9) 16.74%
  • Trident? (IE 8-) 11.72%
  • Presto? (Opera) 1.2%
  • Разное 6.19%
  • 1. Webkit (Safari)
  • 2. Gecko (FireFox)
  • 3. Trident (IE 10+)
  • 4. Blink (Chrome, Yandex, Opera)
  •  
  • Trident (IE 9-) — деградация!
  •  

Мобильные

  • 1. Android 3+ 21%
  • 2. iOS 27%
  • 3. Остальное 52%

А как делать игры?

Из чего состоит игра?

  • 1. Предзагрузчик ресурсов
  • 2. Игровая среда
  • 3. Звук
  • 4. Движок
  • 5. Локализация

Предзагрузчик ресурсов

  • 1. JavaScript
  • 2. Картинки
  • 3. Звуки
  • 4. Видео

require.js

<script
  data-main="scripts/main"
  src="scripts/require.js">
</script>
requirejs.config({
    shim: {
        'PXLoaderImage': ['PXLoader'],
        'jquery-ui.min': ['jquery.min'],
        'renjuController': [
          'PXLoaderImage',
          'handlebars',
          'jquery-ui.min',
          'modernizr'
          ]}});
require(
['modernizr','PXLoaderImage','jquery-ui.min','handlebars'],
function(){
  console.log('Загрузи лося');
});

PXLoader

Images, Sound, Video

var loader = new PxLoader();
loader.addImage("../image/url.png");
loader.addImage("../image/anotherurl.png");
loader.addProgressListener(progressCallback);
loader.addCompletionListener(completeCallback);
loader.start();

Трюки
создания среды

2.5D модели

<div class="mountain m50">
  <div class="m49">
    <div class="m48">
      <div class="m47">
      …
      </div>
    </div>
  </div>
</div>
.mountain div{
  transform-style: preserve-3d;
  transform: translateZ(3.75px);
  ...
  }
.m50{
  background-image:
    url('../img/mountine0050.png');
  }
.m49{
  background-image:
    url('../img/mountine0049.png');
  }

Низкополигональные модели

<div class="church">
  <div class="floor"></div>
  <div class="wall-1"></div>
  <div class="wall-2"></div>
  <div class="wall-3"></div>
  <div class="wall-4"></div>
  <div class="wall-5"></div>
  <div class="wall-6"></div>
  <div class="roof"></div>
</div>
.church{
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  transform-style: preserve-3d;
  }
.church .wall-1{
  width: 250px;
  height: 863px;
  position: absolute;
  top: -863px;
  left: -125px;
  transform-origin: 50% 100% 0;
  transform: rotateX(-90deg) translateZ(217px);
  background:
    url('../img/wall-1.png') 50% 0 no-repeat;
  background-size: 250px auto;
  }

Комбинированные модели

Спрайты

.walk-right{
  overflow: hidden;
  width: 160px;
  height: 320px;
  animation: walk-right .75s steps(4) infinite;
  background-color:     tranparent;
  background-image:     url("../img/sprites_map.png");
  background-position:  -320px -960px;
  background-size:      960px 1280px;
  }
@keyframes walk-right {
  0% {
    background-position: -320px -960px;
    }
  100% {
    background-position: -960px -960px;
    }
  }

Живой фон

В чем подвох?

  • 1. Применить ко всем элементам position:static;
  • 2. Уменьшить количество элементов, к которым применены трансформации
  • 3. Применить z-index.

Я люблю тебя, FireFox, жирная ты панда!

IE 10 поддерживает только transform-style: flat;

Звук?

soundManager.setup({
  url: './script/swf/',
  onready: callbackFunction
});
var bgsound =
soundManager.createSound({
  url: 'bgsound.mp3',
  autoLoad: true,
  onload: function() {
    this.sound.play();
  }.bind(this)
});
var sound = new Howl({
  urls: [
    'sound.mp3',
    'sound.ogg'
    ]}).play();

Трюки создания игровой логики

this.widget
  .find('.hell')
  .on(
    "hover",
    $.proxy(this.fail,this)
  );

hover для изменения лабиринта

<div class="path road piece-1">
  <div class="teleport teleport-1">
    <div class="path force piece-2"></div>
  </div>
</div>
<div class="path road piece-3"></div>
.teleport:hover>.teleport,
.teleport:hover>.path{
  display:block;
  }
.teleport>.teleport,
.teleport>.path{
  display:none;
  }

hover и бесконечные transition для cоздания триггеров

<div class="teleport teleport-2">
  <div class="path force piece-12"></div>
</div>
<div class="path road piece-5"></div>
<div class="path road piece-6 cracked"></div>
<div class="path road piece-7 cracked"></div>
<div class="path road piece-8 cracked"></div>
<div class="path road piece-9 cracked"></div>
<div class="path road piece-10"></div>
<div class="path road piece-11"></div>
.cracked{
  transition:
    transform 999999s linear,
    opacity 999999s ease-out;
  transform:
    translateZ(0) rotateX(0)
    rotateY(0) scale(1);
  opacity: 1;
  }
.teleport-2:hover~.path.cracked{
  transition:
    transform .6s ease-out,
    opacity .6s ease-out;
  transform:
    translateZ(-1000px) rotateX(40deg)
    rotateY(10deg) scale(.75);
  opacity: 0;
  }

animation для cоздания платформ

<div class="path
            road
            piece-20"></div>
<div class="path
            road
            horizontal-bridge-2piece
            piece-21"></div>
<div class="path
            road
            piece-22"></div>
.horizontal-bridge-2piece{
  transform: translateZ(1px);
  animation:
    horizontal-bridge-2piece
    2.5s ease-in-out
    infinite both;
  }
@keyframes horizontal-bridge-2piece {
  0%{margin:0 0 0 -1px;}
  7%{margin:0 0 0 -1px;}
  43%{margin:0 0 0 76px;}
  57%{margin:0 0 0 76px;}
  93%{margin:0 0 0 -1px;}
  100%{margin:0 0 0 -1px;}
}

Контролируем события с помощью pointer-event

  pointer-events:
  auto | none | visiblePainted |
  visibleFill | visibleStroke |
  visible | painted | fill |
  stroke | all | inherit

Движки

Локализация

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>
<link
  rel="localization"
  href="language/l20n.json">
<script
  src="scripts/l20n.js"></script>
{
  "locales": [
    "en",
    "ru"
  ],
  "default_locale": "ru",
  "resources": [
    "./{{locale}}/data.l20n"
  ]
}
<title "Renju">
<hello """
Hello, {{ $user.name }}.
Let's play!
""">
<h1
  data-l10n-id="title"
  ></h1>
<script
  type="application/l10n-data+json">
  {
    "user": {
      "name": "Jane",
      "gender": "feminine"
    }
  }
</script>
<h1 data-l10n-id="title"></h1>
<p data-l10n-id="hello"></p>
var node = document.querySelector(
  '[data-l10n-id="hello"]');
node.textContent = document.l10n.get(
  'title',
  {"user": {
    "name": "Jane",
    "gender": "feminine"
  }});

Адаптивность в играх

Feature Detection

<html lang="ru-RU" class="js
  canvas
  canvastext
  webgl
  no-touch
  geolocation
  postmessage
  websqldatabase
  ...
  inlinesvg
  svgclippaths">

Как определять поддержку preserve-3d

(function getPerspective(){
  var element = document.createElement('p'),
      html = document.getElementsByTagName('HTML')[0],
      body = document.getElementsByTagName('BODY')[0],
      propertys = {
        'webkitTransformStyle':'-webkit-transform-style',
        'MozTransformStyle':'-moz-transform-style',
        'msTransformStyle':'-ms-transform-style',
        'transformStyle':'transform-style'
      };
  //…
})();
(function getPerspective(){
  //…
  body.insertBefore(element, null);
  for (var i in propertys) {
      if (element.style[i] !== undefined) {
          element.style[i] = "preserve-3d";
      }
  }
  //…
})();
(function getPerspective(){
  //…
  var st = window.getComputedStyle(element, null),
      transform = st.getPropertyValue("-webkit-transform-style") ||
                  st.getPropertyValue("-moz-transform-style") ||
                  st.getPropertyValue("-ms-transform-style") ||
                  st.getPropertyValue("transform-style");
  if(transform!=='preserve-3d'){html.className += ' no-preserve-3d ';}
  document.body.removeChild(element);
})();
define(['Modernizr', 'testAllProps'],
  function (Modernizr, testAllProps) {
  Modernizr.addTest(
    'preserve3d',
    testAllProps(
      'transformStyle',
      'preserve-3d'));
});

Graceful degradation Progressive enhancement

.no-preserve-3d .rotation{
  display: none;
  }
.no-preserve-3d .zoom{
  top: 84px;
  }
.no-preserve-3d .church{
    background:
      url("../img/curch.png")
      50% 50% no-repeat;
    background-size: contain;
    }
.no-preserve-3d .church *{
  display: none;
  }

Cобытия мыши и мобильные устройства

Как определить
есть ли проблемы с производительностью?

Chrome FPS counter

chrome://flags/

stats.js

Developer tools profiler

Developer tools timeline

Полезные ссылки

Spriter

Spine

ShoeBox

TexturePacker

HTML5 Game Dev

Поиграем?

vcard

Anton Nemtsev

http://frontender.info/

thesilentimp@gmail.com

@silentimp

skype: ravencry