И так, настало время запустить наши "Пятнашки" на операционной системе Андроид! Наши любимые телефоны и планшеты должны запустить эту замечательную игру и позволить нам насладиться головоломкой где бы мы ни находились.
Но сначала нам нужно проделать небольшую работу по переводу нашей игры на платформу Adobe Air, подкорректировать управление под мультитач, а так же попробовать заставить игру корректно отображаться экранах различных пропорций и разрешений.
Перед началом работы удостоверьтесь, что у вас установлен AIR SDK. Если нет, то скачать его можно здесь. В своём уроке я использую старую версию Air 3.1. Для нашей задачи его вполне достаточно. Для перевода нашей игры под Андроид мы создадим новый проект и подключим к нему нашу библиотеку SWC и созданные в прошлом уроке файлы Game.as и Tile.as.
Итак, создаём во FlashDevelop новый Air проект для мобильных устройств:
В отличии от обычного проекта, здесь в структуре проекта мы получаем много автоматически сгенерированных батников, папок и прочей ерунды. Нам потребуются как минимум три файла из всего этого списка. Эти файлы я выделил на скриншоте.
Для начала нужно создать сертификат для нашего приложения. Это необходимое условие для запуска андроид-приложений. Щелкаем правой кнопкой файл CreateCertificate.bat и выбираем пункт "Execute". Откроется консоль и автоматически будет создан сертификат.
Далее двойным щелчком открываем application.xml. Ищем в начале строчку
<application xmlns="http://ns.adobe.com/air/application/3.1">
и изменяем число в конце в соответствии с той версией AIR, коротая у вас установлена. Сохраняем файл. У меня версия AIR 3.1, поэтому я поставил 3.1.
Если вы не знаете какая версия AIR у вас, узнать можно так:
Меню Project -> Properites -> SDK. В пункте "Installed SDK" вы увидете, что у вас установлено.
Так же не забудьте в этом же окне, во вкладке "Output" проверить соответствие версии AIR которая используется в проекте, с той версией что у вас установлена.
Кроме того нам нужно посетить меню Project -> Air App Properites. В этом окне можно настраивать различные параметры будущего приложения. Например во вкладке Details можно установить имя приложения, копирайты, версию и прочее. Я не заполнял эти поля, так как мы делаем игру в ознакомительных целях. Нас больше интересует вкладка Initial Window. Здесь в подвкладаке Non-Windowed Platforms нужно настроить следующие параметры, которые говорят сами за себя. Убрать автоориентацию, убрать фулскрин (растягивать будем программно), рендер через CPU, а ориентация портретная.
С настройками закончили.
Идём далее. Перекидываем в папку lib нашу библиотеку SWC (ту которую мы создали в первом уроке и подключали к проекту во втором) и подключаем её к проекту. Так же перекидываем в папку src файлы Game.as и Tile.as, которые мы создали на прошлом уроке.
В листинг файла Game.as были внесены некоторые изменения. Полностью переписана функция для обработки ходов. Теперь игра реагирует не на клики мышью, а на касания мультитача (в частности на жест "провел пальцем по экрану"). Считываются жесты пальцем вверх, вниз, вправо и влево. В зависимости от жеста ищется подходящая для перемещения плитка. Слушатель теперь всего один на весь экран (раньше было по одному на каждую плитку), что есть хорошо.
package { import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.TransformGestureEvent; import flash.ui.MultitouchInputMode; import flash.ui.Multitouch; /** * ... * @author RablV */ public class Game extends Sprite { // матрица плиток private var matrix:Vector.<Vector.<Tile>> = new <Vector.<Tile>>[ new <Tile> [null, null, null, null], new <Tile> [null, null, null, null], new <Tile> [null, null, null, null], new <Tile> [null, null, null, null] ]; // эталонная матрица для проверки победы private var checkMatrix:Array = new Array( [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 0] ); //массив для хранения рандомной последовательности private var randomMas:Array = new Array(); // счетчик ходов private var countMc:Count_mc; // переменная для хранения колличества ходов private var moveCount:int = 0; // флаг победы private var flagVictory:Boolean = false; private var flag:Boolean = false; // флаг для определения завершения хода public function Game() { Multitouch.inputMode = MultitouchInputMode.GESTURE; // мультитач поддерживаем жесты. ВНИМАНИЕ! Проверка на поддержку // утсройством жестов не реализована! setFon(); // устанавливаем фон setRestartBtn(); // устанавливаем кнопку Рестарт setCountMc(); // устанавливаем счетчик ходов setTiles(); // устанавливаем плитки addEventListener(TransformGestureEvent.GESTURE_SWIPE , move2); // слушатель жестов пальцев по экрану } // устанавливаем фон private function setFon():void { var fon:Fon_mc = new Fon_mc(); // берем фон из библиотеки SWC addChild(fon); fon.height = Main.newHeight; // растянем фон полностью на всю высоту экрану (допустимо так как фон однородный) } // установка кнопки Рестарт private function setRestartBtn():void { var restartBtn:Restart_btn = new Restart_btn(); addChild(restartBtn); restartBtn.x = 44, 7; restartBtn.y = 326, 5; restartBtn.addEventListener(MouseEvent.CLICK, newRandGame); } // установка счетчика private function setCountMc():void { countMc = new Count_mc(); addChild(countMc); countMc.x = 192,55; countMc.y = 326,5; } // устанавливаем плитки private function setTiles():void { for (var j:int = 0; j < 4; j++ ) { // в двойном цикле ставим плитки по вертикали и горизонтали for (var i:int = 0; i < 4; i++ ) { matrix[i][j] = new Tile(); matrix[i][j].i = i; // присваемвам плиткам их координаты в матрице matrix[i][j].j = j; matrix[i][j].x = 4 + i * 64 + 8*i; // расставляем плитки на игровом поле matrix[i][j].y = 4 + j * 64 + 8*j; matrix[i][j].tileMc.stop(); // каждую плитку нужно остановить, чтоб не мерцала (т.к. в ней 2 кадра) addChild(matrix[i][j]); } } newRandGame(); } // обработка клика на плику, перемещение плит private function move2(e:TransformGestureEvent):void { var currNumber:int; // для хранения номера той плитки, которую будем двигать var numberStr:String; // для хранения строки номера той плитки, которую будем двигать var j:int; // вспомогательные для циклов var i:int; flag = false; // сбрасываем флаг движения. Т.е. двигать плитки разрешено. if (e.offsetY == 1) { // если провели пальцем вниз for (j = 0; j < 4; j++ ) { // в двойном цикле перебираем плитки и ищем ту, которую можно сдвинуть for (i = 0; i < 4; i++ ) { if (j + 1 < 4 && matrix[i][j + 1].number == 0 && flag!=true) { // если такую плитку нашли currNumber = matrix[i][j].number; // сохраняем номер текущей плитки numberStr = String(currNumber); // номер текущей плитки запишем в строку matrix[i][j + 1].tileMc.gotoAndStop(1); matrix[i][j+1].tileMc.Number_field.text = numberStr; matrix[i][j + 1].number = currNumber; endMove(i,j); }; }} } if (e.offsetY == -1) { // если провели пальцем вверх for (j = 0; j < 4; j++ ) { // в двойном цикле перебираем плитки и ищем ту, которую можно сдвинуть for (i = 0; i < 4; i++ ) { if (j - 1 > -1 && matrix[i][j - 1].number == 0 && flag!=true) { // если такую плитку нашли currNumber = matrix[i][j].number; // сохраняем номер текущей плитки numberStr = String(currNumber); // номер текущей плитки запишем в строку matrix[i][j - 1].tileMc.gotoAndStop(1); matrix[i][j - 1].tileMc.Number_field.text = numberStr; matrix[i][j - 1].number = currNumber; endMove(i,j); }; }} } if (e.offsetX == 1) { // если провели пальцем вправо for (j = 0; j < 4; j++ ) { // в двойном цикле перебираем плитки и ищем ту, которую можно сдвинуть for (i = 0; i < 4; i++ ) { if (i + 1 < 4 && matrix[i + 1][j].number == 0 && flag!=true) { // если такую плитку нашли currNumber = matrix[i][j].number; // сохраняем номер текущей плитки numberStr = String(currNumber); // номер текущей плитки запишем в строку matrix[i + 1][j].tileMc.gotoAndStop(1); // пустое поле превращаем в плитку matrix[i + 1][j].tileMc.Number_field.text = numberStr; // задаем новой плитке строку с числом matrix[i + 1][j].number = currNumber; // теперь новая плитка превратилась в ту, на которую мы нажали endMove(i,j); }; }} } if (e.offsetX == -1) { // если провели пальцем влево for (j = 0; j < 4; j++ ) { // в двойном цикле перебираем плитки и ищем ту, которую можно сдвинуть for (i = 0; i < 4; i++ ) { if (i - 1 > -1 && matrix[i - 1][j].number == 0 && flag!=true) { // если такую плитку нашли currNumber = matrix[i][j].number; // сохраняем номер текущей плитки numberStr = String(currNumber); // номер текущей плитки запишем в строку matrix[i - 1][j].tileMc.gotoAndStop(1); matrix[i - 1][j].tileMc.Number_field.text = numberStr; matrix[i - 1][j].number = currNumber; endMove(i,j); } }} } } // завершение хода private function endMove(i:int, j:int):void { matrix[i][j].number = 0; // текущую плитку делаем пустым полем matrix[i][j].tileMc.gotoAndStop(2); moveCount++; // счетчик ходов flag = true; countMc.Count_field.text = String(moveCount); // увеличиваем счетчик if(check()) flagVictory=true; // проверка на победу } // функция создания новой игры private function newRandGame(e:MouseEvent = null):void { flagVictory = false; // сброс флага победы moveCount=0; // сброс счетчика ходов countMc.Count_field.text = String(moveCount); var tempcount:int = 0; // временная переменная для присвоения занчений в массиве random(); // генерируем рандомную последовательность от 1 до 15 в массив for (var j:int = 0; j < 4; j++ ) { // в двойном цикле пишем рандомную последовательность в нашу матрицу for (var i:int = 0; i < 4; i++ ) { matrix[i][j].tileMc.gotoAndStop(1); matrix[i][j].tileMc.Number_field.text = String(randomMas[tempcount]); matrix[i][j].number = randomMas[tempcount]; tempcount++; } } matrix[3][3].tileMc.gotoAndStop(2); // плитку в правом нижнем углу делаем пустым полем matrix[3][3].number = 0; } // создание масива с рандомной последовательностью от 1 до 15 private function random():void { var flag:Boolean = true; var temp:int = 0; var count:int = 0; for (var k:int = 0; k < 16; k++ ) randomMas[k]=0 // обнуляем массив for (var j:int = 0; j <15; j++ ) { // в цикле создаем и записываем в массив 15 чисел while (flag) { // этот цикл работает до тех пор, пока не сгенерируется число, которого еще нет в массиве if (count == 15) break; temp = 15 * Math.random() + 1; flag = false; for (var i:int = 0; i < 16; i++ ) { // цикл проверяет сгенерированное число, на его наличие в массиве if (randomMas[i] == temp) { flag = true; break; } } } randomMas[j] = temp; count++; flag = true; } } // проверка победы private function check():Boolean { // сравниваем текущую матрицу плиток с эталонной матрицей for (var j:int = 0; j < 4; j++ ) { for (var i:int = 0; i < 4; i++ ) { if (matrix[i][j].number != checkMatrix[j][i]) { return false; } }} return true; } } }
В классе Main.as мы помимо создания объекта класса Game, так же напишем небольшой код, который будет подгонять размер нашей игры под размер экрана устройства. Но не тупо растягивать, а увеличивать пропорционально.
package { import flash.desktop.NativeApplication; import flash.events.Event; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.ui.Multitouch; import flash.ui.MultitouchInputMode; import flash.system.Capabilities; import flash.events.TransformGestureEvent; import flash.text.TextField; import flash.events.GesturePhase; import flash.events.TouchEvent; /** * ... * @author RablV */ public class Main extends Sprite { public static const newHeight:Number=Capabilities.screenResolutionY; public function Main():void { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.addEventListener(Event.DEACTIVATE, deactivate); // touch or gesture? Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT; // entry point var game:Game = new Game(); // создаём новую игру addChild(game); var currW:int = game.width; // сохраним текущую ширину игры stage.stageWidth = Capabilities.screenResolutionX; // подгоним игру под разреешение экрана stage.stageHeight = Capabilities.screenResolutionY; game.width = stage.stageWidth; // растянем всю графику под ширину экрана game.height = game.height / currW * game.width; // пропорционально растянем игру по вертикали // new to AIR? please read *carefully* the readme.txt files! } private function deactivate(e:Event):void { // make sure the app behaves well (or exits) when in background //NativeApplication.nativeApplication.exit(); } } }
Итак почти всё готово. Осталось скомпилировать игру и упаковать в файл с расширением .apk. Если сейчас попробовать запустить игру на компьютере, мы получим что-то вроде этого:
Это нормально, т.к. игра пытается подстроиться под разрешение монитора, но ей мешает ограниченное пространство окна эмулятора.
В общем приступим к упаковке игры. Окне "Project" щелкаем правой кнопкой по файлу PackageApp.bat и выбираем "Execute". В появившемся окне нажимаем клавишу "1", затем нажимаем "Enter". Через несколько секунд файл .apk появится в папке dist.
Закидываем этот файл на наше Andriod устройство, устанавливаем. Но игра скорее всего не запуститься. Почему?! Потому что наша игра выполняется в среде Adobe AIR, а это виртуальная машина которую сначала нужно установить на устройство. Скачать Adobe AIR можно бесплатно скачать с Play Marketa и потом спокойно запускать любые Air приложения.
Установив Adobe AIR, мы наконец можем запустить нашу игру
Ну вот мы и добились своего. Создали игру "Пятнашки" и играем на Андроиде =)
Скачать исходники можно здесь.
Комментариев нет:
Отправить комментарий