И так, настало время запустить наши "Пятнашки" на операционной системе Андроид! Наши любимые телефоны и планшеты должны запустить эту замечательную игру и позволить нам насладиться головоломкой где бы мы ни находились.
Но сначала нам нужно проделать небольшую работу по переводу нашей игры на платформу 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, мы наконец можем запустить нашу игру
Ну вот мы и добились своего. Создали игру "Пятнашки" и играем на Андроиде =)
Скачать исходники можно здесь.
Комментариев нет:
Отправить комментарий