Esempio n. 1
0
void AnimationBase::updateStateMachine(AnimStateInput input, double param)
{
    if (!m_compAnim)
        return;

    // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state.
    if (input == AnimationStateInputMakeNew) {
        if (m_animState == AnimationStateStartWaitStyleAvailable)
            m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
        LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState));
        m_animState = AnimationStateNew;
        m_startTime = 0;
        m_pauseTime = -1;
        m_requestedStartTime = 0;
        m_nextIterationDuration = -1;
        endAnimation();
        return;
    }

    if (input == AnimationStateInputRestartAnimation) {
        if (m_animState == AnimationStateStartWaitStyleAvailable)
            m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
        LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState));
        m_animState = AnimationStateNew;
        m_startTime = 0;
        m_pauseTime = -1;
        m_requestedStartTime = 0;
        m_nextIterationDuration = -1;
        endAnimation();

        if (!paused())
            updateStateMachine(AnimationStateInputStartAnimation, -1);
        return;
    }

    if (input == AnimationStateInputEndAnimation) {
        if (m_animState == AnimationStateStartWaitStyleAvailable)
            m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this);
        LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState));
        m_animState = AnimationStateDone;
        endAnimation();
        return;
    }

    if (input == AnimationStateInputPauseOverride) {
        if (m_animState == AnimationStateStartWaitResponse) {
            // If we are in AnimationStateStartWaitResponse, the animation will get canceled before 
            // we get a response, so move to the next state.
            endAnimation();
            updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
        }
        return;
    }

    if (input == AnimationStateInputResumeOverride) {
        if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) {
            // Start the animation
            startAnimation(beginAnimationUpdateTime() - m_startTime);
        }
        return;
    }

    // Execute state machine
    switch (m_animState) {
        case AnimationStateNew:
            ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused);
            if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) {
                m_requestedStartTime = beginAnimationUpdateTime();
                LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animState));
                m_animState = AnimationStateStartWaitTimer;
            } else {
                // We are pausing before we even started.
                LOG(Animations, "%p AnimationState %s -> AnimationStatePausedNew", this, nameForState(m_animState));
                m_animState = AnimationStatePausedNew;
            }
            break;
        case AnimationStateStartWaitTimer:
            ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused);

            if (input == AnimationStateInputStartTimerFired) {
                ASSERT(param >= 0);
                // Start timer has fired, tell the animation to start and wait for it to respond with start time
                LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState));
                m_animState = AnimationStateStartWaitStyleAvailable;
                m_compAnim->animationController()->addToAnimationsWaitingForStyle(this);

                // Trigger a render so we can start the animation
                if (m_object && m_object->element())
                    m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element());
            } else {
                ASSERT(!paused());
                // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait
                m_pauseTime = beginAnimationUpdateTime();
                LOG(Animations, "%p AnimationState %s -> PausedWaitTimer", this, nameForState(m_animState));
                m_animState = AnimationStatePausedWaitTimer;
            }
            break;
        case AnimationStateStartWaitStyleAvailable:
            ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused);

            if (input == AnimationStateInputStyleAvailable) {
                // Start timer has fired, tell the animation to start and wait for it to respond with start time
                LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState));
                m_animState = AnimationStateStartWaitResponse;

                overrideAnimations();

                // Start the animation
                if (overridden()) {
                    // We won't try to start accelerated animations if we are overridden and
                    // just move on to the next state.
                    LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState));
                    m_animState = AnimationStateStartWaitResponse;
                    m_isAccelerated = false;
                    updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
                } else {
                    double timeOffset = 0;
                    // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
                    if (m_animation->delay() < 0)
                        timeOffset = -m_animation->delay();
                    bool started = startAnimation(timeOffset);

                    m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
                    m_isAccelerated = started;
                }
            } else {
                // We're waiting for the style to be available and we got a pause. Pause and wait
                m_pauseTime = beginAnimationUpdateTime();
                LOG(Animations, "%p AnimationState %s -> PausedWaitStyleAvailable", this, nameForState(m_animState));
                m_animState = AnimationStatePausedWaitStyleAvailable;
            }
            break;
        case AnimationStateStartWaitResponse:
            ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused);

            if (input == AnimationStateInputStartTimeSet) {
                ASSERT(param >= 0);
                // We have a start time, set it, unless the startTime is already set
                if (m_startTime <= 0) {
                    m_startTime = param;
                    // If the value for 'animation-delay' is negative then the animation appears to have started in the past.
                    if (m_animation->delay() < 0)
                        m_startTime += m_animation->delay();
                }

                // Now that we know the start time, fire the start event.
                onAnimationStart(0); // The elapsedTime is 0.

                // Decide whether to go into looping or ending state
                goIntoEndingOrLoopingState();

                // Dispatch updateStyleIfNeeded so we can start the animation
                if (m_object && m_object->element())
                    m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element());
            } else {
                // We are pausing while waiting for a start response. Cancel the animation and wait. When 
                // we unpause, we will act as though the start timer just fired
                m_pauseTime = beginAnimationUpdateTime();
                pauseAnimation(beginAnimationUpdateTime() - m_startTime);
                LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState));
                m_animState = AnimationStatePausedWaitResponse;
            }
            break;
        case AnimationStateLooping:
            ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused);

            if (input == AnimationStateInputLoopTimerFired) {
                ASSERT(param >= 0);
                // Loop timer fired, loop again or end.
                onAnimationIteration(param);

                // Decide whether to go into looping or ending state
                goIntoEndingOrLoopingState();
            } else {
                // We are pausing while running. Cancel the animation and wait
                m_pauseTime = beginAnimationUpdateTime();
                pauseAnimation(beginAnimationUpdateTime() - m_startTime);
                LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState));
                m_animState = AnimationStatePausedRun;
            }
            break;
        case AnimationStateEnding:
#if !LOG_DISABLED
            if (input != AnimationStateInputEndTimerFired && input != AnimationStateInputPlayStatePaused)
                LOG_ERROR("State is AnimationStateEnding, but input is not AnimationStateInputEndTimerFired or AnimationStateInputPlayStatePaused. It is %d.", input);
#endif
            if (input == AnimationStateInputEndTimerFired) {

                ASSERT(param >= 0);
                // End timer fired, finish up
                onAnimationEnd(param);

                LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState));
                m_animState = AnimationStateDone;
                
                if (m_object) {
                    if (m_animation->fillsForwards()) {
                        LOG(Animations, "%p AnimationState %s -> FillingForwards", this, nameForState(m_animState));
                        m_animState = AnimationStateFillingForwards;
                    } else
                        resumeOverriddenAnimations();

                    // Fire off another style change so we can set the final value
                    if (m_object->element())
                        m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element());
                }
            } else {
                // We are pausing while running. Cancel the animation and wait
                m_pauseTime = beginAnimationUpdateTime();
                pauseAnimation(beginAnimationUpdateTime() - m_startTime);
                LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState));
                m_animState = AnimationStatePausedRun;
            }
            // |this| may be deleted here
            break;
        case AnimationStatePausedWaitTimer:
            ASSERT(input == AnimationStateInputPlayStateRunning);
            ASSERT(paused());
            // Update the times
            m_startTime += beginAnimationUpdateTime() - m_pauseTime;
            m_pauseTime = -1;

            // we were waiting for the start timer to fire, go back and wait again
            LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState));
            m_animState = AnimationStateNew;
            updateStateMachine(AnimationStateInputStartAnimation, 0);
            break;
        case AnimationStatePausedNew:
        case AnimationStatePausedWaitResponse:
        case AnimationStatePausedWaitStyleAvailable:
        case AnimationStatePausedRun:
            // We treat these two cases the same. The only difference is that, when we are in
            // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
            // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice
            // that we have already set the startTime and will ignore it.
            ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable || input == AnimationStateInputStartAnimation);
            ASSERT(paused());

            if (input == AnimationStateInputPlayStateRunning) {
                if (m_animState == AnimationStatePausedNew) {
                    // We were paused before we even started, and now we're supposed
                    // to start, so jump back to the New state and reset.
                    LOG(Animations, "%p AnimationState %s -> AnimationStateNew", this, nameForState(m_animState));
                    m_animState = AnimationStateNew;
                    updateStateMachine(input, param);
                    break;
                }

                // Update the times
                if (m_animState == AnimationStatePausedRun)
                    m_startTime += beginAnimationUpdateTime() - m_pauseTime;
                else
                    m_startTime = 0;
                m_pauseTime = -1;

                if (m_animState == AnimationStatePausedWaitStyleAvailable) {
                    LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState));
                    m_animState = AnimationStateStartWaitStyleAvailable;
                } else {
                    // We were either running or waiting for a begin time response from the animation.
                    // Either way we need to restart the animation (possibly with an offset if we
                    // had already been running) and wait for it to start.
                    LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState));
                    m_animState = AnimationStateStartWaitResponse;

                    // Start the animation
                    if (overridden()) {
                        // We won't try to start accelerated animations if we are overridden and
                        // just move on to the next state.
                        updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime());
                        m_isAccelerated = true;
                    } else {
                        bool started = startAnimation(beginAnimationUpdateTime() - m_startTime);
                        m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started);
                        m_isAccelerated = started;
                    }
                }
                break;
            }
            
            if (input == AnimationStateInputStartTimeSet) {
                ASSERT(m_animState == AnimationStatePausedWaitResponse);
                
                // We are paused but we got the callback that notifies us that an accelerated animation started.
                // We ignore the start time and just move into the paused-run state.
                LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState));
                m_animState = AnimationStatePausedRun;
                ASSERT(m_startTime == 0);
                m_startTime = param;
                m_pauseTime += m_startTime;
                break;
            }

            ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable);
            // We are paused but we got the callback that notifies us that style has been updated.
            // We move to the AnimationStatePausedWaitResponse state
            LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState));
            m_animState = AnimationStatePausedWaitResponse;
            overrideAnimations();
            break;
        case AnimationStateFillingForwards:
        case AnimationStateDone:
            // We're done. Stay in this state until we are deleted
            break;
    }
}
Esempio n. 2
0
void GAFObject::realizeFrame(cocos2d::Node* out, uint32_t frameIndex)
{
    const AnimationFrames_t& animationFrames = m_timeline->getAnimationFrames();

    if (animationFrames.size() <= frameIndex)
    {
        return;
    }

    GAFAnimationFrame *currentFrame = animationFrames[frameIndex];

    const GAFAnimationFrame::SubobjectStates_t& states = currentFrame->getObjectStates();

    for (const GAFSubobjectState* state : states)
    {
        GAFObject* subObject = m_displayList[state->objectIdRef];

        CCASSERT(subObject, "Error. SubObject with current ID not found");
        if (!subObject)
            continue;

        if (state->colorMults()[GAFColorTransformIndex::GAFCTI_A] >= 0.f && subObject->m_isInResetState)
        {
            subObject->m_currentFrame = subObject->m_currentSequenceStart;
        }
        subObject->m_isInResetState = state->colorMults()[GAFColorTransformIndex::GAFCTI_A] < 0.f;

        if (!state->isVisible())
            continue;

        if (subObject->m_charType == GAFCharacterType::Timeline)
        {
            if (!subObject->m_isInResetState)
            {
                cocos2d::AffineTransform stateTransform = state->affineTransform;
                float csf = m_timeline->usedAtlasScale();
                stateTransform.tx *= csf;
                stateTransform.ty *= csf;
                cocos2d::AffineTransform t = GAF_CGAffineTransformCocosFormatFromFlashFormat(state->affineTransform);
                subObject->setAdditionalTransform(t);
                subObject->m_parentFilters.clear();
                const Filters_t& filters = state->getFilters();
                subObject->m_parentFilters.insert(subObject->m_parentFilters.end(), filters.begin(), filters.end());

                const float* cm = state->colorMults();
                subObject->m_parentColorTransforms[0] = cocos2d::Vec4(
                    m_parentColorTransforms[0].x * cm[0],
                    m_parentColorTransforms[0].y * cm[1],
                    m_parentColorTransforms[0].z * cm[2],
                    m_parentColorTransforms[0].w * cm[3]);
                subObject->m_parentColorTransforms[1] = cocos2d::Vec4(state->colorOffsets()) + m_parentColorTransforms[1];

                if (m_masks[state->objectIdRef])
                {
                    rearrangeSubobject(out, m_masks[state->objectIdRef], state->zIndex);
                }
                else
                {
                    //subObject->removeFromParentAndCleanup(false);
                    if (state->maskObjectIdRef == IDNONE)
                    {
                        rearrangeSubobject(out, subObject, state->zIndex);
                    }
                    else
                    {
                        // If the state has a mask, then attach it 
                        // to the clipping node. Clipping node will be attached on its state
                        auto mask = m_masks[state->maskObjectIdRef];
                        CCASSERT(mask, "Error. No mask found for this ID");
                        if (mask)
                            rearrangeSubobject(mask, subObject, state->zIndex);
                    }
                }

                subObject->step();
            }
        }
        else if (subObject->m_charType == GAFCharacterType::Texture)
        {
            cocos2d::Vect prevAP = subObject->getAnchorPoint();
            cocos2d::Size  prevCS = subObject->getContentSize();

#if ENABLE_RUNTIME_FILTERS
            if (subObject->m_objectType == GAFObjectType::MovieClip)
            {
                // Validate sprite type (w/ or w/o filter)
                const Filters_t& filters = state->getFilters();
                GAFFilterData* filter = NULL;

                GAFMovieClip* mc = static_cast<GAFMovieClip*>(subObject);

                if (m_parentFilters.size() > 0)
                {
                    filter = *m_parentFilters.begin();
                }
                else if (filters.size() > 0)
                {
                    filter = *filters.begin();
                }

                if (filter)
                {
                    filter->apply(mc);
                }

                if (!filter || filter->getType() != GAFFilterType::GFT_Blur)
                {
                    mc->setBlurFilterData(nullptr);
                }

                if (!filter || filter->getType() != GAFFilterType::GFT_ColorMatrix)
                {
                    mc->setColorMarixFilterData(nullptr);
                }

                if (!filter || filter->getType() != GAFFilterType::GFT_Glow)
                {
                    mc->setGlowFilterData(nullptr);
                }

                if (!filter || filter->getType() != GAFFilterType::GFT_DropShadow)
                {
                    GAFDropShadowFilterData::reset(mc);
                }
            }
#endif

            cocos2d::Size newCS = subObject->getContentSize();
            cocos2d::Vect newAP = cocos2d::Vect(((prevAP.x - 0.5f) * prevCS.width) / newCS.width + 0.5f,
                ((prevAP.y - 0.5f) * prevCS.height) / newCS.height + 0.5f);
            subObject->setAnchorPoint(newAP);


            if (m_masks[state->objectIdRef])
            {
                rearrangeSubobject(out, m_masks[state->objectIdRef], state->zIndex);
            }
            else
            {
                //subObject->removeFromParentAndCleanup(false);
                if (state->maskObjectIdRef == IDNONE)
                {
                    rearrangeSubobject(out, subObject, state->zIndex);
                }
                else
                {
                    // If the state has a mask, then attach it 
                    // to the clipping node. Clipping node will be attached on its state
                    auto mask = m_masks[state->maskObjectIdRef];
                    CCASSERT(mask, "Error. No mask found for this ID");
                    if (mask)
                        rearrangeSubobject(mask, subObject, state->zIndex);
                }
            }

            cocos2d::AffineTransform stateTransform = state->affineTransform;
            float csf = m_timeline->usedAtlasScale();
            stateTransform.tx *= csf;
            stateTransform.ty *= csf;
            cocos2d::AffineTransform t = GAF_CGAffineTransformCocosFormatFromFlashFormat(state->affineTransform);
            
            if (isFlippedX() || isFlippedY())
            {
                float flipMulX = isFlippedX() ? -1 : 1;
                float flipOffsetX = isFlippedX() ? getContentSize().width - m_asset->getHeader().frameSize.getMinX() : 0;
                float flipMulY = isFlippedY() ? -1 : 1;
                float flipOffsetY = isFlippedY() ? -getContentSize().height + m_asset->getHeader().frameSize.getMinY() : 0;

                cocos2d::AffineTransform flipCenterTransform = cocos2d::AffineTransformMake(flipMulX, 0, 0, flipMulY, flipOffsetX, flipOffsetY);
                t = AffineTransformConcat(t, flipCenterTransform);
            }

            subObject->setExternalTransform(t);

            if (subObject->m_objectType == GAFObjectType::MovieClip)
            {
                GAFMovieClip* mc = static_cast<GAFMovieClip*>(subObject);
                float colorMults[4] = {
                    state->colorMults()[0] * m_parentColorTransforms[0].x,
                    state->colorMults()[1] * m_parentColorTransforms[0].y,
                    state->colorMults()[2] * m_parentColorTransforms[0].z,
                    state->colorMults()[3] * m_parentColorTransforms[0].w
                };
                float colorOffsets[4] = {
                    state->colorOffsets()[0] + m_parentColorTransforms[1].x,
                    state->colorOffsets()[1] + m_parentColorTransforms[1].y,
                    state->colorOffsets()[2] + m_parentColorTransforms[1].z,
                    state->colorOffsets()[3] + m_parentColorTransforms[1].w
                };

                mc->setColorTransform(colorMults, colorOffsets);
            }
        }
        else if (subObject->m_charType == GAFCharacterType::TextField)
        {
            GAFTextField *tf = static_cast<GAFTextField*>(subObject);
            rearrangeSubobject(out, subObject, state->zIndex);

            cocos2d::AffineTransform stateTransform = state->affineTransform;
            float csf = m_timeline->usedAtlasScale();
            stateTransform.tx *= csf;
            stateTransform.ty *= csf;
            cocos2d::AffineTransform t = GAF_CGAffineTransformCocosFormatFromFlashFormat(state->affineTransform);

            if (isFlippedX() || isFlippedY())
            {
                float flipMulX = isFlippedX() ? -1 : 1;
                float flipOffsetX = isFlippedX() ? getContentSize().width - m_asset->getHeader().frameSize.getMinX() : 0;
                float flipMulY = isFlippedY() ? -1 : 1;
                float flipOffsetY = isFlippedY() ? -getContentSize().height + m_asset->getHeader().frameSize.getMinY() : 0;

                cocos2d::AffineTransform flipCenterTransform = cocos2d::AffineTransformMake(flipMulX, 0, 0, flipMulY, flipOffsetX, flipOffsetY);
                t = AffineTransformConcat(t, flipCenterTransform);
            }

            subObject->setExternalTransform(t);
        }

        if (state->isVisible())
        {
            subObject->m_lastVisibleInFrame = frameIndex + 1;
        }
    }

    GAFAnimationFrame::TimelineActions_t timelineActions = currentFrame->getTimelineActions();
    for (GAFTimelineAction action : timelineActions)
    {
        switch (action.getType())
        {
        case GAFActionType::Stop:
            pauseAnimation();
            break;
        case GAFActionType::Play:
            resumeAnimation();
            break;
        case GAFActionType::GotoAndStop:
            gotoAndStop(action.getParam(GAFTimelineAction::PI_FRAME));
            break;
        case GAFActionType::GotoAndPlay:
            gotoAndPlay(action.getParam(GAFTimelineAction::PI_FRAME));
            break;
        case GAFActionType::DispatchEvent:
            _eventDispatcher->dispatchCustomEvent(action.getParam(GAFTimelineAction::PI_EVENT_TYPE), &action);
            break;

        case GAFActionType::None:
        default:
            break;
        }
    }
}