Actions¶
The action system provides simple time-based tweens. Actions run through ActionRunner, which is automatically added to a node when you call runAction(). You can attach per-frame callbacks with Action::onUpdate().
using namespace snowpulse;
auto* node = scene()->createNode("runner");
scene()->rootNode()->addChild(node);
auto action = Actions::moveX(120.0f, 0.4f);
action->onUpdate([](Node* owner, float dt) {
(void)owner;
(void)dt;
});
node->runAction(std::move(action));
Easing functions are provided by EaseType.
runAction() transfers ownership to the node's ActionRunner. Its returned
Action* is a non-owning, short-lived handle: it becomes invalid as soon as
the action completes or the runner is cleared. Register onUpdate() and
onComplete() before transferring the action when possible. onUpdate() also
runs on the final update, and onComplete() runs during completion before the
action is removed.
Call node->stopAllActions() to clear active and pending actions; cleared
actions do not invoke onComplete(). Pointer-based actions such as valueTo()
and colorTo() do not own their destination, so that storage must remain valid
until the action finishes or is stopped.
Move Actions¶
moveTo tweens all axes, while moveX/Y/Z target a single axis.
using namespace snowpulse;
auto* node = scene()->createNode("moving_item");
scene()->rootNode()->addChild(node);
node->runAction(Actions::moveTo({ 200.0f, 100.0f, 0.0f }, 0.5f, EaseType::OutCubic));
node->runAction(Actions::moveX(120.0f, 0.25f, EaseType::OutQuad));
node->runAction(Actions::moveY(-80.0f, 0.2f));
node->runAction(Actions::moveZ(40.0f, 0.3f));
Rotate Actions¶
using namespace snowpulse;
auto* node = scene()->createNode("spinner");
scene()->rootNode()->addChild(node);
node->runAction(Actions::rotateTo({ 0.0f, 0.0f, 180.0f }, 0.6f, EaseType::OutBack));
node->runAction(Actions::rotateX(15.0f, 0.2f));
node->runAction(Actions::rotateY(25.0f, 0.2f));
node->runAction(Actions::rotateZ(360.0f, 1.0f, EaseType::Linear));
Scale Actions¶
using namespace snowpulse;
auto* node = scene()->createNode("pulse");
scene()->rootNode()->addChild(node);
node->runAction(Actions::scaleTo({ 1.5f, 1.5f, 1.0f }, 0.3f, EaseType::OutBack));
node->runAction(Actions::scaleX(2.0f, 0.2f));
node->runAction(Actions::scaleY(0.5f, 0.2f));
node->runAction(Actions::scaleZ(1.2f, 0.2f));
Color Actions¶
using namespace snowpulse;
auto* node = scene()->createNode("sprite");
scene()->rootNode()->addChild(node);
auto* sprite = node->addComponent<SpriteRenderer>();
sprite->color = { 1.0f, 1.0f, 1.0f, 1.0f };
node->runAction(Actions::colorTo(&sprite->color, { 1.0f, 0.4f, 0.4f, 1.0f }, 0.4f, EaseType::OutQuad));
node->runAction(Actions::colorChannel(&sprite->color, ColorChannel::A, 0.2f, 0.3f));
Value Actions¶
Keep tweened values in storage that outlives the action, such as members of an attached component:
class AnimatedValues final : public snowpulse::Script {
public:
void onStart() override {
owner()->runAction(snowpulse::Actions::valueTo(
&_level, 1.0f, 0.6f, snowpulse::EaseType::OutCubic));
owner()->runAction(snowpulse::Actions::valueTo(
&_offset, { 12.0f, -8.0f, 0.0f }, 0.4f,
snowpulse::EaseType::OutQuad));
}
private:
float _level = 0.0f;
glm::vec3 _offset { 0.0f, 0.0f, 0.0f };
};
auto* node = scene()->createNode("animated_values");
node->addComponent<AnimatedValues>();
scene()->rootNode()->addChild(node);
Delay Action¶
using namespace snowpulse;
auto* node = scene()->createNode("delayed");
scene()->rootNode()->addChild(node);
auto delay = Actions::delay(0.4f);
delay->onComplete([]() {
// Continue after the delay.
});
node->runAction(std::move(delay));
Sequence Action¶
using namespace snowpulse;
auto* node = scene()->createNode("combo");
scene()->rootNode()->addChild(node);
auto seq = Actions::sequence();
seq->add(Actions::moveY(80.0f, 0.25f, EaseType::OutQuad));
seq->add(Actions::delay(0.1f));
seq->add(Actions::scaleTo({ 1.2f, 1.2f, 1.0f }, 0.2f, EaseType::OutBack),
[]() { printf("Scale done!\n"); });
node->runAction(std::move(seq));
Parallel Action¶
ParallelAction completes when its first (anchor) action finishes. Any other actions that are still running are deferred back to the node's ActionRunner to finish independently.
using namespace snowpulse;
auto* node = scene()->createNode("combo");
scene()->rootNode()->addChild(node);
auto par = Actions::parallel();
par->add(Actions::moveX(140.0f, 0.3f, EaseType::OutCubic)); // anchor
par->add(Actions::scaleTo({ 1.4f, 1.4f, 1.0f }, 0.6f, EaseType::OutBack));
par->add(Actions::rotateZ(180.0f, 0.6f, EaseType::OutQuad));
node->runAction(std::move(par));