Здравствуйте, Хабражители!
В этом топике я хочу рассказать о том, как я создал браузерную игру «DoodleJump» на HTML5 без использования каких либо то фреймворков. Для тех кто не знает, DoodleJump — это популярная мобильная игра где главный герой «doodler» бесконечно прыгает вверх по платформам, преодолевая различные препятствия, и собирая бонусы. Эта игра широко распространена почти на всех мобильных платформах, но приличной браузерной версии этой игры нет, поэтому я и решили написать браузерную версию этой игры, пусть даже управляемую клавишами а не гироскопом.
Итак, начнем. Для начало обозначим сцену, где и будет все происходить:
<div id="stage"></div>
и применяем к ней стили:
#stage{
position:absolute;
top:0px; left:0px;
background-color:#fff3f7;
background-image:url(grid.gif);
width:320px;
height:480px;
}
где мы заливаем сцену клетчатым фоном (grid.gif), и делаем разрешение 320 на 480 пикселей. В эту сцену мы помещаем ещё несколько div'ов: верхняя панель (tray), где будут писаться набранные очки и распологатся кнопка паузы(pause), основной персонаж игры (doodler) и пулька (bullet), которой будет стрелять наш персонаж. Для таких объектов как платформы и бонусы мы создаем div'ы — контейнеры в которые, с помощью ява-цикла, мы поместим отдельно по диву для каждого объекта.
<div id="stage">
<div id="header">
<div id="tray"></div>
<div id="tray-border"></div>
<div id="pause" onClick="gotoPause();"></div>
</div>
<div id="doodler"></div>
<div id="bullet"></div>
<div id="footer"></div>
<div id="obstacle"></div>
<div id="platforms">
<!--несколько дивов-->
</div>
<div id="objects">
<!--несколько дивов-->
</div>
<div id="doodler"></div>
<div id="ammunition"></div>
<div id="bullet"></div>
<div id="records"></div>
</div>
Теперь надо применить стили к нашим объектам, залить их спрайтами, задать ширину и высоту объектам, а также сделать им абсолютное позиционирование, для того чтобы мы смогли размещать каждый объект не зависимо от других объектов на сцене.
Я подготовил несколько спрайтов и картинок для раскраски нашей игры:
Вот пример стилей к элементу doodler:
#doodler{
position:absolute;
background:url(sprite.png);
width:62px;
height:59px;
}
Зная координаты расположения элементов на спрайте, мы можем залить все объекты по образцу выше.
А вот стили к пружинками и платформам будут немного отличаться, так как они лежат в контейнере, и остальные параметры к ним будут задаваться из java-скрипта:
#objects div{
position:absolute;
background:url(sprite.png);
}
После того как мы создали все объекты на сцене и придал им стили, мы можем приступать к написанию java скрипта. Сначала для удобства написания кода игры, мы запихнем селекторы объектов в переменные:
var d = document.getElementById('doodler');
var tray = document.getElementById('tray');
var header = document.getElementById('header');
var ammun = document.getElementById('ammunition');
// и другие...
Как я и говорил ранее, div'ы — контейнеры такие как: platforms и objects мы заполняем ява циклом:
for(i=0;i'<numPlatform;i++){
document.getElementById('platforms').innerHTML+='<div class="platform" id="p'+i+'"></div>';
}
где numPlatform — это количество платформ в контейнере (максимальное количество платформ, умещаемых на сцене, в нашем случае их 16). Для удобства можно создать функцию для перемещения платформ:
function setPlatform(n,x,y,t){
p = document.getElementById('p'+n);
p.style.top = y+"px";
p.style.left = x+"px";
if(t==-1){p.style.backgroundPosition = "100px 100px";}
if(t>=0&&t<8){
p.style.height=16+"px";
p.style.backgroundPosition = "-399px -"+(16*t)+"px";
}
if(t==8){p.style.height=24+"px"; p.style.backgroundPosition = "-399px -128px";}
if(t==9){p.style.height=16+"px"; p.style.backgroundPosition = "-399px -151px";}
if(t==10){p.style.height=34+"px"; p.style.backgroundPosition = "-399px -168px";}
}
Теперь можно легко переместить любую платформу на желаемое место на сцене, и изменять её тип:
setPlatform([номер платформы],[координаты платформы по иксу],[координаты по игрику],[тип платформы]);
setPlatform(9,50,100,0);
создадим еще несколько подобных функций:
setBullet(x,y);//перемещает пульку
setObstacle(x,y,t);//перемещает монстров и дырки
setObject(n,x,y,t);//перемещает пружинки и бонусы
doodle(x,y,t,a,alpha);//перемещает Дудлера.
Создадим несколько глобальных переменных и массивов, которые нам понадобятся в дальнейшем.
Вот некоторые из них:
var life = true;//жив ли дудлер
var stageSpeed = 0;//скорость движения сцены
var gravitation = 0.08;гравитация
var ySpeed = 5;//начальная скорость дудлера
var numPlatform = 16;//количество платформ
var numObjects = numPlatform;//количество объектов
var xObject = new Array;//X объекта
var yObject = new Array;//Y объекта
var tObject = new Array;//тип объекта
var yFooter = 1000;//обрыв бумаги (footer.png)
var tAmmunition = 0;//начальная амуниция на дудлере
var xPlatform = new Array;//X платформы
var yPlatform = new Array;//Y платформы
var tPlatform = new Array;//тип платформы
var xDoodler = 136;//начальный X дудлера
var yDoodler = 136;//начальный Y дудлера
var tDoodler = 136;//начальное направление дудлера
var record = 0;//текущее количество очков
var xBullet;//X пульки
var yBullet;//Y пульки
var xSpeedBullet;//горизонтальная скорость пули
var ySpeedBullet;//вертикальная скорость пули
var pause = false;//пауза
...
Теперь рассмотрим основную часть ява-скрипта самой игры, бóльшая часть кода будет находится в функции frame(), и запускается 100 раз в секунду. Внутри этой функции дудлер будет постоянно проверятся на столкновение с монстрами и бонусами, а так же там будет находится цикл, внутри которого мы будим проверять дудлера на столкновение с каждой платформой по отдельности:
function frame(){//функция фрейма запускаемая 100 раз в секунду
ySpeed -= gravitation;//уменьшаем скорость дудлера
yDoodler -= ySpeed;//перемещаем дудлера
if(xDoodler+46>=xObstacle&&xDoodler+16<=xObstacle+65&&yDoodler+59>=yObstacle&&
yDoodler+59<=yObstacle+60&&tObstacle != 0){//проверяем на столкновение с припядствием
if(tObstacle != 6){//если это не дырка
if(tAmmunition>=1){//и на дудлере есть амуниция
tObstacle = 0;//то убираем монстра
}else if(ySpeed<0){//в противном случае, если дудлер падает на монстра
tObstacle = 0;//монстр исчезает
ySpeed = 10;//дудлер отпрыгивает
}else{//если на монстре нет амуниции
tAmmunition = 8;//даем дудлеру амуницию звездочек
stageSpeed = 7;//смещаем все элементы сцены вверх
ySpeed = 0;//дудлера вниз
life = false;//обозначаем что он мертв
}
}else{//если это дырка
if(tAmmunition == 0){//и на дудлере нет амуниции
ySpeed = 0;//останавливаем дудлера
gravitation = 0;//останавливаем падение дудлера
yDoodler -= (yDoodler - yObstacle)/6;//засасываем дудллера в дыру по X
xDoodler -= (xDoodler - (xObstacle+10))/6;//засасываем дудллера в дыру по Y
alphaDoodler -= 3;//уменьшаем прозрачность дудлера
life = false;//убиваем дудлера
}
}
}
yObstacle -= stageSpeed;//в случае падения монстры сместятся вверх
setObstacle(parseInt(xObstacle), parseInt(yObstacle+obstacleYPosition), tObstacle);//перемещаем монстра
doodle(parseInt(xDoodler), parseInt(yDoodler), tDoodler, tAmmunition, alphaDoodler);//перемещаем дудлера
...
for(i=0;i < numPlatform;i++){
yPlatform[i]-=stageSpeed;//в случае падения все блоки смещаются вверх
if(xDoodler+46>=xPlatform[i]&&xDoodler+16<=xPlatform[i]+65&&
yDoodler+59>=yPlatform[i]&&yDoodler+59<=yPlatform[i]+16&&tPlatform[i]!=-1&&life&&
tAmmunition == 0||tAmmunition == 7){//если живой дудлер столкнулся с активной платформой
if(tPlatform[i]==2){//если платформа белая
if(ySpeed<0){//и дудлер падает на нею
ySpeed = 5;//дудлер отпрыгивает от платформы
tPlatform[i]=-1;//платформа исчезает
}
}else if(tPlatform[i]==9){//если это не белая а коричневая платформа
if(ySpeed<0){ //и дудлер падает на неё
tPlatform[i]=10;//коричневая платформа ломается
}
}else{//если это не коричневая и не белая
if(ySpeed<=0){//и дудлер падает на неё
ySpeed = 5;//дудлер отпрыгивает от этой платформы
}
}
}
...//проверяем на столкновение с пружинами и бонусами (см. далее)
}
if(tAmmunition>0&&tAmmunition<7&&ySpeed<2){ //если дудлер падает с амуницией
gravitation = 0.08;
ySpeed -= 2;
tAmmunition=0;//убираем амуницию
}
if(yFooter > 434){//если обрыв бумаги не на сцене
yFooter -= stageSpeed;//смещаем его
}else{//как только он встал на нужное место
death();//вызываеи меню с результатами
}
footer.style.top = yFooter+"px";
if(xDoodler>296){lDoodler = xDoodler = -24;} //делаем сцену «безграничной»
if(xDoodler<-24){lDoodler = xDoodler = 296;}
}
fr = setInterval("frame()", 10);//интервал запуска функции frame() (100раз в секунду)
Что бы обеспечить падение сломанных платформ пишем:
if(tPlatform[i]==10){//если это сломанная платформа
yPlatform[i]+=4;//смещаем ее вниз на 4 пикселя
}
Все платформы которые уезжают за нижний край сцены должны появляется сверху, то есть если yPlatform[i] > 480 то yPlatform[i] = -30, но так как у нас есть коричневые платформы, которые могут падать вниз не в зависимости от других, нарушая порядок исчезновения — этот вариант приведет к произвольному расстоянию между платформ:
Поэтому все платформы которые вышли за область видимости мы ставим на 30 пикселей выше предыдущей скрываемой платформы:
if(yPlatform[i] > 480){
xPlatform[i] = parseInt(Math.random()*260);
yPlatform[i] = yPlatform[lastPlatform]-30;
lastPlatform = i;
...
}
В зависимости от того, на какой бонус натыкается дудлер — выполняется одно из следующих условий: например если дудлер падает на пружинку:
if(tObject[i] == 0&&ySpeed<0){ //если дудлер падает на пружинку
ySpeed = 10;//его скорость становится равна 10 (начальная скорость обычного прыжка = 5)
tObject[i] = 1;//и пружинка меняет свой тип на раскрытый
}
Вот что происходит когда дудлер натыкается на другие бонусы:
if(tObject[i] == 2){//шапка
gravitation = 0.01;
ySpeed = 10;
tObject[i] = -1;//скрываем шапку
tAmmunition = 1;
}
if(tObject[i] == 3){//ранец
gravitation = 0.008;
ySpeed = 15;
tObject[i] = -1;
tAmmunition = 5;
}
if(tObject[i] == 4){//сфера
tObject[i] = -1;
tAmmunition = 7;
setTimeout('stopBonus()',5000);//запускает функцию отключения бонуса через 5 секунд
}
Конечно это не все, осталось много всего необъясненного, но ведь мы рассматривали основную часть нашей работы. Думаю принцип понятен, далее нам оставалось сделать так чтобы сложность игры увеличивалась в зависимости от набранных очков:
далее мы добавили заставку (с возможностью пропуска), меню, рекорды, справку, меню паузы, звуковое сопровождение, и информацию о достижениях после падения дудлера:
При запуске игры, она обращается к серверу и получает список результатов, который доступен по кнопке «Рекорды». Если пользователь преодолевает минимальный рекорд, (расположенный в списе на 16-ой строчке), то игра спрашивает его никнэйм, далее в зависимости от набранных очков располагает его на определенной позиции, смещая меньшие рекорды вниз.