Статьи

Работа на уровне пикселей с BitmapData и Away3D

Добро пожаловать в это введение в работу на уровне пикселей с объектом ActionScript 3 BitmapData. Мы возьмем несколько 2D-изображений, разбим их на составляющие их пиксели, а затем соберем их в виде 3D-изображений, которые мы можем перемещать и вращать.


Давайте посмотрим на конечный результат, к которому мы будем стремиться:


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

Внутри этой папки вы найдете две другие папки; SRC и бен . В папке src мы будем сохранять весь наш код и FLA-файлы, а в папке bin — Flash, где будут сохраняться SWF-файлы. Внутри папки src находятся классы Main.FLA и Main.AS.

Если по какой-либо причине вы обнаружите ошибку в своем коде, попытайтесь ее исправить (всегда полезно учиться на ошибках), но если вы не можете, то не беспокойтесь! Вы можете сразу перейти обратно и использовать одну из папок шагов в исходном zip-файле, ближайшую к тому шагу, на котором вы были.


Если вы уже Main.as файл Main.as вы уже заметили несколько ссылок на Away3D, 3D-фреймворк для Flash. Нам нужно скачать это и добавить его в наш проект, чтобы продолжить.

Вы можете получить их последнюю версию с сайта Away3D .

После завершения загрузки откройте zip-файл, и внутри папки away3d_3_6_0 \ src вы найдете три папки away3d , nochump и wumedia . Скопируйте их, как показано ниже, в папку src .

Копирование Away3D в вашу папку src

Если вы еще этого не сделали, откройте Main.fla и Main.as Глядя в библиотеку Flash, вы можете увидеть изображение с именем img1 и MovieClip с именем экземпляра img1 , которое будет служить основным контейнером для png.

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


Main.as файл Main.as мы увидим несколько переменных, которые используются в Away3D, на Away3D уже есть множество учебных пособий, но мы быстро повторим их:

1
2
3
4
// basic Away3D properties
protected var scene:Scene3D;
protected var camera:TargetCamera3D;
protected var view:View3D;
  • Scene3D — это пространство, которое мы можем использовать для добавления трехмерных объектов, таких как кубы и сферы.
  • TargetCamera3D — это один из многих типов камер, доступных в Away3D, и именно его мы используем, чтобы взглянуть на Scene3D.
  • View3D — это окно просмотра, часто описываемое как «окно», в котором мы видим нашу сцену.

Не вдаваясь в подробности, вы также можете увидеть, что базовая сцена настроена для использования с initAway3d() . Обратите внимание, что он добавляет ENTER_FRAME EventListener , это просто говорит Away3D render() (или рисовать) любые объекты, добавленные в Scene3D каждый кадр.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
/**
 * Away3D basic scene setup
 */
private function initAway3d():void
{
    scene = new Scene3D();
    camera = new TargetCamera3D({z: -200});
    view = new View3D({scene:scene, camera:camera});
    addChild(view);
     
    addEventListener(Event.ENTER_FRAME, renderLoop);
}
 
/**
 * the render loop
 */
private function renderLoop(event:Event):void
{
    view.render();
}

Вот и все для ознакомления с классом Main.as , мы будем строить все остальное по ходу дела.


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

Давайте проверим это, добавив экземпляр img1 MovieClip на stage и сделав его копию, используя Bitmap/BitmapData .

Изменить Main.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
/**
 * constructor
 */
public function Main()
{
    initAway3d();
    drawExample();
}
 
/**
 * a quick example of BitmapData and Bitmap usage
 */
private function drawExample():void
{
    // create an instance of the img1 object on the stage to copy
    var img:MovieClip = new img1();
    addChild(img);
     
    // create a BitmapData object with the following parameters: width, height, transparent, color
    var bmpData:BitmapData = new BitmapData(img.width, img.height, true, 0x000000);
     
    // draws a copy of the img MovieClip in to the BitmapData
    bmpData.draw(img);
     
    // adds a Bitmap to the stage with the BitmapData (copy of the img1) information to display
    var bmp:Bitmap = new Bitmap(bmpData);
    bmp.y = img.height;
    addChild(bmp);
}

Глядя на код drawExample() , первые две строки просто добавляют объект img1 на stage , это изображение, которое мы сделаем из копии.

После этого мы создаем объект BitmapData со следующими параметрами:

  • width , ширина, чтобы сделать BitmapData
  • height , высота, чтобы сделать BitmapData
  • transparent , должен ли BitmapData содержать прозрачные пиксели
  • color , цвет фона

Поскольку мы знаем ширину и высоту из img1 мы установили их напрямую, так как нам понадобится прозрачность, мы устанавливаем следующий параметр в значение true и, наконец, мы указываем 0x000000 или черный в качестве цвета фона, так как он будет выглядеть прозрачным, пока мы не заполним Это.


Продолжая, теперь у нас есть настроенный объект BitmapData , у нас есть несколько доступных нам опций, мы могли бы, например, перебирать пиксель за пикселем и копировать изображение (мы будем использовать что-то подобное позже в уроке), или мы могли бы используйте метод draw() .

Метод draw() принимает MovieClip или Sprite в качестве параметра и копирует всю информацию о пикселях из объекта в BitmapData .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
/**
 * a quick example of BitmapData and Bitmap usage
 */
private function drawExample():void
{
    // create an instance of the img1 object on the stage to copy
    var img:MovieClip = new img1();
    addChild(img);
     
    // create a BitmapData object with the following parameters: width, height, transparent, color
    var bmpData:BitmapData = new BitmapData(img.width, img.height, true, 0x000000);
     
    // draws a copy of the img MovieClip in to the BitmapData
    bmpData.draw(img);
     
    // adds a Bitmap to the stage with the BitmapData (copy of the img1) information to display
    var bmp:Bitmap = new Bitmap(bmpData);
    bmp.y = img.height;
    addChild(bmp);
}

После этого следующие несколько строк создают объект Bitmap с информацией о пикселях BitmapData в качестве параметра, который затем перемещается ниже исходного img MovieClip и добавляется на stage .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
/**
 * a quick example of BitmapData and Bitmap usage
 */
private function drawExample():void
{
    // create an instance of the img1 object on the stage to copy
    var img:MovieClip = new img1();
    addChild(img);
     
    // create a BitmapData object with the following parameters: width, height, transparent, color
    var bmpData:BitmapData = new BitmapData(img.width, img.height, true, 0x000000);
     
    // draws a copy of the img MovieClip in to the BitmapData
    bmpData.draw(img);
     
    // adds a Bitmap to the stage with the BitmapData (copy of the img1) information to display
    var bmp:Bitmap = new Bitmap(bmpData);
    bmp.y = img.height;
    addChild(bmp);
}

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

BitmapData draw

Теперь у нас есть содержимое внутри объекта BitmapData вещи начинают становиться интересными, поскольку мы можем начать манипулировать изображениями с помощью getPixel32() и setPixel32() .

Начиная с getPixel32() drawExample() код drawExample() сверху на следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * a quick example of BitmapData and Bitmap usage
 */
private function drawExample():void
{
    // create an instance of the img1 object on the stage to copy
    var img:MovieClip = new img1();
    addChild(img);
     
    // create a BitmapData object with the following parameters: width, height, transparent, color
    var bmpData:BitmapData = new BitmapData(img.width, img.height, true, 0x000000);
     
    // draws a copy of the img MovieClip in to the BitmapData
    bmpData.draw(img);
     
    // adds a Bitmap to the stage with the BitmapData (copy of the img1) information to display
    var bmp:Bitmap = new Bitmap(bmpData);
    bmp.y = img.height;
    addChild(bmp);
     
    // read pixel information from the BitmapData
    var pixelInformation:uint = bmpData.getPixel32(5, 0);
    trace(pixelInformation, pixelInformation.toString(16));
}

Изучив код, мы создали обычную переменную uint и присвоили ей значение пикселя в bmpData по 5 пикселей по горизонтали и 0 пикселей по вертикали. Помните, что значения начинаются с 0 так:

Пиксельные координаты

Зная, что мы решили получить информацию о пикселях для 5,0, это сделало бы его черным пикселем в верхней строке и достаточно уверенным выходным 4278190080 ff000000 Flash: 4278190080 ff000000

setPixel32 это может показаться неправильным, но setPixel32 считывает альфа-значение пикселя (тогда как setPixel просто читает цвет). Обычно мы работаем с шестнадцатеричными значениями для цветов, таких как FFFFFF или 000000 поэтому мы можем сказать Flash toString(16) чтобы получить шестнадцатеричное значение:

getPixel32 и шестнадцатеричные значения

Теперь мы знаем, как считывать информацию о пикселях, рисование пикселей в BitmapData очень похоже, только на этот раз мы используем setPixel32() для рисования пикселей в BitmapData, и мы также добавим цикл for для рисования некоторых пикселей.

Сначала измените код на следующее:

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
/**
 * a quick example of BitmapData and Bitmap usage
 */
private function drawExample():void
{
    // create an instance of the img1 object on the stage to copy
    var img:MovieClip = new img1();
    addChild(img);
     
    // create a BitmapData object with the following parameters: width, height, transparent, color
    var bmpData:BitmapData = new BitmapData(img.width, img.height, true, 0x000000);
     
    // draws a copy of the img MovieClip in to the BitmapData
    bmpData.draw(img);
     
    // adds a Bitmap to the stage with the BitmapData (copy of the img1) information to display
    var bmp:Bitmap = new Bitmap(bmpData);
    bmp.y = img.height;
    addChild(bmp);
     
    // read pixel information from the BitmapData
    var pixelInformation:uint = bmpData.getPixel32(5, 0);
    trace(pixelInformation, pixelInformation.toString(16));
     
    // write pixel information to the BitmapData
    var color:uint = 0xffff0000;
    var row:uint = 0;
    var column:uint = 0;
    for(row; row < bmpData.height; row++)
    {
        bmpData.setPixel32(column, row, color);
         
        column++;
        if(column > bmpData.width)
        {
            column = 0;
        }
    }
}

Новый код начинается с создания обычной переменной uint именем color которой мы храним 0xffff0000 : ff полностью прозрачный, ff полностью красный, 00 нет зеленого, 00 нет синего.

Затем создаются два счетчика для строк и столбцов (строки — это линия горизонтальных пикселей, столбцы — это линия вертикальных пикселей). Затем эти счетчики помещаются в цикл for который каждый раз увеличивает значение строки и счетчика, поэтому при смешивании с setPixel32() он будет рисовать диагональную линию:

setPixel32 на работе

На этом шаге мы собираемся представить класс PixelObject3D.as . Чтобы сэкономить немного времени, возьмите копию класса из папки Step 8 в исходном zip- Main.fla и Main.as ее в папку src кроме Main.fla и Main.as

Как только вы это сделаете, давайте взглянем на это, прежде чем мы начнем добавлять код для создания 3D-объектов из пикселей.

1
2
3
4
5
// properties
protected var _bitmapData:BitmapData = null;
public var _scaleFactor:Number = 1;
protected var _width:Number = 0;
protected var _height:Number = 0;

У нас есть несколько защищенных переменных в верхней части класса, одна для BitmapData и три Numbers для ширины, высоты и масштаба объекта.

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * constructor
 */
public function PixelObject3D() {}
     
 
/**
 * begins the creation process
 */
public function createFromMovieClip(mc:MovieClip):void
{
     
}

За ними следует пустой конструктор класса и метод, с которым мы будем работать, createFromMovieClip() . Вы заметите, что этот метод принимает параметр типа MovieClip , так что вы уже можете догадаться, что мы передадим ему MovieClip и он вернет нам его трехмерное представление. Когда это закончено, то есть!


Хотя класс PixelObject3D.as самом деле ничего не делает, давайте добавим его экземпляр в класс Main.as чтобы мы могли видеть изменения на экране по ходу.

Начиная с добавления приватной переменной:

1
2
3
4
5
6
7
// basic Away3D properties
protected var scene:Scene3D;
protected var camera:TargetCamera3D;
protected var view:View3D;
 
// the Pixel3DObject
protected var po3d:PixelObject3D;

После этого добавьте в конструктор вызов createPixelObect3D() .

1
2
3
4
5
6
7
8
9
/**
 * constructor
 */
public function Main()
{
    initAway3d();
    drawExample();
    createPixelObect3D();
}

Наконец добавьте следующую функцию в файл Main.as Это создаст экземпляр класса PixelObject3D , вызовет метод createFromMovieClip() и передаст ему новый MovieClip , img1 мы использовали ранее.

Последняя строка, на которую следует обратить внимание: мы добавляем класс PixelObject3D качестве дочернего элемента сцены, поскольку это 3D-объект, а не Stage .

01
02
03
04
05
06
07
08
09
10
/**
 * creates a PixelObject3D
 */
private function createPixelObect3D():void
{
    po3d = new PixelObject3D();
    po3d.createFromMovieClip(new img1());
     
    scene.addChild(po3d);
}

Зная, что нам передали MovieClip мы хотим воссоздать с помощью этого метода, первое, что стоит в нашей повестке дня, — это сделать его копию с использованием BitmapData точно так же, как мы делали раньше. Затем мы можем использовать данные пикселей, чтобы начать создавать трехмерные объекты.

Как и прежде, мы собираемся создать объект BitmapData и нарисовать объект mc MovieClip :

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * begins the creation process
 */
public function createFromMovieClip(mc:MovieClip):void
{
    // store references and create the bitmapdata
    _bitmapData = new BitmapData(mc.width, mc.height, true, 0x000000);
    _bitmapData.draw(mc);
     
    // set width / height
    _width = mc.width * (2 * _scaleFactor);
    _height = mc.height * (2 * _scaleFactor);
}

Мы также устанавливаем переменные _width и _height соответствии с шириной и высотой mc и умножаем это на переменную _scaleFactor , что позволяет нам увеличивать или уменьшать размер трехмерных пикселей, если мы хотим. Подробнее об этом позже.


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

createFromMovieClip() ваш createFromMovieClip() чтобы он соответствовал этому:

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
/**
 * begins the creation process
 */
public function createFromMovieClip(mc:MovieClip):void
{
    // store references and create the bitmapdata
    _bitmapData = new BitmapData(mc.width, mc.height, true, 0x000000);
    _bitmapData.draw(mc);
     
    // set width / height
    _width = mc.width * (2 * _scaleFactor);
    _height = mc.height * (2 * _scaleFactor);
     
    // pixel information
    var pixelValue:uint = 0;
    var red:uint = 0;
    var green:uint = 0;
    var blue:uint = 0;
    var alpha:uint = 0;
     
    // loop through each pixel horizontally
    for (var i:int = 0; i < mc.width; i++)
    {
        pixelValue = _bitmapData.getPixel32(i, 0);
        alpha = pixelValue >> 24 & 0xFF;
        red = pixelValue >> 16 & 0xFF;
        green = pixelValue >> 8 & 0xFF;
        blue = pixelValue & 0xFF;
         
        trace(«alpha:» + alpha + » red:» + red + » green:» + green + » blue:» + blue);
    }
}

Здесь мы установили несколько переменных для значений цвета и альфа, затем запустили цикл for на основе ширины mc's .

Этот цикл for устанавливает переменную pixelValue в значение текущего пикселя, используя метод getPixel32() который мы использовали ранее, но на этот раз заметим, что мы использовали 0 для второго параметра, который является y , поэтому мы собираемся обрабатывать только первая горизонтальная линия пикселей.

После этого есть некоторая довольно сложная математика, известная как битовая маскировка и сдвиг, чтобы сэкономить немного времени, вы можете предположить, что каждый из цветов извлекается из переменной pixelValue а затем выводить ее для просмотра с помощью trace() . Если вы хотите больше узнать о побитовых операторах, сдвиговых битах и ​​маскировании, вы можете найти отличный пост на веб-сайте Polygonal Labs .

То, что вы должны увидеть, это вывод целой связки значений 0 но обратите внимание на две строки alpha:255 , это два черных пикселя вверху руки.

разделение значений цвета

Фу, в этих последних нескольких шагах было довольно много логики! Теперь у нас есть все основы, давайте начнем использовать информацию о пикселях, которую мы получили ранее, чтобы создать 3D-шедевр… почти.

Если вы уже использовали Away3D или Papervision 3D до того, как освоите этот шаг, мы начнем создавать 3D-кубы и применять к ним материалы. Для каждого пикселя, который имеет альфа-значение 255 (непрозрачный), мы берем его цвет и создаем материал на основе цвета для применения к 3D-кубу. Ниже приведен код, который запускает это:

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
/**
 * begins the creation process
 */
public function createFromMovieClip(mc:MovieClip):void
{
    // store references and create the bitmapdata
    _bitmapData = new BitmapData(mc.width, mc.height, true, 0x000000);
    _bitmapData.draw(mc);
     
    // set width / height
    _width = mc.width * (2 * _scaleFactor);
    _height = mc.height * (2 * _scaleFactor);
     
    // pixel information
    var pixelValue:uint = 0;
    var red:uint = 0;
    var green:uint = 0;
    var blue:uint = 0;
    var alpha:uint = 0;
     
    // loop through each pixel horizontally
    for (var i:int = 0; i < mc.width; i++)
    {
        pixelValue = _bitmapData.getPixel32(i, 0);
        alpha = pixelValue >> 24 & 0xFF;
        red = pixelValue >> 16 & 0xFF;
        green = pixelValue >> 8 & 0xFF;
        blue = pixelValue & 0xFF;
         
        // if pixel is opaque
        if(alpha == 255)
        {
            // create a regular hex color string ie FFFFFF or 000000
            var color:String = red.toString(16) + green.toString(16) + blue.toString(16);
            if(color == «000») color = «000000»;
            trace(«#» + color);
             
            // create a material from the color and apply to a 3D cube
            var material:Material = new ColorMaterial(color);
            var cube:Cube = new Cube({material:material, width:2 * _scaleFactor, height:2 * _scaleFactor, depth:2 * _scaleFactor});
             
            // position the cube from a — value so registration/transformation point is always center
            cube.x = 0 — (_width/2) + cube.width * i;
             
            this.addChild(cube);
        }
    }
}

В приведенном выше коде мы использовали red , green и blue переменные и создали обычный шестнадцатеричный цвет, который можно увидеть в результате выполнения trace() .

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

После этого мы создаем объект Cube и указываем material который будет material объектом, мы создали линию перед ним. Также стоит отметить, что мы установили width , height и depth (помните, что мы сейчас работаем в трех измерениях!) В значение, в два раза _scaleValue переменной _scaleValue , это позволяет нам _scaleValue кубы путем изменение _scaleValue .

Наконец, мы помещаем Cube в ноль минус половину ширины mc умноженную for счетчик циклов i for i , это делает точку регистрации или преобразования готового трехмерного объекта в центре. Затем он добавляется как ребенок, и при тестировании вы увидите два маленьких черных 3D- Cube .


Теперь два 3D-куба великолепны, и все, но мы действительно хотим, чтобы вся фигура руки была в 3D-кубах. Мы уже используем цикл for чтобы перебрать все пиксели в первой строке, но как нам получить его, чтобы перебрать оставшиеся строки пикселей?

Вы догадались, еще один цикл!

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
/**
 * begins the creation process
 */
public function createFromMovieClip(mc:MovieClip):void
{
    // store references and create the bitmapdata
    _bitmapData = new BitmapData(mc.width, mc.height, true, 0x000000);
    _bitmapData.draw(mc);
     
    // set width / height
    _width = mc.width * (2 * _scaleFactor);
    _height = mc.height * (2 * _scaleFactor);
     
    // pixel information
    var pixelValue:uint = 0;
    var red:uint = 0;
    var green:uint = 0;
    var blue:uint = 0;
    var alpha:uint = 0;
     
    // loop through each row of pixels
    for (var j:int = 0; j < mc.height; j++)
    {
        // loop through each pixel horizontally
        for (var i:int = 0; i < mc.width; i++)
        {
            pixelValue = _bitmapData.getPixel32(i, j);
            alpha = pixelValue >> 24 & 0xFF;
            red = pixelValue >> 16 & 0xFF;
            green = pixelValue >> 8 & 0xFF;
            blue = pixelValue & 0xFF;
             
            // if pixel is opaque
            if(alpha == 255)
            {
                // create a regular hex color string ie FFFFFF or 000000
                var color:String = red.toString(16) + green.toString(16) + blue.toString(16);
                if(color == «000») color = «000000»;
                trace(«#» + color);
                 
                // create a material from the color and apply to a 3D cube
                var material:Material = new ColorMaterial(color);
                var cube:Cube = new Cube({material:material, width:2 * _scaleFactor, height:2 * _scaleFactor, depth:2 * _scaleFactor});
                 
                // position the cube from a — value so registration/transformation point is always center
                cube.x = 0 — (_width/2) + cube.width * i;
                cube.y = (_height/2) + -cube.height * j;
                 
                this.addChild(cube);
            }
        }
    }
}

На этот раз мы действительно изменили только три вещи, новый цикл for который на этот раз имеет j в качестве счетчика. В getPixel32() теперь добавлена ​​переменная j в качестве параметра y и, наконец, Cube расположен вертикально с помощью счетчика j .

Это в значительной степени завершает основную логику, теперь она будет проходить по горизонтали, считывать значения пикселей, создавать ColorMaterial и Cube и позиционировать их соответственно. Как только он достигает конца горизонтальной линии, из-за нового цикла for он переходит к следующему пикселю вниз и снова проходит по горизонтали, пока изображение не будет завершено. Посмотрите сами, протестировав фильм:

Циклы для работы отлично

Теперь у нас есть все эти 3D объекты, но они выглядят очень 2D, поэтому мы собираемся добавить немного движения и заставить весь объект вращаться.

Для этого нам нужно вернуться к файлу Main.as и найти метод renderLoop() . Помните, что Away3D потребуется визуализировать (или рисовать) 3D-изображение в каждом кадре, поэтому мы можем добавить несколько простых вращений в наш PixelObject3D чтобы увидеть, как вращаются все дочерние Cubes :

1
2
3
4
5
6
7
8
/**
 * the render loop
 */
private function renderLoop(event:Event):void
{
    pObject3D.rotationZ++;
    view.render();
}

Не стесняйтесь экспериментировать с rotationX , rotationY и rotationZ здесь, просто не забудьте сбросить его обратно в код выше, прежде чем продолжить. Вы также можете добавить в create3DObject() чтобы лучше центрировать и выровнять Cubes по камере.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
/**
 * creates a 3D pixel object from a MovieClip
 */
public function create3DObject():void
{
    pObject3D = new PixelObject3D();
    pObject3D.createFromMovieClip(new img1());
     
    pObject3D.x = 80;
    pObject3D.y = -55;
    pObject3D.rotationX = -5;
     
    scene.addChild(pObject3D);
}

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

Вернитесь к классу PixelObject3d.as и найдите строки, в которых мы PixelObject3d.as Cube x и y и добавьте следующее:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
// if pixel is opaque
if(alpha == 255)
{
    // create a regular hex color string ie FFFFFF or 000000
    var color:String = red.toString(16) + green.toString(16) + blue.toString(16);
    if(color == «000») color = «000000»;
    trace(«#» + color);
 
    // create a material from the color and apply to a 3D cube
    var material:Material = new ColorMaterial(color);
    var cube:Cube = new Cube({material:material, width:2 * _scaleFactor, height:2 * _scaleFactor, depth:2 * _scaleFactor});
     
    // position the cube from a — value so registration/transformation point is always center
    cube.x = 0 — (_width/2) + cube.width * i;
    cube.y = (_height/2) + -cube.height * j;
    cube.z = -25 + (Math.random() * 50);
     
    this.addChild(cube);
}

Это переместит каждый Cube на произвольную глубину от -25 до положительной 25 и создаст хороший эффект взрыва:


Так как PixelObject3D немного мал на экране, мы собираемся немного изменить масштаб. Мы можем сделать это быстро, _scaleValue переменную PixelObject3D.as классе PixelObject3D.as и увеличив ее до 1,5.

01
02
03
04
05
06
07
08
09
10
11
12
/**
 * creates a 3D object from a MovieClip
 *
 * @author Anton Mills
 */
public class PixelObject3D extends ObjectContainer3D
{
    // properties
    protected var _bitmapData:BitmapData = null;
    public var _scaleFactor:Number = 1.5;
    protected var _width:Number = 0;
    protected var _height:Number = 0;

Использовать класс PixelObject3D для создания других изображений легко, просто импортируйте изображение, которое вы хотите обработать, во Flash. Затем преобразуйте его в мувиклип, как обычно, на этот раз img2 ему имя класса img2 например:

Разные изображения

Теперь вы можете изменить Main.as чтобы использовать новый объект img2 с одним небольшим изменением:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
/**
 * creates a 3D pixel object from a MovieClip
 */
public function create3DObject():void
{
    pObject3D = new PixelObject3D();
    pObject3D.createFromMovieClip(new img2());
     
    pObject3D.x = 80;
    pObject3D.y = -55;
    pObject3D.rotationX = -5;
     
    scene.addChild(pObject3D);
}

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

Main.as с другим PixelObject3D добавил:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/**
 * A tutorial aimed at introducing ActionScript 3’s BitmapData
 * and how to use the BitmapData information to create a 3D
 * pixel shape using Away3D.
 *
 * @author Anton Mills
 */
public class Main extends MovieClip
{
    // basic Away3D properties
    protected var scene:Scene3D;
    protected var camera:TargetCamera3D;
    protected var view:View3D;
    protected var pObject3D:PixelObject3D;
    protected var pObject3D2:PixelObject3D;

Затем создайте еще один экземпляр:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * creates a 3D pixel object from a MovieClip
 */
public function create3DObject():void
{
    pObject3D = new PixelObject3D();
    pObject3D.createFromMovieClip(new img2());
     
    pObject3D.x = 40;
    pObject3D.y = -55;
    pObject3D.rotationX = -5;
     
    scene.addChild(pObject3D);
     
    pObject3D2 = new PixelObject3D();
    pObject3D2.createFromMovieClip(new img1());
     
    pObject3D2.x = 115;
    pObject3D2.y = -55;
    pObject3D2.rotationX = -5;
     
    scene.addChild(pObject3D2);
}

И, наконец, поверните его в цикле рендеринга Away3D:

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * the render loop
 */
private function renderLoop(event:Event):void
{
    pObject3D.rotationY++;
     
    pObject3D2.rotationY—;
    pObject3D2.rotationZ—;
    pObject3D2.rotationX++;
     
    view.render();
}

Осталось только протестировать фильм и насладиться великолепием 2D-пикселей, преобразованных в 3D-объекты. Теперь, что вы можете сделать с BitmapData в вашем следующем приложении или игре?


В этом руководстве мы рассмотрели различные элементы, но в первую очередь сосредоточились на использовании BitmapData например на рисовании MovieClips в BitmapData , используя setPixel32() для рисования отдельных пикселей, отображения BitmapData с использованием Bitmap и чтения значений пикселей с использованием getPixel32() .

Мы также рассмотрели некоторую цветовую математику, получив шестнадцатеричные цвета и даже отдельные значения альфа, красного, зеленого и синего, используя toString(16) . Наконец, мы написали небольшой цикл для создания 3D- Cubes используя значения пикселей, которые мы прочитали, фу!

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