Создание игр для мобильных телефонов - Майкл Моррисон
Шрифт:
Интервал:
Закладка:
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 22, 3,
3, 18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 3,
3, 18, 2, 2, 2, 5, 15, 15, 15, 15, 15, 15, 6, 2, 20, 3,
3, 18, 2, 2, 2, 7, 10, 1, 1, 1, 1, 1, 16, 2, 20, 3,
3, 18, 2, 2, 2, 2, 14, 1, 1, 1, 1, 1, 16, 2, 20, 3,
3, 18, 2, 2, 2, 2, 7, 10, 1, 1, 1, 1, 16, 2, 20, 3,
3, 18, 2, 2, 2, 2, 2, 14, 1, 1, 1, 1, 16, 2, 20, 3,
3, 18, 2, 2, 2, 2, 2, 14, 1, 9, 10, 1, 16, 2, 20, 3,
3, 18, 2, 5, 15, 6, 2, 14, 1, 11, 12, 1, 16, 2, 20, 3,
3, 18, 2, 14, 1, 16, 2, 7, 13, 13, 13, 13, 8, 2, 20, 3,
3, 18, 2, 7, 13, 8, 2, 2, 2, 2, 2, 2, 2, 2, 20, 3,
3, 18, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 3,
3, 23, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 24, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
};Этот массив должен быть вам знаком: вы разрабатывали карту, которую он описывает, в предыдущих разделах. Очевидно, что объявления массива не достаточно для определения замощенного слоя. Чтобы задать слой, вы должны определить значение каждой ячейки, для чего необходимо использовать метод setCell() класса TiledLayer. К счастью, это не так сложно сделать с помощью цикла for:
for (int i = 0; i < layerMap.length; i++) {
int column = i % 16; //Размер карты 16x16
int row = (i – column) / 16;
backgroundLayer.setCell(column, row, layerMap[i]);
}Наиболее важный момент в этом цикле, на который следует обратить внимание, – это использование числа 16 во второй и третьей строках кода. Это число во второй строке означает количество столбцов, а в третьей – количество строк. Если вы измените размер карты, то вы должны изменить и эти числа в соответствии с изменениями. Самое приятное в этом коде – это то, что весь слой инициализируется всего пятью строками кода.
...Совет Разработчику
Вместо того чтобы работать с одномерным массивом и проходить по всем его элементам, вы можете создать двумерный массив и использовать вложенные циклы. Оба подхода справедливы и, вероятно, приблизительно одинаково эффективны. Здесь я рассмотрел лишь случай одномерного массива.
Когда фоновый слой создан и заполнен картой, остается только завершить инициализацию, установив его в начальное положение. Помните, что положение фонового слоя задается относительно начала системы координат, связанной с экраном (верхний левый угол дисплея). Если вы хотите центрировать экран по отношению к карте, инициализируйте замощенный слой отрицательными значениями. Посмотрите на рис. 10.11, который поясняет, почему фоновый слой необходимо инициализировать отрицательными координатами.
Рис. 10.11. Чтобы центрировать игровой экран относительно карты, замощенный слой необходимо инициализировать отрицательными числами
Ниже приведен код, который инициализирует фоновый слой так, что игровой экран оказывается в центре карты:
backgroundLayer.setPosition((getWidth() – backgroundLayer.getWidth()) / 2, (getHeight() – backgroundLayer.getHeight()) / 2);
А где же отрицательные значения, инициализирующие положение слоя? Поскольку высота и ширина холста меньше, чем высота и ширина фонового слоя, координаты положения слоя, вычисляемые в приведенном коде, будут отрицательными. Поэтому выполнение таких вычислений освобождает вас от необходимости вводить координаты вручную. Итак, вы установили фоновый слой. Теперь можно сосредоточиться на спрайте героя, он объявляется точно так же, как и любой другой спрайт:
try {
personSprite = new Sprite(Image.createImage("/Person.png"), 20, 24);
personSprite.setPosition((getWidth() – personSprite.getWidth()) / 2, //Спрайт героя располагается в центре экрана
(getHeight() – personSprite.getHeight()) / 2);
}
catch (IOException e) {
System.err.println("Failed loading images!");
}Спрайт героя состоит из двух фреймов, но вся информация, которая необходима для его создания – это его размер (20 24 пикселя). Конструктор Sprite() очень умен, чтобы понять, что изображение Person.png, размером 20 48 пикселей содержит два фрейма. После того как спрайт создан, он выводится в центре экрана, для чего выполняются несложные вычисления.
Метод update() обрабатывает пользовательский ввод. В этом примере в результате нажатия клавиш перемещается фоновый слой, расположенный под спрайтом персонажа, который остается неподвижным в центре экрана. В листинге 10.1 приведен код метода update().
Листинг 10.1. Метод update() класса WCanvas передвигает карту в соответствии с нажатиями клавиш пользователемprivate void update() {
// обработка пользовательского ввода, перемещение фона, имитирующее
ходьбу героя
if (++inputDelay > 2) {
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED) != 0) {
backgroundLayer.move(12, 0); //Фоновый слой перемещается в ответ на нажатие клавиш
personSprite.nextFrame();
}
else if ((keyState & RIGHT_PRESSED) != 0) {
backgroundLayer.move(-12, 0);
personSprite.nextFrame(); //Спрайт героя – анимационный, он симулирует хождение человека
}
if ((keyState & UP_PRESSED) != 0) {
backgroundLayer.move(0, 12);
personSprite.nextFrame();
}
else if ((keyState & DOWN_PRESSED) != 0) {
backgroundLayer.move(0, -12);
personSprite.nextFrame();
}
checkBackgroundBounds(backgroundLayer); //Этот код гарантирует, что фоновый слой не выйдет за свои границы
// обнулить задержку ввода
inputDelay = 0;
}
}Метод update() несложный, он перемещает фоновый слой в соответствии с нажатыми клавишами. Единственное, что вас может удивить, – необходимость перемещать замощенный слой в направлении, противоположном направлению перемещения героя. Например, чтобы создать иллюзию того, что герой перемещается влево, фон необходимо переместить вправо. Фреймы спрайта героя сменяют друг друга при каждом движении. Поскольку спрайт состоит из двух фреймов, то они отображаются поочередно.
Почти в конце метода update() производится вызов метода checkBackgroundBounds(), который проверяет, чтобы герой не вышел за границы карты. Этот метод приведен в листинге 10.2.
Листинг 10.2. Метод checkBackgroundBounds() проверяет, чтобы герой не вышел за пределы картыprivate void checkBackgroundBounds(TiledLayer background) {
// при необходимости остановить фон
if (background.getX() > -15) //Числа в этом коде аккуратно вычислены так, чтобы герой не вышел за границы замощенного слоя
background.setPosition(-15, background.getY());
else if (background.getX() < -572)
background.setPosition(-572, background.getY());
if (background.getY() > -25)
background.setPosition(background.getX(), -25);
else if (background.getY() < -572)
background.setPosition(background.getX(), -572);
}Хотя основной целью метода checkBackgroundBounds() является проверка того, чтобы герой не вышел за пределы замощенного слоя, ограничение – чуть более жесткое. Необходимо создать иллюзию того, что спрайт героя не может передвигаться по воде и скалам, поэтому такие перемещения необходимо заблокировать. Числа, которые вы видите в представленном листинге, ограничивают перемещение спрайта героя лишь краем воды и скал.
Последний фрагмент кода мидлета Wanderer, который представляет интерес, – это метод draw(), который отвечает за вывод фонового слоя и спрайта. В листинге 10.3 приведен код этого метода.
Листинг 10.3. Метод draw() выводит фоновый замощенный слой и спрайт герояprivate void draw(Graphics g) {
// вывести фоновый слой
backgroundLayer.paint(g); //Чтобы вывести замощенный слой на экран, достаточно одной строки кода
// вывести спрайт героя
personSprite.paint(g);
// вывести содержимое буфера на экран
flushGraphics();
}В этом коде нет ничего особенного. Объекты backgroundLayer и personSprite вызывают методы paint(), который выводит на экран замощенный слой и спрайт героя. Поскольку, возможно, позиционирование фонового слоя непросто понять с первого раза, я поясню иначе. Сказав, что следует использовать отрицательные координаты, я, вероятно, ввел вас в заблуждение. Все можно представить по-другому, чтобы вы лучше поняли. Попробуйте вывести на экран текущее положение фонового слоя. Для этого в метод draw() перед вызовом метода flushGraphics() необходимо вставить следующий код:
//вывести текущее положение фонового слоя
String s = "X = " + backgroundLayer.getX() + ", Y = " + backgroundLayer.getY();
g.drawString(s, 0, 0, Graphics.TOP | Graphics.LEFT);Следует отметить, что такой же подход вы можете использовать для вывода любой необходимой информации. Например, можно отобразить текущую скорость или положение спрайта, который ведет себя не так, как вы предполагали.
Тестирование готового приложения
Когда весь код мидлета Wanderer написан, пора приступать к тестированию. Посмотрите на рис. 10.12, на котором показан экран при запуске игры.
Рис. 10.12. При запуске мидлета Wanderer герой появляется в центре карты
Герой находится в центре экрана и в центре игрового мира (карты). Если вы вспомните программный код, то персонаж никогда не уходит со своего места, перемещается лишь фоновый слой, создавая иллюзию ходьбы героя. Когда вы нажимаете клавиши в J2ME-эмуляторе или на клавиатуре телефона, замощенный слой перемещается, выводя на экран новые фрагменты карты. В результате персонаж перемещается по виртуальному миру (рис. 10.13).
Рис. 10.13. В игре Wanderer персонаж остается неподвижным, а фоновый слой перемещаетсяТак же, как и в реальном мире, виртуальный мир в Wanderer имеет границы. Соответствующий код в программе тщательно проверяет, не достиг ли герой края карты, и если да, то фоновый слой далее перемещаться не будет – графика застынет. В играх, в которых применяются замощенные слои, очень важно выполнять такие проверки и запрещать дальнейшее перемещение. В мидлете Wanderer используется первый подход. Результат показан на рис. 10.14.
Рис. 10.14. Мидлет Wanderer достаточно умен, чтобы позволить вам выйти за границы виртуального мира