Часть 1 » Часть 2 » Часть 3 » Часть 4 » Часть 5 » Часть 6 » Часть 7 // Конец )


Сегодня разберёмся с системой координат, которая применяется в Cocos2d-JS и поговорим о том, как перемещать игровые объекты на экране.



Система координат


В Cocos2d-JS положение объектов в игровом пространстве определяется посредством системы координат. Каждый узел в движке имеет координаты x и y, определяющие его позицию. Здесь используется правая прямоугольная система координат, точно такая же, которую изучают на занятиях по математике.


Система координат

Размер видимой области


Максимальные координаты по осям x и y определяются размерами видимой области. Cocos2d-JS предоставляет объект, который называется «view», или «вид», который занимается поддержкой различных разрешений и размеров экрана.

Вот строка кода из main.js, которая задаёт размер видимой области.

cc.view.setDesignResolutionSize(480, 800, cc.ResolutionPolicy.SHOW_ALL);

Первый аргумент вызываемого метода – это ширина, второй – высота. В нашем случае собственное разрешение видимой области составляет 480x800. Третий аргумент задаёт правила изменения размера игрового пространства, когда программа запускается на устройстве, экран которого имеет разрешение и соотношение сторон, которые отличаются от заданных параметров.

Движок предоставляет пять способов решения проблемы несоответствия характеристик экрана параметрам, заданным в программе. Их описания можно найти в файле main.js, выше той строки кода, которую мы только что рассмотрели. Мы будем пользоваться значением по умолчанию, SHOW_ALL. При таком подходе соотношение сторон видимой области сохранится на любом дисплее, при этом незанятые изображением участки экрана будут закрашены чёрным цветом – по бокам игрового поля появятся чёрные полосы.

Пространство координат


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

Задавая позицию узла, мы задаём его место в пространстве координат объекта-родителя. Например, задавая позицию для красной точки (угощения для змеи), изображённой на рисунке выше, вы заставляете её перемещаться в пределах координатной системы слоя (оси, нарисованные чёрными линиями), а не в пределах собственного пространства координат (оси жёлтого цвета).

Вытаскиваем змею из угла


Теперь, когда мы разобрались с системой координат, мы можем вытащить голову змеи из угла и поместить её в более приличное место. А именно, в нашем варианте игры Snake всё будет начинаться в центре экрана.

Обновите код слоя SnakeLayer так, как показано ниже. Новый код надо добавить ниже комментария «Добавьте код ниже».

var SnakeLayer = cc.Layer.extend({    
    snakeHead: null 
    ctor: function () {
        
        // Добавьте код ниже
        /* Получим размер окна */
        var winSize = cc.view.getDesignResolutionSize();

        /* Вызовем конструктор суперкласса */
        this._super();
        
        /* Создадим голову змеи */
        this.snakeHead = new SnakePart(asset.SnakeHead_png);
        
        // Добавьте код ниже
         /* Установим координаты для головы змеи */
        this.snakeHead.x = winSize.width / 2;
        this.snakeHead.y = winSize.height / 2;
                
        /* Добавим объект в качестве потомка слоя */
        this.addChild(this.snakeHead);
    },
});

Переменная winSize – это объект, который содержит размеры окна – его ширину и высоту. Используя эти данные, мы назначаем голове змеи позицию, координата x которой равна половине ширины окна, а координата y – половине высоты.

Теперь запустим то, что получилось, в эмуляторе.

Голова змеи переместилась к центру экрана

Научим змею двигаться.

Перемещение объектов


После того, как с системой координат мы разобрались, очевидно то, что для перемещения узла по экрану, нужно изменить его координаты x и y. Займёмся этим.

Для того, чтобы змея смогла двигаться, добавьте в код слоя SnakeLayer следующий метод.

moveSnake: function(dir) {
  /* Набор значений, задающих направление перемещения */
  var up = 1, down = -1, left = -2, right = 2,
	
  step = 20;
  /* Перенесём переменную snakeHead в локальную область видимости */ 
  var snakeHead = this.snakeHead; 
 
  /* Сопоставление направлений и реализующего перемещения кода */
  var dirMap = {};
  dirMap[up] = function() {snakeHead.move(snakeHead.x, snakeHead.y + step);};
  dirMap[down] = function() {snakeHead.move(snakeHead.x, snakeHead.y - step);};
  dirMap[left] = function() {snakeHead.move(snakeHead.x - step, snakeHead.y);};
  dirMap[right] = function() {snakeHead.move(snakeHead.x + step, snakeHead.y);};

  /* Перемещаем голову в заданном направлении */
  if (dirMap[dir] !== undefined) {
	
  dirMap[dir]();
  }
 
},

Этот метод занимается тем, что перемещает голову змеи с постоянным, жёстко заданным, шагом (переменная step) всякий раз, когда его вызывают. В качестве шага мы выбрали 20, так как это значение нацело делит и ширину, и высоту игрового экрана. Для указания направления перемещения мы пользуемся числами, которые задают нужные изменения координат объектов. Теперь этот метод надо как-то вызвать.

Игровой цикл


В игровом движке имеется бесконечный цикл, который решает следующие задачи: обработка событий ввода без блокирования программы, исполнение игровой логики, вывод графики. Каждый проход цикла называется «тактом» или «тиком» (tick). Его можно сравнить с регулярно срабатывающим таймером.

Если описать структуру игрового цикла в псевдокоде, то получится следующее:

while (true) {
processInput();
runGameLogic();
renderGame();
}

Когда движок занимается исполнением игровой логики, он проходит по списку игровых объектов, выполняет код и обновляет их состояние. Например, если в игре есть персонаж, который падает с неба, игре нужно будет обновлять его позицию каждый такт, основываясь на заданном алгоритме. Добавим слой SnakeLayer в список обновления Cocos2d-JS.

Добавьте в код SnakeLayer метод update и вызовите метод scheduleUpdate в методе ctor. Остальной код в листинге приведен для того, чтобы помочь сориентироваться.

var SnakeLayer = cc.Layer.extend({
    snakeHead: null,
    ctor: function () {
        ...
        /* Запланируем обновления */
        this.scheduleUpdate();
    },    
    moveSnake: function(dir) {
        ...
    },
    update: function() {
        /* Число, соответствующее направлению */
       var up = 1;
       this.moveSnake(up);
    },    
});

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

Ограничение скорости перемещения


Змейка сейчас мгновенно улетает с экрана, хотя каждый вызов метода moveSnake перемещает её лишь на один шаг. Это происходит потому, что игровой цикл исполняется очень быстро. В отличие от той примитивной схемы подобного цикла, которую мы приводили выше, настоящий игровой цикл отслеживает время между тактами. Мы можем использовать этот факт для того, чтобы метод обновления позиции объекта работал с нужной скоростью. Вот, как это сделать.

  1. Добавьте два новых члена класса – это interval и counter.

    var SnakeLayer = cc.Layer.extend({
        snakeHead: null,
        // Добавьте код ниже
        interval: 0.25, /* 1/4 секунды */
        counter: this.interval,
        ctor: function () {...},    
        moveSnake: function(dir) {...},
        update: function(dt) {...},    
    });

  2. Замените код метода update на приведенный ниже.

    update: function(dt) {
            /* Число, соответствующее направлению */
            var up = 1;
            /* Перемещаем объект только если истёк заданный срок */
            if (this.counter < this.interval) {
                this.counter += dt;   
            } else {
                this.counter = 0;
                this.moveSnake(up);    
            }	
    	
    },

У метода update теперь есть аргумент «dt» (delta time, изменение времени). Это – время в секундах после последнего кадра. Данный аргумент передаёт этому методу игровой цикл. Мы создали два новых члена класса: interval и counter. Interval (интервал) задаёт число секунд, которое должно пройти между вызовами update(). Counter (счётчик) нужен для подсчёта числа секунд, прошедших с момента предыдущего перемещения объекта. При этом в счётчике накапливаются данные, поступающие из игрового цикла, доступные в аргументе метода dt.

Теперь, если запустить игру в эмуляторе, змея будет двигаться куда медленнее.

Выводы


Подведём итоги сегодняшнего занятия:
  • Cocos2d-JS использует правую систему координат.
  • Имеется такое понятие, как собственный размер видимой области.
  • Узлы позиционируются в координатном пространстве объектов-родителей
  • Игровой движок имеет бесконечный цикл, который обновляет логику игры.
  • Один проход игрового цикла называют «тактом» или «тиком» (tick).

Теперь вы умеете следующее:

  • Располагать игровые объекты в нужной позиции экрана.
  • Перемещать объекты по экрану.

В следующий раз увеличим длину змейки и сделаем её движения боле осмысленными.


Часть 1 » Часть 2 » Часть 3 » Часть 4 » Часть 5 » Часть 6 » Часть 7 // Конец )

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


  1. bunnyrock
    15.04.2016 19:19

    А есть возможность в cocos 2d использовать нативную систему координат? Это у которой начало в верхнем левом углу экрана.