Анимация OpenFL в preloader всегда заикается с первого раза

1

Я использую встроенный NMEPReloader в качестве базового класса для моего preloader в openfl.

То, что я делаю, воспроизводит анимацию по кадре

class AttractAnimation extends Sprite
{
var currentFrame:Int;
var previousFrame:Int;
var bitmapFrames:Array<Bitmap>;
var loadScreenTimer:Timer;

public function new()
{
    super();
    currentFrame = 0;
    previousFrame = -1;
    bitmapFrames = new Array<Bitmap>();
}

public function assignBitmapData(bitmapData : Array<BitmapData>){
    for(bmap in bitmapData){
        var frame = new Bitmap( null, flash.display.PixelSnapping.AUTO, true );
        frame.bitmapData = bmap;
        pushFramesAsBitmap(frame);
    }
}
public function pushFramesAsBitmap(frameAsBitmap:Bitmap) {
    bitmapFrames.push(frameAsBitmap);

    frameAsBitmap.visible = false;
    addChild(frameAsBitmap);
}

public function startAnimation(fps:Int) {
    var milliseconds:Float = 1000 / fps;
    loadScreenTimer = new Timer(milliseconds, 10);
    loadScreenTimer.addEventListener(TimerEvent.TIMER, updateAnimation);
    loadScreenTimer.start();
}

    private function updateAnimation(e : TimerEvent):Void {
        if (currentFrame > bitmapFrames.length -1) { currentFrame = 0; }
        if (previousFrame != -1) {
            bitmapFrames[previousFrame].visible = bitmapFrames[previousFrame].__combinedVisible = false;
        }
        bitmapFrames[currentFrame].visible = bitmapFrames[currentFrame].__combinedVisible = true;
        previousFrame = currentFrame;
        currentFrame++;
    }

}

что я вижу, когда я называю playAnimation, это заикается очень плохо, в следующий раз, когда я играю в анимацию, она играет гладко.

Я попытался установить альфа-0, играя в анимацию, а затем вернул ее в 1 и сыграл, но в этом случае будет заикаться второй раз.

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

Есть ли способ воспроизвести эту анимацию без заикания?

Теги:
haxe
openfl

2 ответа

1
Лучший ответ

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

Я изменил имя, добавив вторую анимацию. Вы можете видеть, что теперь я использую Tilesheet вместо массива Bitmap.

Я обновил ответ ниже, чтобы включить предложенное изменение onEnterFrame. Использование spritesheet избавилось от заикания, но анимация теперь намного более гладкая с событием enterframe.

class AnimatedSprite extends Sprite
{
public var currentFrame:Int;

private var loadScreenTimer:Timer;
private var tiles: Array<Int>;
private var tilesheet : Tilesheet;

private var fps : Int;
private var mDeltaTime:Float = 0; //the current delta time for time-based updates
private var mTime:Float; //a variable containing the current time since the last frame change


public function new(framesPerSecond : Int, looping : Bool = false)
{
    super();
    currentFrame = 0;
    fps = framesPerSecond;
}

//public methods
public function assignBitmapData(bitmapData : BitmapData, tileData : Array<Rectangle>) : Void {
    tilesheet = new Tilesheet(bitmapData);
    tiles = new Array<Int>();

    for(i in 0...tileData.length){
        tiles.push(tilesheet.addTileRect(tileData[i]));
    }
    mTime = 0;
}

public function playAnimation() : Void {
    trace("playAnimation");
    visible = true;
    currentFrame = 0;
    addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
}

//private methods
private function endAnimation(?e : TimerEvent) : Void {
    removeEventListener(Event.ENTER_FRAME, onEnterFrame);
    graphics.clear();
    trace("end animation");
    currentFrame = 0;
    mTime = 0;
}

private function onEnterFrame(e:Event):Void
{
    trace("enter frame");
    var currentTime = Lib.getTimer(); //get the current time
    var elapsed:Float = (currentTime - mDeltaTime); //calculate the time since the last update
    mDeltaTime = currentTime;

    mTime += elapsed; //add the elapeed time to mTime
    if (mTime >= 1000 / fps) //if it is time to change frame...
    {
        mTime = 0;
        graphics.clear();
        tilesheet.drawTiles(this.graphics, [0, 0, tiles[currentFrame], 1, 1]);
        trace("Draw frame : " + currentFrame);
        if (currentFrame == tiles.length-1) endAnimation();

        currentFrame++;
    }
}
}
1

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

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

//Using this event listener
addEventListener(Event.ENTER_FRAME, OnUpdateAnimation);

//which would call this function every frame
function OnUpdateAnimation(e:Event):Void
{
    //calculate delay since last frame
    //play next frame
}

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

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

class Animation extends Sprite 
{

    private var mBitmapDataList:Array<BitmapData>; //a list of bitmapdata
    private var mBitmap:Bitmap; //the bitmap that will contain all bitmapdata
    private var mCurrentIndex:Int; //the currently displayed bitmapdata
    private var mFrameLength:Float; //the duration of a frame in miliseconds
    private var mDeltaTime:Float = 0; //the current delta time for time-based updates

    private var mTime:Float; //a variable containing the current time since the last frame change
    public function new(aFrameLength:Float) 
    {
        super();
        mFrameLength = aFrameLength;
        mBitmapDataList = new Array<BitmapData>();
        mBitmap = new Bitmap();
        addChild(mBitmap); //add the bitmap to the scene 
                        //(it will display nothing as no bitmapdata is set)
        mCurrentIndex = 0;
    }

    //add a bitmapdata instead of a complete bitmap object
    public function addBitmapData(aBD:BitmapData):Void
    {
        mBitmapDataList.push(aBD);
    }

    public function play():Void
    {
        addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
    }

    private function onEnterFrame(e:Event):Void 
    {
        var currentTime = Lib.getTimer(); //get the current time
        var elapsed:Float = (currentTime - mDeltaTime); //calculate the time since the last update
        mDeltaTime = currentTime;

        mTime += elapsed; //add the elapeed time to mTime
        if (mTime >= mFrameLength) //if it is time to change frame...
        {
            mTime = 0;
            mCurrentIndex++;
            if (mCurrentIndex == mBitmapDataList.length) mCurrentIndex = 0;
            mBitmap.bitmapData = mBitmapDataList[mCurrentIndex]; //change it
        }
    }
}

Таким образом, даже в крайнем случае заикания будет пропускаться только FIRST-кадр. Также обратите внимание, что на некоторых объектах вы не сможете отображать изображения, поскольку они еще не загружены. Альтернативное решение состоит в том, чтобы не вставлять активы <assets path="assets" embed="false"/> и загружать их с помощью класса OpenFL 'Asset (и, таким образом, отказаться от прелоадера вообще, смехотворная вещь по моему скромному мнению)

PS: Извините, мой m и префиксы для переменных. m - для переменных-членов и a для аргументов.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню