
Здравствуйте, Хабражители!
В этом топике я хочу рассказать о том, как я создал браузерную игру «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-ой строчке), то игра спрашивает его никнэйм, далее в зависимости от набранных очков располагает его на определенной позиции, смещая меньшие рекорды вниз.
Простой рабочий пример: тут