Статьи

Создайте свою собственную псевдо-игру в понг

Два раза в месяц мы возвращаемся к любимым постам наших читателей на протяжении всей истории Activetuts +. Это руководство было впервые опубликовано в мае 2009 года.

В этом выпуске я опишу, как создать базовую 3D-сцену с использованием встроенных 3D-параметров Flash Player 10. Затем я объясню, как добавить интерактивность элементам и настроить базовую игру в понг. Пошли..


Давайте настроим сцену и создадим трехмерную сцену. В обычной игре в понг мяч отскакивает от верха и низа экрана, но поскольку мы добавляем некоторую перспективу в микс, нам нужно будет также обеспечить эти границы. Нужны пять видеороликов: пол и четыре стены (две из которых на самом деле весла). Все точки регистрации стен должны быть расположены напротив пола. Вы можете создать все это просто с помощью инструмента прямоугольник или использовать импортированное растровое изображение (не забудьте включить «Разрешить сглаживание» в свойствах). Сделайте мувиклип из каждой из стен и дайте им имя экземпляра (я назвал их «wallTop», «wallBottom», «wallLeft» и «wallRight» и буду ссылаться на них позже). Назовите фон «пол».

Выберите все стены и пол, создайте один большой видеоклип из них и назовите его «bg».

Чтобы отобразить счет позже, нам также понадобятся два динамических текстовых поля. Если вы поместите их в видеоклип «bg», у них также будет трехмерная перспектива. Назовите их «ScoreLeft» и «ScoreRight», а затем вставьте шрифты.


Создайте шар, используя овальный и градиентный инструмент на сцене. Не помещайте мяч внутрь видеоклипа «bg», иначе он будет искажен перспективой. Мы не дадим мячу реальные 3D-свойства, но заставим его действовать так, как если бы он был 3D. Поместите мяч в центр экрана и дайте ему имя экземпляра, например, «мяч».


Теперь мы настроим трехмерную сцену, используя новые свойства вращения X, Y и Z, которые были добавлены в Flash Player 10. Однако прежде чем мы сможем выполнить некоторый код ActionScript, нам нужно создать класс документа . Создайте пустой файл Main.as и заполните его приведенным ниже кодом.

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() : void
        {
            // Replace this comment with code later
        }
    }
}

Теперь, чтобы использовать его в качестве основного класса, мы можем заполнить «Main» в поле «Class:» свойств документов.


Давайте попробуем вращение. Добавьте эту строку в функцию Main (), чтобы дать всему некоторое представление:

1
bg.rotationX = -30;

Как видите, все фрагменты ролика теперь повернуты на 30 ° вокруг оси x.

Каждая из стен нуждается в повороте на 90 °, чтобы встать. Как вы можете видеть, я также изменил режим смешивания стен на «экран», чтобы градиенты выглядели лучше. Следующий код предназначен только для предварительного просмотра, поскольку в следующем шаге мы заменим его на анимацию (поэтому удалите его после тестирования).

1
2
3
4
bg.wallTop.rotationX = 90;
bg.wallBottom.rotationX = -90;
bg.wallRight.rotationY = 90;
bg.wallLeft.rotationY = -90;

Чтобы добавить эффект к стенам, нам сначала нужно создать кнопку запуска. Создайте мувиклип с текстом «Нажмите здесь, чтобы начать». Опять же, вы можете поместить это в «bg», если хотите, чтобы оно тоже имело перспективу. Дайте ему имя экземпляра вроде «startText». Теперь мы можем ссылаться на него в коде и добавить к нему EventListener. Также установите для buttonMode значение true, которое будет возвращать курсор в виде руки, когда курсор мыши находится над кнопкой. При нажатии вызывается функция запуска, а кнопка скрыта. Мы также скрываем указатель мыши, потому что пользователь будет управлять правой стеной с помощью мыши.

01
02
03
04
05
06
07
08
09
10
11
12
13
public function Main() : void
{
    bg.rotationX = -30;
    startText.addEventListener( MouseEvent.MOUSE_UP, start );
    startText.buttonMode = true;
}
private function start( _e : Event ) : void
{
    startText.buttonMode = false;
    startText.visible = false;
    Mouse.hide();
    // This will contain the tweens
}

Мы можем использовать TweenLite для скручивания стен от поворота на 0 — 90 °. Конечно, подойдет любой анимационный двигатель, но я предпочитаю этот. Следующий код будет обрабатывать стены через 2 секунды и использовать функцию замедления «Bounce.easeOut». Чтобы изменить альфа-значение стен, нам нужно сначала установить его на 0. Вы можете сделать это во Flash, установив альфа на 0 в настройках цвета. После того, как эти анимации завершены, игра должна запуститься, поэтому добавьте свойство «onComplete» к одной из анимаций и добавьте к нему имя функции (вы можете оставить это для тестирования, так как функция еще не существует).

01
02
03
04
05
06
07
08
09
10
private function start( _e : Event ) : void
{
    startText.buttonMode = false;
    startText.visible = false;
    Mouse.hide();
    new TweenLite( bg.wallRight, 2, { rotationY: 90, alpha: 1, ease: Bounce.easeOut } );
    new TweenLite( bg.wallLeft, 2, { rotationY: -90, alpha: 1, ease: Bounce.easeOut } );
    new TweenLite( bg.wallTop, 2, { rotationX: 90, alpha: 1, ease: Bounce.easeOut } );
    new TweenLite( bg.wallBottom, 2, { rotationX: -90, alpha: 1, ease: Bounce.easeOut, onComplete: goBall } );
}

Прежде чем мы сможем начать игру, нам нужно добавить интерактивность к левой и правой стенам и мячу. Давайте начнем с весла игрока. Создайте новый класс «Player.as» и добавьте следующий код.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package
{
    import flash.display.MovieClip;
    import flash.events.MouseEvent;
     
    public class Player extends MovieClip
    {
        public function Player() : void
        {
            stage.addEventListener( MouseEvent.MOUSE_MOVE, moveAlong );
        }
         
        private function moveAlong( _e : MouseEvent ) : void
        {
            var mousePos : Number = stage.mouseY — parent.y;
            if ( mousePos < 0 )
                y = 0;
            else if ( mousePos > 340 )
                y = 340;
            else
                y = mousePos;
        }
    }
}

В конструкторе (функция Player) мы добавляем обработчик событий на сцену, чтобы проверить, когда мышь движется, и вызываем функцию «moveAlong» всякий раз, когда это происходит. В функции moveAlong мы вычисляем локальное положение мыши (только положение y, поскольку мы движемся только вертикально). Далее мы проверяем, выходит ли мышь за пределы и сбрасываем позицию, если это так. Я нашел значение 340 методом проб и ошибок, так как «parent.height — height» не возвращает ожидаемое значение, когда мы используем трехмерную перспективу.

Затем измените свойства мувиклипа wallRight; установите флажок «Экспорт для ActionScript» и установите для класса «Player». Оставьте «Базовый класс» пустым.


Создание ИИ очень похоже на создание игрока. Только на этот раз мы заставим его двигаться к значению y шара, но с немного меньшей скоростью, чем мяч. Создайте еще один класс «AI.as»:

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
package
{
    import flash.display.MovieClip;
    import flash.events.Event;
     
    public class AI extends MovieClip
    {
        public function AI() : void
        {
            addEventListener( Event.ENTER_FRAME, followBall );
        }
         
        private function followBall( _e : Event ) : void
        {
            var ball : MovieClip = MovieClip( parent.parent ).ball;
            if ( ball.xspeed || ball.yspeed )
            {
                var newy : Number = ball.y — height;
                if ( newy > 345 )
                    newy = 345;
                if ( y <= newy )
                    y += 9;
                else
                    y -= 9;
            }
        }
    }
     
}

Сначала мы должны иметь возможность ссылаться на мяч. Так как мувиклип wallLeft находится внутри мувиклипа «bg», вызов «parent» будет ссылаться на «bg». По этой причине нам нужно использовать «parent.parent». Первое утверждение «if» проверяет, имеет ли шар x или yspeed. Это публичные переменные шара, которые мы установим позже. Проверка препятствует перемещению ИИ до начала игры. Значение «newy» содержит значение y шара за вычетом высоты wallLeft. Это значение, к которому он должен двигаться, чтобы ударить по мячу. Как и раньше, измените класс wallLeft на «AI».


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

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
package
{
    import flash.display.MovieClip;
    import flash.events.Event;
     
    public class Ball extends MovieClip
    {
        public var xspeed : Number = 0;
        public var yspeed : Number = 0;
         
        public function Ball() : void { }
         
        public function start() : void
        {
            xspeed = -12;
            yspeed = 12;
            addEventListener( Event.ENTER_FRAME, moveBall );
        }
         
        private function moveBall( _e : Event ) : void
        {
            depth();
            collision();
            x += xspeed;
            y += yspeed;
        }
         
        private function depth() : void
        {
            // Scale the ball based on its y position
        }
         
        private function collision() : void
        {
            // Make the ball bounce
        }
    }
     
}

Сначала мы даем мячу х и скорость. Они выставлены на всеобщее обозрение, поэтому мы можем проверить по другим объектам, движется ли мяч Затем добавьте список событий onenterframe в функцию запуска. Когда вызывается функция запуска, шар начинает двигаться каждый кадр. Я дал мячу стартовую скорость 12 пикселей на кадр. Вы можете изменить это, чтобы ускорить игру, но вы также можете увеличить частоту кадров. Функция moveBall фактически увеличивает значения x и y шара, основываясь на значениях x и yspeed. Он также вызывает функции глубины и столкновения для каждого кадра.


Поскольку шар не является трехмерным объектом и не имеет значения z, он не будет выглядеть меньшим после изменения значения y. Чем выше шарик попадает на экран (низкое значение y), тем меньше он должен появиться. Мы можем изменить это, просто масштабируя его в зависимости от его позиции:

1
2
3
4
5
private function depth() : void
{
    var smaller : Number = (( y / stage.stageHeight ) * 0.6) + 0.6;
    scaleX = scaleY = smaller;
}

Функция «столкновения» решает, как и когда мяч будет отражаться от других объектов. В первом утверждении «если» мы просто проверяем значение y, чтобы узнать, попадает ли шар в верхнюю или нижнюю стенку. Я снова нашел эти значения методом проб и ошибок, потому что мяч должен отскочить в правдоподобной манере. Если проверка верна, это приводит к изменению скорости, поэтому шар меняет вертикальное направление.

1
2
3
4
5
6
private function collision() : void
{
    if ( y >= 463 || y <= 105 )
    {
        yspeed *= -1;
    }

Проверить границы х не так просто из-за перспективы и движущихся лопастей. Мы можем выполнить «hitTest» со стенами и отправить мяч обратно, если это правда. Хит-тесты немного тяжелые, поэтому лучше не злоупотреблять ими. Однако, поскольку мы имеем дело с простой игрой в понг, она не будет заметно тормозить игру. Я добавил дополнительную проверку, хотя; чтобы увидеть, находится ли значение x шара на левой или правой стороне сцены, и проверьте стенку соответствующим образом. Это гарантирует, что для каждого кадра необходим только один хит-тест вместо двух.

1
2
3
4
if( (x > stage.stageWidth / 2 && hitTestObject(MovieClip(parent).bg.wallRight)) || (x < stage.stageWidth / 2 && hitTestObject( MovieClip(parent).bg.wallLeft)) )
{
    xspeed *= -1;
}

Наконец, нам нужно выяснить, находится ли мяч на платформе. Мы можем выяснить, проверив, находится ли точка на дне шара все еще на «полу». Точная точка имеет значение x шара и значение y плюс радиус. Если это правда, мы выясняем, ушел ли мяч слева или справа (опять же, сравнивая значение x с центром экрана) и обновляя счет соответственно. В конце установите значения x и y мяча обратно в центр, чтобы игра могла продолжаться.

01
02
03
04
05
06
07
08
09
10
11
    if ( !MovieClip(parent).bg.floor.hitTestPoint( x, y + (height / 2 * scaleY), true) )
    {
        if ( x < stage.stageWidth / 2 )
            MovieClip(parent).bg.scoreRight.text = Number(MovieClip(parent).bg.scoreRight.text) + 1;
        else
            MovieClip(parent).bg.scoreLeft.text = Number(MovieClip(parent).bg.scoreLeft.text) + 1;
        y = stage.stageHeight / 2;
        x = stage.stageWidth / 2;
         
    }
}

Изменить класс мяча на «Мяч».


Теперь, когда мы создали и весла, и мяч, все, что нужно, — это запустить игру в действие. Добавьте эту функцию в класс Main и убедитесь, что onComplete, который мы использовали в шаге 6, ссылается на эту функцию.

1
2
3
4
private function goBall() : void
{
    ball.start();
}

После обновления счета мяч просто возвращается в центр и снова начинает катиться. Нам нужно немного оживить мяч при возвращении, прежде чем игра начнется снова.

Замените этот код в функции столкновения шара:

1
2
y = stage.stageHeight / 2;
x = stage.stageWidth / 2;

По:

1
2
3
4
5
6
xspeed = yspeed = 0;
removeEventListener( Event.ENTER_FRAME, moveBall );
y = -height;
x = stage.stageWidth / 2;
var scale : Number = (0.5 * 0.6) + 0.6;
new TweenLite( this, 1.5, { y: stage.stageHeight / 2, scaleX: scale, scaleY: scale, ease: Bounce.easeOut, onComplete: start } );

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


Я надеюсь, что вы смогли следовать без особых проблем. Игра, как она есть сейчас, очень проста и может использовать некоторые улучшения. Мяч, например, всегда отскакивает от углов 90 °, что делает движение очень предсказуемым.

Если вы хотите продвинуться дальше, вы можете попробовать изменить x и yspeed в зависимости от того, где мяч касается ракетки (сравните значения y). Обнаружение столкновения с веслами далеко не идеально, но показывает основной способ проверки на попадание. Наконец, если вы не использовали градиенты для стен, вы заметите, что шар, кажется, катится по нижней стенке, а не исчезает под ней. Чтобы это исправить, вы можете легко отделять объекты, которые должны появляться над мячом, и помещать их в новый видеоклип, который вы искажаете так же, как видеоклип «bg», и располагаете выше, чем шар во Flash.