Хочу раcсказать, как без особых сложностей сделать свою первую мини игру на html5 (если точнее: js, html5, css).
Суть игры будет в следующем: человечек ходит по полю, между камнями и собирает цветочки, у каждого цветочка есть 1 охранник. Количество цветов с каждым уровнем увеличивается, карты создаются в случайном порядке.
Выглядит это все будет так:

Итак, для нашей задачи я буду использовать js библиотеку craftyjs. Так как для того, что бы нарисовать самостоятельно sprites, у меня руки не оттуда растут, я позаимствую sprites из примера на сайте, все остальное будем делать с нуля, да и взятый sprite мы дополним врагами в красных шапочках и футболках:

Теперь пора сделать каркас приложения, у меня он выглядит вот так:

Так же, давайте сразу создадим:
/index.html
/css/game.css
/js/game.js
В последнем файле мы создаем канву с заданной шириной и высотой, создаем sprites из нашего файла и запускаем первую сцену «loading»
Давайте сделаем нашу сцену «loading»:
/js/scenes/loading.jd
Здесь мы просто подгружаем sprite, делаем черный фон и выводим на фоне текст. К тому как мы вывели текст мы еще вернемся, а пока давайте сразу сделаем еще 2 аналогичные сцены, для выигрыша и проигрыша.
/js/scenes/win.js
/js/scenes/lose.js
Главную сцену пока оставим на сладкое и перейдем к объектам
В craftyjs есть 2 основных типа, компоненты Crafty.c и сущности Crafty.e. Сущности аккумулируют в себе свойства компонентов. В нашей игре будет 6 сущностей: цветок, камень, трава (фон), unit (базовый класс человечка), игрок и монстр. Для каждой сущности мы создадим свой компонент.
Начнем с самого простого, трава:
/js/objects/grass.js
Заметьте, что здесь мы подключили компонент Canvas, а в сценах, к тексту мы подключали компонент DOM, это дает нам разное поведение объектов, например в тексте сцен у нас появилась возможность использовать метод css. Так же тут мы подключили наш sprite, выбирая в случайном порядке какой из 2 рисунков травы нам использовать. Теперь, так же сделаем компонент для камня:
/js/objects/bush.js
Здесь все тоже самое, обратите только внимание на hard_bush, оно нам скоро пригодится. Перейдем к цветам, они у нас будут развиваться на ветру:
/js/objects/flower.js
Для создания анимации мы подключаем компонент SpriteAnimation, который дает нам методы:
public this .animate(String id, Number fromX, Number y, Number toX) — анимация по sprite
public Boolean .isPlaying([String reel]) — проверка, играет ли анимация
Далее по событию EnterFrame мы создаем ветер. Так же в этом компоненте есть модуль clear, который убирает цветок если мы его собрали.
Пора перейти к созданию unit, я опишу все в комментариях:
/js/objects/unit.js
Тут вся магия заключается в компоненте Collision, который позволяет нам задать границы столкновений, и отрабатывать различные события, так же в этом компоненте обрабатывается событие Moved, данное событие мы будем генерировать в наших компонентах игрока и монстра, параметром данного события будет x и y предидущей позиции.
Создадим игрока:
/js/objects/player.js
Подключаем созданный ранее компонент Unit, sprite player и компонент движения Fourway. Fourway — это компонент который изменяет положение нашего sprite в зависимости от нажатой стрелки на клавиатуре, при создание принимает параметр скорости перемещения. Далее с помощью того же компонента столкновения мы отлавливаем 2 события, столкновение с цветком (тогда мы его собираем) и столкновение с монстром (тогда мы умираем).
Пора создать монстра:
/js/objects/monster.js
Обратите внимание на компонент FourwayAI, такого компонента нет, нам нужно будет создать его. Данный компонент будет отвечать за самостоятельное передвижение монстра:
/js/objects/fourwai_ai.js
this.trigger — как не сложно догадаться, создает событие, которое мы потом и отлавливаем для анимации.
Теперь нам осталось создать последнюю главную сцену, которая будет генерировать всю нашу карту и расставлять на ней игрока, монстров, камни и цветки.
/js/scenes/main.js
Тут мы наконец создаем сущности для наших компонентов, рисуем стенку из камней по периметру, заполняем фон травой, а внутри для каждого квадрата 16x16 создаем пустоту, камень или цветок + врага. В конце мы размещаем нашего игрока.
Вот и все, мы сделали простую, бесконечную, игру — бродилку на html5. Исходный код доступен на github. Проверял только в chrome и firefox под mac os.
Если кто нибудь подскажет устойчивый сервис где можно выложить демку, буду благодарен.
UPD: По совету RiderSx выложил демо тут, надеюсь выдержит.
Суть игры будет в следующем: человечек ходит по полю, между камнями и собирает цветочки, у каждого цветочка есть 1 охранник. Количество цветов с каждым уровнем увеличивается, карты создаются в случайном порядке.
Выглядит это все будет так:

Подготовка каркаса
Итак, для нашей задачи я буду использовать js библиотеку craftyjs. Так как для того, что бы нарисовать самостоятельно sprites, у меня руки не оттуда растут, я позаимствую sprites из примера на сайте, все остальное будем делать с нуля, да и взятый sprite мы дополним врагами в красных шапочках и футболках:

Теперь пора сделать каркас приложения, у меня он выглядит вот так:

Так же, давайте сразу создадим:
/index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/crafty.js"></script>
<!-- objects -->
<script type="text/javascript" src="js/objects/flower.js"></script>
<script type="text/javascript" src="js/objects/bush.js"></script>
<script type="text/javascript" src="js/objects/grass.js"></script>
<script type="text/javascript" src="js/objects/unit.js"></script>
<script type="text/javascript" src="js/objects/player.js"></script>
<script type="text/javascript" src="js/objects/fourway_ai.js"></script>
<script type="text/javascript" src="js/objects/monster.js"></script>
<!-- scenes -->
<script type="text/javascript" src="js/scenes/loading.js"></script>
<script type="text/javascript" src="js/scenes/main.js"></script>
<script type="text/javascript" src="js/scenes/win.js"></script>
<script type="text/javascript" src="js/scenes/lose.js"></script>
<script type="text/javascript" src="js/game.js"></script>
<link rel="stylesheet" href="css/game.css" type="text/css" media="screen" charset="utf-8">
<title>Simpe RPG</title>
</head>
<body></body>
</html>
* This source code was highlighted with Source Code Highlighter.
/css/game.css
body, html { margin:0; padding: 0; overflow:hidden; font-family:Arial; font-size:20px }
#cr-stage { border:2px solid black; margin:5px auto; color:white }
* This source code was highlighted with Source Code Highlighter.
/js/game.js
var Settings = {
width: 400, // ширина игрового поля
height: 320, // высота
poligon: 16, // размер полигона 16x16
level: 1, // текущий уровень
flower_count: 0 // цветков на уровне
};
window.onload = function() {
Crafty.init(Settings.width, Settings.height); // создаем игровое поле
// подгружаем sprite
Crafty.sprite(Settings.poligon, "images/sprite.png", {
grass1: [0,0],
grass2: [1,0],
grass3: [2,0],
grass4: [3,0],
flower: [0,1],
bush1: [0,2],
bush2: [1,2],
player: [0,3],
monster: [0,4]
});
// запускаем первую сцену
Crafty.scene("loading");
};
* This source code was highlighted with Source Code Highlighter.
В последнем файле мы создаем канву с заданной шириной и высотой, создаем sprites из нашего файла и запускаем первую сцену «loading»
Создание сцен
Давайте сделаем нашу сцену «loading»:
/js/scenes/loading.jd
Crafty.scene("loading", function() {
Crafty.load(["images/sprite.png"], function() {
// выполним это действие, после того как images/sprite.png будет загружен
setTimeout(function() {
Crafty.scene("main");
}, 100);
});
// меняем цвет фона
Crafty.background("#000");
// выводим по центру текст
Crafty.e("2D, DOM, Text").attr({w: 100, h: 20, x: 150, y: 120})
.text("Loading... <br/>Level: " + Settings.level)
.css({"text-align": "center"});
});
* This source code was highlighted with Source Code Highlighter.
Здесь мы просто подгружаем sprite, делаем черный фон и выводим на фоне текст. К тому как мы вывели текст мы еще вернемся, а пока давайте сразу сделаем еще 2 аналогичные сцены, для выигрыша и проигрыша.
/js/scenes/win.js
Crafty.scene("win", function() {
Settings.level += 1;
Crafty.background("#000");
Crafty.e("2D, DOM, Text").attr({w: 100, h: 20, x: 150, y: 120})
.text("You win! <br/>Level: " + Settings.level)
.css({"text-align": "center"});
setTimeout(function() {
Crafty.scene("main");
}, 1000);
});
* This source code was highlighted with Source Code Highlighter.
/js/scenes/lose.js
Crafty.scene("lose", function() {
Settings.level = 1;
Crafty.background("#000");
Crafty.e("2D, DOM, Text").attr({w: 100, h: 20, x: 150, y: 120})
.text("You lose! <br/>Level: " + Settings.level)
.css({"text-align": "center"});
setTimeout(function() {
Crafty.scene("main");
}, 1000);
});
* This source code was highlighted with Source Code Highlighter.
Главную сцену пока оставим на сладкое и перейдем к объектам
Создание компонентов
В craftyjs есть 2 основных типа, компоненты Crafty.c и сущности Crafty.e. Сущности аккумулируют в себе свойства компонентов. В нашей игре будет 6 сущностей: цветок, камень, трава (фон), unit (базовый класс человечка), игрок и монстр. Для каждой сущности мы создадим свой компонент.
Начнем с самого простого, трава:
/js/objects/grass.js
Crafty.c('Grass', {
init: function() {
this.requires("2D");
this.requires("Canvas");
this.requires("grass"+Crafty.randRange(1,2));
this.attr({x: 0, y: 0});
}
});
* This source code was highlighted with Source Code Highlighter.
Заметьте, что здесь мы подключили компонент Canvas, а в сценах, к тексту мы подключали компонент DOM, это дает нам разное поведение объектов, например в тексте сцен у нас появилась возможность использовать метод css. Так же тут мы подключили наш sprite, выбирая в случайном порядке какой из 2 рисунков травы нам использовать. Теперь, так же сделаем компонент для камня:
/js/objects/bush.js
Crafty.c('Bush', {
init: function() {
this.requires("2D");
this.requires("Canvas");
this.requires("bush"+Crafty.randRange(1,2));
this.requires("hard_bush");
this.attr({x: 0, y: 0, z: 2});
}
});
* This source code was highlighted with Source Code Highlighter.
Здесь все тоже самое, обратите только внимание на hard_bush, оно нам скоро пригодится. Перейдем к цветам, они у нас будут развиваться на ветру:
/js/objects/flower.js
Crafty.c('Flower', {
init: function() {
this.requires("2D");
this.requires("Canvas");
this.requires("flower");
this.requires("SpriteAnimation");
this.attr({x: 0, y: 0});
this.animate("wind", 0, 1, 3);
this.bind("EnterFrame", function() {
if(!this.isPlaying())
this.animate("wind", 80);
});
},
clear: function() {
this.removeComponent('flower');
this._visible = false;
}
});
* This source code was highlighted with Source Code Highlighter.
Для создания анимации мы подключаем компонент SpriteAnimation, который дает нам методы:
public this .animate(String id, Number fromX, Number y, Number toX) — анимация по sprite
public Boolean .isPlaying([String reel]) — проверка, играет ли анимация
Далее по событию EnterFrame мы создаем ветер. Так же в этом компоненте есть модуль clear, который убирает цветок если мы его собрали.
Пора перейти к созданию unit, я опишу все в комментариях:
/js/objects/unit.js
Crafty.c('Unit', {
init: function() {
this.requires("2D");
this.requires("Canvas");
this.requires("SpriteAnimation");
this.requires("Collision"); // компонент столкновения
this.attr({x: 0, y: 0, z: 1});
this.collision(); // подключаем компонент столкновения
// отрабатываем событие столкновения с камнем
this.onHit("hard_bush", function(e) {
var object = e[0].obj;
// left
if (object.x > this.x && (this.x + Settings.poligon) > object.x) {
this.x -= this._speed;
this.stop();
}
// right
if (object.x < this.x && this.x < (object.x + Settings.poligon)) {
this.x += this._speed;
this.stop();
}
// top
if (object.y < this.y && (this.y + Settings.poligon) > object.y) {
this.y += this._speed;
this.stop();
}
// bottom
if (object.y > this.y && this.y < (object.y + Settings.poligon)) {
this.y -= this._speed;
this.stop();
}
});
// анимация движения, сами указатели на sprite
// находятся в дочерних компонентах
this.bind("Moved", function(e) {
if(this.x < e.x) {
if(!this.isPlaying("walk_left"))
this.stop().animate("walk_left", 10);
}
if(this.x > e.x) {
if(!this.isPlaying("walk_right"))
this.stop().animate("walk_right", 10);
}
if(this.y < e.y) {
if(!this.isPlaying("walk_up"))
this.stop().animate("walk_up", 10);
}
if(this.y > e.y) {
if(!this.isPlaying("walk_down"))
this.stop().animate("walk_down", 10);
}
});
}
});
* This source code was highlighted with Source Code Highlighter.
Тут вся магия заключается в компоненте Collision, который позволяет нам задать границы столкновений, и отрабатывать различные события, так же в этом компоненте обрабатывается событие Moved, данное событие мы будем генерировать в наших компонентах игрока и монстра, параметром данного события будет x и y предидущей позиции.
Создадим игрока:
/js/objects/player.js
Crafty.c('Player', {
init: function() {
this.requires("Unit"); // подключаем компонент unit
this.requires("player"); // подключаем sprite игрока
this.requires("Fourway"); // подключаем компонент движения
this.attr({x: 0, y: 0, z: 1});
this.animate("walk_left", 6, 3, 8);
this.animate("walk_right", 9, 3, 11);
this.animate("walk_up", 3, 3, 5);
this.animate("walk_down", 0, 3, 2);
this.fourway(1);
this.onHit("flower", function(e) {
var object = e[0].obj;
object.clear();
if ((Settings.flower_count -= 1) == 0) Crafty.scene("win");
});
this.onHit("monster", function(e) {
var object = e[0].obj;
object.clear();
Crafty.scene("lose");
});
}
});
* This source code was highlighted with Source Code Highlighter.
Подключаем созданный ранее компонент Unit, sprite player и компонент движения Fourway. Fourway — это компонент который изменяет положение нашего sprite в зависимости от нажатой стрелки на клавиатуре, при создание принимает параметр скорости перемещения. Далее с помощью того же компонента столкновения мы отлавливаем 2 события, столкновение с цветком (тогда мы его собираем) и столкновение с монстром (тогда мы умираем).
Пора создать монстра:
/js/objects/monster.js
Crafty.c('Monster', {
init: function() {
this.requires("Unit");
this.requires("monster");
this.requires("FourwayAI");
this.attr({x: 0, y: 0, z: 1});
this.animate("walk_left", 6, 4, 8);
this.animate("walk_right", 9, 4, 11);
this.animate("walk_up", 3, 4, 5);
this.animate("walk_down", 0, 4, 2);
this.fourway_ai(1);
},
clear: function() {
clearInterval(this.removeComponent('monster')._interval);
}
});
* This source code was highlighted with Source Code Highlighter.
Обратите внимание на компонент FourwayAI, такого компонента нет, нам нужно будет создать его. Данный компонент будет отвечать за самостоятельное передвижение монстра:
/js/objects/fourwai_ai.js
Crafty.c('FourwayAI', {
_speed: 3,
_interval: null,
init: function() {
this._movement= { x: 0, y: 0};
this.bind("EnterFrame",function() {
if (this.disableControls) return;
if(this._movement.x !== 0) {
this.x += this._movement.x;
this.trigger('Moved', {x: this.x - this._movement.x, y: this.y});
}
if(this._movement.y !== 0) {
this.y += this._movement.y;
this.trigger('Moved', {x: this.x, y: this.y - this._movement.y});
}
});
},
fourway_ai: function(speed) {
this._speed = speed;
this.make_step();
var kclass = this;
this._interval = setInterval(function() {
kclass.make_step();
}, 1000 * this._speed);
},
make_step: function() {
step = Crafty.randRange(-1,1);
if (Crafty.randRange(1,2) == 1) {
this._movement.x = step;
this._movement.y = 0;
} else {
this._movement.x = 0;
this._movement.y = step;
}
this.trigger('NewDirection', this._movement);
}
});
* This source code was highlighted with Source Code Highlighter.
this.trigger — как не сложно догадаться, создает событие, которое мы потом и отлавливаем для анимации.
Теперь нам осталось создать последнюю главную сцену, которая будет генерировать всю нашу карту и расставлять на ней игрока, монстров, камни и цветки.
Сново создание сцен
/js/scenes/main.js
Crafty.scene("main", function() {
var flower_count = Settings.level + 1;
Settings.flower_count = 0;
//generate the grass along the x-axis
for(var i = 0; i < 25; i++) {
//generate the grass along the y-axis
for(var j = 0; j < 20; j++) {
Crafty.e("Grass").attr({x: i * Settings.poligon, y: j * Settings.poligon});
if (i * Settings.poligon == 160 && j * Settings.poligon == 144) continue;
if(i > 0 && i < 24 && j > 0 && j < 19 && Crafty.randRange(0, 50) > 40) {
if (Crafty.randRange(1,10) == 1 && (flower_count -= 1) > 0) {
Crafty.e("Flower").attr({x: i * Settings.poligon, y: j * Settings.poligon});
Settings.flower_count += 1
// one monster for one flower
Crafty.e("Monster").attr({x: i * Settings.poligon, y: j * Settings.poligon});
} else {
Crafty.e("Bush").attr({x: i * Settings.poligon, y: j * Settings.poligon});
}
}
}
}
//create the bushes along the x-axis which will form the boundaries
for(var i = 0; i < 25; i++) {
Crafty.e("Bush").attr({x: i * Settings.poligon, y: 0});
Crafty.e("Bush").attr({x: i * Settings.poligon, y: 304});
}
//create the bushes along the y-axis
//we need to start one more and one less to not overlap the previous bushes
for(var i = 1; i < 19; i++) {
Crafty.e("Bush").attr({x: 0, y: i * Settings.poligon});
Crafty.e("Bush").attr({x: 384, y: i * Settings.poligon});
}
Crafty.e("Player").attr({x: 160, y: 144, z: 1});
});
* This source code was highlighted with Source Code Highlighter.
Тут мы наконец создаем сущности для наших компонентов, рисуем стенку из камней по периметру, заполняем фон травой, а внутри для каждого квадрата 16x16 создаем пустоту, камень или цветок + врага. В конце мы размещаем нашего игрока.
Вот и все, мы сделали простую, бесконечную, игру — бродилку на html5. Исходный код доступен на github. Проверял только в chrome и firefox под mac os.
Если кто нибудь подскажет устойчивый сервис где можно выложить демку, буду благодарен.
UPD: По совету RiderSx выложил демо тут, надеюсь выдержит.