Speed action easing?

Hi,
I think Speed action is very cool to pause/resume animations as well as change their speed (obviously). For example I have an animation of a sprite going from right to the left and back (looped). When I click it a boost speed to 4.0 and then after few seconds back to 1.0. It works, but there’s no smooth transition, because it’s straight from 1.0 to 4.0 and back. Is there some way to ease speed action? So let’s say something like this:

auto speedAction = Speed::create(someAction, 1.0f);
increaseSpeedTo(speedAction, 4.0f, 5.0f); //raise speed to 4.0 in 5 seconds
decreaseSpeedTo(speedAction, 1.0f, 5.0f); //lower speed to 1.0 in 5 seconds

Have you tried speedAction->setSpeed(5.0f);
I’d probably schedule an update function, store the speedAction (or its tag), and call setSpeed after 1s elapsed.
You should also be able to do it by spawning a parallel action that sequences a Delay + CallFunc({setSpeed}) twice.

I even tried doing it like this:
setSpeed(2), Delay(1), setSpeed(3), Delay(1), setSpeed(4)
but it looks bad. It’s not smooth. It has to be float transition changing in each frame.

Ok, I did it! I tried to extend Action, but I’ve realized Action has a target, which is Node. But, in this case, it’s a Speed object.

So here it is.

.h file:

#ifndef SpeedActions_hpp
#define SpeedActions_hpp

#include "cocos2d.h"

USING_NS_CC;

class ChangeSpeedTo : public Ref {
    
public:
    static ChangeSpeedTo* create(Speed* target, float duration, float destSpeed);
    void update(float delta);
    bool isDone();
    
protected:
    bool initWithDuration(Speed* target, float duration, float destSpeed);
    
    Speed* _target;
    float _time;
    float _duration;
    float _startSpeed;
    float _destSpeed;
    bool _increase;
    bool _done;
    
    ChangeSpeedTo(){}
};

#endif /* SpeedActions_hpp */

.cpp file:

#include "SpeedActions.h"

ChangeSpeedTo* ChangeSpeedTo::create(Speed* target, float duration, float destSpeed){
    ChangeSpeedTo *ret = new (std::nothrow) ChangeSpeedTo();
    
    if (ret && ret->initWithDuration(target, duration, destSpeed))
    {
        ret->autorelease();
        return ret;
    }
    
    delete ret;
    return nullptr;
}

bool ChangeSpeedTo::initWithDuration(Speed* target, float duration, float destSpeed){
    assert(duration > 0);
    assert(target != nullptr);
    _target = target;
    _duration = duration;
    _destSpeed = destSpeed;
    _startSpeed = target->getSpeed();
    _time = 0.0f;
    _increase = _destSpeed > _startSpeed;
    _done = false;
    return true;
}

void ChangeSpeedTo::update(float delta){
    if(_done) return;
    _time += delta;
    if(_time >= _duration){
        _time = _duration;
        _done = true;
    }
    float speed = _increase ? (_time / _duration) * (_destSpeed - _startSpeed) + _startSpeed : (1.0f - (_time / _duration)) * (_startSpeed - _destSpeed) + _destSpeed;
    _target->setSpeed(speed);
}

bool ChangeSpeedTo::isDone(){
    return _done;
}

Usage:

Declare update function and Vector to hold “actions”:

virtual void update(float delta) override;
Vector<ChangeSpeedTo* > changeSpeedActions;

Adding:

changeSpeedActions.pushBack(ChangeSpeedTo::create(speedAction, 1.0f, 4.0f)); //target, duration, destination speed

Update function:

void GameScene::update(float delta){
    for(int i = changeSpeedActions.size() - 1; i >= 0; i--){
        auto cSpeed = changeSpeedActions.at(i);
        cSpeed->update(delta);
        if(cSpeed->isDone()){
            changeSpeedActions.erase(i);
        }
    }
}
1 Like

Also it’s import to clear changeSpeedActions vector in class destructor:

GameScene::~GameScene(){
    changeSpeedActions.clear();
}

Nice, thanks for sharing.

Important: app will crash if speed action will end before ChangeSpeedTo.