Игра на QuickTiGame2d в Appcelerator Titanium. Часть 2

в 17:10, , рубрики: appcelerator titanium, mobile development, Блог компании «ООО «Рус Визардс»», кроссплатформенная разработка, мобильная разработка, метки: , ,

Игра на QuickTiGame2d в Appcelerator Titanium. Часть 2
В первой части данной статьи "Игра на QuickTiGame2d в Appcelerator Titanium. Часть 1" я рассказывала о некоторых моментах работы с движком, которые, на мой взгляд, будут полезны тем, кто собирается ознакомиться с этой публикацией. Сейчас я предлагаю рассмотреть создание простейшей игры на Appcelerator Titanium.

Суть игры заключается в том, что из появляющихся букв нужно собрать слово «TITANUIM», после чего будет выведено соответствующее сообщение о победе и игра будет закончена.

Для начала создадим файл pathsToFiles.js, в котором присвоим пути к имеющимся ресурсам:

//menuScene
var pngMenuBackground = "pictures/menu/background.png";
var pngButtonNewGame = "pictures/menu/newGame.png";
var pngButtonHelp = "pictures/menu/help.png";
//helpScene
var pngHelp = "pictures/help/help.png";
var pngBackFromHelp = "pictures/help/back.png";
//mainLoadingScene
var pngMainBackground = "pictures/game/background.png";
var pngBackToMenu = "pictures/game/back.png";
var pngVictory = "pictures/game/victory.png";
var xmlCharactersPosition = "pictures/game/charactersPosition.xml";
//sound
var soundClick = "sound/click.wav";

Перво-наперво обратимся к файлу app.js. Создадим экран игры, подключим файлы js, с которыми будем работать, а также опишем расположение экрана для Android и iOS:

var window = Ti.UI.createWindow({
    backgroundColor : 'black'
});
var quicktigame2d = require('com.googlecode.quicktigame2d');
var game = quicktigame2d.createGameView();
if (Titanium.Platform.osname === 'android') {
    game.orientation = Ti.UI.LANDSCAPE_LEFT;
    window.orientationModes = [Titanium.UI.LANDSCAPE_LEFT];
} else {
    game.orientation = Ti.UI.LANDSCAPE_RIGHT;
    window.orientationModes = [Titanium.UI.LANDSCAPE_RIGHT];
}
Ti.include('pathsToFiles.js');
Ti.include('helpScene.js');
Ti.include('menuScene.js');
Ti.include('mainScene.js');

Далее рассчитаем необходимый масштаб игры для устройства, проинициализируем подгружаемые сцены, которые будут описаны ниже, добавим игровой экран в окно приложения, откроем его и запустим MenuScene.

var WINDOW_SCALE_FACTOR_X = 1;
var WINDOW_SCALE_FACTOR_Y = 1;
game.addEventListener('onload', function(e) {
    var screenScale = game.size.height / 640;
    game.screen = {
        width : game.size.width / screenScale,
        height : game.size.height / screenScale
    };
    WINDOW_SCALE_FACTOR_X = game.screen.width / game.size.width;
    WINDOW_SCALE_FACTOR_Y = game.screen.height / game.size.height;
    MenuScene.init();
    HelpScene.init();
    MainScene.init();
    game.pushScene(MenuScene.scene);
    game.start();
});
window.add(game);
window.open({
    fullscreen : true,
    navBarHidden : true
});

Теперь займемся созданием MenuScene для игры. Для начала непосредственно создадим её:

var buttonMargin = 20;
var clickSound = null;

var MenuScene = {
    scene : null,
    background : null,
    buttonNewGame : null,
    buttonHelp : null,
    init : function() {
        this.scene = quicktigame2d.createScene();
...
    },
};

Здесь buttonMargin — это отступ, который будет использоваться далее, а clickSound — переменная для хранения звука.

Теперь стоит добавить некоторые действия. Запускаем текущую сцену при загрузке спрайтов:

 var onloadsprite = (function(self) {
            return function(e) {
                if (e.name == pngButtonHelp) {
                    game.startCurrentScene();
                }
            };
})(this);

Опишем события нажатия на кпопки «New game» и «Help». При нажатии и при отпускании кнопки будет изменяться её картинкаю Также, при нажатии будет издаваться короткий звук, а при «отжатии» — сцена игры будет изменена на выбранную:

var touchstart = (function(self) {
            return function(e) {
                var x = e.x * WINDOW_SCALE_FACTOR_X;
                var y = e.y * WINDOW_SCALE_FACTOR_Y;
                if (self.buttonNewGame.contains(x, y)) {
                    self.buttonNewGame.frame = 0;
                    clickSound.play();
                }
                if (self.buttonHelp.contains(x, y)) {
                    self.buttonHelp.frame = 0;
                    clickSound.play();
                }
            }
})(this);

var touchend = (function(self) {
            return function(e) {
                var x = e.x * WINDOW_SCALE_FACTOR_X;
                var y = e.y * WINDOW_SCALE_FACTOR_Y;
                self.buttonNewGame.frame = 1;
                self.buttonHelp.frame = 1;
                if (self.buttonNewGame.contains(x, y)) {
                    game.pushScene(MainScene.scene);
                }          
                if (self.buttonHelp.contains(x, y)) {
                    game.pushScene(HelpScene.scene);
                }
            };
})(this);

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

var activated = (function(self) {
            return function(e) {
                 if (clickSound == null) {
                    clickSound = Ti.Media.createSound({
                        url : soundClick
                    });
                }
                if (self.background == null) {
                    self.background = quicktigame2d.createSprite({
                        image : pngMenuBackground
                    });
                }
                if (self.buttonNewGame == null) {
                    self.buttonNewGame = quicktigame2d.createSpriteSheet({
                        image : pngButtonNewGame,
                        width : 275,
                        height : 100
                    });
                }              
                if (self.buttonHelp == null) {
                    self.buttonHelp = quicktigame2d.createSpriteSheet({
                        image : pngButtonHelp,
                        width : 275,
                        height : 100
                    });
                }
                self.background.x = (game.screen.width * 0.5) - (self.background.width * 0.5);
                self.background.y = (game.screen.height * 0.5) - (self.background.height * 0.5);

                self.buttonNewGame.x = (game.screen.width - self.buttonNewGame.width) / 4;
                self.buttonNewGame.y = (game.screen.height * 0.8);

                self.buttonHelp.x = (game.screen.width - self.buttonNewGame.width) / 4*3;
                self.buttonHelp.y = self.buttonNewGame.y;

                self.buttonNewGame.frame = 1;
                self.buttonHelp.frame = 1;
                
                self.scene.add(self.background);
                self.scene.add(self.buttonNewGame);
                self.scene.add(self.buttonHelp);

                game.addEventListener('touchstart', touchstart);
                game.addEventListener('touchend', touchend);
                self.scene.addEventListener('enterframe', enterframe);
            };
})(this);

Опишем функцию удаления всех созданных элементов (выгрузка текстур, удаление слушателей событий):

var deactivated = (function dea(self) {
            return function(e) {
                self.scene.remove(self.background);
                self.background = null;
                self.scene.remove(self.buttonNewGame);
                self.scene.remove(self.buttonHelp);
                self.buttonNewGame = null;
                self.buttonHelp = null;
                game.unloadTexture(pngButtonNewGame);
                game.unloadTexture(pngButtonHelp);
                game.removeEventListener('touchstart', touchstart);
                game.removeEventListener('touchend', touchend);
            };
})(this);

Осталось только добавить слушателей событий активации/деактивации сцен и загрузки спрайтов:

      this.scene.addEventListener('activated', activated);
      this.scene.addEventListener('deactivated', deactivated);
      this.scene.addEventListener('onloadsprite', onloadsprite);

Сцена меню готова. Перейдем к сцене Help. В принципе, все процессы загрузки и выгрузки элементов очень похожи, поэтому не будем повторяться и рассмотрим наиболее интересующие нас элементы. Одним из них является создание фона help:

 if (self.background == null) {
     self.background = quicktigame2d.createSprite({
          image : pngMenuBackground
      });
}
if (self.menuLayer == null) {
     self.menuLayer = quicktigame2d.createSprite({
          width : self.background.width,
          height : self.background.height
     });
     self.menuLayer.color(0.5, 0.5, 0.5);
     self.menuLayer.alpha = 0.78;

}
if (self.foreground == null) {
     self.foreground = quicktigame2d.createSpriteSheet({
         image : pngHelp,
         width : 960,
         height : 640
     });
}

Здесь мы подгружаем 3 слоя фона. Один из них — это слой в меню, он будет нижним. Второй фон мы устанавливаем поверх (в нем присутствует цвет и прозрачность). Третий — это слой с текстом. Определяем его как SpriteSheet, чтобы использовать для него несколько изображений.

Опишем расположение кнопки в углу экрана относительно его размеров:

if (game.screen.width > self.background.width) {
     self.okButton.x = self.background.x + self.background.width - self.okButton.width - buttonMargin;
     self.okButton.y = self.background.y + self.background.height - self.okButton.height - buttonMargin;
} else {
     self.okButton.x = game.screen.width - self.okButton.width - buttonMargin;
     self.okButton.y = game.screen.height - self.okButton.height - buttonMargin;                   
}

И добавим анимацию текстового фона:

self.foreground.animate(0, 2, 2000, -1);

Теперь перейдем к созданию сцены для процесса игры. Стоит создать несколько переменых:

var COUNT_OF_CHARACTERS = 9;
var word = "";
var wordTitanium = "titanium";

Где COUNT_OF_CHARACTERS — это количество мест на поле для появления букв, word — собранное слово, wordTitanium — слово которое нужно собрать.

В функцию activated добавим процесс создания массива элементов с изображениями букв:

var xCoef = self.background.width / 4;
       var yCoef = self.background.height / 4;
       var xParam = 1;
       var yParam = 1;
       for (var i = 0; i < COUNT_OF_CHARACTERS; i++) {
                    self.characters[i] = quicktigame2d.createSpriteSheet({
                        image : xmlCharactersPosition
        });
        self.characters[i].x = self.background.x + xCoef * xParam;
        self.characters[i].y = self.background.x + yCoef * yParam;
        xParam++;
        if (xParam > 3) {
                 xParam = 1;
                 yParam++;
        }
        self.characters[i].z = 3;
        self.characters[i].hide();
        self.characters[i].index = i;
        self.characters[i].status = "waiting";
        self.characters[i].selectFrame("character0");
        self.scene.add(self.characters[i]);
        if (self.charactersTransforms[i] == null) {
              self.charactersTransforms[i] = quicktigame2d.createTransform();
              self.charactersTransforms[i].addEventListener('complete', oncharactersCompleted);
              self.charactersTransforms[i].index = i;
        }
}

Здесь мы устанавливаем местоположение для каждого элемента, скрываем его, устанавливаем статус, текущее изображение. Здесь же устанавливается свой слушатель событий для каждого элемента. Слушатель для событий будет выглядить таким образом:

var oncharactersCompleted = (function(self) {
            return function(e) {
                var transform = e.source;
                var choosenCharacter = self.characters[transform.index];
                if (choosenCharacter != null) {
                    if (choosenCharacter.status == "moving") {
                        choosenCharacter.show();
                        choosenCharacter = changeStatus(choosenCharacter, transform, "living");
                    } else if (choosenCharacter.status == "living") {
                        choosenCharacter = changeStatus(choosenCharacter, transform, "dying");
                    } else if (choosenCharacter.status == "dying") {
                        choosenCharacter = changeStatus(choosenCharacter, transform, "hiding");
                    } else if (choosenCharacter.status == "hiding") {
                        transform.scale(0, 0);
                        choosenCharacter = changeStatus(choosenCharacter, transform, "queue_waiting");
                    } else if (choosenCharacter.status == "queue_waiting") {
                        choosenCharacter.hide();
                        choosenCharacter.status = "waiting";
                        transform.duration = 1000;
                        choosenCharacter.transform(transform);
                    } else if (choosenCharacter.status == "killed") {
                        transform.rotate(-360);
                        transform.scale(0, 0);
                        choosenCharacter = changeStatus(choosenCharacter, transform, "queue_waiting");
                    }
                }
            };
})(this);

function changeStatus(choosenCharacter, transform, statuscharacter) {
            transform.duration = 2000;
            choosenCharacter.status = statuscharacter;
            choosenCharacter.transform(transform);
            return choosenCharacter;
}

Здесь в зависимости от статуса элемента с ним будут происходить разного рода действия: вращение, изменение масштаба, скрытие и появление на экране.

Добавим метод для завершения игры, в котором мы убираем текст в уже набранном слове, а также добавляем на сцену информацию о том, что игрок выиграл, что в свою очередь вызовет слушателя событий victoryText:

function finishActivity(self) {
            word = "";
            self.scene.add(self.victoryText);
            self.victoryTextTransform.duration = 1000;
            self.victoryText.transform(self.victoryTextTransform);
}

var onVictoryTextCompleted = (function(self) {
            return function(e) {
                if (self.victoryTextTransform.completed) {
                    self.scene.remove(self.victoryText);
                    closeGame(self);
                } else {
                    self.victoryTextTransform.y = game.screen.height;
                    self.victoryTextTransform.completed = true;
                    self.victoryText.transform(self.victoryTextTransform);
                }
            };
})(this);

В enterframe следует добавить процесс смены букв в соответствующих элементах. Это будет происходить при статусе waiting: элементы будут появляться на экране со случайно созданной для них буквой.

self.charactersTransforms[i].show();
self.charactersTransforms[i].scale(1, 1);
self.charactersTransforms[i].duration = 500;
self.charactersTransforms[i].delay = 0;
self.charactersTransforms[i].easing = quicktigame2d.ANIMATION_CURVE_CUBIC_IN;
var face = Math.floor((randomNumber * 100) % 8);
                                
for (var count = 0; count < wordTitanium.length; count++) {
       if (face == count) {
             self.characters[i] = addDataToWord(self.characters[i], self.charactersTransforms[i], "character"+count, wordTitanium[count]);
             self.characters[i].transform(self.charactersTransforms[i]);
             break;
        }
} 

где функция addDataToWord:

function addDataToWord(choosenCharacter, choosenCharacterTransform, frame, symbol) {
            choosenCharacter.isMoving = false;
            choosenCharacter.faceName = frame;
            choosenCharacter.symbol = symbol;
            choosenCharacter.status = "moving";
            choosenCharacter.selectFrame(frame);
            choosenCharacterTransform.duration = 0;
            choosenCharacter.transform(choosenCharacterTransform);
            return choosenCharacter;
}

Последнее, что осталось сделать, — это прописать функцию клика по элементу:

var dblclick = (function(self) {
            return function(e) {
                var x = e.x * WINDOW_SCALE_FACTOR_X;
                var y = e.y * WINDOW_SCALE_FACTOR_Y;
                if (!self.loaded)
                    return;
                if (!self.started)
                    return;
                for (var i = 0; i < COUNT_OF_CHARACTERS; i++) {
                    if (self.characters[i].status != "killed") {
                        if (self.characters[i].contains(x, y)) {
                            clickSound.play();
                            self.charactersTransforms[i].rotate(360);
                            self.charactersTransforms[i].duration = 2000;
                            self.characters[i].transform(self.charactersTransforms[i]);
                            self.characters[i].status = "killed";
                            word += self.characters[i].symbol;
                            Ti.API.info(word);
                            if (wordTitanium.indexOf(word) == 0) {
                                Ti.API.info(word + " " + wordTitanium);
                                if(wordTitanium.length == word.length){
                                    finishActivity(self);
                                }
                            } else {
                                word = "";
                            }
                        }
                    }
                }
            };
 })(this);

При этом мы изменяем его статус, добавляем анимацию и делаем проверку вхождения этой буквы в порядок букв в соответствующем слове.

Получившийся результат:

Игра на QuickTiGame2d в Appcelerator Titanium. Часть 2

Игра на QuickTiGame2d в Appcelerator Titanium. Часть 2

Игра на QuickTiGame2d в Appcelerator Titanium. Часть 2

Игра на QuickTiGame2d в Appcelerator Titanium. Часть 2

Игра на QuickTiGame2d в Appcelerator Titanium. Часть 2

Автор: mkolenchukova

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js