====== Создание игр на JavaScript: часть 1 ====== **От Zanathel** //«Передвижение белого квадрата по темному полю»// ===== Создайте Вашу первую игру на JavaScript! ===== ==== Введение. ==== JavaScript – очень мощный язык, гораздо более мощный, чем можно предположить. Я понял, на что способны скриптовые языки после того, как поэкспериментировал с обработчиками событий от клавиатуры и полной перерисовкой экрана в сжатые сроки. В IE 5.0 я поставил перерисовку экрана каждые 10 милисекунд, и это не вызвало ни задержек, ни увеличения потребляемого объема памяти. Когда я это увидел, я понял, что просто обязан углубиться в изучение JavaScript. В результате получилась в чем-то простая, а в чем-то сложная игра, которая пользовалась успехом среди моих друзей. Сейчас я собираюсь попытаться создать универсальную относительно браузера игру с дружественным интерфейсом. Пусть семейство браузеров с открытым кодом представляет FireFox, как наиболее стандартный из них. ==== Тест. ==== Взгляните на нижеследующий код. Он корректно работает и в IE, и в FireFox. Что меня поразило, так это то, что FireFox по сравнению с IE предоставляет расширенную поддержку обработки событий: Browser hooking capabilities Это отличная новость. ==== Как все собрать. ==== Начинать следует с передвижения объекта (квадрата) по экрану. Для этого потребуются обработчики событий, передвигаемый объект, перерисовка экрана и система координат. Перед тем как создать систему координат, нужно получить реальные размеры клиентской области окна. В IE они хранятся в document.body.offsetWidth и document.body.offsetHeight. А как в Firefox? Воспользовавшись Google, я нашел свойства window.innerWidth и window.innerHeight. И все прекрасно работает. Итак, высота и ширина системы координат определена. Теперь надо расположить наш объект точно в центре экрана. Как? Я попробовал разные способы, но наиболее подходящей мне показалась система позиционирования CSS. Перерисовывая конкретный участок экрана (ограниченный ячейкой таблицы), можно добиться высокой эффективности перерисовки. ==== Создание ядра ==== В обычных языках программирования Вы не перерисовываете постоянно весь экран. Вы перерисовываете конкретные участки (блиттинг). Эмуляция блиттинга в JavaScript заключается в том, что движущийся элемент располагается внутри отдельного слоя, который в общем случае может заполнять весь экран. Это позволяет не перерисовывать статические объекты, такие как дома, фон, статистика игры, сообщения и т.д. и спасает от мерцания картинки. Простой движущийся слой!
ID тела документа – gContainer, а ID перерисовываемой поверхности – gSurface. Я также создал функцию, выполняющуюся всякий раз при загрузке страницы (не обновлении, а именно загрузке). В ней инициализируется передвигаемый слой (gBox). **Инициализация сцены.** Мы хотим расположить объект точно в центре документа, поэтому нам нужны точные координаты. В этом случае нельзя использовать метод приближенного центрирования. Воспользуемся следующей формулой: ''(ширина поверхности – ширина объекта)/2'' ''(высота поверхности – высота объекта)/2'' Перед тем как записать эту формулу на JavaScript, нужно определить класс движущегося объекта. Я назвал его «_box» (я ставлю символ подчеркивания в начале имен всех моих классов JavaScript). Он содержит x и y-координаты слоя, пропорции, скорость (в пикселях за такт) и параметры движения. //////////////////////////// Классы //////////////////////////// function _box(x, y, w, h, speed) { this.x = x; this.y = y; this.w = w; this.h = h; this.speed = speed; this.ix = false; this.dx = false; this.iy = false; this.dy = false; } Ix, dx, iy и dy – флаги увеличения (increase) и уменьшения (decrease) координат x и y. Так как сначала объект должен покоиться, установим все четыре флага в **false**. Объект не движется ни вниз, ни вверх, ни влево, ни вправо. Теперь давайте напишем функцию, создающую движущийся объект. Для этого понадобится объявить глобальную переменную box. var surface = null, box = null; Объявив её, можно перейти к написанию функции: //////////////////////////// Загрузка игры ////////////////////////// function prepareBox() { box = new _box(0, 0, 100, 100, 8); box.x = (getScreenSize(SCREEN_X) - box.w) / 2; box.y = (getScreenSize(SCREEN_Y) - box.h) / 2; } Если Вы новичок в объектно-ориентированном программировании на JavaScript, поясняю, что я определил экземпляр класса _box как переменную box. Параметрами конструктора являются начальные значения свойств. Обратиться к свойству созданного экземпляра можно посредством точки и имени нужного свойства. Как Вы могли заметить, я использовал ещё не определенную функцию **getScreenSize**. Нам нужно быстро получать размеры экрана, а так как соответствующие команды для разных браузеров различны, я решил выделить определение размеров в самостоятельную функцию: //////////////////////////// Экран ///////////////////////////// function getScreenSize(s) { if (s == SCREEN_X) { if (bIE) return document.body.offsetWidth; return window.innerWidth; } else { if (bIE) return document.body.offsetHeight; return window.innerHeight; } } У нас есть класс движущегося объекта, функция для его создания и функция определения размеров экрана. Что осталось? Нужна функция, которая будет вызываться всеми нашими обрабтчиками событий, такими как OnKeyUp и OnKeyDown. Нужна также функция, которая будет вызываться каждые 25 милисекунд (это значение хранится в переменной iMs, см. второй листинг). Ну и, разумеется, нужна функция для вывода объекта на экран и передвижения его в целевую точку в соответствии с заданными параметрами движения. Начнем с методов-обработчиков событий. Расположим их перед функцией **PrepareBox()**. function attachEvents() { document.onkeydown = keyDown; document.onkeyup = keyUp; } Просто? Да, но больше ничего и не требуется. Теперь надо обработать события клавиатуры: //////////////////////////// Обработка событий клавиатуры /////////////////////////// function keyDown(e) { switch ((bIE) ? event.keyCode : e.which) { case 37: // left box.ix = false; box.dx = true; break; case 38: // up box.iy = true; box.dy = false; break; case 39: // right box.ix = true; box.dx = false; break; case 40: // down box.iy = false; box.dy = true; break; } } function keyUp(e) { switch ((bIE) ? event.keyCode : e.which) { case 37: // left box.dx = false; break; case 38: // up box.iy = false; break; case 39: // right box.ix = false; break; case 40: // down box.dy = false; break; } } А теперь позвольте мне пояснить приведеннй код, так как он может оказаться непрозрачным. Здесь я меняю параметры движения объекта. Когда пользователь нажимает на правую стрелку, х-координата объекта должна увеличиваться, а сам объект – двигаться от левой границы экрана к правой. Поэтому я отслеживаю нажатие 39-ой кнопки (это и есть правая стрелка), и как только она нажата, сбрасываю флаг уменьшения х-координаты (нам не нужно двигаться влево) и устанавливаю флаг её увеличения (двигаемся вправо). Прямо противоположные действия нужно осуществить по нажатию левой стрелки (37-ой кнопки). У-координата равна нулю в самом низу экрана. Если я хочу поднять объект вверх, я должен увеличивать у-координату. Для этого я устанавливаю флаг увеличения у-координаты (**true**) и сбрасываю флаг уменьшения (**false**). Так как я хочу, чтобы объект двигался только пока нажаты соответствующие кнопки, нужно сбрасывать все флаги, когда кнопку отпускают. Например, если я отпускаю верхнюю стрелку, то это означает, что я не хочу, чтобы объект продолжал двигаться вверх. Поэтому я сбрасываю флаг увеличения у-координаты, что немедленно остановит дальнейшее движение объекта вверх. Если в это время я продолжаю нажимать, например, левую стрелку, то движение влево не остановится, пока я и её не отпущу. А сейчас напишем функцию вывода объекта на экран! При изменении координат следует учитывать, что объект не должен выходить за пределы экрана. //////////////////////////// Перерисовка ///////////////////// function printBox() { if (box.ix) { if (box.x + box.w + box.speed <= getScreenSize(SCREEN_X)) box.x += box.speed; } if (box.dx) { if (box.x - box.speed >= 0) box.x -= box.speed; } if (box.iy) { if (box.y + box.h + box.speed <= getScreenSize(SCREEN_Y)) box.y += box.speed; } if (box.dy) { if (box.y - box.speed >= 0) box.y -= box.speed; } surface.innerHTML = '
'; }
Система координат в HTML/CSS начинается в левом нижнем углу (см. рис. 1, «0») H X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X O X X X X X W **Рис. 1 – Система координат** С помощью выражений «X > ширина экрана» и «У > высота экрана» невозможно определить, что объект вышел за пределы экрана. В этом случае объект будет останавливаться только тогда, когда границы экрана достигнет его точка «0» (см. рис. 1). Чтобы объект останавливался по достижении границ экрана точками «H» и «W», нужно добавлять к координате Х в условиях остановки высоту и ширину объекта. То есть, вместо «Х > ширина экрана» используем «W > ширина экрана». Тогда никакая часть объекта не покинет область рисования. Передвинув координаты объекта, выведем его как слой. Заметьте, что я значительно сократил код, поместив всю статическую информацию об объекте в тег style. Мы почти достигли финала! Остался последний шаг. Нужна функция, которая будет вызываться через определенный интервал (25 милисекунд). После этого нужно будет вызвать все написанные функции в теле функции **loadGame()**, которая пока пуста. function updateScreen() { surface.innerHTML = ''; printBox(); window.setTimeout('updateScreen()', iMs); } Очистим поверхность и выведем новый объект. Повторяйте все время! А теперь – последний штрих! function loadGame() { surface = document.getElementById("gSurface"); if (surface == null) { alert("Can't find the required surface!\nCancelling..."); return; } attachEvents(); prepareBox(); updateScreen(); } Мы отследили все события, подготовили объект к движению (поместили его в центр экрана и инициализировали глобальную переменную для дальнейшего использования) и вызвали бесконечно повторяющуюся функцию **updateScreen()**. Вот готовый код: Простой движущийся слой!
Наслаждайтесь!