Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Aloran

Оценить эту запись

AnimationManager - мой взгляд на замену MovieClip

Запись от Aloran размещена 21.03.2011 в 01:34

Вот я и решился на первый свой бложек =) Заранее прошу за некоторую косноязычность в написании - я исправлюсь со временем.
В своей работе я стараюсь как можно реже открывать Flash CSx... Ну не лежит у меня душа к сему творенью. По этой причине я всячески стараюсь все делать программно.
Одним из желаний было перевести анимацию на программные рельсы. И вот как я это делаю:

Класс AnimationManager
задачи:
- Получать на вход картинку в виде набора тайлов, "разрезать" ее на отдельные BitmapData и хранить их для последующего использования
- по запросу из вне возвращать AnimationBitmap (класс в отображающий анимацию)
- реагировать на события от AnimationBitmap, по сути управлять им.

Код AS3:
package al.managers {
import al.display.AnimationBitmap;
import al.event.AnimationEvent;
import al.utils.ObjectPool;
import flash.display.BitmapData;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Timer;
 
 
/**
 * ...
 * @author Stepan Ermilov
 */
public class AnimationManager
{
    private static const BASE_POINT:Point   = new Point();
 
    private var _frames:Object;         //Array of Bitmaps
    private var _bitmaps:Array;         //AnimationBitmap
    private var _timers:Array;
    private var _pool:ObjectPool;
 
    public function AnimationManager()
    {
        _frames     = { };
        _bitmaps    = [];
        _timers     = [];
        _pool       = new ObjectPool(new AnimationBitmap());
    }
 
    public function free():void
    {
        removeAllAnimationFrames();
        _frames     = null;
 
        removeAllAnimationBitmap();
        _bitmaps    = null;
        _timers     = null;
 
        _pool.free();
        _pool       = null;
    }
 
    public function addAnimationFrames(name:String, sourceBitmapData:BitmapData, animRect:Rectangle):Boolean
    {
        var result:Boolean;
        var frames:Array        = createFrames(sourceBitmapData, animRect);
 
        if (frames.length)
        {
            _frames[name]       = frames;
            result              = true;
        }
 
        return result;
    }
 
    public function removeAnimationFrames(name:String):void
    {
        _frames[name]       = null;
    }
 
    public function removeAllAnimationFrames():void
    {
        for (var name:String in _frames)
        {
            removeAnimationFrames(name);
        }
    }
 
    public function addAnimationBitmap(name:String, startIndex:int = 0, finishIndex:int = 0):AnimationBitmap
    {
        var bitmap:AnimationBitmap;
        if (_frames[name])
        {
            bitmap                  = _pool.getItem() as AnimationBitmap;
            bitmap.animationType    = name;
            bitmap.startIndex       = startIndex ? startIndex : 0;
            bitmap.finishIndex      = finishIndex ? finishIndex : (_frames[name] as Array).length - 1;
            bitmap.frameIndex       = bitmap.startIndex;
            bitmap.bitmapData       = (_frames[name] as Array)[bitmap.frameIndex] as BitmapData;
            addBitmapListeners(bitmap);
            _bitmaps.push(bitmap);
 
            var timer:Timer         = new Timer(0, 1);
            timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
            _timers.push(timer);
        }
 
        return bitmap;
    }
 
    public function removeAnimationBitmap(bitmap:AnimationBitmap):void
    {
        var index:int       = _bitmaps.indexOf(bitmap);
 
        if (index > -1)
        {
            removeBitmapListeners(bitmap);
            bitmap.clear();
            bitmap          = null;
 
            var timer:Timer = _timers[index] as Timer;
            timer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
            timer.stop();
            timer           = null;
        }
    }
 
    public function removeAllAnimationBitmap():void
    {
        for (var i:int = 0; i < _bitmaps.length; i++)
        {
            removeAnimationBitmap(_bitmaps[i] as AnimationBitmap);
        }
    }
 
    private function createFrames(sourceBitmapData:BitmapData, animRect:Rectangle):Array
    {
        var numCol:int              = int(sourceBitmapData.width / animRect.width);
        var numRow:int              = int(sourceBitmapData.height / animRect.height);
        var numFrames:int           = numCol * numRow;
 
        var result:Array            = new Array(numFrames);
 
        var index:int;
 
        if (numFrames)
        {
            var item:BitmapData;
            for (var i:int = 0; i < numRow; i++)
            {
                for (var j:int = 0; j < numCol; j++)
                {
                    animRect.x      = j * animRect.width;
                    animRect.y      = i * animRect.height;
 
                    item            = new BitmapData(animRect.width, animRect.height);
                    item.copyPixels(sourceBitmapData, animRect, BASE_POINT);
 
                    index           = j + i * numCol;
                    result[index]   = item;
                }
            }
        }
 
        return result;
    }
 
    private function addBitmapListeners(bitmap:AnimationBitmap):void
    {
        bitmap.addEventListener(AnimationEvent.ANIMATION_START,     onBitmapEvent);
        bitmap.addEventListener(AnimationEvent.ANIMATION_STOP,      onBitmapEvent);
        bitmap.addEventListener(AnimationEvent.ANIMATION_RESUME,    onBitmapEvent);
    }
 
    private function removeBitmapListeners(bitmap:AnimationBitmap):void
    {
        bitmap.removeEventListener(AnimationEvent.ANIMATION_START,  onBitmapEvent);
        bitmap.removeEventListener(AnimationEvent.ANIMATION_STOP,   onBitmapEvent);
        bitmap.removeEventListener(AnimationEvent.ANIMATION_RESUME, onBitmapEvent);
    }
 
    private function onBitmapEvent(event:AnimationEvent):void
    {
        var bitmap:AnimationBitmap  = event.target as AnimationBitmap;
        var index:int               = _bitmaps.indexOf(bitmap);
        var timer:Timer             = _timers[index] as Timer;
 
        switch (event.type)
        {
            case AnimationEvent.ANIMATION_START:
                bitmap.frameIndex   = bitmap.startIndex;
                bitmap.isPlayed     = true;
                setFrame(bitmap, timer);
                break;
            case AnimationEvent.ANIMATION_STOP:
                timer.stop();
                bitmap.isPlayed     = false;
                break;
            case AnimationEvent.ANIMATION_RESUME:
                timer.start();
                bitmap.isPlayed     = true;
                break;
        }
    }
 
    private function setFrame(bitmap:AnimationBitmap, timer:Timer):void
    {
        bitmap.bitmapData   = (_frames[bitmap.animationType] as Array)[bitmap.frameIndex] as BitmapData;
        timer.delay         = bitmap.duration;
 
        if (bitmap.isPlayed)
            timer.start();
    }
 
    private function onTimerComplete(event:TimerEvent):void
    {
        var timer:Timer             = event.target as Timer;
        var index:int               = _timers.indexOf(timer);
        var bitmap:AnimationBitmap  = _bitmaps[index] as AnimationBitmap;
 
        if (bitmap.frameIndex == bitmap.finishIndex)
        {
            if (bitmap.isLooped)
                bitmap.frameIndex   = bitmap.startIndex;
            else
                bitmap.isPlayed     = false;
        }
        else
        {
            bitmap.frameIndex++;
        }
 
        if (bitmap.isPlayed)
            setFrame(bitmap, timer);
    }
}
}



Класс AnimationBitmap
задачи:
- Отображать анимацию

Код AS3:
package al.display {
import al.event.AnimationEvent;
import al.utils.IPoolObject;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
 
 
/**
 * ...
 * @author Stepan Ermilov
 */
public class AnimationBitmap extends Bitmap implements IPoolObject, IDisplayObject
{
    private var _durations:Array;
 
    private var _isLooped:Boolean;
    private var _animationType:String;
    private var _frameIndex:int;
    private var _startIndex:int;
    private var _finishIndex:int;
    private var _isPlayed:Boolean;
 
    public function AnimationBitmap()
    {
        super();
        _durations = [];
    }
 
    public function free():void
    {
        bitmapData = null;
    }
 
    public function copy():IPoolObject
    {
        return new AnimationBitmap();
    }
 
    public function clear():void
    {
        bitmapData  = null;
        _durations  = [];
        isPlayed    = false;
 
        dispatchEvent(new Event(Event.CLEAR));
    }
 
    public function start():void
    {
        dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_START));
    }
 
    public function stop():void
    {
        dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_STOP));
    }
 
    public function resume():void
    {
        dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_RESUME));
    }
 
    public function get animationType():String
    {
        return _animationType;
    }
 
    public function set animationType(value:String):void
    {
        if (_animationType != value)
            _animationType = value;
    }
 
    public function get frameIndex():int
    {
        return _frameIndex;
    }
 
    public function set frameIndex(value:int):void
    {
        if (_frameIndex != value)
            _frameIndex = value;
    }
 
    public function get startIndex():int
    {
        return _startIndex;
    }
 
    public function set startIndex(value:int):void
    {
        if (_startIndex != value)
            _startIndex = value;
    }
 
    public function get finishIndex():int
    {
        return _finishIndex;
    }
 
    public function set finishIndex(value:int):void
    {
        if (_finishIndex != value)
            _finishIndex = value;
    }
 
    public function get isLooped():Boolean
    {
        return _isLooped;
    }
 
    public function set isLooped(value:Boolean):void
    {
        if (_isLooped != value)
            _isLooped = value;
    }
 
    override public function set bitmapData(value:BitmapData):void
    {
        super.bitmapData = value;
        if (value)
            dispatchEvent(new AnimationEvent(AnimationEvent.ANIMATION_UPDATE));
    }
 
    public function get isPlayed():Boolean
    {
        return _isPlayed;
    }
 
    public function set isPlayed(value:Boolean):void
    {
        if (_isPlayed != value)
            _isPlayed = value;
    }
 
    public function get durations():Array
    {
        return _durations;
    }
 
    public function set durations(value:Array):void
    {
        if (_durations != value)
            _durations = value;
    }
 
    public function get duration():Number
    {
        var index:int = frameIndex - startIndex;
        return durations[index]
                    ? durations[index]
                    : durations[0];
    }
}
}



Main.as
Код AS3:
package {
import al.display.AnimationBitmap;
import al.managers.AnimationManager;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.text.TextField;
 
/**
 * ...
 * @author Stepan Ermilov
 */
public class Main extends Sprite
{
    [Embed(source = '../lib/dcbb4e3fecdc.png')] private static const image:Class;
    private var _tfText:TextField;
 
 
    private var _animationManager:AnimationManager;
    private var _animation:AnimationBitmap;
 
    public function Main():void
    {
        if (stage)
            onAddedToStage();
        else
            addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    }
 
    private function onAddedToStage(event:Event = null):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
 
        var bitmap:Bitmap       = new image();
 
        _animationManager       = new AnimationManager();
        _animationManager.addAnimationFrames("horse", bitmap.bitmapData, new Rectangle(0, 0, 80, 80));
 
        _animation              = _animationManager.addAnimationBitmap("horse", 4, 7); //в анимации используем только кадры с 4 по 7 
        _animation.durations    = [100]; // длительность для всех кадров одинакова
        _animation.isLooped     = true;  // анимация циклична
        addChild(_animation);
        _animation.start();
    }
}
}



Первое что интересует это, где держать таймер в менеджере, как сейчас, или все таки он должен быть в AnimationBitmap?
Второе это необходимость в самом классе AnimationBitmap. Есть мысли юзать просто Bitmap, а все переменные и операции старт/стоп вынести в менеджер?
Вот в принципе и все. Хотелось бы услышать ваше мнение о правильности такого подхода, наверняка я что то делаю не так. =)

Образец картинки с тайлами:
[IMG]http://s006.***********/i213/1102/f0/dcbb4e3fecdc.png[/IMG]



Демо-флешку и код можно посмотреть тут git@github.com:stepan23/aloran.git
Всего комментариев 14

Комментарии

Старый 21.03.2011 10:14 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Так то-да. Но художники-аниматоры не оценят.
Старый 21.03.2011 10:57 scarbo вне форума
scarbo
 
Аватар для scarbo
Это почему? Мы когда осенью игру делали изометрическую, мой диз. как раз за такой подход ратовал.
To автор? может быть так:
Код AS3:
public function removeAllAnimationFrames():void
    {
        _frames = {};
    }
И хотелось бы увидеть ObjectPool.as
Обновил(-а) scarbo 21.03.2011 в 11:08
Старый 21.03.2011 11:54 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Ну, это скорее исключение.
Обычно аниматоры, анимирующие в Flash IDE ратуют за мувиклипы)
Старый 21.03.2011 12:09 ChuwY вне форума
ChuwY
 
Аватар для ChuwY
По образцу картинки не скажешь, что анимацию делали во флеше.
Думаю, таких не мало.
Старый 21.03.2011 12:23 scarbo вне форума
scarbo
 
Аватар для scarbo
Цитата:
Ну, это скорее исключение.
Обычно аниматоры, анимирующие в Flash IDE ратуют за мувиклипы)
Ой, не сказал бы, просто дофига игр, где именно используют 3D-Sprite(простыню из пнгшек, не знаю правильно ли назвал). Мало того, используют даже там, где логичнее было бы во флеше сделать.
Старый 21.03.2011 12:26 Aloran вне форума
Aloran
Цитата:
To автор? может быть так:
А чем зануление объекта хуже создания пустого?

Цитата:
И хотелось бы увидеть ObjectPool.as
Код AS3:
package al.utils {
import al.utils.IPoolObject;
import flash.events.Event;
import flash.events.EventDispatcher;
 
/**
 * ...
 * @author Stepan Ermilov
 */
public class ObjectPool extends EventDispatcher implements IPool
{
    private var _pool:Array;
    private var _usedObjects:Array;
    private var _specimen:IPoolObject;
 
    public function ObjectPool(specimen:IPoolObject)
    {
        _specimen       = specimen;
        _pool           = [];
        _usedObjects    = [];
    }
 
    public function free():void
    {
        _specimen.free();
        _specimen       = null;
 
        while (_pool.length)
        {
            freeObject(_pool.shift() as IPoolObject);
        }
        _pool           = null;
 
        while (_usedObjects.length)
        {
            freeObject(_usedObjects.shift() as IPoolObject);
        }
        _usedObjects    = null;
    }
 
    public function getItem():IPoolObject
    {
        var item:IPoolObject = _pool.length ? _pool.shift() as IPoolObject : _specimen.copy();
        _usedObjects.push(item);
        item.addEventListener(Event.CLEAR, onItemClear);
        return item;
    }
 
    private function freeObject(item:IPoolObject):void
    {
        item.free();
        item = null;
    }
 
    private function onItemClear(event:Event):void
    {
        var item:IPoolObject    = event.target as IPoolObject;
        item.removeEventListener(Event.CLEAR, onItemClear);
       _usedObjects.splice(_usedObjects.indexOf(item), 1);
       _pool.push(item);
    }
}
}
Старый 21.03.2011 12:31 scarbo вне форума
scarbo
 
Аватар для scarbo
Цитата:
А чем зануление объекта хуже создания пустого?
Да ничем особо, просто лишний цикл. Хотя битмапок этих бывает обычно не много, так что не существенно.
Старый 21.03.2011 13:42 Aloran вне форума
Aloran
А понлял о чем ты. да можно и так. А количество битмапокдаток зависит думаю от конкретного проекта.
Кстати вот еще один вопрос. Реализовать AnimationManager надо было скорее всего через Singleton/Статичный класс? Не думаю что в проекте он нужен более чем в 1 экземпляре
Старый 21.03.2011 17:01 alatar вне форума
alatar
 
Аватар для alatar
Зачем вообще нарезать на отдельные битмапы? Чем не угодил scrollRect или копирование пикселей "на лету"?
ИМХО AnimationManager вообще не нужен.
Обновил(-а) alatar 21.03.2011 в 17:04
Старый 21.03.2011 22:44 Anton Riot вне форума
Anton Riot
Это же определённого рода ущербность - работать только с растровыми изображениями. Тем более во флэш.
Старый 22.03.2011 00:40 dimarik вне форума
dimarik
 
Аватар для dimarik
Код AS1/AS2:
- по запросу из вне возвращать AnimationBitmap (класс в отображающий анимацию)
Давай вернем обычный наследник мувиклипа. Это удобно.
Старый 22.03.2011 00:54 dimarik вне форума
dimarik
 
Аватар для dimarik
Кстати, старенькая статья, но кое-что расскажет.
Старый 22.03.2011 23:26 Aloran вне форума
Aloran
Спасибо большое за ссылочку. Очень интересное чтиво.
Старый 23.03.2011 00:50 Котяра вне форума
Котяра
 
Аватар для Котяра
я использовал вот это:
http://blog.hexagonstar.com/animatedbitmapclass/
 
Последние записи от Aloran

 


Часовой пояс GMT +4, время: 10:57.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.