Задался я вопросом, как вообще быстро можно разработать игру для Android. На скорую руку, абы как, но, чтобы запустилась и начала играть.
Про конструкторы подумал, но решил их не трогать, ибо на изучение уйдет много времени. Для чего мне такие жесткие условия? Для конкурса. Хочу себя поднатаскать.
Основные требования к разрабатываемой игре:
— Запускаемость в эмуляторе Android
— Запускаемость на конкретном Android устройстве (обязательно!!!)
— Высокая скорость разработки
Какую игру буду делать: простая игрушка, три игровых состояния: Стартовое меню, Игра, Меню GameOver.
На поле есть айфон, который ловит падающие яблочки от Эппл.
В качестве инструмента был взят JavaScript, J2ds (2D движок) и Intel XDK.
Подготовить проект оказалось проще простого: распаковать архив с движком, запустить текстовые редактор и браузер. Вот и все. Заодно и отладка в виде консоли браузера.
Если все еще интересно, прошу под кат =)
Что ж, теперь к реализации задуманного. У меня есть всего час на разработку, с допущениями и поблажками, т.к. это первый опыт такой скоростной разработки, я приступил. Изучить J2ds проще простого, т.к. нет ничего сложного.
Далее я буду приводить куски кода, с некоторыми пояснениями.
Подготовка игровой сцены.
Так как я выбрал 2D движок в HTML5, то подготовка сцены — это создание специального формата HTML странички.
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="j2ds/engineMath.js"></script> <script type="text/javascript" src="j2ds/engineKey.js"></script> <script type="text/javascript" src="j2ds/engineDOM.js"></script> <script type="text/javascript" src="j2ds/engine2D.js"></script> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width,user-scalable=no" /> <title></title> </head> <body id="gameBody"> <img id="buttons" src="img/buttons.png" alt=""> <canvas id="iCanvas" width="100" height="100"></canvas> <br> <div id="hint"></div> <script type="text/javascript"> initKeyBoard('gameBody'); scene= createScene('iCanvas', '#ceeeee'); startGame(); </script> </body> </html>
Т.к. в нашей игре будут не только примитивы, но и красивые картинки, загрузим одну большую картинку этой строчкой:
<img id="buttons" src="img/buttons.png" alt="">
Сама картинка выглядит так:
Основной файл, в котором я подглядывал функции: namespaces.js, там все функции разобраны по разделам и с комментариями.
В игре будет производиться работа с тачскрином, поэтому нам нужно инициализировать устройство ввода. В J2ds это делается командой «initKeyBoard()». Кроме того, нужно создать игровую сцену, чтобы движок ее обрабатывал и а так же создать из нашего изображения «buttons» спрайт-карту. Делается следующими командами:
initKeyBoard('game'); buttons= CreateImageMap('buttons'); // id объекта HTML scene= createScene('canvas', // Первый агрумент - id объекта Сanvas 'rgb(250, 250, 200)'); // Второй агрумент - цвет фона
Как только мы инициализировали игровую сцену, развернем ее на весь экран:
scene.fullScreen(true);
Далее нам нужно создать анимации. В J2ds анимация — это один и более кадров одного и того же изображения. Кадров может быть сколько угодно. Кроме того, спайт-карт так же может быть сколько угодно. Мы загрузили только одну, пришло время вытащить из нее необходимые кадры:
// Кнопочки управления платформой anim= buttons.CreateAnimation( 0, 0, // Начальная позция первого кадра 300, 300, // Размер кадра 2 // Количество кадров (по горизонтали) ); // Анимация яблочка apples= buttons.CreateAnimation( 8, 325, // Начальная позция первого кадра 81, 89, // Размер кадра 2 // Количество кадров (по горизонтали) ); // Айфон iPhone= buttons.CreateAnimation( 210, 345, // Начальная позция первого кадра 312, 56, // Размер кадра 2 // Количество кадров (по горизонтали) );
Пришло время поговорить об игровых состояниях.
При запуске игры запускается первое игровое состояние — Стартовое меню. Давайте с ним и поработаем.
Разработка стартового меню
Выглядит стартовое меню так:
Ниже приведен код функции «Menu()» с комментариями:
// Создание зеленого прямоугольника newGame= CreateRect( 90, 20, // Положение 150, 100, // Размеры 'green' // Цвет ); // Создание красного прямоугольника exitGame= CreateRect( 90, 20, // Положение 150, 100, // Размеры 'red' // Цвет ); dY= -100; // Переменная для смещения меню (для плавного появления сверху вниз) // А теперь опишем саму функцию меню function Menu() { Mouse.updPosition(scene); // Обновляем позицию курсора/тач-касания относительно сцены dY+= dY > 0 ? 0 : 1; // смещаем меню вниз до -100px // Если произведен клик/тач по зеленому прямоугольнику - переключаемся на игровой процесс // а так же окращиваем зеленый прямоугольник белым цветом, символизируя реакцию игры на действия пользователя if (Mouse.Click && Mouse.onNode(newGame)) { newGame.color= 'white'; SetActivEngine(Game); } // Аналогично и для красного прямоугольника, только с выходом из игры if (Mouse.Click && Mouse.onNode(exitGame)) { exitGame.color= 'white'; ExitGame(); } // Выводим текст со смещением dY scene.DrawTextOpt( 170, 100+dY, // Позиция 'Rect Game', // Текст 'bold 30px sans-serif', // Шрифт (аналогично CSS) 'white', // Цвет текста 'green', // Цвед обводки 6 // Толщина обводки ); // Позиционируем зеленый прямоугольник со смещением newGame.setPosition(120, 200+dY); newGame.Draw(scene); // рисуем его // То же самое для красного прямоугольника exitGame.setPosition(370, 200+dY); exitGame.Draw(scene); // Рисуем надпись поверх зеленого прямоугольника scene.DrawText(90, 190+dY, // Позиция 'Новая игра'); // Текст // Аналогично для красного scene.DrawText(350, 190+dY, // Позиция 'Bыход'); // Текст }
В этой функции все просто: выводим нужные объекты, и ждем, пока юзер нажмет на тот или другой прямоугольники.
Аналогично этой функции есть функция GameOver, которая отличается только выводимым текстом «Game Over» вместо названия «Rect Game»:
function GameOver() { Mouse.updPosition(scene); dY+= dY > 0 ? 0 : 1; if (Mouse.Click && Mouse.onNode(newGame)) { newGame.color= 'white'; SetActivEngine(Game); } if (Mouse.Click && Mouse.onNode(exitGame)) { exitGame.color= 'white'; ExitGame(); } scene.DrawTextOpt( 170, 100+dY, // Позиция 'Game Over', // Текст 'bold 30px sans-serif', // Шрифт (аналогично CSS) 'white', // Цвет текста 'green', // Цвед обводки 6 // Толщина обводки ); newGame.setPosition(120, 200+dY); newGame.Draw(scene); exitGame.setPosition(370, 200+dY); exitGame.Draw(scene); scene.DrawText(90, 190+dY, // Позиция 'Новая игра'); // Текст scene.DrawText(350, 190+dY, // Позиция 'Bыход'); // Текст }
Уже хорошо. Осталось самое главное: игровой процесс. Тут все гораздо интереснее!
Помните, мы ранее загрузили спрайт-карту с кнопками? Пришло время ею воспользоваться! Создадим две кнопки для управления айфоном:
move= []; // массив с двумя кнопками move[0]= CreateSprite( 0, 200, // Позиция в игре 100, 100, // Размеры в игре anim // Анимация ); move[1]= CreateSprite( 400, 200, // Позиция в игре 100, 100, // Размеры в игре anim // Анимация );
Немного поясню: anim — это объект анимации, который мы создавали вначале, прокрутите вверх, посмотрите, чтобы лучше понять. amin хранит у нас два кадра: первый — стрелка влево, второй — стрелка вправо. Идем дальше.
А дальше нам нужно создать сам айфон, который будет ловить яблочки от эппл:
me= CreateSprite( 250, 180, // Позиция в игре 100, 20, // Размеры в игре iPhone // Анимация );
Вроде всё. А, не все! Самих яблочек то нет! Даавй тоже их создадим:
arr= []; count= 5; // Объявим массив и количество яблок // Затем циклом создадим их for (var i=0; i<count; i+=1) { arr[i]= CreateSprite( Random(0, 450), -50, // Позиция в игре (по оси Х они рандомятся, чтобы падали по всей ширине) 20, 20, // Размеры в игре apples // Анимация (хранит в себе два кадра анимации) ); arr[i].speed= Random(1, 3); // Устанавливаем скорость падения яблочка рандомно }
Объявим еще три переменных:
speed= 4; score= 0; gameOverScore= 0;
Они нужны для подсчета пойманных и упущенных яблочек, а так же скорости перемещения айфона.
Теперь пришло время написать самую главную функцию: саму игру!
function Game() { // Обновляем позицию курсора/тач-касания Mouse.updPosition(scene); // Если игрок пропустил более 10 яблок, то Гейм Овер if (gameOverScore > 10) { dY= -100; SetActivEngine(GameOver); gameOverScore= 0; } // Если кликает ли жмет на экран влево, двигаем айфон влево, пока не упрется в стену if (Mouse.Click && Mouse.onNode(move[0])) { if (me.posX > 0) me.Move(-speed, 0); } // Аналогично и вправо if (Mouse.Click && Mouse.onNode(move[1])) { if (me.posX+me.sizeX < scene.Canvas.width) me.Move(speed, 0); } // Начинаем цикл для проверки яблок for (var i=0; i<count; i+=1) { // Если яблоко коснулось айфона, мы перекидываем его за пределы видимости вверх (оно снова падает) // А игрок получает очко (игровое) и прибавляет в скорости if (arr[i].Collision(me)) { score+=1; speed+=0.2; arrNewPos(i); // фнукция для перекидывания яблочка наверх } // Если пользователь пропустил яблочко, он приближается к своему концу // А яблочко снова перемещается и падает if (arr[i].posY > me.posY) { arrNewPos(i); gameOverScore+=1; } // двигаем яблочко вниз с его скоростью arr[i].Move(0, arr[i].speed); // рисуем яблочко, анимируя его (в этом случае скорость анимации: 10) arr[i].DrawAnimate(scene, 10); } // После всех яблочек рисуем айфон me.Draw(scene); // Выводим счет игрока вверху экрана scene.DrawTextOpt( 5, 5, // Позиция 'Игровой счет: '+score, // Текст 'bold 25px sans-serif', // Шрифт (аналогично CSS) 'white', // Цвет текста 'green', // Цвед обводки 3 // Толщина обводки ); // рисуем наши кнопки управления, где второй параметр функции Draw() - это номер кадра. // Если не забыли, 1 кадр - стрелка влево, 2 вправо move[0].Draw(scene, 1); move[1].Draw(scene, 2); // дебаг, выводит количество упущенных яблочек dbg(gameOverScore); } // функция, перемещающая яблочко за пределы видимости камеры вверх // рандомно устанавливает позицию и рандомно присваивает скорость function arrNewPos(_i) { arr[_i].setPosition(Random(0, scene.Canvas.width-arr[_i].sizeY), -Random(50, 300)); arr[_i].speed= Random(2, 5); }
Вот и весь игровой цикл, выглядит это вот так:
И остался последний штрих, игру нужно запустить:
startGame(Menu, 30); // запускает игровое состояние Menu() и устанавливает сцене ограничение в 30 fps
Вот и все. Теперь, код целиком:
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="j2ds/engineMath.js"></script> <script type="text/javascript" src="j2ds/engineKey.js"></script> <script type="text/javascript" src="j2ds/engineDOM.js"></script> <script type="text/javascript" src="j2ds/engine2D.js"></script> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <meta name="viewport" content="width=device-width,user-scalable=no" /> <title>Rect Game</title> </head> <body id="game"> <img id="buttons" src="img/buttons.png" alt=""> <canvas id="canvas" width="500" height="300"></canvas> <br> <div id="hint"></div> <script type="text/javascript"> initKeyBoard('game'); buttons= CreateImageMap('buttons'); // id объекта HTML scene= createScene('canvas', // Первый агрумент - id объекта Сanvas 'rgb(250, 250, 200)'); // Второй агрумент - цвет фона scene.fullScreen(true); anim= buttons.CreateAnimation( 0, 0, // Начальная позция первого кадра 300, 300, // Размер кадра 2 // Количество кадров (по горизонтали) ); apples= buttons.CreateAnimation( 8, 325, // Начальная позция первого кадра 81, 89, // Размер кадра 2 // Количество кадров (по горизонтали) ); iPhone= buttons.CreateAnimation( 210, 345, // Начальная позция первого кадра 312, 56, // Размер кадра 2 // Количество кадров (по горизонтали) ); newGame= CreateRect( 90, 20, // Положение 150, 100, // Размеры 'green' // Цвет ); exitGame= CreateRect( 90, 20, // Положение 150, 100, // Размеры 'red' // Цвет ); dY= -100; function Menu() { Mouse.updPosition(scene); dY+= dY > 0 ? 0 : 1; if (Mouse.Click && Mouse.onNode(newGame)) { newGame.color= 'white'; SetActivEngine(Game); } if (Mouse.Click && Mouse.onNode(exitGame)) { exitGame.color= 'white'; ExitGame(); } scene.DrawTextOpt( 170, 100+dY, // Позиция 'Rect Game', // Текст 'bold 30px sans-serif', // Шрифт (аналогично CSS) 'white', // Цвет текста 'green', // Цвед обводки 6 // Толщина обводки ); newGame.setPosition(120, 200+dY); newGame.Draw(scene); exitGame.setPosition(370, 200+dY); exitGame.Draw(scene); scene.DrawText(90, 190+dY, // Позиция 'Новая игра'); // Текст scene.DrawText(350, 190+dY, // Позиция 'Bыход'); // Текст } function GameOver() { Mouse.updPosition(scene); dY+= dY > 0 ? 0 : 1; if (Mouse.Click && Mouse.onNode(newGame)) { newGame.color= 'white'; SetActivEngine(Game); } if (Mouse.Click && Mouse.onNode(exitGame)) { exitGame.color= 'white'; ExitGame(); } scene.DrawTextOpt( 170, 100+dY, // Позиция 'Game Over', // Текст 'bold 30px sans-serif', // Шрифт (аналогично CSS) 'white', // Цвет текста 'green', // Цвед обводки 6 // Толщина обводки ); newGame.setPosition(120, 200+dY); newGame.Draw(scene); exitGame.setPosition(370, 200+dY); exitGame.Draw(scene); scene.DrawText(90, 190+dY, // Позиция 'Новая игра'); // Текст scene.DrawText(350, 190+dY, // Позиция 'Bыход'); // Текст } move= []; move[0]= CreateSprite( 0, 200, // Позиция в игре 100, 100, // Размеры в игре anim // Анимация ); move[1]= CreateSprite( 400, 200, // Позиция в игре 100, 100, // Размеры в игре anim // Анимация ); me= CreateSprite( 250, 180, // Позиция в игре 100, 20, // Размеры в игре iPhone // Анимация ); arr= []; count= 5; for (var i=0; i<count; i+=1) { arr[i]= CreateSprite( Random(0, 450), -50, // Позиция в игре 20, 20, // Размеры в игре apples // Анимация ); arr[i].speed= Random(1, 3); } speed= 4; score= 0; gameOverScore= 0; function Game() { Mouse.updPosition(scene); if (gameOverScore > 10) { dY= -100; SetActivEngine(GameOver); gameOverScore= 0; } if (Mouse.Click && Mouse.onNode(move[0])) { if (me.posX > 0) me.Move(-speed, 0); } if (Mouse.Click && Mouse.onNode(move[1])) { if (me.posX+me.sizeX < scene.Canvas.width) me.Move(speed, 0); } for (var i=0; i<count; i+=1) { if (arr[i].Collision(me)) { score+=1; speed+=0.2; arrNewPos(i); } if (arr[i].posY > me.posY) { arrNewPos(i); gameOverScore+=1; } arr[i].Move(0, arr[i].speed); arr[i].DrawAnimate(scene, 10); } me.Draw(scene); scene.DrawTextOpt( 5, 5, // Позиция 'Игровой счет: '+score, // Текст 'bold 25px sans-serif', // Шрифт (аналогично CSS) 'white', // Цвет текста 'green', // Цвед обводки 3 // Толщина обводки ); move[0].Draw(scene, 1); move[1].Draw(scene, 2); dbg(gameOverScore); } function arrNewPos(_i) { arr[_i].setPosition(Random(0, scene.Canvas.width-arr[_i].sizeY), -Random(50, 300)); arr[_i].speed= Random(2, 5); } startGame(Menu, 30); </script> </body> </html>
Разработка игры окончена. Осталось запустить ее на Android. Для этого я скачал и установил Intel XDK, прошел регистрацию, верифицировал данные, и создал новый проект. Передо мной открылось окно нового проекта.
Выбрал я пустой (blank) HTML5 документ, и сохранил проект.
После чего открыл его в файловом менеджере, и заменил все файлы в папке проекта WWW на свои, а там у меня всего одна папка с картиной, папка с движком и файл index.html, Intel XDK тут же подхватила этот файл и предложила мне открыть его для редактирования. Я его открыл и перешел в режим эмуляции:
Все работает как надо, ничего не глючит.
Далее открыл вкладку с проектом и ввел необходимые данные, такие как версия программы, название, аписание, иконка и т.д. Заполнив все необходимые данные — я прошел с вежливого приглашения XDK во вкладку Build, и выбрал Build for Android, после чего мой проект выгрузился на сервер, откомпилировался, а система предоставела мне удобную ссылку для загруки уже готового apk файла.
С замиранием сердца я скинул его на гугл.Диск и открыл с телефона, и Чудо! Пошла установка приложения. Через пару секунд я уже во всю ловил яблочки айфоном в новоиспеченной игре.
Кстати, таким же образом я собрал еще одну игрушку (на том же J2ds)
Любой желающий так же может скачать APK файл и протестировать ловлю яблочек айфоном: Скачать APK файл
А так же попрыгать в небольшой 2D игрушке по платформам и понаблюдать красивый задний фон: Скачать APK или Запустить и поиграть в браузере
Если запускаете в браузере, то работает опять же — и на компах и на планшетах/смарфонах.
ссылка на оригинал статьи http://habrahabr.ru/post/265033/
Добавить комментарий