Статьи

Создайте музыкальную поздравительную открытку с Papervision3D

Это первый день рождения ActiveTuts +! Давайте праздновать, создав музыкальную трехмерную открытку с использованием Papervision3D и TweenMax.


Начните с создания новой папки (это будет корневая папка вашего проекта) и создайте в этой папке новый FLA AS3, который называется Main.fla. Он будет содержать весь интерфейс, который нам нужен для создания карты.

Для этого урока нам понадобятся две популярные библиотеки Flash: Papervision3D и TweenMax. Загрузите Papervision (я использую версию 2.1.932) и извлеките папки com и nochump из zip- файла в корневую папку вашего проекта. Загрузите TweenMax (я использую версию 11) и распакуйте zip-файл в корневую папку вашего проекта.

Теперь создайте пустой файл AS; сохраните его как «CardViewport.as» в корневой папке вашего проекта. Это будет окно просмотра papervision3d, содержащее карту. Создайте еще один пустой файл AS в той же папке с именем «Main.as.» Это будет ваш класс документов. (Не знаете, как использовать класс документа? Прочтите это краткое введение .)

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


Откройте Main.fla и отредактируйте сцену, сделав ее 500×440 с частотой кадров 30 кадров в секунду. Установите класс документа в Main:


Нам нужны только два слоя для этого урока:

  1. Контейнер для карт, который будет заполнен нашим papervision
  2. Слой кнопок, который вызовет действие открытия / закрытия на карте

В слое области просмотра создайте прямоугольную форму. Увеличьте его до размера сцены и переместите в положение 0,0. Нажмите F8, чтобы преобразовать форму в символ MovieClip. Установите флажок для экспорта в ActionScript и добавьте имя класса CardViewport. Он подключится к классу, который мы создадим позже.

Теперь нажмите на мувиклип области просмотра и присвойте ему имя экземпляра «область просмотра».


Откройте символ клипа области просмотра и создайте любой фон, который вам нравится. Это то, что я сделал: пара градиентных фигур, одна для неба и одна для земли и логотип ActiveTuts + с центром в небе.


На кнопочном слое создайте прямоугольную форму. Преобразуйте его в символ клипа и назовите его «open_mc»; убедитесь, что вы даете ему то же имя экземпляра.

Внутри open_mc создайте текстовое поле. Установите его как динамический, присвойте ему имя экземпляра label_txt и внедрите шрифты, нажав «Внедрение символов» и выбрав алфавит, который вы хотите использовать.

Сделай так, как хочешь. Я построил мой с градиентом и поперечными диагональными линиями:

ПРИМЕЧАНИЕ: я не имею права распространять стандартный 07_66 , шрифт, который я использую в этом проекте. Простите за это.


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

Наши самолеты будут иметь размер 700×900, поэтому наши четыре изображения также будут такого размера. Причина этого размера в том, что самолеты будут плавать в трехмерном пространстве на некотором расстоянии от камеры; они должны быть большими, чтобы мы могли их видеть. Назовите изображения «page_1_front.jpg», «page_1_back.jpg», «page_2_front.jpg», «page_2_back.jpg» и поместите их в корневую папку вашего проекта.

Вот мои изображения:

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

Сделайте то же самое для звука, если хотите добавить его. Скотт Уиллс из audiojungle был достаточно любезен, чтобы собрать для нас этот фанк-трек .


Откройте CardViewport.as. Я буду использовать FlashDevelop для написания кода, но вы можете использовать Flash, если хотите.

Создайте пакет и импортируйте нужные нам классы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package
{
    //basic classes
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    //papervision classes
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.materials.BitmapFileMaterial;
    import org.papervision3d.view.BasicView;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.render.QuadrantRenderEngine;
    //tweening engine
    import com.greensock.TweenMax;

Установите приватные переменные, которые нам понадобятся для CardViewport:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class CardViewport extends BasicView //BasicView its a class that includes a basic viewport setup for our scene
{
    //Creates 4 planes
    private var front_cover:Plane;
    private var front_inside:Plane;
    private var back_cover:Plane;
    private var back_inside:Plane;
     
    //Creates 4 materials
    private var front_cover_img:BitmapFileMaterial;
    private var front_inside_img:BitmapFileMaterial;
    private var back_cover_img:BitmapFileMaterial;
    private var back_inside_img:BitmapFileMaterial;
     
    //Creates the front page container, that will nest 2 planes
    private var front_container:DisplayObject3D;
    //Creates the back page container, that will nest the other 2 planes
    private var back_container:DisplayObject3D;
    //Creates a card container that will nest the front container and the back container
    private var card_container:DisplayObject3D;
}

Вот функция, которая будет запускаться при создании CardViewport:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public function CardViewport ()
{
    super();
     
    //to get rid of a few errors about how z-depth is calculated in our scene
    //we need to change the render type to one that can correct it
    setRenderType();
     
    //Instantiates the materials and sets its properties
    setMaterials();
     
    //Instantiates the planes, adds the materials to the planes, adds the planes to the wrappers (containers)
    setPlanes();
     
    //renders the camera every frame
    stage.addEventListener(Event.ENTER_FRAME, render);
     
    //repositions the planes based on mouse’s position
    stage.addEventListener(MouseEvent.MOUSE_MOVE, positionPlanes);
}

Чтобы избавиться от нескольких ошибок о том, как z-глубина вычисляется в нашей сцене, нам нужно изменить тип рендеринга на тот, который можно исправить. QuadrantRenderEngine принимает один параметр: тип коррекции. На этот раз мы будем исправлять z-фильтр, используйте это с умом, поскольку он может перегружать ваш процессор в сложных ситуациях.

1
2
3
4
private function setRenderType()
{
    this.renderer = new QuadrantRenderEngine(QuadrantRenderEngine.CORRECT_Z_FILTER)
}

Несмотря на то, что мы не видим четвертый самолет (оборотная сторона карты), я все равно решил добавить его на тот случай, если вы захотите поиграть со сценой.
Мы будем использовать BitmapFileMaterial, установив «точное» в true, я улучшаю общее качество, но я также использую больше ресурсов процессора.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
private function setMaterials():void
{
        //Front Cover Page
        front_cover_img = new BitmapFileMaterial(«page_1_front.jpg»,true);
        front_cover_img.doubleSided = false;
        front_cover_img.fillAlpha = 1.0;
         
        //Front Inside Page
        front_inside_img = new BitmapFileMaterial(«page_1_back.jpg»,true);
        front_inside_img.doubleSided = false;
        front_inside_img.fillAlpha = 1.0;
         
        //Back Inside Page
        back_inside_img = new BitmapFileMaterial(«page_2_front.jpg»,true);
        back_inside_img.doubleSided = false;
        back_inside_img.fillAlpha = 1.0;
         
        //Back Cover Page
        back_cover_img = new BitmapFileMaterial(«page_2_back.jpg»);
        back_cover_img.doubleSided = false;
        back_cover_img.fillAlpha = 1.0;
    }

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

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 setPlanes():void
{
    //Creates the front Plane Cover
    front_cover = new Plane( front_cover_img, 700, 900, 3, 3 );
    front_cover.z = 0;
    front_cover.x = 350//this offset will be used later so we can open the card
     
    //Creates the inside of the front Plane
    front_inside = new Plane( front_inside_img, 700, 900, 3, 3 );
    front_inside.z = 0;
    front_inside.rotationY = 180;//rotated 180 degrees so we can see it from the inside
    front_inside.x = 350//its exactly half the width of the plane
     
    //Creates a wrapper object so we can rotate the front page from a different axis
    //that of the pages.
    //coordinates and we can just offset it.
    front_container = new DisplayObject3D();
    front_container.addChild(front_cover);
    front_container.addChild(front_inside);
    front_container.x-=350//here we are offsetting it
     
    //Repeat the process for the second page
    back_inside = new Plane( back_inside_img, 700, 900, 3, 3 );
    back_inside.z = 0.1;
    back_inside.x = 350
     
    back_cover = new Plane( back_cover_img, 700, 900, 3, 3 );
    back_cover.z = 0.1;
    back_cover.rotationY = 180;
    back_cover.x = 350
     
    back_container = new DisplayObject3D();
    back_container.addChild(back_inside);
    back_container.addChild(back_cover);
    back_container.x -= 350
     
     
    //And after everything add the front and back containers to the main wrapper and add the wrapper to the scene.
    card_container = new DisplayObject3D();
    card_container.addChild(front_container)
    card_container.addChild(back_container)
    scene.addChild( card_container );
}

Обратите внимание, что это открытая функция, поэтому мы можем получить к ней доступ через класс основного документа, когда нажмем кнопку:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public function openCard()
{
    //When we open the card we need to remove the mouse listeners from stage because
    //when we mouse move we rotate stuff, sometimes the containers sometimes the planes themselves,
    //therefore we cannot rotate both simultaneously as we will end up with animation errors
    stage.removeEventListener(MouseEvent.MOUSE_MOVE, positionPlanes);
    //we need to reset the planes’ positions inside the containers
    resetPlanesPosition();
    //we rotate the front container 180 degrees and set the x to 0,
    //because we offset the planes earlier it now rotates accordingly.
    TweenMax.to(front_container, 1.4, { rotationY:180, x:0,onComplete:function(){ stage.addEventListener(MouseEvent.MOUSE_MOVE, positionContainer); } } );
    TweenMax.to(back_container, 1.4, { x:0 } );
    //after the rotation we add a new listener to the mouse.
}

То же самое для закрытия карты:

01
02
03
04
05
06
07
08
09
10
11
public function closeCard()
{
    //when we close the card the steps are very similar to when we open.
    stage.removeEventListener(MouseEvent.MOUSE_MOVE, positionContainer);
    //difference is where we reset the main container’s rotation
    card_container.rotationY = card_container.rotationX = 0
    //we reset the front and back containers’ x and rotationY properties
    TweenMax.to(front_container, 1.4, { rotationY:0, x:-350, onComplete:function(){ stage.addEventListener(MouseEvent.MOUSE_MOVE, positionPlanes); } } );
    TweenMax.to(back_container, 1.4, { x: -350 } );
    //and after the animation we add the previous mouse move event action to position the planes
}

Это вызывается всякий раз, когда мышь перемещают, когда карта закрыта:

1
private function positionPlanes(event:Event):void { //it rotates between -25º and 25º depending on the mouse position front_cover.rotationY = back_inside.rotationY = (stage.mouseX / stage.stageWidth * 50)-25 front_cover.rotationX = front_inside.rotationX = back_cover.rotationX = back_inside.rotationX = (stage.mouseY / stage.stageHeight * 50) — 25 //we only need to add 180 to the faces that are supposed to be against us front_inside.rotationY = back_cover.rotationY = ((stage.mouseX / stage.stageWidth * 50) — 25) + 180 }

Это вызывается всякий раз, когда мышь перемещается, когда карта открыта:

1
2
3
4
5
6
private function positionContainer(event:Event):void
{
    //just like the planes.
    card_container.rotationY = (stage.mouseX / stage.stageWidth * 50)-25
    card_container.rotationX = (stage.mouseY / stage.stageHeight * 50) — 25
}

Это вызвано, когда карта нажата, чтобы открыть:

1
2
3
4
5
6
private function resetPlanesPosition()
{
    //the function name sais it all, it rotates the planes back to its original created position
    front_inside.rotationY = back_cover.rotationY = 180
    front_cover.rotationY = back_inside.rotationY = front_cover.rotationX = front_inside.rotationX = back_cover.rotationX = back_inside.rotationX =0
}

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

1
2
3
4
5
private function render(event:Event):void
{
    //singleRender is a BasicView function that activates the render in that given time.
    singleRender();
}

Это конец класса. Не забудьте добавить закрывающие фигурные скобки.


Мы ничего не изменим в классе CardViewport, поэтому не стесняйтесь получить исходный код из zip-файла и сравнить его с вашим собственным.


Этот шаг довольно прост, нам просто нужно связать открытую карту и закрыть действия карты с кнопкой. Перед этим давайте импортируем следующие классы в наш пакет:

1
2
3
4
5
6
7
package
{
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.media.Sound;
    import flash.net.URLRequest;

Опять же, ничего особенного, он расширяет MovieClip и создает объект Sound:

1
2
3
4
public class Main extends MovieClip
{
    private var birthday_song:Sound
}

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

1
2
3
public function Main():void {
        addEventListener(Event.ADDED_TO_STAGE,init)
}

… и эта функция будет запущена, когда класс документа будет добавлен на сцену:

1
2
3
4
5
6
7
8
private function init(event:Event):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init) //we don’t need this anymore
    open_mc.label_txt.text = «_!OPEN CARD!_» //Labels the button
    open_mc.buttonMode = true;
    open_mc.mouseChildren = false;
    open_mc.addEventListener(MouseEvent.CLICK, openCard) //Adds an event listener to the mouse click
}

Эта функция будет запущена при нажатии карты, если она закрыта:

1
2
3
4
5
6
7
8
9
private function openCard(event:MouseEvent):void
{
    open_mc.removeEventListener(MouseEvent.CLICK, openCard);
    viewport.openCard();
    open_mc.label_txt.text = «_!CLOSE CARD!_» // changes the label
    open_mc.addEventListener(MouseEvent.CLICK, closeCard) //adds a listener to close the card on mouse click
    birthday_song = new Sound(new URLRequest(«birthday.mp3»));
    birthday_song.play();
}

Это в основном то же самое, что и openCard (), но в обратном порядке. 😉

1
2
3
4
5
6
7
private function closeCard(event:MouseEvent):void
{
    open_mc.removeEventListener(MouseEvent.CLICK, closeCard)
    viewport.closeCard();
    open_mc.label_txt.text = «_!OPEN CARD!_»
    open_mc.addEventListener(MouseEvent.CLICK, openCard)
}

Законченный. Нет секретов в этом классе документов, это довольно просто. Не забудьте добавить закрывающие фигурные скобки.


Я хотел бы, чтобы вы развились из этого урока, взяв несколько небольших советов, которые я вам дал, от ADDED_TO_STAGE до оболочки displayObject3d, до использования класса анимации движения для вращения и перемещения трехмерного объекта. Возьми эту карту и сделай ее своей.

С Днем Рождения Activetuts +! Надеюсь, вам понравился этот урок, спасибо за чтение!