Суббота, 21 Декабря 2024, 16:19

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [358]
Статьи об общих понятиях связанных с созданием игр.
Программирование [84]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [149]
Статьи о программах для создания игр, уроки и описания.
Софт [44]
Различные программы, в том числе в помощь игроделам.
2D-графика [14]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [17]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [169]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [133]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Используете ли вы ИИ?
Всего ответов: 31
Главная » Статьи » Создание игр

Ваша первая HTML5 игра на JavaScript и Phaser (перевод)
Источник

Запустить демку в браузере

Скачать исходники финального проекта

Все любят классические игры. Многие же из вас помнят ретро игру "змейка" на старых Nokia? Я уверен, что многие. Поэтому мы сделаем "змейку", используя HTML5. Есть такой великолепный фреймворк с открытым исходным кодом для разработки игр под названием Phaser. Его мы и будем использовать.

Приготовления

Скачайте .zip архив с рисунками и с пустыми файлами кода здесь. Код мы напишем позже. Папки и файлы отражают структуру нашего проекта.

Откройте index.html, напишите название игры в теле тега "title" и подключите скрипты из папки "assets/js/". Позже, чтобы играть вам нужно будет всего лишь открыть index.html в браузере.

index.html
Код

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Snake</title>
    <script src="assets/js/phaser.min.js"></script>
    <script src="assets/js/game.js"></script>
    <script src="assets/js/menu.js"></script>
    <script src="assets/js/game_over.js"></script>
    <script src="assets/js/main.js"></script>
</head>
<body>
</body>
</html>


Сейчас ваша рабочая директория должна выглядеть так:



Как организована игра

Игры, которые используют Phaser организованы вокруг состояний. Представляйте состояния, как разные части вашей игры.

Это состояния нашей игры:


  • Состояние "Меню". Управляется скриптом menu.js. Это состояние отображает рисунок стартового меню. Когда пользователь кликнет по стартовому рисунку, то состояние "Меню" перейдёт в состояние "Игра"
  • Состояние "Игра". Управляется скриптом game.js. Здесь непосредственно происходит игра. Вы управляете змейкой, едите яблоки и просто получаете радость от процесса. Когда змейка умирает, то состояние "Игра" переходит в состояние "Game_Over"
  • Состояние "Game_Over". Это состояние показывает рисунок "gameover.png" и показывает сколько вы набрали очков. Когда вы кликните, то произойдёт переход в состояние "Игра"


main.js - это наш самый главный скрипт. В этом скрипте мы сейчас создадим объект game и добавим наше первое состояние "Меню"

1. Загрузка изображения

К настоящему моменту наша игра не делает ничего. Давайте добавим состояние "Меню" и сделаем так, чтобы оно отображало изображение с заголовком игры.

В разделе "Установка" мы подключили скрипт phaser.js в нашем файле index.html, поэтому нам доступен глобальный объект Phaser, с помощью которого мы можем получать доступ к функциям и методам библиотеки phaser.js

Сейчас мы, используя глобальный объект Phaser, создадим объект game. Этот объект представляет всю нашу игру. Мы будем добавлять состояния к этому объекту.

main.js

Код

var game;

// Создание объекта game (600 пикселей в ширину и 450 пикселей в высоту)
game = new Phaser.Game(600, 500, Phaser.AUTO, '');

// Первый параметр - название нашего состояния
// Второй параметр - объект, который будет хранить методы нашего состояния
game.state.add('Menu', Menu);

game.state.start('Menu');


Теперь нам нужно создать и инициализировать объект Menu, который мы задействовали в скрипте выше. Откройте скрипт menu.js и добавьте объект Menu и методы для него (смотрите листинг ниже).

menu.js

Код

var Menu =
    {
        preload: function ()
        {
            // Загрузка изображения, которое нам будет необходимо позже для создания спрайта
            // Первый аргумент - название, по которому мы будем обращаться к изображению
            // Второй аргумент - путь к файлу изображения
            game.load.image('menu', './assets/images/menu.png');
        },
        create: function ()
        {
            // Создаём и добавляем спрайт в нашу игру. Спрайтом будет игровой лого для меню
            // Аргументы: X, Y, имя изображения (см. выше)
            this.add.sprite(0, 0, 'menu');
        }
    };


Когда состояние Меню запустится (команда game.state.start('Menu')), то первым делом выполнится тело функции preload, которая загрузит все необходимые ассеты (ассеты - это изображения, звуки, музыка и т.д.). Как только функция preload закончит свою работу, то начнёт выполнятся функция create. В теле функции create должен располагаться код, который инициализирует игру (например, расставляет картинки по требуемым местам). В данном случае, create располагает стартовое изображение меню в координате (0, 0)

От переводчика:
Начало координат игры располагается в левом верхнем углу области рисования. Ось X направлена вправо, а ось Y направлена вниз. По умолчанию якорная точка изображения располагается в левом верхнем углу изображения. Эту точку можно менять с помощью метода anchor.setTo. Например, можно установить якорную точку на середину изображения: this.obj.anchor.setTo(0.5, 0.5); Изображения, которые располагаются в игровом мире называют спрайтами.

Из-за ограничения безопасности браузера мы не можем загружать изображения с локального диска. Простыми словами, мы не можем просто кликнуть два раза по index.html и запустить игру. Поэтому запускать игру нужно с локального сервера, например, из среды Visual Studio, которая автоматически запустит локальный сервер IIS. Для запуска игры из среды Visual Studio 2015 нужно кликнуть правой кнопкой мыши по файлу index.html и выбрать "View in Browser"

Если всё было сделано правильно, то стартовое изображение должно появится в вашем браузере: запустить демку



2. Вывод змейки на экран

Как было упомянуто ранее, сам процесс игры происходит в состоянии "Игра". В этом же состоянии мы выводим змейку на экран. Так же как мы поступали с состоянием "Меню" ранее, мы должны зарегистрировать состояние "Игра" в скрипте main.js:

main.js

Код

var game;

// Создание объекта game (600 пикселей в ширину и 450 пикселей в высоту)
game = new Phaser.Game(600, 450, Phaser.AUTO, '');

// Первый параметр - название нашего состояния
// Второй параметр - объект, который будет хранить методы нашего состояния
game.state.add('Menu', Menu);

// Добавляем состояние "Игра"
game.state.add('Game', Game);

game.state.start('Menu');


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

Это окончательный код скрипта menu.js:

Код

var Menu =
    {
        preload: function ()
        {
            // Загрузка изображения, которое нам
            // будет необходимо позже для создания спрайта
            // Первый аргумент - название, по которому мы будем обращаться к
            // изображению
            // Второй аргумент - путь к файлу изображения
            game.load.image('menu', './assets/images/menu.png');
        },
        create: function ()
        {
            // Создаём и добавляем кнопку в нашу игру. Спрайтом будет
            // игровой лого для меню
            // Аргументы: X, Y, имя изображения (см. выше), функция обработки клика
            this.add.button(0, 0, 'menu', this.startGame, this);
        },
        startGame: function ()
        {
            // Меняем состояние "Меню" на состояние "Игра"
            this.state.start('Game');
        }
    };


Теперь мы продолжим писать код для состояния "Игра". Структура кода очень похожа на структуру из скрипта menu.js для состояния "Меню".

game.js

Код

var snake, apple, squareSize, score, speed,
    updateDelay, direction, new_direction,
    addNew, cursors, scoreTextValue, speedTextValue,
    textStyle_Key, textStyle_Value;
var Game = {
    preload: function ()
    {
        // Здесь мы загружаем все необходимые ресурсы для уровня
        // В нашем случае, это просто два квадрата: один для тела змейки и другой для яблока
        game.load.image('snake', './assets/images/snake.png');
        game.load.image('apple', './assets/images/apple.png');
    },
    create: function ()
    {
        // Перед стартом игры, мы здесь инициализируем глобальные переменные, объявленные в начале файла
        // Мы сделали эти переменные глобальными, чтобы они были доступны в функции update
        snake = []; // Этот список работает как хранилище для частей нашей змейки
        apple = {}; // Объект для яблока
        squareSize = 15; // Длина сторон всех наших квадратов (яблока и части для
                                        // змейки). Наши изображения имеют размер 15x15 пикселей
        score = 0; // Игровые очки
        speed = 0; // Скорость игры
        updateDelay = 0; // Переменная для контроля за частотой обновления
        direction = 'right'; // Направление нашей змейки
        new_direction = null; // Буфер для хранения нового направления
        addNew = false; // Переменная, которая показывает, что еда была съедена
        // Сообщить Phaser'у, что мы хотим использовать для управления клавиатуру (клавиши со стрелками)
        cursors = game.input.keyboard.createCursorKeys();
        game.stage.backgroundColor = '#061f27';
        // Генерируем 10 элементов для нашей змейки
        // Начинаем с позиции X=150 Y=150 и увеличиваем X на каждой итерации
        for (var i = 0; i < 10; i++)
        {
            // Параметры: координата X, координата Y, изображение
            snake[i] = game.add.sprite(150 + i * squareSize, 150, 'snake');
        }
        // Создаём первое яблоко
        this.generateApple();
        // Добавляем текст в верхнюю часть игры
        textStyle_Key = { font: "bold 14px sans-serif", fill: "#46c0f9", align: "center" };
        textStyle_Value = { font: "bold 18px sans-serif", fill: "#fff", align: "center" };
        // Игровые очки
        game.add.text(30, 20, "SCORE", textStyle_Key);
        scoreTextValue = game.add.text(90, 18, score.toString(), textStyle_Value);
        // Скорость игры
        game.add.text(500, 20, "SPEED", textStyle_Key);
        speedTextValue = game.add.text(558, 18, speed.toString(), textStyle_Value);
    },
    update: function ()
    {
        // Функция update вызывается постоянно с высокой частотой (где-то около 60 кадров в секунду)
        // обновляя игровое поле
        // Мы пока оставим тело этой функции пустым
    },
    generateApple: function ()
    {
        // Выбираем случайное место на игровом поле
        // X между 0 и 585 (39*15)
        // Y между 0 и 435 (29*15)
        var randomX = Math.floor(Math.random() * 40) * squareSize,
            randomY = Math.floor(Math.random() * 30) * squareSize;
        // Добавляем новое яблоко
        apple = game.add.sprite(randomX, randomY, 'apple');
    }
};


Так должны сейчас выглядеть змейка, яблоко и вывод текста:



3. Движение и контроль

Для того, чтобы змейка двигалась нам нужно написать код в функции update скрипта game.js

Мы создадим функции-обработчики нажатий клавиш-стрелок.

Реализация движения немного сложновато для понимания на первый взгляд. Функция update срабатывает с очень высокой частотой (около 60 кадров в секунду (60 fps)) и если мы будем перемещать змейку в каждом вызове функции update, то скорость змейки будет огромная. Для того, чтобы решить эту проблему мы напишем условие, которое будет определять, какой это вызов update по счёту. Например, мы может вызывать код передвижения только каждый 10-й вызов функции update. Для того чтобы узнать, какой это вызов по счёту, мы будем использовать переменную-счётчик updateDelay.

Если это 10-й вызов функции update, то мы удаляем последний квадрат нашей змейки (первый элемент в списке), даём ему новые координаты, согласно текущему направлению и помещаем его перед головой змейки (наверх списка).

Так выглядит код в функции update (скрипт game.js):

Код

    update: function ()
    {
        // Обработчики нажатия клавиш-стрелок
        if (cursors.right.isDown && direction != 'left')
        {
            new_direction = 'right';
        }
        else if (cursors.left.isDown && direction != 'right')
        {
            new_direction = 'left';
        }
        else if (cursors.up.isDown && direction != 'down')
        {
            new_direction = 'up';
        }
        else if (cursors.down.isDown && direction != 'up')
        {
            new_direction = 'down';
        }
        // Формула, которая вычисляет скорость игры, которая в свою очередь, базируется на очках
        // Наибольшая скорость игры может быть равна максимум 10-и
        speed = Math.min(10, Math.floor(score / 5));
        // Обновляем значение скорости на экране
        speedTextValue.text = '' + speed;
        // Так как функция update имеет частому вызова около 60 раз в секунд, то
        // нам нужно уменьшить частоту вызова кода перемещения змейки
        // Увеличиваем счётчик на единицу каждый вызов функции update
        updateDelay++;
        // Выполняем эту часть кода только если счётчик кратен значению (10 - скорость игры)
        // Do game stuff only if the counter is aliquot to (10 - the game speed).
        // Чем больше значение переменной speed, тем чаще вызывается этот код и тем быстрее движется змейка
        if (updateDelay % (10 - speed) == 0)
        {
            // Передвижение змейки
            var firstCell = snake[snake.length - 1],
                lastCell = snake.shift(),
                oldLastCellx = lastCell.x,
                oldLastCelly = lastCell.y;
            // Если новое направление было выбрано в обработчике нажатия клавиши, то меняем текущее направление змейки
            if (new_direction)
            {
                direction = new_direction;
                new_direction = null;
            }
            // Меняем координаты последней ячейки относительно головы змейки согласно направлению
            // То есть меняем координаты последней ячейки, чтобы эта ячейка оказалась перед головой змейки
            if (direction == 'right')
            {
                lastCell.x = firstCell.x + 15;
                lastCell.y = firstCell.y;
            }
            else if (direction == 'left')
            {
                lastCell.x = firstCell.x - 15;
                lastCell.y = firstCell.y;
            }
            else if (direction == 'up')
            {
                lastCell.x = firstCell.x;
                lastCell.y = firstCell.y - 15;
            }
            else if (direction == 'down')
            {
                lastCell.x = firstCell.x;
                lastCell.y = firstCell.y + 15;
            }
            // Помещаем последнюю ячейку наверх стека
            // Помечаем её, как первую ячейку
            snake.push(lastCell);
            firstCell = lastCell;
        }


Попробуйте управлять змейкой с помощью клавиш-стрелок: запустить демо



4. Определение столкновений

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

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

В функции update (скрипт game.js), после кода, который перемещает змейку, мы вызовем несколько методов. Эти методы будут сравнивать координаты, чтобы определить было столкновение или нет.

Код

    update: function ()
    {
        // ...
        if (updateDelay % (10 - speed) == 0)
        {
            // ...
            // Увеличиваем размер змейки, если яблоко было съедено
            // Создаём блок позади змейки со старыми координатами последнего блока
            // (последний блок, к настоящему моменту, переместился вместе с остальной частью змейки)
            if (addNew)
            {
                snake.unshift(game.add.sprite(oldLastCellx, oldLastCelly, 'snake'));
                addNew = false;
            }
            // Проверяем столкновение с яблоком
            this.appleCollision();
            // Проверяем столкновение головы змейки со своим телом
            // Параметр - голова змейки
            this.selfCollision(firstCell);
            // Проверяем столкновение головы змейки со стеной
            // Параметр - голова змейки
            this.wallCollision(firstCell);
        }
    },
    generateApple: function ()
    {
        // Выбираем случайное место на игровом поле
        // X между 0 и 585 (39*15)
        // Y между 0 и 435 (29*15)
        var randomX = Math.floor(Math.random() * 40) * squareSize,
            randomY = Math.floor(Math.random() * 30) * squareSize;
        // Добавляем новое яблоко
        apple = game.add.sprite(randomX, randomY, 'apple');
    },
    appleCollision: function ()
    {
        // Проверяем, что любая часть змейки пересекла яблоко
        // Проверка пересечения со всеми частями змейки необходима, чтобы
        // учесть случай, если новое яблоко появится внутри змейки
        for (var i = 0; i < snake.length; i++)
        {
            if (snake[i].x == apple.x && snake[i].y == apple.y)
            {
                // В следущий момент времени, когда змейка переместится, то
                // новый блок будет добавлен к ней
                addNew = true;
                // Удаляем старое яблоко
                apple.destroy();
                // Создаём новое яблоко
                this.generateApple();
                // Увеличиваем число заработанных очков
                score++;
                // Обновняем очки на экране
                scoreTextValue.text = score.toString();
            }
        }
    },
    selfCollision: function (head)
    {
        // Проверяем, что голова змейки не пересекла одну из своих частей
        for (var i = 0; i < snake.length - 1; i++)
        {
            if (head.x == snake[i].x && head.y == snake[i].y)
            {
                // Если пересекла, то показываем экран Game Over
                game.state.start('Game_Over');
            }
        }
    },
    wallCollision: function (head)
    {
        // Проверяем, что голова змейки находится в границах игрового поля
        if (head.x >= 600 || head.x < 0 || head.y >= 450 || head.y < 0)
        {
            // Если это не так, что мы врезались в стену. Значит, показываем экран Game Over
            game.state.start('Game_Over');
        }
    }


Когда змейка сталкивается с яблоком, то мы увеличиваем количество набранных очков и размер змейки. Но когда змейка сталкивается со стеной или со своим телом, мы должны завершить игру. Для того, чтобы сделать это нам нужно создать новое состояние с именем Game_Over. Снова нам нужно зарегистрировать новое состояние в скрипте main.js. Добавим эту строчку кода в самый низ скрипта main.js:

Код

game.state.add('Game_Over', Game_Over);


Добавим код самого состояния в скрипт game_over.js:

Код

var Game_Over = {
    preload: function ()
    {
        // Загружаем рисунок
        game.load.image('gameover', './assets/images/gameover.png');
    },
    create: function ()
    {
        // Создаём кнопку для старта игры, как мы делали в скрипте main.js
        this.add.button(0, 0, 'gameover', this.startGame, this);
        // Выводим на экран сколько мы набрали очков
        game.add.text(235, 350, "LAST SCORE", { font: "bold 16px sans-serif", fill: "#46c0f9", align: "center" });
        game.add.text(350, 348, score.toString(), { font: "bold 20px sans-serif", fill: "#fff", align: "center" });
    },
    startGame: function ()
    {
        // Меняем текущее состояние обратно на состояние "Игра"
        // Change the state back to Game.
        this.state.start('Game');
    }
};


Наша первая игра готова!

Запустить финальную демку
Скачать исходники финального проекта

Для дальнейшего изучения:



Список книг:

An Introduction to HTML5 Game Development with Phaser.js
Discover Phaser
Interphase #1
Starship Defender
Game Development for Human Beings (Free Ebook)
Making Your First HTML5 Game with Phaser
From null to full HTML5 cross platform
Категория: Создание игр | Добавил: 8Observer8 (01 Декабря 2016) | Автор: Иван Енжаев
Просмотров: 29172 | Комментарии: 1 | Рейтинг: 4.5/19 |
Теги: phaser, Photon Storm, Canvas, WebGL, open source, Game Framework, Fork, HTML5, Free, 2D game framework
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «Ваша первая HTML5 игра на JavaScript и Phaser (перевод)», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 1
+1-
1 8Observer8   (02 Декабря 2016 16:26) [Материал]
8Observer8Есть небольшая проблемка со скачиванием файлов. Вы увидите надпись "Oops! There was a problem with your download" Но ничего страшного! Просто подождите 5 секунд и вы увидите, что появится зелёная кнопка "DOWNLOAD"

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • DEngine
  • JMonkeyEngine
  • GameDirector
  • OpenMW
  • Adventuron Classroom
  • Torque 3D
  • OpenBOR
  • Tilengine
  • Devana
  • DevelNext
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2024 Рейтинг