PDA

Просмотр полной версии : BitmapData.draw для вывода покадровой анимации


Juster
10.12.2009, 15:35
Здравствуйте!

Допустим, я пишу игру на Flex и у меня есть класс AnimatedObject, который не наследует от DisplayObject и не участвует в иерархии flash сцены. Грубо говоря, он должен выводить определенный кадр своей картинки в заданных координатах. Для этого я в менеджере объектов прохожу по всем объектам и вызываю onDraw, передавая в качестве аргумента backBuffer : BitmapData, в который каждый должен нарисовать себя.

Вот так метод был реализован:

var frame : Number;

public function onDraw(backBuffer : BitmapData) : void
{
var frameClipRect : Rectangle = new Rectangle(int(frame) * celWidth, 0, frameWidth, frameHeight);

var matrix : Matrix = new Matrix();
matrix.translate(-frameWidth / 2, -frameHeight / 2);
if ( scale )
matrix.scale(scale, scale);
if ( angle )
matrix.rotate(Math.PI * angle / 180.0);
matrix.translate(frameWidth / 2, frameHeight / 2);
matrix.translate(x, y);

backBuffer.draw(bitmap, matrix, null, null, frameClipRect, true);
}


Проблема в отсечении. В документации сказано, что clipRect задает область из источника, которая будет отрисована на целевой BitmapData. Если не задавать clipRect, то объект выводится, но со всеми кадрами. НО Если задавать clipRect как я это делаю, то ничего вообще не выводится, потому что.... clipRect в моем случае задает область отсечения для backBuffer!!!
Что происходит? :)

Спасибо большое!

Волгоградец
10.12.2009, 16:24
Для этой цели надо использовать copyPixels, т.к. clipRect обрезает изображение, но не двигает его. Т.е. если у вас конечная битмапа размером 100х100, а исходная 200х100 и вы к исходной применяете clipRect(100, 0, 100, 100), то ее видно не будет, т.к. она не попадает в размеры конечной битмапы.

Juster
10.12.2009, 16:41
Проблема с copyPixels в том, что она не позволяет применять трансформации поворота и масштабирования, которые в моем случае обязательны.

Волгоградец
10.12.2009, 16:46
Как это? Т.е. надо из исходной битмапы вырезать кусок, причем координаты этого куска ависят от скэйла и угла? Не могу представить для чего это нужно.

Juster
10.12.2009, 18:41
Представьте, что у меня есть картинка, в которую последовательно врисованы 30 кадров анимации. Мне нужно вывести определенный кадр из этой анимации, причем вывести в позиции (x, y), повернутый на угол rotation, увеличенный на scale коэффициент.

Волгоградец
11.12.2009, 11:16
Тогда я правильно говорил - copyPixels, затем экземпляру Bitmap задаете масштаб и поворот. Или создаете еще одну битмапДату, куда методом draw и с заданной матрицей рисуете скопированную методом copyPixels битмапДату.

cleptoman
11.12.2009, 14:10
Matrix спасет мир)

Wolsh
11.12.2009, 16:59
Набросал тут классик)
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Timer;
/**
* ...
* @author wolsh
*/
public class FilmViewer extends Bitmap
{
private var _film:BitmapData;
private var _frameRect:Rectangle;
private var _fps:uint;
private var _width:Number;
private var _height:Number;
private var _timer:Timer;
private var _loop:uint;
private var _loopCount:uint = 0;
private var _position:Number;
private var _frame:uint = 0;

public function FilmViewer(screenWidth:Number, screenHeight:Number)
{
_width = screenWidth;
_height = screenHeight;
bitmapData = new BitmapData(_width, _height);
super(bitmapData, "auto", true);
}
public function mount(film:BitmapData, cadrRect:Rectangle, fps:uint):void
{
_film = film;
_frameRect = cadrRect;
_fps = Math.round(1000/fps);
bitmapData.copyPixels(_film, _frameRect, new Point());
}
public function demount():void
{
_timer.stop();
_timer = null;
_film = null, _frameRect = null, _fps = 0;
bitmapData.dispose();
_frame = 0;
_loopCount = 0;
}
public function play(loop:uint = 1):void
{
_loop = loop;
_loopCount = 0;
_position = _frameRect.x;
_timer = new Timer(_fps);
_timer.addEventListener(TimerEvent.TIMER, viewNextFrame);
_timer.start();
}
public function stop():void
{
_timer.stop();
}
public function get currentFrame():uint
{
return _frame;
}
private function viewNextFrame(event:TimerEvent):void
{
if (_loopCount < _loop || _loop == 0)
{
var cadr:Rectangle = new Rectangle(_position, _frameRect.y, _frameRect.width, _frameRect.height);
bitmapData.copyPixels(_film, cadr, new Point());
_position += _frameRect.width;
_frame += 1;
if (_position > _film.width - _frameRect.width)
{
_position = _frameRect.x;
_loopCount += 1;
_frame = 0;
}
}
}

}
}
Создаете экземпляр FilmViewer, монтируете вашу битмапДату, задаете размер кадра и отступы (Rectangle), фпс, и вперед - плей. Количество лупов указываете обязательно, если бесконечно - 0.
пример
var projector:FilmViewer = new FilmViewer(122, 122);
addChild(projector);
projector.x = projector.y = 50;
var cadr:Rectangle = new Rectangle(0, 0, 122, 122);
var moveData:BitmapData = (new moveFilm() as Bitmap).bitmapData;
projector.mount(moveData, cadr, 12);
projector.rotation = 25;
projector.scaleX = projector.scaleY = 1.5;
projector.play(5);

Psycho Tiger
11.12.2009, 18:18
MovieClipFactory у etc кстати ещё есть.

Juster
11.12.2009, 18:38
Большое спасибо всем за ответы!

Wolsh, хотя ваш FilmViewer решает немного другие задачи и не показывает ту проблему, с которой я столкнулся, но вы мне подсказали кое-что интересное.

Вобщем, я решил свою задачу другим способом, но проблема все еще актуальна. Сведения в документации по параметру clipRect для метода BitmapData.draw не соответствуют действительности в моем случае. Пожалуйста, если кто-то может, сделайте пробный тест?

Wolsh
12.12.2009, 03:17
Juster, Вы не поняли. Все нормально в документации. Берется действительно ТОТ Ректангл из исходного изображения, который Вы указываете. Просто он отрисовывается в вашу конечную битмапДату в ТЕ ЖЕ координаты. А битмапДаты там у Вас уже нет, правда?)) У копиПиксел есть параметр - Пойнт, В КОТОРЫЙ отрисовывать этот Ректангл, а у метода draw такого параметра тупо нет. Отрисовка идет в те же координаты, что указаны в Ректангле.

Juster
14.12.2009, 15:13
Теперь я понимаю. Спасибо!
Значит мою задачу нельзя решить одним вызовом draw.

Wolsh
14.12.2009, 15:56
Ну попробуйте задать смещение на координату кадра по х в Матрице (назад, влево))).
Но все равно совершенно не понимаю, чем Вас копиПиксел не устраивает))

Juster
16.12.2009, 12:42
CopyPixels - не дает масштабирования и поворота :)
Смещение пробовал задавать, но это не помогло, все сходится к тому, что отсечение производится уже после трансформаций. Проблема решена, просто менее производительным способом, чем я ожидал.