Этот учебник предоставит вам альтернативу 3D для гоночных игр в ActionScript 3. Для этого примера стиля старой школы не требуется никакой внешней среды.
Окончательный результат предварительного просмотра
Давайте посмотрим на конечный результат, к которому мы будем стремиться:
Шаг 1: Настройте документ FLA
Создайте новый набор документов Flash для ActionScript 3.0. Я буду использовать размеры 480x320px, частоту кадров 30 кадров в секунду и светло-синий фон. Сохраните файл с именем по вашему выбору.
Шаг 2. Создание класса документа
Помимо FLA, нам также нужно создать класс документа . Создайте новый файл Actionscript и добавьте этот код:
01
02
03
04
05
06
07
08
09
10
11
12
|
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
}
}
}
|
Сохраните этот файл в том же каталоге, что и наш FLA. Назовите это Main.as.
Шаг 3: Свяжите основной класс с FLA
Чтобы скомпилировать код из класса Main , нам нужно связать его с FLA. На панели « Свойства» FLA рядом с « Класс» введите имя класса документа, в данном случае « Основной» .
Затем сохраните изменения в FLA.
Шаг 4: Нарисуйте линию дороги
Нам нужно начать с линии, представляющей один отрезок дороги. Нажмите R, чтобы выбрать инструмент Прямоугольник . В этом примере я собираюсь сделать серый прямоугольник для самой дороги, два маленьких красных прямоугольника на каждом краю серого и зеленые прямоугольники, чтобы заполнить остальную часть линии. Зеленые должны быть даже шире сцены, я делаю их шириной 1500 пикселей. Ширина дороги может варьироваться в зависимости от ваших потребностей, я буду использовать один из 245 пикселей в ширину. Они не обязательно должны быть очень высокими, так как мы будем использовать несколько экземпляров, чтобы нарисовать всю дорогу на экране. Я сделаю их высотой 10 пикселей.
Шаг 5: Создайте мувиклип для дорожных линий
После того как вы нарисовали все прямоугольники, выделите их все ( Ctrl + A ) и нажмите F8, чтобы создать мувиклип из тех прямоугольников, которые вы только что создали. Назовите его «Дорога», убедитесь, что точка регистрации находится в центре, и установите флажок «Экспорт для ActionScript».
В итоге вы получите дорожный видеоклип в библиотеке.
Вам решать, хотите ли вы нарисовать каждый прямоугольник на разных слоях. Я просто собираюсь нанести серый на второй слой. Если у вас есть какой-либо экземпляр Road на сцене, удалите его. Мы добавим Road MovieClip по коду позже.
Шаг 6: Настройте игровую зону
Вернемся к основному классу. Мы собираемся использовать этот Road MovieClip, чтобы создать иллюзию гоночной трассы.
Мы собираемся определить глубину видимой дороги, а также размеры игровой площадки. Кроме того, в нашем классе все экземпляры Road, которые мы добавляем на сцену, будут доступны из массива. Мы будем использовать другой массив ( zMap ) для определения глубины каждой линии.
В этом примере я установлю глубину 150 дорожных линий в игровой зоне 480×320 (необязательно иметь одинаковый размер сцены, но так как это все, что будет показано, я буду использовать эти цифры) ,
01
02
03
04
05
06
07
08
09
10
11
12
13
|
//Depth of the visible road
private const roadLines:int = 150;
//Dimensions of the play area.
private const resX:int = 480;
private const resY:int = 320;
//Line of the player’s car.
private const noScaleLine:int = 8;
//All the road lines will be accessed from an Array.
private var zMap:Array = [];
private var lines:Array = [];
private var halfWidth:Number;
private var lineDepth:int;
private const widthStep:Number = 1;
|
Шаг 7: Показать дорогу по коду
Мы будем использовать все предыдущие переменные и константы внутри функции Main . Мы будем масштабировать каждую строку в соответствии с их глубиной.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public function Main()
{
//Populate the zMap with the depth of the road lines
for (var i:int = 0; i < roadLines; i++)
{
zMap.push(1 / (i — resY / 2));
}
//We want the line at the bottom to be in front of the rest,
//so we’ll add every line at the same position, bottom first.
lineDepth = numChildren;
for (i = 0; i < roadLines; i++)
{
var line = new Road();
lines.push(line);
addChildAt(line, lineDepth);
line.x = resX / 2;
line.y = resY — i;
}
//Scaling the road lines according to their position
halfWidth = resX / 2;
for (i = 0; i < roadLines; i++)
{
lines[i].scaleX = halfWidth / 60 — 1.2;
halfWidth -= widthStep;
}
}
|
Если вы опубликуете (Ctrl + Enter) документ на этом этапе, вы увидите прямую дорогу.
Вы можете поиграть с масштабными расчетами, чтобы получить разные результаты. Вы могли бы хотеть более широкую дорогу или более длинное расстояние просмотра.
Шаг 8: Сделайте второй дорожный рисунок
Прямо сейчас дорога выглядит такой ровной, что вы не сможете определить, движемся ли мы вперед. Нам нужны как минимум два разных стиля сегмента, чтобы различать, насколько быстро или медленно мы движемся.
Перейдите на панель « Библиотека» и дважды щелкните дорожный видеоклип, чтобы вернуться к нарисованным прямоугольникам. Теперь нажмите F6, чтобы вставить новый ключевой кадр (если у вас более одного слоя, вы можете захотеть вставить новый ключевой кадр на каждый слой). Теперь, основываясь на первом кадре, вы можете изменить цвета прямоугольников или каким-то образом изменить их дизайн. Я поменяю их цвет и добавлю несколько полос на второй кадр.
Шаг 9: удержать линию игрока от масштабирования
Мы собираемся определить новую переменную в классе Main, чтобы сохранить последовательность на линии игрока (при условии, что в игре будет автомобиль, мы будем сохранять масштабирование до 1 на этой линии)
1
|
private var playerZ:Number;
|
Далее мы изменим функцию Main .
Шаг 10: добавь чередующиеся линии к дороге
Эта переменная будет использоваться в функции Main . Теперь дорожные линии будут сегментированы, некоторые будут отображать второй кадр, а остальные будут показывать первый кадр, усиливая иллюзию гоночной трассы.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public function Main()
{
for (var i:int = 0; i < roadLines; i++)
{
zMap.push(1 / (i — resY / 2));
}
playerZ = 100 / zMap[noScaleLine];
for (i = 0; i < roadLines; i++)
{
zMap[i] *= playerZ;
}
lineDepth = numChildren;
for (i = 0; i < roadLines; i++)
{
var line = new Road();
lines.push(line);
addChildAt(line, lineDepth);
line.x = resX / 2;
line.y = resY — i;
}
halfWidth = resX / 2;
for (i = 0; i < roadLines; i++)
{
if (zMap[i] % 100 > 50)
lines[i].gotoAndStop(1);
else
lines[i].gotoAndStop(2);
lines[i].scaleX = halfWidth / 60 — 1.2;
halfWidth -= widthStep;
}
}
|
Возможно, нет необходимости умножать на 100, чтобы получить сегменты правильно, но это числа, которые я буду использовать в этом примере, вы можете изменять числа по своему вкусу (и если вы что-то напортачили, у вас есть это как ссылка).
Шаг 11: Установите скорость и смещение
Давайте начнем заставлять вещи двигаться. Мы собираемся установить переменную для скорости. Это будет указывать глубину, которую мы будем продвигать по кадрам. Я собираюсь начать со скорости 20, вы можете использовать любое число, которое вы хотите.
Нам также нужен индикатор для сегментов дороги, который будет меняться в зависимости от скорости.
1
2
|
private var speed:int = 20;
private var texOffset:int = 100;
|
Шаг 12: начни двигаться вперед
Прежде чем мы сможем что-либо сделать с этими переменными, нам нужно импортировать новое событие в этот класс. Мы могли бы использовать либо таймер, либо EnterFrame. В этом примере я буду использовать событие EnterFrame.
1
|
import flash.events.Event;
|
Далее мы собираемся вырезать последнее условие в функции Main()
и переместить его в новую функцию, которую мы создаем. Эта новая функция будет вызвана событием EnterFrame, поэтому мы будем постоянно двигаться по дороге. Давайте назовем это race()
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public function Main()
{
for (var i:int = 0; i < roadLines; i++)
{
zMap.push(1 / (i — resY / 2));
}
playerZ = 100 / zMap[noScaleLine];
for (i = 0; i < roadLines; i++)
{
zMap[i] *= playerZ;
}
lineDepth = numChildren;
for (i = 0; i < roadLines; i++)
{
var line = new Road();
lines.push(line);
addChildAt(line, lineDepth);
line.x = resX / 2;
line.y = resY — i;
}
halfWidth = resX / 2;
for (i = 0; i < roadLines; i++)
{
lines[i].scaleX = halfWidth / 60 — 1.2;
halfWidth -= widthStep;
}
addEventListener(Event.ENTER_FRAME, race);
}
|
Шаг 13: Определите функцию гонки
Теперь давайте вернем условие, которое было обрезано, в новую функцию, чтобы мы получили движение. TexOffset будет указывать положение дороги, чтобы сохранить точную иллюзию движения.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
private function race(event:Event):void
{
for (var i:int = 0; i < roadLines; i++)
{
if ((zMap[i] + texOffset) % 100 > 50)
lines[i].gotoAndStop(1);
else
lines[i].gotoAndStop(2);
}
texOffset = texOffset + speed;
while (texOffset >= 100)
{
texOffset -= 100;
}
}
|
Если вы опубликуете это сейчас, вы должны получить анимированный путь.
Шаг 14: Управление
Постоянно прямые дороги скучны, и есть тысячи способов сделать перспективу только вперед. Теперь давайте добавим несколько новых переменных, чтобы позаботиться о кривых во время движения.
В этом примере я буду чередовать кривые справа с прямыми участками. Дальнейший путь будет сохранен в переменной nextStretch . Кроме того, мы будем перемещать положение х линий на кривых.
1
2
3
4
5
|
private var rx:Number;
private var dx:Number;
private var ddx:Number = 0.02;
private var segmentY:int = roadLines;
private var nextStretch = «Straight»;
|
Шаг 15: добавь кривые к дороге
Переменная rx будет хранить позицию x каждой строки, поэтому мы хотим, чтобы она начиналась с центра и брала кривые оттуда. Кроме того, ddx
контролирует четкость кривых. В этом примере у меня это будет 0,02; Вы могли бы хотеть изменить его значение между кривыми. Вот как будет выглядеть новая функция race()
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
private function race(event:Event):void
{
rx = resX / 2;
dx = 0;
for (var i:int = 0; i < roadLines; i++)
{
if ((zMap[i] + texOffset) % 100 > 50)
lines[i].gotoAndStop(1);
else
lines[i].gotoAndStop(2);
lines[i].x = rx;
if (nextStretch == «Straight»)
{
if (i >= segmentY)
dx += ddx;
else
dx -= ddx / 64;
}
else if (nextStretch == «Curved»)
{
if (i <= segmentY)
dx += ddx;
else
dx -= ddx / 64;
}
rx += dx;
}
texOffset = texOffset + speed;
while (texOffset >= 100)
{
texOffset -= 100;
}
segmentY -= 1;
while (segmentY < 0)
{
segmentY += roadLines;
if (nextStretch == «Curved»)
nextStretch = «Straight»;
else
nextStretch = «Curved»;
}
}
|
На этот раз мы не будем касаться основной функции. Если вы публикуете его сейчас, вы должны получить что-то вроде этого:
Возможно, вы захотите изменить значение Curve для Left и Right и изменить значения рулевого управления. К этому моменту вы уже сможете добавить автомобиль к сцене и контролировать скорость вручную.
Шаг 16: Холмы, склоны
Помните, прямоугольники для дороги имеют высоту более 1 пикселя? Это может помочь нам расширить обзор дороги, если мы хотим, чтобы в нашей игре были холмы.
Есть метод создания холмов, который очень похож на создание кривых. Там может быть много разных методов, но это тот, который я буду использовать здесь. Для простоты я буду перерабатывать столько кода, который у нас уже есть, и просто добавлю несколько строк для этого нового эффекта. Как обычно, если вам не нравятся результаты, вы можете изменить значения по своему желанию.
Мы только что создали переменные для x- позиций дорожных линий, теперь давайте сделаем таковые для y- позиций.
1
2
3
|
private var ry:Number;
private var dy:Number;
private var ddy:Number = 0.01;
|
Шаг 17: скоростной спуск, подъем
Для простоты в этом примере я собираюсь использовать одни и те же прямые сегменты как для прямолинейного эффекта с подъемом, так и кривых как для кривой, так и для наклонного эффекта.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
private function race(event:Event):void
{
rx = resX / 2;
ry = resY;
dx = 0;
dy = 0;
for (var i:int = 0; i < roadLines; i++)
{
if ((zMap[i] + texOffset) % 100 > 50)
lines[i].gotoAndStop(1);
else
lines[i].gotoAndStop(2);
lines[i].x = rx;
lines[i].y = ry;
if (nextStretch == «Straight»)
{
if (i >= segmentY)
{
dx += ddx;
dy -= ddy;
}
else
{
dx -= ddx / 64;
dy += ddy;
}
}
else if (nextStretch == «Curved»)
{
if (i <= segmentY)
{
dx += ddx;
dy -= ddy;
}
else
{
dx -= ddx / 64;
dy += ddy;
}
}
rx += dx;
ry += dy — 1;
}
texOffset = texOffset + speed;
while (texOffset >= 100)
{
texOffset -= 100;
}
segmentY -= 1;
while (segmentY < 0)
{
segmentY += roadLines;
if (nextStretch == «Curved»)
nextStretch = «Straight»;
else
nextStretch = «Curved»;
}
}
|
В вашей игре вы должны отделить кривые от холмов и создать два разных алгоритма, но этот пример показывает, насколько они могут быть похожими.
Шаг 18: Повысить эстетику дороги
Игры старой школы не могли использовать преимущества Flash, но мы можем. Что-то простое, например, добавление градиента к дорожным линиям, будет иметь большое значение. Если вы хотите, вы можете использовать любые фильтры и текстуры, которые вам нравятся, но в этом примере я просто добавляю несколько простых градиентов, поэтому давайте вернемся к Road MovieClip.
В кадре 1 выберите серый прямоугольник, затем перейдите на панель «Цвет» и выберите « Линейный градиент» в раскрывающемся меню, затем выберите « Отражать цвет как поток», чтобы градиент продолжался от первого до последнего цвета. Я не говорю вам выбирать те же цвета, что и я, но я буду использовать # 666666 и # 999999 здесь. Если вам нужно повернуть градиент, нажмите клавишу F, чтобы переключиться на инструмент «Преобразование градиента», который позволит вам перемещать, вращать и изменять размер градиента. В этом случае я перемещаю градиент на четверть прямоугольника и изменяю его размер до половины размера прямоугольника, чтобы центр был светлее, а края — темнее. Я использую аналогичный размер для зеленой части, поэтому он будет постоянно меняться от темно-зеленого (# 006600) до светло-зеленого (# 009900).
Теперь перейдите к кадру 2 и создайте новые градиенты с разными цветами. Для серого прямоугольника я сохранил более светлый цвет и изменил только более темный цвет на # 777777. В зеленой части я изменил размер градиента, чтобы попытаться избежать появления шахматной доски, и изменение цветов было очень тонким (# 007700 и # 008800).
Может быть, теперь вы захотите добавить хороший фон на горизонте или немного графики для неба.
Вывод
Если у вас мало ресурсов для 3D-фреймворков или вы просто хотите уйти в школу, теперь у вас есть простой пример того, как создать иллюзию глубины для гоночной игры. Теперь вам решать, будет ли это Гран-при по мотоциклам, или уличная гонка по шоссе, полному движения, или, может быть, что-то не связанное с гонками.
Надеюсь, вы нашли этот урок полезным. Спасибо за прочтение!