Статьи

Анимация игровых меню и переходов экрана в HTML5: руководство для разработчиков Flash

Разработка HTML5 и Flash может иметь много общего, но, как разработчик Flash, я все же обнаружил, что это огромная задача — заново изучить мои старые навыки в HTML5. В этом уроке я покажу вам, как я создал анимированное меню и переход на другой экран для стрелялки HTML5.


Посмотрите на результат, к которому мы будем стремиться:

Меню, над которым мы будем работать
Нажмите, чтобы попробовать демо

Обратите внимание на фон прокрутки, корабли, которые появляются и вращаются с обеих сторон каждого пункта меню, и то, как экран становится черным при выборе параметра.


HTML5 и JavaScript во многом похожи на ActionScript; есть много совпадений в синтаксисе, прослушивателях событий и методах. Однако в этом уроке я расскажу о некоторых совершенно разных различиях:

  • Рисование фигур
  • Рисование изображений
  • Использование интервалов
  • оживляющий
  • События мыши
  • Добавление поддержки для нескольких браузеров

Стоит отметить, что в этом учебнике в основном используются изображения, которые можно загрузить вместе с источником, или вы можете использовать свои собственные изображения, если хотите (вам нужно знать ширину и высоту).


Первое, что нам нужно сделать, это добавить элемент <canvas> в тело HTML-файла, поэтому создайте его с именем ShootEmUp.html и вставьте следующее:

01
02
03
04
05
06
07
08
09
10
11
12
<!DOCTYPE html>
<html>
    <head>
        <title>Shoot ‘Em Up</title>
    </head>
    <body>
        <canvas id=»myCanvas» width=»480″ height=»320″>
            <p>Your browser does not support HTML5!</p>
        </canvas>
    </body>
 
</html>

Выделенные строки вставляют элемент canvas, который будет отображать наше актуальное меню. Смотрите это руководство для руководства по холсту с нуля.

Уже почти пора начинать кодировать JavaScript! У нас есть два варианта того, куда может идти код; Он может быть записан внутри HTML внутри тегов <script> или во внешнем файле. Для простоты я напишу весь код ниже элемента <canvas>. Но не стесняйтесь использовать внешний файл, если хотите; просто не забудьте в источник.

1
2
3
<script type=»text/javascript»>
// Javascript Goes Here
</script>

Нашим следующим шагом будет создание четырех переменных для простой ссылки на элемент canvas.

1
2
3
4
var canvas = document.getElementById(«myCanvas»);
var context = canvas.getContext(«2d»);
var width = canvas.getAttribute(‘width’);
var height = canvas.getAttribute(‘height’);

Сначала мы myCanvas переменной myCanvas и установили ее так, чтобы она указывала на элемент HTML-холста. Другая переменная с именем context была создана для получения размерности холста (2D). Подобно Flash, мы создаем последние две переменные, width и height , чтобы упростить процесс доступа к свойствам ширины и высоты холста.


Как и в ActionScript, мы собираемся создавать экземпляры наших изображений.

1
2
3
4
5
6
7
var bgImage = new Image();
var logoImage = new Image();
var playImage = new Image();
var instructImage = new Image();
var settingsImage = new Image();
var creditsImage = new Image();
var shipImage = new Image();

Мы упускаем важный фрагмент кода для каждого экземпляра — путь к исходному коду! Я сохранил все изображения в папке «Изображения» в том же каталоге, что и файл HTML, поэтому:

1
2
3
4
5
6
7
shipImage.src = «Images/ship.png»;
bgImage.src = «Images/Background.png»;
logoImage.src = «Images/logo.png»;
playImage.src = «Images/play.png»;
instructImage.src = «Images/instructions.png»;
settingsImage.src = «Images/settings.png»;
creditsImage.src = «Images/credits.png»;

Прежде чем рисовать изображения на холсте, давайте создадим четыре массива для хранения положений и размеров кнопок ( playImage , instructImage , settingsImage , creditsImage ). Эти массивы будут использованы позже для создания функции наведения мыши.

1
2
3
4
var buttonX = [192,110,149,160];
var buttonY = [100,140,180,220];
var buttonWidth = [96,260,182,160];
var buttonHeight = [40,40,40,40];

Теперь мы можем нарисовать изображения на холсте; это можно сделать с помощью функции onload для каждого изображения, но не обязательно включать функцию onload — мы можем просто использовать drawImage() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
bgImage.onload = function(){
    context.drawImage(bgImage, 0, 0);
};
logoImage.onload = function(){
    context.drawImage(logoImage, 50, -10);
}
playImage.onload = function(){
    context.drawImage(playImage, buttonX[0], buttonY[0]);
}
instructImage.onload = function(){
    context.drawImage(instructImage, buttonX[1], buttonY[1]);
}
settingsImage.onload = function(){
    context.drawImage(settingsImage, buttonX[2], buttonY[2]);
}
creditsImage.onload = function(){
    context.drawImage(creditsImage, buttonX[3], buttonY[3]);
}

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


У JavaScript нет эквивалента onEnterFrame() , но мы можем легко создать наш собственный, используя интервал (таймер).

1
2
3
4
var frames = 30;
var timerId = 0;
 
timerId = setInterval(update, 1000/frames);

Вы можете быть смущены тем, как работает интервал, поэтому я объясню вкратце. Интервал вызывает функцию update() каждые (1000 / frames ) миллисекунд для создания плавной частоты обновления. Значение frames управляет частотой кадров; если число frames равно 25, то браузер будет пытаться вызывать update() каждые (1000/25 =) 40 миллисекунд.

Наш следующий очевидный шаг — создать функцию update()

1
2
3
4
5
function update() {
    clear();
    move();
    draw();
}

Еще три функции были только что вызваны. clear() используется для очистки холста, потому что в отличие от flash, холст работает как наклейка на доску; изображения не могут быть перемещены после того, как они были размещены. Следующая функция, move() , используется для изменения значений переменных, которые используются с изображениями. Наконец, вызывается draw() для размещения этих «стикеров».

1
2
3
funcion clear(){
    context.clearRect(0, 0, width, height);
}

Проще говоря, этот код очищает все внутри прямоугольника размером с холст и рисуется из (0,0) верхнего левого угла. Это означает, что он очищает весь видимый холст.

Прежде чем перейти к следующей функции, необходимо ввести две переменные. backgroundY будет переменной для y-позиции фонового изображения, а speed будет использоваться для вычитания из backgroundY каждом цикле обновления.

1
2
var backgroundY = 0;
var speed = 1;

Эффект, который мы собираемся произвести — это непрерывно прокручивающийся фон. Изображение состоит из двух идентичных изображений звездного поля, одно над другим, в увеличенном изображении (изображение в два раза больше высоты холста). Мы будем медленно перемещать изображение до тех пор, пока вторая половина полностью не будет видна, а затем мы сбросим положение изображения обратно к первой половине.

1
2
3
4
5
6
function move(){
    backgroundY -= speed;
    if(backgroundY == -1 * height){
        backgroundY = 0;
    }
}

Наконец, у нас есть функция draw() . Все изображения будут перерисованы, но следует заметить, что bgImage sy в bgImage было заменено на переменную backgroundY .

1
2
3
4
5
6
context.drawImage(bgImage, 0, backgroundY);
context.drawImage(logoImage, 50,-10);
context.drawImage(playImage, buttonX[0], buttonY[0]);
context.drawImage(instructImage, buttonX[1], buttonY[1]);
context.drawImage(settingsImage, buttonX[2], buttonY[2]);
context.drawImage(creditsImage, buttonX[3], buttonY[3]);

Попробуйте сейчас и полюбуйтесь плавной прокруткой фона.


В HTML5 <canvas> отсутствует одна вещь — поддержка прослушивателей событий изображений, то есть мы не можем просто написать playImage.mouseover = function(){} . Вместо этого мы должны получить положение мыши относительно холста и выяснить, находится ли он над объектом.

1
2
3
4
var mouseX;
var mouseY;
 
canvas.addEventListener(«mousemove», checkPos);

Две введенные переменные будут использоваться для получения текущей позиции мыши. Мы добавили прослушиватель событий, как в ActionScript, который вызывает функцию checkPos() каждый раз, когда движется мышь.

1
2
3
4
function checkPos(mouseEvent){
    mouseX = mouseEvent.pageX — this.offsetLeft;
    mouseY = mouseEvent.pageY — this.offsetTop;
}

Если вы будете предупреждать значения mouseX и mouseY каждый раз, когда будете mouseY мышь, вы получите правильное положение. Но есть одна проблема: не все современные настольные браузеры поддерживают этот метод. Чтобы преодолеть эту проблему, мы можем использовать небольшой взлом вместо этого:

1
2
3
4
5
6
7
if(mouseEvent.pageX || mouseEvent.pageY == 0){
    mouseX = mouseEvent.pageX — this.offsetLeft;
    mouseY = mouseEvent.pageY — this.offsetTop;
}else if(mouseEvent.offsetX || mouseEvent.offsetY == 0){
    mouseX = mouseEvent.offsetX;
    mouseY = mouseEvent.offsetY;
}

Он проверяет, использует ли браузер свойства «page» или «offset», чтобы вернуть положение мыши, и корректирует значения (при необходимости), чтобы получить положение мыши относительно холста.

Теперь помните тот корабль, который мы создали, но не нарисовали? Мы возьмем это статичное изображение, закручиваем его и заставляем его появляться всякий раз, когда мы наводим курсор на кнопки!

1
2
3
4
5
6
7
8
var shipX = [0,0];
var shipY = [0,0];
var shipWidth = 35;
var shipHeight = 40;
 
var shipVisible = false;
var shipSize = shipWidth;
var shipRotate = 0;

Первые четыре переменные такие же, как и раньше (у нас есть две позиции, потому что будет два корабля). Переменная shipVisible будет установлена ​​в true, когда мышь находится над кнопкой. Что касается shipSize и shipRotate , они будут использоваться для масштабирования корабля по вертикали и изменения положения, чтобы создать иллюзию его вращения. Имейте в виду, что изображения масштабируются справа налево.

1
2
3
4
5
6
7
8
9
for(i = 0; i < buttonX.length; i++){
    if(mouseX > buttonX[i] && mouseX < buttonX[i] + buttonWidth[i]){
        if(mouseY > buttonY[i] && mouseY < buttonY[i] + buttonHeight[i]){
             
        }
    }else{
         
    }
}

Добавьте код в checkPos() . Сначала мы перебираем кнопки, которые есть (я вычислил значение, используя buttonX.length ). Далее мы сравниваем mouseX чтобы увидеть, больше ли оно текущей кнопки buttonX и меньше ее buttonX + buttonWidth — т.е. в пределах горизонтальных границ кнопки. Затем мы повторим процесс в другом операторе if для значений Y. Если это все правда, тогда мышь должна быть над кнопкой, поэтому установите shipVisible в true :

1
shipVisible = true;

И в пустом операторе else установите его в false ; он будет вызываться всякий раз, когда вы нажимаете кнопку мыши:

1
shipVisible = false;

В shipVisible = true мы установим начальные значения для shipX и shipX и shipY все масштабирование в функциях перемещения и рисования.

1
2
3
4
shipX[0] = buttonX[i] — (shipWidth/2) — 2;
shipY[0] = buttonY[i] + 2;
shipX[1] = buttonX[i] + buttonWidth[i] + (shipWidth/2);
shipY[1] = buttonY[i] + 2;

Для первого shipX , который мы хотим расположить слева от кнопки, мы установили значение (текущая кнопка Х — половина ширины корабля), и я переместил его на 2 пикселя влево, чтобы он выглядел лучше. Аналогичный процесс повторяется для первого shipY Для второго shipX мы позиционируемся в (текущая кнопка X + ширина этой кнопки + половина ширины корабля), а затем мы устанавливаем Y как прежде.

Сложная часть приходит сейчас. Мы должны масштабировать корабль и переместить его, чтобы компенсировать масштабирование. Внутри функции move() напишите оператор if .

1
2
3
4
5
6
7
if(shipSize == shipWidth){
    shipRotate = -1;
}
if(shipSize == 0){
    shipRotate = 1;
}
shipSize += shipRotate;

Код начинает вычитать значение shipSize , которое будет использоваться для масштабирования изображения при его рисовании; как только он достигнет нуля, процесс полностью изменится до полной шкалы.

Теперь мы можем перейти к функции draw() . Ниже всех других методов рисования добавьте следующий оператор if.

1
2
3
4
if(shipVisible == true){
    context.drawImage(shipImage, shipX[0] — (shipSize/2), shipY[0], shipSize, shipHeight);
    context.drawImage(shipImage, shipX[1] — (shipSize/2), shipY[1], shipSize, shipHeight);
}

Корабли тянутся нормально, за исключением того, что X-позиции компенсируются вычитанием половины текущей шкалы.


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

1
2
var fadeId = 0;
canvas.addEventListener(«mouseup», checkClick);

Создайте функцию checkClick ().

1
2
3
4
5
6
7
8
9
function checkClick(mouseEvent){
    for(i = 0; i < buttonX.length; i++){
        if(mouseX > buttonX[i] && mouseX < buttonX[i] + buttonWidth[i]){
            if(mouseY > buttonY[i] && mouseY < buttonY[i] + buttonHeight[i]){
                 
            }
        }
    }
}

Как и прежде, мы проверяем правильность положения мыши. Теперь нам нужно создать новый интервал и остановить другой интервал и прослушиватели событий.

1
2
3
4
fadeId = setInterval(«fadeOut()», 1000/frames);
clearInterval(timerId);
canvas.removeEventListener(«mousemove», checkPos);
canvas.removeEventListener(«mouseup», checkClick);

Здесь нет ничего нового, кроме того, что нам нужно создать функцию с именем fadeOut() . Нам также нужно создать еще одну переменную под названием time .

1
var time = 0.0;
01
02
03
04
05
06
07
08
09
10
11
12
function fadeOut(){
    context.fillStyle = «rgba(0,0,0, 0.2)»;
    context.fillRect (0, 0, width, height);
    time += 0.1;
    if(time >= 2){
        clearInterval(fadeId);
        time = 0;
        timerId = setInterval(«update()», 1000/frames);
        canvas.addEventListener(«mousemove», checkPos);
        canvas.addEventListener(«mouseup», checkClick);
    }
}

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

Переменное time увеличивается каждый раз, когда вызывается функция, и как только она достигает определенного значения (в данном случае через 20 «кадров»), мы очищаем текущий интервал. Здесь я перезагружаю меню, но здесь вы бы нарисовали новый раздел меню.

Последнее, на что следует обратить внимание, это то, что при рисовании фигур на холсте для fillStyle устанавливается значение rgb (красный, зеленый, синий). Когда вы хотите нарисовать прозрачные фигуры, вы используете rgba (красный, зеленый, синий, альфа).

Я надеюсь, что это демистифицировало немного процесса обучения для перехода от простого программирования AS3 к простому программированию холста. Оставьте комментарий, если у вас есть какие-либо вопросы!