Пользователь
0,0
рейтинг
13 сентября 2011 в 21:02

Разработка → Создание Doodle jump на HTML5 из песочницы

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

Теперь надо применить стили к нашим объектам, залить их спрайтами, задать ширину и высоту объектам, а также сделать им абсолютное позиционирование, для того чтобы мы смогли размещать каждый объект не зависимо от других объектов на сцене.
Я подготовил несколько спрайтов и картинок для раскраски нашей игры:

спрайт заставки

главный спрайт

footer

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

Простой рабочий пример: тут
Бородин Максим @BorodinKO
карма
53,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (72)

  • +11
    Ничего ж себе!
    Вам чуть-чуть переоформить и уже продавать можно! :)
    • +3
      Автору респект и спасибо за статью!
      А насчет «переоформить и продавать», кстати, на AppStore она примерно в таком оформлении и продается. Разве что сделать некое подобие уровней, чтобы герой игры как в оригинале подымался до самого космоса. Но самое важное уже сделано, и сделано очень даже хорошо.
    • +2
      вот чуть что — сразу продавать
      именно из-за такой позиции андроид.маркет и аппстор и загажены шлаком до предела :(

      а автор молодец, сразу видно человека, прежде всего, доброй воли, а не коммерсанта
      • 0
        А почему бы не продавать? Конечно в данном виде она для этого не готова, но если доработать очень даже можно продавать. Я считаю, что качественная работа программера должна оплачиваться, а на всякой рекламе и донейтах сильно не поднимешься…
  • +9
    Спасибо, поигрался :)
    Только как по мне, было бы интереснее, если бы сложность менялась немного быстрее, а то после нескольких минут игры становится скучновато :)
  • +6
    Вообще, на маке можно и с акселерометром игру устроить =)
    • +2
      Да, на GDD-2010 гугл хвастались, что HTML5 доступ к акселерометру тож даёт как-то…
  • +3
    Прикрутите управление мышкой.
    • 0
      Да-да, со стрелками как-то скучновато, теряется часть игровой фишки.
  • 0
    Круто ребята :)
  • 0
    Кстати, заметил какой-то глюк в опере, после нажатия пробела в игре начинается хаос %)
  • НЛО прилетело и опубликовало эту надпись здесь
    • +3
      Возможность стрельбы присутствует – стрелка вверх ↑. :)
    • +1
      пробелом стреляет
    • +1
      Насчёт стрельбы, нажмите пробел и увидите чудо :)
  • –3
    Здорово! Очень качественно сделано. Правда с балансом проблема: слишком просто получается:) Во-первых, с клавиатуры значтельно проще управлять, во-вторых, у вас тут с высотой плотность платформ почти не уменьшается и монстры попадаются крайне редко. В общем, играть надоело раньше чем проиграл, будет интереснее если поднять сложность.

    А зачем вы делали точный клон, а не что-то свое? Свое-то приятнее иметь)

    И почему решили не пользоваться jQuery? C ним бы меньше писанины было.
  • 0
    На Chrome 13 игра не идет. Пустое поле сразу после нажатия Начать игру.



    В Firefox 6.0.2 всё отлично, игра затягивает)
    • +2
      image

      :(
    • 0
      В Chrome 13 идет игра :)
      • 0
        Уже пошла =0 В чем дело?
        • +3
          Выглядит как вопрос в мой адрес, но я честно не знаю! :)
  • 0
    Я бы еще рекомендовал звук подкрутить, а то при прижке на пружине он нормальный, а на ранце такой, что у меня даже соседи игрой заинтересовались… :D
    • 0
      Звук взят с андроидского приложения.
      • 0
        Ну, я в оригинал не играл, но тут явно что-то с уровнями не так.
  • 0
    Кто прикрутит бота к этой игре?
    Играть интересно, да.
    • 0
      Бота, конечно, интересно, но тут уже целый ИИ нужен.
      А так посмотрите таблицу рекордов. Проблема HTML5-игр в том, что изменением пары параметров в коде можно попасть в топ без особых усилий.
      • +1
        Это проблема практически любых игр. Обсфусцированный html5 ничем особо от остальных не отличается — клиенту доверять нельзя даже если он написан на Си.
        • 0
          Все же с клиентом на си придется еще повозиться в случае маленькх приложений мало кому это будет интересно. А в случае HTML подделать данные настолько просто, что с этим справится каждый школьник.
          • +3
            У игры из топика ужасная архитектура. Достаточно взять весь код в анонимную функцию и без дебагера уже не обойтись, потом обфусцировать и сделать одну строку — брейкпоинт уже так просто не поставить. Особые извращенцы могут заюзать p,a,c,k,e,r — тогда без декодирования вообще никак. Но всё это не имеет смысла, потому что любой код, который выполняется на клиенте может соврать.
            • 0
              деобфускаторы гуглятся так же просто как и обфускаторы:) И то, что получается после деобфускации читается намного проще чем нескриптовый код после дизассембляции. Половина школьников при этом отвалится, но половина останется. А там достаточно будет найти функцию, готовящую данные для отправки на сервер и подменить данные. Ну или просто можно посмотреть в консоли хрома или фаербага данные, отправляемые приложением и подменить их.

              А идеи, как от этого защититься у вас есть? Кроме параллельного выполнения игры на сервере) Меня просто сейчас как раз тоже интересует данная тема.
              • +2
                На самом деле обфускация — это достаточный для клиента шаг. Что-то большее делать не имеет смысла. Лично я бы обламался именно в этом месте.

                Объясняю. Сейчас достаточно написать что-то типа такого в адресной строке браузера:
                javascript:(window.count=9001)
                

                Такое бы распространялось по всяким вконтактикам с текстом: «стать самым крутым в этой игрушке — очень просто» и все секретутки почувствовали бы себя хакерами

                Если бы автор не поленился взять весь код в анонимную функцию, то пришлось бы открывать отладчик, ставить брейкпоинт, заменять значение переменной во время выполнения. такую штуку блондинке уже не объяснишь, потому ломали бы только более-менее программисты. хотя ук программистов на это бы уходило секунды 23-24, потому это тоже не вариант.

                Теперь — обфусцированный код. Мне надо скачать исходники на комп, найти деобфускатор, деобфусцировать их, разобраться в непонятных названиях переменных, запустить. В этом месте можно человеку кинуть пару подлянок — хедеры там всякие проверять, запускается ли код с нашего сервака или с локального.

                Для того, чтобы труднее было подделать запрос можно добавить хеш из его json'a, скажем — без деобфусцирования опять не разобраться.

                Помнится, игрушки на том же флеше страдают такими же проблемами. В той же контре кучу клиентов со всякими дополнительными радостями.

                С читерством бороться практически невозможно.

                К сожалению, кроме выполнения игры на сервере мыслей у меня никаких. Но сейчас, в век мультиплеерных игр код на клиенте должен только рендерить, а вся логика должна быть на сервере. Иначе, как вы сделаете игрушку из топика на двоих?

                И тогда отпадает проблема с тем, что доступны исходники)
  • 0
    Вроде бы под мобильник холст заточен, но на айфоне странно экран загрузки смотрится :)
    Добавил вьюпорт.
  • 0
    Статья отличная, но слишком много магических чисел в коде :) Можете поподробнее рассказать про то, как и по какому принципу рассчитываются координаты в той же setObstacle?
    • 0
      примерно так же как и в setPlatform(n,x,y,t)
  • –7
    Вас не смущает, что вы нарушили копирайт, фактически совершили банальное воровство?
    • –3
      Можно относиться к этому как угодно, но факт есть факт — прочтя подобное многим думается примерно следующее:
      «Я мненью вашему вращенье придавал, и осью был ...» =)
      Вы только не подумайте, ничего личного, это не о Вашем мнении, а о копирастах и иже с ними.
      • 0
        Мне как-то не верится, что вы серьезно.

        Компания Lima Sky выпустила игру Doodle Jump. Эта игра очень популярна.
        Какое право иметт какойто Вася Пупкин брать ИХ графику, ИХ звуки, 1в1 копировать полностью все??? Не дав даже никакой ссылки на авторов. Даже те, кто делают кросовки Абибас хоть одну букву в названии меняют, тут нет даже этого.

        И при чем здесь копирасты?

        • 0
          Смайлик-то специально оставил чтобы уж точно не поверилось.
          Даже с бодуна.
          А копирасты всегда нипричём, но всегда где-то рядом.
        • +3
          и что? он же не пытается на ней зарабатывать. И не говорил, что это он сам все сделал.
          • –1
            а какая разница, как он это использует. Это НЕ ЕГО. Он не имеет права это брать. Ни продавать ни отдавать бесплатно.

            А за свою работу он это как раз выдает dl.dropbox.com/u/1777621/Screenshots/41.png
            • +3
              я все же воспринимаю это скорее как демонстрацию того, что можно сделать на HTML5, а не как воровство. Фанворк, а не плагиат.
              • –3
                Хороший фанворк, который
                1. полностью копирует оригинал
                2. не имеет ни одной ссылки на оригинал
                • +3
                  Ну, оригинал настолько известен, что и без ссылок все понятно. Не то чтобы я защищаю автора, я и сам не понимаю, зачем было делать точную копию — свое-то всяко интереснее делать, к тому же из-за правовых соображений у игры нет будущего. Но мне кажется, что вы придираетесь)
                • +4
                  Вы высосали проблему из пальца.
          • 0
            Иначе был бы Болдженос… )
    • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Больше всего нравится таблица рекордов. Игра только вышла, а все уже достигли максимальных высот.
  • –2
    Ваш счет: 37393
  • 0
    Если уж придираться, то регистрировать попадание на платформу нужно по ее нижнему краю, как и в оригинале.

    То есть на эту платформу можно запрыгнуть на андроиде (это самая высокая точка полета):
    image
  • 0
    Возможно было бы интересно сделать управление мышью…
    • 0
      Думаю в ближайшее время это появиться!
  • +1
    Очень интерестно!
    Автор молодец!
    Будит свободное время, попробую сделать.
  • +4
    И прада затягивает. Спасибо за статью.
    А вот так не честно — он просто не допрыгивает
    • 0
      Там могла быть платформа которая взорвалась, а может быть просто где то ошибка.
      • +2
        Там была платформа, которая поломалась (бревно треснутое). Но ведь это ничего не меняет — от нее нельзя оттолкнуться.
  • +1
    2147483647 — integer is full! )
  • 0
    До чего дошел прогресс, спать не получится — захотелось написать что-нибудь на js :-) А таблица рекордов необъективна, да — 2 минуты и 1 место.
  • +1
    Хотел все прочитать и понять как работает, а потом сорвался, докрутил до конца, нашел ссылку и пошел играть.
    • 0
      На это я и расчитывал :)
  • +1
    Браво!
  • +1
    Ничего себе игрушка запустилась

  • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    Спасибо, интересно.

    Правда, смутил момент:
    java-скрипта
    java скрипта
    ява-цикла
    ява циклом
    ява-скрипта


    Понятно, что речь идет о JS, но было такое ощущение, будто имеется в виду «скрипт на языке java» и «цикл на языке java» :)
  • 0
    Блин, я так и не понял как народ умудряется накручивать очки :)
    • +1
      Тебе это и знать не нужно, меньше взломов меньше мне работы.
  • 0
    Работа несомненно интересная, но что здесь есть от html5, не считая доктайпа? :)
    • 0
      звук
  • 0
    Так дела не делаются бро!

  • 0
    Эх… а я хотел поиграть
    • 0
    • 0
      При 3500 посещениях в день, дешевый хостинг за 12р/месяц не выдерживает, а тариф менять не охота…
  • 0
    Хром 17 — пустое окно, никто не прыгает. Хотя по нажатию на клавиши что-то слышно =)

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