refactor(action): 重构动作系统架构并添加新功能

重构动作系统核心架构,主要变更包括:
1. 将动作系统拆分为多个模块化头文件
2. 新增动作管理器实现集中管理
3. 添加瞬时动作、间隔动作和特殊动作类
4. 实现完整的缓动动画系统
5. 优化节点动作接口与性能

新增功能:
1. 支持颜色和翻转动画
2. 添加回调动作和节点管理动作
3. 实现跟随和速度控制等特殊动作
4. 提供30+种缓动函数支持

BREAKING CHANGE: 动作系统API不兼容旧版本,需更新相关调用代码
This commit is contained in:
ChestnutYueyue 2026-02-13 18:46:42 +08:00
parent 3ffcd692b6
commit f02b368dc9
30 changed files with 5503 additions and 1009 deletions

View File

@ -1,16 +1,30 @@
#pragma once
#include <extra2d/core/types.h>
#include <functional>
#include <memory>
namespace extra2d {
class Node;
enum class ActionState { Idle, Running, Paused, Completed };
/**
* @brief
*/
enum class ActionState {
Idle,
Running,
Paused,
Completed
};
/**
* @brief
*
*
* Node
*/
class Action {
public:
using ProgressCallback = std::function<void(float)>;
using CompletionCallback = std::function<void()>;
Action();
@ -21,56 +35,138 @@ public:
Action(Action&&) = default;
Action& operator=(Action&&) = default;
virtual void start(Node *target);
/**
* @brief
* @return true
*/
virtual bool isDone() const;
/**
* @brief 使
* @param target
*/
virtual void startWithTarget(Node* target);
/**
* @brief
*/
virtual void stop();
virtual void update(float dt);
/**
* @brief
* @param dt
*/
virtual void step(float dt);
virtual bool isDone() const = 0;
/**
* @brief
* @param time [0, 1]
*/
virtual void update(float time);
/**
* @brief
* @return
*/
virtual Action* clone() const = 0;
/**
* @brief
* @return
*/
virtual Action* reverse() const = 0;
/**
* @brief
*/
void pause();
/**
* @brief
*/
void resume();
/**
* @brief
*/
void restart();
ActionState getState() const { return state_; }
float getElapsed() const { return elapsed_; }
float getDuration() const { return duration_; }
/**
* @brief
* @param callback
*/
void setCompletionCallback(const CompletionCallback& callback) {
completionCallback_ = callback;
}
/**
* @brief
* @return
*/
Node* getTarget() const { return target_; }
/**
* @brief
* @return
*/
Node* getOriginalTarget() const { return originalTarget_; }
void setDuration(float duration) { duration_ = duration; }
void setSpeed(float speed) { speed_ = speed; }
float getSpeed() const { return speed_; }
/**
* @brief
* @return
*/
ActionState getState() const { return state_; }
void setProgressCallback(ProgressCallback callback) {
progressCallback_ = std::move(callback);
}
void setCompletionCallback(CompletionCallback callback) {
completionCallback_ = std::move(callback);
}
void setTag(int tag) { tag_ = tag; }
/**
* @brief
* @return
*/
int getTag() const { return tag_; }
protected:
virtual void onStart() {}
virtual void onUpdate(float progress) = 0;
virtual void onComplete() {}
/**
* @brief
* @param tag
*/
void setTag(int tag) { tag_ = tag; }
/**
* @brief
* @return
*/
unsigned int getFlags() const { return flags_; }
/**
* @brief
* @param flags
*/
void setFlags(unsigned int flags) { flags_ = flags; }
protected:
/**
* @brief
*/
virtual void onStart() {}
/**
* @brief
*/
virtual void onComplete() {
if (completionCallback_) {
completionCallback_();
}
}
/**
* @brief
*/
void setDone() { state_ = ActionState::Completed; }
Node* target_ = nullptr;
Node* originalTarget_ = nullptr;
ActionState state_ = ActionState::Idle;
float elapsed_ = 0.0f;
float duration_ = 0.0f;
float speed_ = 1.0f;
int tag_ = -1;
ProgressCallback progressCallback_;
unsigned int flags_ = 0;
CompletionCallback completionCallback_;
};
using ActionPtr = std::unique_ptr<Action>;
} // namespace extra2d

View File

@ -0,0 +1,344 @@
#pragma once
#include <extra2d/action/action_interval.h>
#include <extra2d/action/ease.h>
namespace extra2d {
/**
* @brief
*
* 使
*/
class ActionEase : public ActionInterval {
public:
virtual ~ActionEase();
/**
* @brief
* @return
*/
ActionInterval* getInnerAction() const { return innerAction_; }
void startWithTarget(Node* target) override;
void stop() override;
void update(float time) override;
ActionInterval* clone() const override = 0;
ActionInterval* reverse() const override = 0;
protected:
ActionEase() = default;
bool initWithAction(ActionInterval* action);
void onUpdate(float progress) override {}
ActionInterval* innerAction_ = nullptr;
};
// ============================================================================
// 指数缓动
// ============================================================================
class EaseExponentialIn : public ActionEase {
public:
static EaseExponentialIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseExponentialOut : public ActionEase {
public:
static EaseExponentialOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseExponentialInOut : public ActionEase {
public:
static EaseExponentialInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 正弦缓动
// ============================================================================
class EaseSineIn : public ActionEase {
public:
static EaseSineIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseSineOut : public ActionEase {
public:
static EaseSineOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseSineInOut : public ActionEase {
public:
static EaseSineInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 弹性缓动
// ============================================================================
class EaseElasticIn : public ActionEase {
public:
static EaseElasticIn* create(ActionInterval* action, float period = 0.3f);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
protected:
float period_ = 0.3f;
};
class EaseElasticOut : public ActionEase {
public:
static EaseElasticOut* create(ActionInterval* action, float period = 0.3f);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
protected:
float period_ = 0.3f;
};
class EaseElasticInOut : public ActionEase {
public:
static EaseElasticInOut* create(ActionInterval* action, float period = 0.3f);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
protected:
float period_ = 0.3f;
};
// ============================================================================
// 弹跳缓动
// ============================================================================
class EaseBounceIn : public ActionEase {
public:
static EaseBounceIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseBounceOut : public ActionEase {
public:
static EaseBounceOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseBounceInOut : public ActionEase {
public:
static EaseBounceInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 回震缓动
// ============================================================================
class EaseBackIn : public ActionEase {
public:
static EaseBackIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseBackOut : public ActionEase {
public:
static EaseBackOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseBackInOut : public ActionEase {
public:
static EaseBackInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 二次缓动
// ============================================================================
class EaseQuadIn : public ActionEase {
public:
static EaseQuadIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseQuadOut : public ActionEase {
public:
static EaseQuadOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseQuadInOut : public ActionEase {
public:
static EaseQuadInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 三次缓动
// ============================================================================
class EaseCubicIn : public ActionEase {
public:
static EaseCubicIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseCubicOut : public ActionEase {
public:
static EaseCubicOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseCubicInOut : public ActionEase {
public:
static EaseCubicInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 四次缓动
// ============================================================================
class EaseQuartIn : public ActionEase {
public:
static EaseQuartIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseQuartOut : public ActionEase {
public:
static EaseQuartOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseQuartInOut : public ActionEase {
public:
static EaseQuartInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 五次缓动
// ============================================================================
class EaseQuintIn : public ActionEase {
public:
static EaseQuintIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseQuintOut : public ActionEase {
public:
static EaseQuintOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseQuintInOut : public ActionEase {
public:
static EaseQuintInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 圆形缓动
// ============================================================================
class EaseCircleIn : public ActionEase {
public:
static EaseCircleIn* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseCircleOut : public ActionEase {
public:
static EaseCircleOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
class EaseCircleInOut : public ActionEase {
public:
static EaseCircleInOut* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
};
// ============================================================================
// 自定义缓动
// ============================================================================
/**
* @brief
*/
class EaseCustom : public ActionEase {
public:
static EaseCustom* create(ActionInterval* action, EaseFunction easeFunc);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
void update(float time) override;
protected:
EaseFunction easeFunc_ = nullptr;
};
} // namespace extra2d

View File

@ -0,0 +1,57 @@
#pragma once
#include <extra2d/action/finite_time_action.h>
namespace extra2d {
/**
* @brief
*
*
* FiniteTimeAction 0
*/
class ActionInstant : public FiniteTimeAction {
public:
ActionInstant();
virtual ~ActionInstant() = default;
/**
* @brief
* @return true
*/
bool isDone() const override;
/**
* @brief 使
* @param target
*/
void startWithTarget(Node* target) override;
/**
* @brief
* @param dt
*/
void step(float dt) override;
/**
* @brief
* @return
*/
ActionInstant* clone() const override = 0;
/**
* @brief
* @return
*/
ActionInstant* reverse() const override = 0;
protected:
/**
* @brief
*/
virtual void execute() = 0;
bool done_ = false;
};
} // namespace extra2d

View File

@ -0,0 +1,169 @@
#pragma once
#include <extra2d/action/action_instant.h>
#include <extra2d/core/math_types.h>
#include <functional>
namespace extra2d {
// ============================================================================
// 回调动作
// ============================================================================
/**
* @brief
*/
class CallFunc : public ActionInstant {
public:
using Callback = std::function<void()>;
static CallFunc* create(const Callback& callback);
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
Callback callback_;
};
/**
* @brief
*/
class CallFuncN : public ActionInstant {
public:
using Callback = std::function<void(Node*)>;
static CallFuncN* create(const Callback& callback);
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
Callback callback_;
};
// ============================================================================
// 位置动作
// ============================================================================
/**
* @brief
*/
class Place : public ActionInstant {
public:
static Place* create(const Vec2& position);
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
Vec2 position_;
};
// ============================================================================
// 翻转动作
// ============================================================================
/**
* @brief X轴翻转动作
*/
class FlipX : public ActionInstant {
public:
static FlipX* create(bool flipX);
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
bool flipX_ = false;
};
/**
* @brief Y轴翻转动作
*/
class FlipY : public ActionInstant {
public:
static FlipY* create(bool flipY);
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
bool flipY_ = false;
};
// ============================================================================
// 可见性动作
// ============================================================================
/**
* @brief
*/
class Show : public ActionInstant {
public:
static Show* create();
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
};
/**
* @brief
*/
class Hide : public ActionInstant {
public:
static Hide* create();
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
};
/**
* @brief
*/
class ToggleVisibility : public ActionInstant {
public:
static ToggleVisibility* create();
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
};
// ============================================================================
// 节点管理动作
// ============================================================================
/**
* @brief
*/
class RemoveSelf : public ActionInstant {
public:
static RemoveSelf* create();
ActionInstant* clone() const override;
ActionInstant* reverse() const override;
protected:
void execute() override;
};
} // namespace extra2d

View File

@ -0,0 +1,103 @@
#pragma once
#include <extra2d/action/finite_time_action.h>
#include <extra2d/action/ease.h>
namespace extra2d {
/**
* @brief
*
*
* FiniteTimeAction
*/
class ActionInterval : public FiniteTimeAction {
public:
ActionInterval() = default;
explicit ActionInterval(float duration);
virtual ~ActionInterval() = default;
/**
* @brief
* @return
*/
float getElapsed() const { return elapsed_; }
/**
* @brief
* @return true
*/
bool isDone() const override;
/**
* @brief 使
* @param target
*/
void startWithTarget(Node* target) override;
/**
* @brief
*/
void stop() override;
/**
* @brief
* @param dt
*/
void step(float dt) override;
/**
* @brief
* @param amp
*/
void setAmplitudeRate(float amp) { amplitudeRate_ = amp; }
/**
* @brief
* @return
*/
float getAmplitudeRate() const { return amplitudeRate_; }
/**
* @brief
* @param easeFunc
*/
void setEaseFunction(EaseFunction easeFunc) { easeFunc_ = easeFunc; }
/**
* @brief
* @return
*/
EaseFunction getEaseFunction() const { return easeFunc_; }
/**
* @brief
* @return
*/
ActionInterval* clone() const override = 0;
/**
* @brief
* @return
*/
ActionInterval* reverse() const override = 0;
protected:
/**
* @brief
*/
virtual void onStart() {}
/**
* @brief
* @param progress [0, 1]
*/
virtual void onUpdate(float progress) = 0;
float elapsed_ = 0.0f;
bool firstTick_ = true;
float amplitudeRate_ = 1.0f;
EaseFunction easeFunc_ = nullptr;
};
} // namespace extra2d

View File

@ -0,0 +1,469 @@
#pragma once
#include <extra2d/action/action_interval.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/color.h>
#include <functional>
#include <vector>
namespace extra2d {
// ============================================================================
// 移动动作
// ============================================================================
/**
* @brief
*/
class MoveBy : public ActionInterval {
public:
static MoveBy* create(float duration, const Vec2& delta);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
Vec2 delta_;
Vec2 startPosition_;
};
/**
* @brief
*/
class MoveTo : public ActionInterval {
public:
static MoveTo* create(float duration, const Vec2& position);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
Vec2 endPosition_;
Vec2 startPosition_;
Vec2 delta_;
};
// ============================================================================
// 跳跃动作
// ============================================================================
/**
* @brief
*/
class JumpBy : public ActionInterval {
public:
static JumpBy* create(float duration, const Vec2& position, float height, int jumps);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
Vec2 startPosition_;
Vec2 delta_;
float height_ = 0.0f;
int jumps_ = 1;
};
/**
* @brief
*/
class JumpTo : public JumpBy {
public:
static JumpTo* create(float duration, const Vec2& position, float height, int jumps);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
Vec2 endPosition_;
};
// ============================================================================
// 贝塞尔曲线动作
// ============================================================================
/**
* @brief 线
*/
struct BezierConfig {
Vec2 controlPoint1;
Vec2 controlPoint2;
Vec2 endPosition;
};
/**
* @brief 线
*/
class BezierBy : public ActionInterval {
public:
static BezierBy* create(float duration, const BezierConfig& config);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
static float bezierat(float a, float b, float c, float d, float t);
BezierConfig config_;
Vec2 startPosition_;
};
/**
* @brief 线
*/
class BezierTo : public BezierBy {
public:
static BezierTo* create(float duration, const BezierConfig& config);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
BezierConfig originalConfig_;
};
// ============================================================================
// 缩放动作
// ============================================================================
/**
* @brief
*/
class ScaleBy : public ActionInterval {
public:
static ScaleBy* create(float duration, float scale);
static ScaleBy* create(float duration, float scaleX, float scaleY);
static ScaleBy* create(float duration, const Vec2& scale);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
Vec2 deltaScale_;
Vec2 startScale_;
};
/**
* @brief
*/
class ScaleTo : public ActionInterval {
public:
static ScaleTo* create(float duration, float scale);
static ScaleTo* create(float duration, float scaleX, float scaleY);
static ScaleTo* create(float duration, const Vec2& scale);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
Vec2 endScale_;
Vec2 startScale_;
Vec2 delta_;
};
// ============================================================================
// 旋转动作
// ============================================================================
/**
* @brief
*/
class RotateBy : public ActionInterval {
public:
static RotateBy* create(float duration, float deltaAngle);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
float deltaAngle_ = 0.0f;
float startAngle_ = 0.0f;
};
/**
* @brief
*/
class RotateTo : public ActionInterval {
public:
static RotateTo* create(float duration, float angle);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
float endAngle_ = 0.0f;
float startAngle_ = 0.0f;
float deltaAngle_ = 0.0f;
};
// ============================================================================
// 淡入淡出动作
// ============================================================================
/**
* @brief
*/
class FadeIn : public ActionInterval {
public:
static FadeIn* create(float duration);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
float startOpacity_ = 0.0f;
};
/**
* @brief
*/
class FadeOut : public ActionInterval {
public:
static FadeOut* create(float duration);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
float startOpacity_ = 0.0f;
};
/**
* @brief
*/
class FadeTo : public ActionInterval {
public:
static FadeTo* create(float duration, float opacity);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
float endOpacity_ = 0.0f;
float startOpacity_ = 0.0f;
float deltaOpacity_ = 0.0f;
};
// ============================================================================
// 闪烁动作
// ============================================================================
/**
* @brief
*/
class Blink : public ActionInterval {
public:
static Blink* create(float duration, int times);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
int times_ = 1;
int currentTimes_ = 0;
bool originalVisible_ = true;
};
// ============================================================================
// 色调动作
// ============================================================================
/**
* @brief
*/
class TintTo : public ActionInterval {
public:
static TintTo* create(float duration, uint8_t red, uint8_t green, uint8_t blue);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
Color3B startColor_;
Color3B endColor_;
Color3B deltaColor_;
};
/**
* @brief
*/
class TintBy : public ActionInterval {
public:
static TintBy* create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
Color3B startColor_;
int16_t deltaR_ = 0;
int16_t deltaG_ = 0;
int16_t deltaB_ = 0;
};
// ============================================================================
// 组合动作
// ============================================================================
/**
* @brief
*/
class Sequence : public ActionInterval {
public:
static Sequence* create(ActionInterval* action1, ...);
static Sequence* create(const std::vector<ActionInterval*>& actions);
~Sequence();
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
std::vector<ActionInterval*> actions_;
size_t currentIndex_ = 0;
float split_ = 0.0f;
float last_ = -1.0f;
};
/**
* @brief
*/
class Spawn : public ActionInterval {
public:
static Spawn* create(ActionInterval* action1, ...);
static Spawn* create(const std::vector<ActionInterval*>& actions);
~Spawn();
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
std::vector<ActionInterval*> actions_;
};
/**
* @brief
*/
class Repeat : public ActionInterval {
public:
static Repeat* create(ActionInterval* action, int times);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
bool isDone() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
ActionInterval* innerAction_ = nullptr;
int times_ = 1;
int currentTimes_ = 0;
};
/**
* @brief
*/
class RepeatForever : public ActionInterval {
public:
static RepeatForever* create(ActionInterval* action);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
bool isDone() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
ActionInterval* innerAction_ = nullptr;
};
/**
* @brief
*/
class DelayTime : public ActionInterval {
public:
static DelayTime* create(float duration);
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onUpdate(float progress) override {}
};
/**
* @brief
*/
class ReverseTime : public ActionInterval {
public:
static ReverseTime* create(ActionInterval* action);
~ReverseTime();
ActionInterval* clone() const override;
ActionInterval* reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
ActionInterval* innerAction_ = nullptr;
};
} // namespace extra2d

View File

@ -0,0 +1,130 @@
#pragma once
#include <extra2d/action/action.h>
#include <unordered_map>
#include <vector>
#include <functional>
namespace extra2d {
/**
* @brief
*
*
*
*/
class ActionManager {
public:
/**
* @brief
* @return ActionManager
*/
static ActionManager* getInstance();
/**
* @brief
*/
static void destroyInstance();
/**
* @brief
* @param action
* @param target
* @param paused
*/
void addAction(Action* action, Node* target, bool paused = false);
/**
* @brief
* @param action
*/
void removeAction(Action* action);
/**
* @brief
* @param tag
* @param target
*/
void removeActionByTag(int tag, Node* target);
/**
* @brief
* @param flags
* @param target
*/
void removeActionsByFlags(unsigned int flags, Node* target);
/**
* @brief
* @param target
*/
void removeAllActionsFromTarget(Node* target);
/**
* @brief
*/
void removeAllActions();
/**
* @brief
* @param tag
* @param target
* @return nullptr
*/
Action* getActionByTag(int tag, Node* target);
/**
* @brief
* @param target
* @return
*/
size_t getActionCount(Node* target) const;
/**
* @brief
* @param target
*/
void pauseTarget(Node* target);
/**
* @brief
* @param target
*/
void resumeTarget(Node* target);
/**
* @brief
* @param target
* @return true
*/
bool isPaused(Node* target) const;
/**
* @brief
* @param dt
*/
void update(float dt);
private:
ActionManager();
~ActionManager();
struct ActionElement {
std::vector<Action*> actions;
Node* target = nullptr;
bool paused = false;
int actionIndex = 0;
Action* currentAction = nullptr;
bool currentActionSalvaged = false;
};
using ActionMap = std::unordered_map<Node*, ActionElement>;
void removeActionAt(size_t index, ActionElement& element);
void deleteAction(Action* action);
ActionMap targets_;
static ActionManager* instance_;
};
} // namespace extra2d

View File

@ -0,0 +1,166 @@
#pragma once
#include <extra2d/action/action.h>
#include <extra2d/action/action_interval.h>
#include <extra2d/core/math_types.h>
namespace extra2d {
/**
* @brief
*
*
*
*/
class Speed : public Action {
public:
/**
* @brief
* @param action
* @param speed 1.0
* @return
*/
static Speed* create(ActionInterval* action, float speed);
~Speed();
/**
* @brief
* @return
*/
float getSpeed() const { return speed_; }
/**
* @brief
* @param speed
*/
void setSpeed(float speed) { speed_ = speed; }
/**
* @brief
* @return
*/
ActionInterval* getInnerAction() const { return innerAction_; }
void startWithTarget(Node* target) override;
void stop() override;
void step(float dt) override;
bool isDone() const override;
Action* clone() const override;
Action* reverse() const override;
protected:
Speed() = default;
ActionInterval* innerAction_ = nullptr;
float speed_ = 1.0f;
};
/**
* @brief
*
* 使
*
*/
class Follow : public Action {
public:
/**
* @brief
* @param followedNode
* @return
*/
static Follow* create(Node* followedNode);
/**
* @brief
* @param followedNode
* @param boundary
* @return
*/
static Follow* create(Node* followedNode, const Rect& boundary);
~Follow();
/**
* @brief
* @return
*/
Node* getFollowedNode() const { return followedNode_; }
/**
* @brief
* @return true
*/
bool isBoundarySet() const { return boundarySet_; }
void startWithTarget(Node* target) override;
void stop() override;
void step(float dt) override;
bool isDone() const override;
Action* clone() const override;
Action* reverse() const override;
protected:
Follow() = default;
Node* followedNode_ = nullptr;
Rect boundary_;
bool boundarySet_ = false;
Vec2 halfScreenSize_;
Vec2 fullScreenSize_;
Vec2 leftBoundary_;
Vec2 rightBoundary_;
Vec2 topBoundary_;
Vec2 bottomBoundary_;
};
/**
* @brief
*
*
*/
class TargetedAction : public Action {
public:
/**
* @brief
* @param target
* @param action
* @return
*/
static TargetedAction* create(Node* target, FiniteTimeAction* action);
~TargetedAction();
/**
* @brief
* @return
*/
Node* getTargetNode() const { return targetNode_; }
/**
* @brief
* @param target
*/
void setTargetNode(Node* target) { targetNode_ = target; }
/**
* @brief
* @return
*/
FiniteTimeAction* getAction() const { return innerAction_; }
void startWithTarget(Node* target) override;
void stop() override;
void step(float dt) override;
bool isDone() const override;
Action* clone() const override;
Action* reverse() const override;
protected:
TargetedAction() = default;
Node* targetNode_ = nullptr;
FiniteTimeAction* innerAction_ = nullptr;
};
} // namespace extra2d

View File

@ -1,278 +0,0 @@
#pragma once
#include "extra2d/action/action.h"
#include "extra2d/core/math_types.h"
#include <functional>
#include <vector>
namespace extra2d {
// Interval Action Base
class IntervalAction : public Action {
public:
explicit IntervalAction(float duration);
bool isDone() const override;
};
// Instant Action Base
class InstantAction : public Action {
public:
InstantAction();
bool isDone() const override;
};
// Move Actions
class MoveBy : public IntervalAction {
public:
MoveBy(float duration, const Vec2 &delta);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
Vec2 delta_;
Vec2 startPosition_;
};
class MoveTo : public IntervalAction {
public:
MoveTo(float duration, const Vec2 &position);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
Vec2 endPosition_;
Vec2 startPosition_;
Vec2 delta_;
};
// Scale Actions
class ScaleBy : public IntervalAction {
public:
ScaleBy(float duration, float scale);
ScaleBy(float duration, float scaleX, float scaleY);
ScaleBy(float duration, const Vec2 &scale);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
Vec2 deltaScale_;
Vec2 startScale_;
};
class ScaleTo : public IntervalAction {
public:
ScaleTo(float duration, float scale);
ScaleTo(float duration, float scaleX, float scaleY);
ScaleTo(float duration, const Vec2 &scale);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
Vec2 endScale_;
Vec2 startScale_;
Vec2 delta_;
};
// Rotate Actions
class RotateBy : public IntervalAction {
public:
RotateBy(float duration, float deltaAngle);
RotateBy(float duration, float deltaAngleX, float deltaAngleY);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
float deltaAngle_ = 0.0f;
float startAngle_ = 0.0f;
};
class RotateTo : public IntervalAction {
public:
RotateTo(float duration, float angle);
RotateTo(float duration, float angleX, float angleY);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
float endAngle_ = 0.0f;
float startAngle_ = 0.0f;
float deltaAngle_ = 0.0f;
};
// Fade Actions
class FadeIn : public IntervalAction {
public:
explicit FadeIn(float duration);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
float startOpacity_ = 0.0f;
};
class FadeOut : public IntervalAction {
public:
explicit FadeOut(float duration);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
float startOpacity_ = 0.0f;
};
class FadeTo : public IntervalAction {
public:
FadeTo(float duration, float opacity);
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
float endOpacity_ = 0.0f;
float startOpacity_ = 0.0f;
float deltaOpacity_ = 0.0f;
};
// Composite Actions
class Sequence : public IntervalAction {
public:
Sequence(const std::vector<Action *> &actions);
~Sequence();
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
std::vector<Action *> actions_;
int currentIndex_ = 0;
float split_ = 0.0f;
float last_ = 0.0f;
};
class Spawn : public IntervalAction {
public:
Spawn(const std::vector<Action *> &actions);
~Spawn();
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
std::vector<Action *> actions_;
};
// Loop Action
class Loop : public Action {
public:
Loop(Action *action, int times = -1);
~Loop();
bool isDone() const override;
Action *clone() const override;
Action *reverse() const override;
protected:
void onStart() override;
void onUpdate(float progress) override;
private:
Action *action_ = nullptr;
int times_ = 1;
int currentTimes_ = 0;
};
// Delay Action
class Delay : public IntervalAction {
public:
explicit Delay(float duration);
Action *clone() const override;
Action *reverse() const override;
protected:
void onUpdate(float progress) override;
};
// CallFunc Action
class CallFunc : public InstantAction {
public:
using Callback = std::function<void()>;
explicit CallFunc(Callback callback);
Action *clone() const override;
Action *reverse() const override;
protected:
void onUpdate(float progress) override;
private:
Callback callback_;
};
// Helper functions
inline Sequence *sequence(const std::vector<Action *> &actions) {
return new Sequence(actions);
}
inline Spawn *spawn(const std::vector<Action *> &actions) {
return new Spawn(actions);
}
inline Loop *loop(Action *action, int times = -1) {
return new Loop(action, times);
}
inline Delay *delay(float duration) { return new Delay(duration); }
inline CallFunc *callFunc(CallFunc::Callback callback) {
return new CallFunc(std::move(callback));
}
} // namespace extra2d

View File

@ -1,67 +1,101 @@
#pragma once
namespace extra2d {
// Easing function type
/**
* @brief
*/
using EaseFunction = float (*)(float);
// Linear (no easing)
// ============================================================================
// 线性缓动
// ============================================================================
/**
* @brief 线
* @param t [0, 1]
* @return
*/
float easeLinear(float t);
// Quadratic
// ============================================================================
// 二次缓动 (Quad)
// ============================================================================
float easeInQuad(float t);
float easeOutQuad(float t);
float easeInOutQuad(float t);
// Cubic
// ============================================================================
// 三次缓动 (Cubic)
// ============================================================================
float easeInCubic(float t);
float easeOutCubic(float t);
float easeInOutCubic(float t);
// Quartic
// ============================================================================
// 四次缓动 (Quart)
// ============================================================================
float easeInQuart(float t);
float easeOutQuart(float t);
float easeInOutQuart(float t);
// Quintic
// ============================================================================
// 五次缓动 (Quint)
// ============================================================================
float easeInQuint(float t);
float easeOutQuint(float t);
float easeInOutQuint(float t);
// Sine
// ============================================================================
// 正弦缓动 (Sine)
// ============================================================================
float easeInSine(float t);
float easeOutSine(float t);
float easeInOutSine(float t);
// Exponential
// ============================================================================
// 指数缓动 (Exponential)
// ============================================================================
float easeInExpo(float t);
float easeOutExpo(float t);
float easeInOutExpo(float t);
// Circular
// ============================================================================
// 圆形缓动 (Circular)
// ============================================================================
float easeInCirc(float t);
float easeOutCirc(float t);
float easeInOutCirc(float t);
// Back
// ============================================================================
// 回震缓动 (Back)
// ============================================================================
float easeInBack(float t);
float easeOutBack(float t);
float easeInOutBack(float t);
// Elastic
// ============================================================================
// 弹性缓动 (Elastic)
// ============================================================================
float easeInElastic(float t);
float easeOutElastic(float t);
float easeInOutElastic(float t);
// Bounce
// ============================================================================
// 弹跳缓动 (Bounce)
// ============================================================================
float easeInBounce(float t);
float easeOutBounce(float t);
float easeInOutBounce(float t);
// Ease Action wrapper
class Action;
class EaseAction {
public:
static Action *create(Action *action, EaseFunction easeFunc);
};
} // namespace extra2d

View File

@ -0,0 +1,47 @@
#pragma once
#include <extra2d/action/action.h>
namespace extra2d {
/**
* @brief
*
*
* Action
*/
class FiniteTimeAction : public Action {
public:
FiniteTimeAction() = default;
explicit FiniteTimeAction(float duration);
virtual ~FiniteTimeAction() = default;
/**
* @brief
* @return
*/
float getDuration() const { return duration_; }
/**
* @brief
* @param duration
*/
void setDuration(float duration) { duration_ = duration; }
/**
* @brief
* @return
*/
FiniteTimeAction* clone() const override = 0;
/**
* @brief
* @return
*/
FiniteTimeAction* reverse() const override = 0;
protected:
float duration_ = 0.0f;
};
} // namespace extra2d

View File

@ -6,6 +6,40 @@
namespace extra2d {
/// RGB 颜色(字节,每通道 0-255
struct Color3B {
uint8_t r = 255;
uint8_t g = 255;
uint8_t b = 255;
constexpr Color3B() = default;
constexpr Color3B(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {}
constexpr bool operator==(const Color3B& other) const {
return r == other.r && g == other.g && b == other.b;
}
constexpr bool operator!=(const Color3B& other) const {
return !(*this == other);
}
Color3B operator+(const Color3B& other) const {
return Color3B(
static_cast<uint8_t>(std::min(255, static_cast<int>(r) + other.r)),
static_cast<uint8_t>(std::min(255, static_cast<int>(g) + other.g)),
static_cast<uint8_t>(std::min(255, static_cast<int>(b) + other.b))
);
}
Color3B operator-(const Color3B& other) const {
return Color3B(
static_cast<uint8_t>(std::max(0, static_cast<int>(r) - other.r)),
static_cast<uint8_t>(std::max(0, static_cast<int>(g) - other.g)),
static_cast<uint8_t>(std::max(0, static_cast<int>(b) - other.b))
);
}
};
/// RGBA 颜色(浮点数,每通道 0.0 - 1.0
struct Color {
float r = 0.0f;

View File

@ -66,7 +66,14 @@
// Action
#include <extra2d/action/action.h>
#include <extra2d/action/actions.h>
#include <extra2d/action/finite_time_action.h>
#include <extra2d/action/action_interval.h>
#include <extra2d/action/action_instant.h>
#include <extra2d/action/action_interval_actions.h>
#include <extra2d/action/action_instant_actions.h>
#include <extra2d/action/action_ease.h>
#include <extra2d/action/action_special.h>
#include <extra2d/action/action_manager.h>
#include <extra2d/action/ease.h>
// Event

View File

@ -76,6 +76,25 @@ public:
void setVisible(bool visible);
bool isVisible() const { return visible_; }
/**
* @brief
* @param color RGB颜色
*/
void setColor(const Color3B& color);
Color3B getColor() const { return color_; }
/**
* @brief X轴翻转
*/
void setFlipX(bool flipX);
bool isFlipX() const { return flipX_; }
/**
* @brief Y轴翻转
*/
void setFlipY(bool flipY);
bool isFlipY() const { return flipY_; }
void setZOrder(int zOrder);
int getZOrder() const { return zOrder_; }
@ -139,12 +158,54 @@ public:
// ------------------------------------------------------------------------
// 动作系统
// ------------------------------------------------------------------------
void runAction(Ptr<Action> action);
/**
* @brief
* @param action
* @return
*/
Action* runAction(Action* action);
/**
* @brief
*/
void stopAllActions();
void stopAction(Ptr<Action> action);
/**
* @brief
* @param action
*/
void stopAction(Action* action);
/**
* @brief
* @param tag
*/
void stopActionByTag(int tag);
Ptr<Action> getActionByTag(int tag) const;
size_t getActionCount() const { return actions_.size(); }
/**
* @brief
* @param flags
*/
void stopActionsByFlags(unsigned int flags);
/**
* @brief
* @param tag
* @return nullptr
*/
Action* getActionByTag(int tag);
/**
* @brief
* @return
*/
size_t getActionCount() const;
/**
* @brief
* @return true
*/
bool isRunningActions() const;
// ------------------------------------------------------------------------
// 事件系统
@ -198,14 +259,10 @@ private:
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
// 4. 动作系统(使用 unordered_map 加速 tag 查找)
std::unordered_map<int, Ptr<Action>> actionByTag_; // 56 bytes
std::vector<Ptr<Action>> actions_; // 24 bytes无 tag 的 Action
// 5. 事件分发器
// 4. 事件分发器
EventDispatcher eventDispatcher_; // 大小取决于实现
// 6. 父节点引用
// 5. 父节点引用
WeakPtr<Node> parent_; // 16 bytes
// 7. 变换属性(按访问频率分组)
@ -221,10 +278,17 @@ private:
float rotation_ = 0.0f; // 4 bytes
float opacity_ = 1.0f; // 4 bytes
// 10. 整数属性
// 10. 颜色属性
Color3B color_ = Color3B(255, 255, 255); // 3 bytes
// 11. 整数属性
int zOrder_ = 0; // 4 bytes
int tag_ = -1; // 4 bytes
// 12. 布尔属性
bool flipX_ = false; // 1 byte
bool flipY_ = false; // 1 byte
// 11. 场景指针
Scene *scene_ = nullptr; // 8 bytes

View File

@ -2,12 +2,16 @@
#include "extra2d/scene/node.h"
namespace extra2d {
Action::Action() : elapsed_(0.0f), duration_(0.0f), speed_(1.0f), tag_(-1) {}
void Action::start(Node *target) {
Action::Action() : tag_(-1), flags_(0) {}
bool Action::isDone() const {
return state_ == ActionState::Completed;
}
void Action::startWithTarget(Node* target) {
target_ = target;
originalTarget_ = target;
elapsed_ = 0.0f;
state_ = ActionState::Running;
onStart();
}
@ -17,52 +21,29 @@ void Action::stop() {
state_ = ActionState::Completed;
}
void Action::update(float dt) {
if (state_ != ActionState::Running)
return;
step(dt);
if (isDone()) {
state_ = ActionState::Completed;
onComplete();
if (completionCallback_)
completionCallback_();
}
}
void Action::step(float dt) {
if (state_ != ActionState::Running)
return;
elapsed_ += dt * speed_;
float progress = 0.0f;
if (duration_ > 0.0f) {
progress = std::min(1.0f, elapsed_ / duration_);
} else {
progress = 1.0f;
(void)dt;
}
if (progressCallback_)
progressCallback_(progress);
onUpdate(progress);
void Action::update(float time) {
(void)time;
}
void Action::pause() {
if (state_ == ActionState::Running)
if (state_ == ActionState::Running) {
state_ = ActionState::Paused;
}
}
void Action::resume() {
if (state_ == ActionState::Paused)
if (state_ == ActionState::Paused) {
state_ = ActionState::Running;
}
}
void Action::restart() {
elapsed_ = 0.0f;
state_ = ActionState::Running;
onStart();
}
} // namespace extra2d

View File

@ -0,0 +1,683 @@
#include "extra2d/action/action_ease.h"
#include <cmath>
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
namespace extra2d {
// ============================================================================
// ActionEase 基类
// ============================================================================
ActionEase::~ActionEase() {
delete innerAction_;
}
bool ActionEase::initWithAction(ActionInterval* action) {
if (!action) {
return false;
}
innerAction_ = action;
duration_ = action->getDuration();
return true;
}
void ActionEase::startWithTarget(Node* target) {
ActionInterval::startWithTarget(target);
innerAction_->startWithTarget(target);
}
void ActionEase::stop() {
innerAction_->stop();
ActionInterval::stop();
}
void ActionEase::update(float time) {
innerAction_->update(time);
}
// ============================================================================
// 指数缓动
// ============================================================================
EaseExponentialIn* EaseExponentialIn::create(ActionInterval* action) {
auto* ease = new EaseExponentialIn();
ease->initWithAction(action);
return ease;
}
void EaseExponentialIn::update(float time) {
innerAction_->update(easeInExpo(time));
}
ActionInterval* EaseExponentialIn::clone() const {
return EaseExponentialIn::create(innerAction_->clone());
}
ActionInterval* EaseExponentialIn::reverse() const {
return EaseExponentialOut::create(innerAction_->reverse());
}
EaseExponentialOut* EaseExponentialOut::create(ActionInterval* action) {
auto* ease = new EaseExponentialOut();
ease->initWithAction(action);
return ease;
}
void EaseExponentialOut::update(float time) {
innerAction_->update(easeOutExpo(time));
}
ActionInterval* EaseExponentialOut::clone() const {
return EaseExponentialOut::create(innerAction_->clone());
}
ActionInterval* EaseExponentialOut::reverse() const {
return EaseExponentialIn::create(innerAction_->reverse());
}
EaseExponentialInOut* EaseExponentialInOut::create(ActionInterval* action) {
auto* ease = new EaseExponentialInOut();
ease->initWithAction(action);
return ease;
}
void EaseExponentialInOut::update(float time) {
innerAction_->update(easeInOutExpo(time));
}
ActionInterval* EaseExponentialInOut::clone() const {
return EaseExponentialInOut::create(innerAction_->clone());
}
ActionInterval* EaseExponentialInOut::reverse() const {
return EaseExponentialInOut::create(innerAction_->reverse());
}
// ============================================================================
// 正弦缓动
// ============================================================================
EaseSineIn* EaseSineIn::create(ActionInterval* action) {
auto* ease = new EaseSineIn();
ease->initWithAction(action);
return ease;
}
void EaseSineIn::update(float time) {
innerAction_->update(easeInSine(time));
}
ActionInterval* EaseSineIn::clone() const {
return EaseSineIn::create(innerAction_->clone());
}
ActionInterval* EaseSineIn::reverse() const {
return EaseSineOut::create(innerAction_->reverse());
}
EaseSineOut* EaseSineOut::create(ActionInterval* action) {
auto* ease = new EaseSineOut();
ease->initWithAction(action);
return ease;
}
void EaseSineOut::update(float time) {
innerAction_->update(easeOutSine(time));
}
ActionInterval* EaseSineOut::clone() const {
return EaseSineOut::create(innerAction_->clone());
}
ActionInterval* EaseSineOut::reverse() const {
return EaseSineIn::create(innerAction_->reverse());
}
EaseSineInOut* EaseSineInOut::create(ActionInterval* action) {
auto* ease = new EaseSineInOut();
ease->initWithAction(action);
return ease;
}
void EaseSineInOut::update(float time) {
innerAction_->update(easeInOutSine(time));
}
ActionInterval* EaseSineInOut::clone() const {
return EaseSineInOut::create(innerAction_->clone());
}
ActionInterval* EaseSineInOut::reverse() const {
return EaseSineInOut::create(innerAction_->reverse());
}
// ============================================================================
// 弹性缓动
// ============================================================================
EaseElasticIn* EaseElasticIn::create(ActionInterval* action, float period) {
auto* ease = new EaseElasticIn();
ease->initWithAction(action);
ease->period_ = period;
return ease;
}
void EaseElasticIn::update(float time) {
float newT = 0.0f;
if (time == 0.0f || time == 1.0f) {
newT = time;
} else {
float s = period_ / 4.0f;
time = time - 1.0f;
newT = -std::pow(2.0f, 10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_);
}
innerAction_->update(newT);
}
ActionInterval* EaseElasticIn::clone() const {
return EaseElasticIn::create(innerAction_->clone(), period_);
}
ActionInterval* EaseElasticIn::reverse() const {
return EaseElasticOut::create(innerAction_->reverse(), period_);
}
EaseElasticOut* EaseElasticOut::create(ActionInterval* action, float period) {
auto* ease = new EaseElasticOut();
ease->initWithAction(action);
ease->period_ = period;
return ease;
}
void EaseElasticOut::update(float time) {
float newT = 0.0f;
if (time == 0.0f || time == 1.0f) {
newT = time;
} else {
float s = period_ / 4.0f;
newT = std::pow(2.0f, -10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_) + 1.0f;
}
innerAction_->update(newT);
}
ActionInterval* EaseElasticOut::clone() const {
return EaseElasticOut::create(innerAction_->clone(), period_);
}
ActionInterval* EaseElasticOut::reverse() const {
return EaseElasticIn::create(innerAction_->reverse(), period_);
}
EaseElasticInOut* EaseElasticInOut::create(ActionInterval* action, float period) {
auto* ease = new EaseElasticInOut();
ease->initWithAction(action);
ease->period_ = period;
return ease;
}
void EaseElasticInOut::update(float time) {
float newT = 0.0f;
if (time == 0.0f || time == 1.0f) {
newT = time;
} else {
time = time * 2.0f;
if (period_ == 0.0f) {
period_ = 0.3f * 1.5f;
}
float s = period_ / 4.0f;
if (time < 1.0f) {
time -= 1.0f;
newT = -0.5f * std::pow(2.0f, 10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_);
} else {
time -= 1.0f;
newT = std::pow(2.0f, -10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_) * 0.5f + 1.0f;
}
}
innerAction_->update(newT);
}
ActionInterval* EaseElasticInOut::clone() const {
return EaseElasticInOut::create(innerAction_->clone(), period_);
}
ActionInterval* EaseElasticInOut::reverse() const {
return EaseElasticInOut::create(innerAction_->reverse(), period_);
}
// ============================================================================
// 弹跳缓动
// ============================================================================
EaseBounceIn* EaseBounceIn::create(ActionInterval* action) {
auto* ease = new EaseBounceIn();
ease->initWithAction(action);
return ease;
}
void EaseBounceIn::update(float time) {
innerAction_->update(easeInBounce(time));
}
ActionInterval* EaseBounceIn::clone() const {
return EaseBounceIn::create(innerAction_->clone());
}
ActionInterval* EaseBounceIn::reverse() const {
return EaseBounceOut::create(innerAction_->reverse());
}
EaseBounceOut* EaseBounceOut::create(ActionInterval* action) {
auto* ease = new EaseBounceOut();
ease->initWithAction(action);
return ease;
}
void EaseBounceOut::update(float time) {
innerAction_->update(easeOutBounce(time));
}
ActionInterval* EaseBounceOut::clone() const {
return EaseBounceOut::create(innerAction_->clone());
}
ActionInterval* EaseBounceOut::reverse() const {
return EaseBounceIn::create(innerAction_->reverse());
}
EaseBounceInOut* EaseBounceInOut::create(ActionInterval* action) {
auto* ease = new EaseBounceInOut();
ease->initWithAction(action);
return ease;
}
void EaseBounceInOut::update(float time) {
innerAction_->update(easeInOutBounce(time));
}
ActionInterval* EaseBounceInOut::clone() const {
return EaseBounceInOut::create(innerAction_->clone());
}
ActionInterval* EaseBounceInOut::reverse() const {
return EaseBounceInOut::create(innerAction_->reverse());
}
// ============================================================================
// 回震缓动
// ============================================================================
EaseBackIn* EaseBackIn::create(ActionInterval* action) {
auto* ease = new EaseBackIn();
ease->initWithAction(action);
return ease;
}
void EaseBackIn::update(float time) {
innerAction_->update(easeInBack(time));
}
ActionInterval* EaseBackIn::clone() const {
return EaseBackIn::create(innerAction_->clone());
}
ActionInterval* EaseBackIn::reverse() const {
return EaseBackOut::create(innerAction_->reverse());
}
EaseBackOut* EaseBackOut::create(ActionInterval* action) {
auto* ease = new EaseBackOut();
ease->initWithAction(action);
return ease;
}
void EaseBackOut::update(float time) {
innerAction_->update(easeOutBack(time));
}
ActionInterval* EaseBackOut::clone() const {
return EaseBackOut::create(innerAction_->clone());
}
ActionInterval* EaseBackOut::reverse() const {
return EaseBackIn::create(innerAction_->reverse());
}
EaseBackInOut* EaseBackInOut::create(ActionInterval* action) {
auto* ease = new EaseBackInOut();
ease->initWithAction(action);
return ease;
}
void EaseBackInOut::update(float time) {
innerAction_->update(easeInOutBack(time));
}
ActionInterval* EaseBackInOut::clone() const {
return EaseBackInOut::create(innerAction_->clone());
}
ActionInterval* EaseBackInOut::reverse() const {
return EaseBackInOut::create(innerAction_->reverse());
}
// ============================================================================
// 二次缓动
// ============================================================================
EaseQuadIn* EaseQuadIn::create(ActionInterval* action) {
auto* ease = new EaseQuadIn();
ease->initWithAction(action);
return ease;
}
void EaseQuadIn::update(float time) {
innerAction_->update(easeInQuad(time));
}
ActionInterval* EaseQuadIn::clone() const {
return EaseQuadIn::create(innerAction_->clone());
}
ActionInterval* EaseQuadIn::reverse() const {
return EaseQuadOut::create(innerAction_->reverse());
}
EaseQuadOut* EaseQuadOut::create(ActionInterval* action) {
auto* ease = new EaseQuadOut();
ease->initWithAction(action);
return ease;
}
void EaseQuadOut::update(float time) {
innerAction_->update(easeOutQuad(time));
}
ActionInterval* EaseQuadOut::clone() const {
return EaseQuadOut::create(innerAction_->clone());
}
ActionInterval* EaseQuadOut::reverse() const {
return EaseQuadIn::create(innerAction_->reverse());
}
EaseQuadInOut* EaseQuadInOut::create(ActionInterval* action) {
auto* ease = new EaseQuadInOut();
ease->initWithAction(action);
return ease;
}
void EaseQuadInOut::update(float time) {
innerAction_->update(easeInOutQuad(time));
}
ActionInterval* EaseQuadInOut::clone() const {
return EaseQuadInOut::create(innerAction_->clone());
}
ActionInterval* EaseQuadInOut::reverse() const {
return EaseQuadInOut::create(innerAction_->reverse());
}
// ============================================================================
// 三次缓动
// ============================================================================
EaseCubicIn* EaseCubicIn::create(ActionInterval* action) {
auto* ease = new EaseCubicIn();
ease->initWithAction(action);
return ease;
}
void EaseCubicIn::update(float time) {
innerAction_->update(easeInCubic(time));
}
ActionInterval* EaseCubicIn::clone() const {
return EaseCubicIn::create(innerAction_->clone());
}
ActionInterval* EaseCubicIn::reverse() const {
return EaseCubicOut::create(innerAction_->reverse());
}
EaseCubicOut* EaseCubicOut::create(ActionInterval* action) {
auto* ease = new EaseCubicOut();
ease->initWithAction(action);
return ease;
}
void EaseCubicOut::update(float time) {
innerAction_->update(easeOutCubic(time));
}
ActionInterval* EaseCubicOut::clone() const {
return EaseCubicOut::create(innerAction_->clone());
}
ActionInterval* EaseCubicOut::reverse() const {
return EaseCubicIn::create(innerAction_->reverse());
}
EaseCubicInOut* EaseCubicInOut::create(ActionInterval* action) {
auto* ease = new EaseCubicInOut();
ease->initWithAction(action);
return ease;
}
void EaseCubicInOut::update(float time) {
innerAction_->update(easeInOutCubic(time));
}
ActionInterval* EaseCubicInOut::clone() const {
return EaseCubicInOut::create(innerAction_->clone());
}
ActionInterval* EaseCubicInOut::reverse() const {
return EaseCubicInOut::create(innerAction_->reverse());
}
// ============================================================================
// 四次缓动
// ============================================================================
EaseQuartIn* EaseQuartIn::create(ActionInterval* action) {
auto* ease = new EaseQuartIn();
ease->initWithAction(action);
return ease;
}
void EaseQuartIn::update(float time) {
innerAction_->update(easeInQuart(time));
}
ActionInterval* EaseQuartIn::clone() const {
return EaseQuartIn::create(innerAction_->clone());
}
ActionInterval* EaseQuartIn::reverse() const {
return EaseQuartOut::create(innerAction_->reverse());
}
EaseQuartOut* EaseQuartOut::create(ActionInterval* action) {
auto* ease = new EaseQuartOut();
ease->initWithAction(action);
return ease;
}
void EaseQuartOut::update(float time) {
innerAction_->update(easeOutQuart(time));
}
ActionInterval* EaseQuartOut::clone() const {
return EaseQuartOut::create(innerAction_->clone());
}
ActionInterval* EaseQuartOut::reverse() const {
return EaseQuartIn::create(innerAction_->reverse());
}
EaseQuartInOut* EaseQuartInOut::create(ActionInterval* action) {
auto* ease = new EaseQuartInOut();
ease->initWithAction(action);
return ease;
}
void EaseQuartInOut::update(float time) {
innerAction_->update(easeInOutQuart(time));
}
ActionInterval* EaseQuartInOut::clone() const {
return EaseQuartInOut::create(innerAction_->clone());
}
ActionInterval* EaseQuartInOut::reverse() const {
return EaseQuartInOut::create(innerAction_->reverse());
}
// ============================================================================
// 五次缓动
// ============================================================================
EaseQuintIn* EaseQuintIn::create(ActionInterval* action) {
auto* ease = new EaseQuintIn();
ease->initWithAction(action);
return ease;
}
void EaseQuintIn::update(float time) {
innerAction_->update(easeInQuint(time));
}
ActionInterval* EaseQuintIn::clone() const {
return EaseQuintIn::create(innerAction_->clone());
}
ActionInterval* EaseQuintIn::reverse() const {
return EaseQuintOut::create(innerAction_->reverse());
}
EaseQuintOut* EaseQuintOut::create(ActionInterval* action) {
auto* ease = new EaseQuintOut();
ease->initWithAction(action);
return ease;
}
void EaseQuintOut::update(float time) {
innerAction_->update(easeOutQuint(time));
}
ActionInterval* EaseQuintOut::clone() const {
return EaseQuintOut::create(innerAction_->clone());
}
ActionInterval* EaseQuintOut::reverse() const {
return EaseQuintIn::create(innerAction_->reverse());
}
EaseQuintInOut* EaseQuintInOut::create(ActionInterval* action) {
auto* ease = new EaseQuintInOut();
ease->initWithAction(action);
return ease;
}
void EaseQuintInOut::update(float time) {
innerAction_->update(easeInOutQuint(time));
}
ActionInterval* EaseQuintInOut::clone() const {
return EaseQuintInOut::create(innerAction_->clone());
}
ActionInterval* EaseQuintInOut::reverse() const {
return EaseQuintInOut::create(innerAction_->reverse());
}
// ============================================================================
// 圆形缓动
// ============================================================================
EaseCircleIn* EaseCircleIn::create(ActionInterval* action) {
auto* ease = new EaseCircleIn();
ease->initWithAction(action);
return ease;
}
void EaseCircleIn::update(float time) {
innerAction_->update(easeInCirc(time));
}
ActionInterval* EaseCircleIn::clone() const {
return EaseCircleIn::create(innerAction_->clone());
}
ActionInterval* EaseCircleIn::reverse() const {
return EaseCircleOut::create(innerAction_->reverse());
}
EaseCircleOut* EaseCircleOut::create(ActionInterval* action) {
auto* ease = new EaseCircleOut();
ease->initWithAction(action);
return ease;
}
void EaseCircleOut::update(float time) {
innerAction_->update(easeOutCirc(time));
}
ActionInterval* EaseCircleOut::clone() const {
return EaseCircleOut::create(innerAction_->clone());
}
ActionInterval* EaseCircleOut::reverse() const {
return EaseCircleIn::create(innerAction_->reverse());
}
EaseCircleInOut* EaseCircleInOut::create(ActionInterval* action) {
auto* ease = new EaseCircleInOut();
ease->initWithAction(action);
return ease;
}
void EaseCircleInOut::update(float time) {
innerAction_->update(easeInOutCirc(time));
}
ActionInterval* EaseCircleInOut::clone() const {
return EaseCircleInOut::create(innerAction_->clone());
}
ActionInterval* EaseCircleInOut::reverse() const {
return EaseCircleInOut::create(innerAction_->reverse());
}
// ============================================================================
// 自定义缓动
// ============================================================================
EaseCustom* EaseCustom::create(ActionInterval* action, EaseFunction easeFunc) {
auto* ease = new EaseCustom();
ease->initWithAction(action);
ease->easeFunc_ = easeFunc;
return ease;
}
void EaseCustom::update(float time) {
if (easeFunc_) {
innerAction_->update(easeFunc_(time));
} else {
innerAction_->update(time);
}
}
ActionInterval* EaseCustom::clone() const {
return EaseCustom::create(innerAction_->clone(), easeFunc_);
}
ActionInterval* EaseCustom::reverse() const {
return EaseCustom::create(innerAction_->reverse(), easeFunc_);
}
} // namespace extra2d

View File

@ -0,0 +1,30 @@
#include "extra2d/action/action_instant.h"
#include "extra2d/scene/node.h"
namespace extra2d {
ActionInstant::ActionInstant() {
duration_ = 0.0f;
}
bool ActionInstant::isDone() const {
return done_;
}
void ActionInstant::startWithTarget(Node* target) {
FiniteTimeAction::startWithTarget(target);
done_ = false;
}
void ActionInstant::step(float dt) {
(void)dt;
if (state_ != ActionState::Running) {
return;
}
execute();
done_ = true;
state_ = ActionState::Completed;
onComplete();
}
} // namespace extra2d

View File

@ -0,0 +1,202 @@
#include "extra2d/action/action_instant_actions.h"
#include "extra2d/scene/node.h"
namespace extra2d {
// ============================================================================
// 回调动作
// ============================================================================
CallFunc* CallFunc::create(const Callback& callback) {
auto* action = new CallFunc();
action->callback_ = callback;
return action;
}
void CallFunc::execute() {
if (callback_) {
callback_();
}
}
ActionInstant* CallFunc::clone() const {
return CallFunc::create(callback_);
}
ActionInstant* CallFunc::reverse() const {
return CallFunc::create(callback_);
}
// CallFuncN
CallFuncN* CallFuncN::create(const Callback& callback) {
auto* action = new CallFuncN();
action->callback_ = callback;
return action;
}
void CallFuncN::execute() {
if (callback_ && target_) {
callback_(target_);
}
}
ActionInstant* CallFuncN::clone() const {
return CallFuncN::create(callback_);
}
ActionInstant* CallFuncN::reverse() const {
return CallFuncN::create(callback_);
}
// ============================================================================
// 位置动作
// ============================================================================
Place* Place::create(const Vec2& position) {
auto* action = new Place();
action->position_ = position;
return action;
}
void Place::execute() {
if (target_) {
target_->setPosition(position_);
}
}
ActionInstant* Place::clone() const {
return Place::create(position_);
}
ActionInstant* Place::reverse() const {
return Place::create(position_);
}
// ============================================================================
// 翻转动作
// ============================================================================
FlipX* FlipX::create(bool flipX) {
auto* action = new FlipX();
action->flipX_ = flipX;
return action;
}
void FlipX::execute() {
if (target_) {
target_->setFlipX(flipX_);
}
}
ActionInstant* FlipX::clone() const {
return FlipX::create(flipX_);
}
ActionInstant* FlipX::reverse() const {
return FlipX::create(!flipX_);
}
// FlipY
FlipY* FlipY::create(bool flipY) {
auto* action = new FlipY();
action->flipY_ = flipY;
return action;
}
void FlipY::execute() {
if (target_) {
target_->setFlipY(flipY_);
}
}
ActionInstant* FlipY::clone() const {
return FlipY::create(flipY_);
}
ActionInstant* FlipY::reverse() const {
return FlipY::create(!flipY_);
}
// ============================================================================
// 可见性动作
// ============================================================================
Show* Show::create() {
return new Show();
}
void Show::execute() {
if (target_) {
target_->setVisible(true);
}
}
ActionInstant* Show::clone() const {
return Show::create();
}
ActionInstant* Show::reverse() const {
return Hide::create();
}
// Hide
Hide* Hide::create() {
return new Hide();
}
void Hide::execute() {
if (target_) {
target_->setVisible(false);
}
}
ActionInstant* Hide::clone() const {
return Hide::create();
}
ActionInstant* Hide::reverse() const {
return Show::create();
}
// ToggleVisibility
ToggleVisibility* ToggleVisibility::create() {
return new ToggleVisibility();
}
void ToggleVisibility::execute() {
if (target_) {
target_->setVisible(!target_->isVisible());
}
}
ActionInstant* ToggleVisibility::clone() const {
return ToggleVisibility::create();
}
ActionInstant* ToggleVisibility::reverse() const {
return ToggleVisibility::create();
}
// ============================================================================
// 节点管理动作
// ============================================================================
RemoveSelf* RemoveSelf::create() {
return new RemoveSelf();
}
void RemoveSelf::execute() {
if (target_) {
target_->removeFromParent();
}
}
ActionInstant* RemoveSelf::clone() const {
return RemoveSelf::create();
}
ActionInstant* RemoveSelf::reverse() const {
return RemoveSelf::create();
}
} // namespace extra2d

View File

@ -0,0 +1,56 @@
#include "extra2d/action/action_interval.h"
#include "extra2d/scene/node.h"
namespace extra2d {
ActionInterval::ActionInterval(float duration)
: FiniteTimeAction(duration) {
}
bool ActionInterval::isDone() const {
return elapsed_ >= duration_;
}
void ActionInterval::startWithTarget(Node* target) {
FiniteTimeAction::startWithTarget(target);
elapsed_ = 0.0f;
firstTick_ = true;
onStart();
}
void ActionInterval::stop() {
FiniteTimeAction::stop();
}
void ActionInterval::step(float dt) {
if (state_ != ActionState::Running) {
return;
}
if (firstTick_) {
firstTick_ = false;
elapsed_ = 0.0f;
} else {
elapsed_ += dt;
}
float progress = 0.0f;
if (duration_ > 0.0f) {
progress = std::min(1.0f, elapsed_ / duration_);
} else {
progress = 1.0f;
}
if (easeFunc_) {
progress = easeFunc_(progress);
}
onUpdate(progress);
if (progress >= 1.0f) {
state_ = ActionState::Completed;
onComplete();
}
}
} // namespace extra2d

View File

@ -0,0 +1,787 @@
#include "extra2d/action/action_interval_actions.h"
#include "extra2d/scene/node.h"
#include <algorithm>
#include <cstdarg>
namespace extra2d {
// ============================================================================
// 移动动作
// ============================================================================
MoveBy* MoveBy::create(float duration, const Vec2& delta) {
auto* action = new MoveBy();
action->duration_ = duration;
action->delta_ = delta;
return action;
}
void MoveBy::onStart() {
startPosition_ = target_->getPosition();
}
void MoveBy::onUpdate(float progress) {
Vec2 newPos = startPosition_ + delta_ * progress;
target_->setPosition(newPos);
}
ActionInterval* MoveBy::clone() const {
return MoveBy::create(duration_, delta_);
}
ActionInterval* MoveBy::reverse() const {
return MoveBy::create(duration_, -delta_);
}
// MoveTo
MoveTo* MoveTo::create(float duration, const Vec2& position) {
auto* action = new MoveTo();
action->duration_ = duration;
action->endPosition_ = position;
return action;
}
void MoveTo::onStart() {
startPosition_ = target_->getPosition();
delta_ = endPosition_ - startPosition_;
}
void MoveTo::onUpdate(float progress) {
Vec2 newPos = startPosition_ + delta_ * progress;
target_->setPosition(newPos);
}
ActionInterval* MoveTo::clone() const {
return MoveTo::create(duration_, endPosition_);
}
ActionInterval* MoveTo::reverse() const {
return MoveTo::create(duration_, startPosition_);
}
// ============================================================================
// 跳跃动作
// ============================================================================
JumpBy* JumpBy::create(float duration, const Vec2& position, float height, int jumps) {
auto* action = new JumpBy();
action->duration_ = duration;
action->delta_ = position;
action->height_ = height;
action->jumps_ = jumps;
return action;
}
void JumpBy::onStart() {
startPosition_ = target_->getPosition();
}
void JumpBy::onUpdate(float progress) {
float frac = (progress * jumps_) - static_cast<int>(progress * jumps_);
float y = height_ * 4.0f * frac * (1.0f - frac);
y += delta_.y * progress;
float x = delta_.x * progress;
target_->setPosition(startPosition_ + Vec2(x, y));
}
ActionInterval* JumpBy::clone() const {
return JumpBy::create(duration_, delta_, height_, jumps_);
}
ActionInterval* JumpBy::reverse() const {
return JumpBy::create(duration_, -delta_, height_, jumps_);
}
// JumpTo
JumpTo* JumpTo::create(float duration, const Vec2& position, float height, int jumps) {
auto* action = new JumpTo();
action->duration_ = duration;
action->endPosition_ = position;
action->height_ = height;
action->jumps_ = jumps;
return action;
}
void JumpTo::onStart() {
JumpBy::onStart();
delta_ = endPosition_ - startPosition_;
}
ActionInterval* JumpTo::clone() const {
return JumpTo::create(duration_, endPosition_, height_, jumps_);
}
ActionInterval* JumpTo::reverse() const {
return JumpTo::create(duration_, startPosition_, height_, jumps_);
}
// ============================================================================
// 贝塞尔曲线动作
// ============================================================================
BezierBy* BezierBy::create(float duration, const BezierConfig& config) {
auto* action = new BezierBy();
action->duration_ = duration;
action->config_ = config;
return action;
}
void BezierBy::onStart() {
startPosition_ = target_->getPosition();
}
void BezierBy::onUpdate(float progress) {
float xa = startPosition_.x;
float xb = config_.controlPoint1.x;
float xc = config_.controlPoint2.x;
float xd = config_.endPosition.x;
float ya = startPosition_.y;
float yb = config_.controlPoint1.y;
float yc = config_.controlPoint2.y;
float yd = config_.endPosition.y;
float x = bezierat(xa, xb, xc, xd, progress);
float y = bezierat(ya, yb, yc, yd, progress);
target_->setPosition(Vec2(x, y));
}
ActionInterval* BezierBy::clone() const {
return BezierBy::create(duration_, config_);
}
ActionInterval* BezierBy::reverse() const {
BezierConfig rev;
rev.controlPoint1 = config_.controlPoint2 + config_.endPosition;
rev.controlPoint2 = config_.controlPoint1 + config_.endPosition;
rev.endPosition = config_.endPosition;
return BezierBy::create(duration_, rev);
}
float BezierBy::bezierat(float a, float b, float c, float d, float t) {
return (powf(1 - t, 3) * a +
3.0f * t * powf(1 - t, 2) * b +
3.0f * t * t * (1 - t) * c +
t * t * t * d);
}
// BezierTo
BezierTo* BezierTo::create(float duration, const BezierConfig& config) {
auto* action = new BezierTo();
action->duration_ = duration;
action->originalConfig_ = config;
return action;
}
void BezierTo::onStart() {
BezierBy::onStart();
config_.controlPoint1 = originalConfig_.controlPoint1 - startPosition_;
config_.controlPoint2 = originalConfig_.controlPoint2 - startPosition_;
config_.endPosition = originalConfig_.endPosition - startPosition_;
}
ActionInterval* BezierTo::clone() const {
return BezierTo::create(duration_, originalConfig_);
}
ActionInterval* BezierTo::reverse() const {
BezierConfig rev;
rev.controlPoint1 = originalConfig_.controlPoint2;
rev.controlPoint2 = originalConfig_.controlPoint1;
rev.endPosition = startPosition_;
return BezierTo::create(duration_, rev);
}
// ============================================================================
// 缩放动作
// ============================================================================
ScaleBy* ScaleBy::create(float duration, float scale) {
return create(duration, scale, scale);
}
ScaleBy* ScaleBy::create(float duration, float scaleX, float scaleY) {
auto* action = new ScaleBy();
action->duration_ = duration;
action->deltaScale_ = Vec2(scaleX - 1.0f, scaleY - 1.0f);
return action;
}
ScaleBy* ScaleBy::create(float duration, const Vec2& scale) {
return create(duration, scale.x, scale.y);
}
void ScaleBy::onStart() {
startScale_ = target_->getScale();
}
void ScaleBy::onUpdate(float progress) {
Vec2 newScale = startScale_ + deltaScale_ * progress;
target_->setScale(newScale);
}
ActionInterval* ScaleBy::clone() const {
return ScaleBy::create(duration_, Vec2(startScale_.x + deltaScale_.x, startScale_.y + deltaScale_.y));
}
ActionInterval* ScaleBy::reverse() const {
return ScaleBy::create(duration_, Vec2(startScale_.x - deltaScale_.x, startScale_.y - deltaScale_.y));
}
// ScaleTo
ScaleTo* ScaleTo::create(float duration, float scale) {
return create(duration, scale, scale);
}
ScaleTo* ScaleTo::create(float duration, float scaleX, float scaleY) {
auto* action = new ScaleTo();
action->duration_ = duration;
action->endScale_ = Vec2(scaleX, scaleY);
return action;
}
ScaleTo* ScaleTo::create(float duration, const Vec2& scale) {
return create(duration, scale.x, scale.y);
}
void ScaleTo::onStart() {
startScale_ = target_->getScale();
delta_ = endScale_ - startScale_;
}
void ScaleTo::onUpdate(float progress) {
Vec2 newScale = startScale_ + delta_ * progress;
target_->setScale(newScale);
}
ActionInterval* ScaleTo::clone() const {
return ScaleTo::create(duration_, endScale_);
}
ActionInterval* ScaleTo::reverse() const {
return ScaleTo::create(duration_, startScale_);
}
// ============================================================================
// 旋转动作
// ============================================================================
RotateBy* RotateBy::create(float duration, float deltaAngle) {
auto* action = new RotateBy();
action->duration_ = duration;
action->deltaAngle_ = deltaAngle;
return action;
}
void RotateBy::onStart() {
startAngle_ = target_->getRotation();
}
void RotateBy::onUpdate(float progress) {
float newAngle = startAngle_ + deltaAngle_ * progress;
target_->setRotation(newAngle);
}
ActionInterval* RotateBy::clone() const {
return RotateBy::create(duration_, deltaAngle_);
}
ActionInterval* RotateBy::reverse() const {
return RotateBy::create(duration_, -deltaAngle_);
}
// RotateTo
RotateTo* RotateTo::create(float duration, float angle) {
auto* action = new RotateTo();
action->duration_ = duration;
action->endAngle_ = angle;
return action;
}
void RotateTo::onStart() {
startAngle_ = target_->getRotation();
deltaAngle_ = endAngle_ - startAngle_;
if (deltaAngle_ > 180.0f) deltaAngle_ -= 360.0f;
if (deltaAngle_ < -180.0f) deltaAngle_ += 360.0f;
}
void RotateTo::onUpdate(float progress) {
float newAngle = startAngle_ + deltaAngle_ * progress;
target_->setRotation(newAngle);
}
ActionInterval* RotateTo::clone() const {
return RotateTo::create(duration_, endAngle_);
}
ActionInterval* RotateTo::reverse() const {
return RotateTo::create(duration_, startAngle_);
}
// ============================================================================
// 淡入淡出动作
// ============================================================================
FadeIn* FadeIn::create(float duration) {
auto* action = new FadeIn();
action->duration_ = duration;
return action;
}
void FadeIn::onStart() {
startOpacity_ = target_->getOpacity();
target_->setOpacity(0.0f);
}
void FadeIn::onUpdate(float progress) {
target_->setOpacity(progress);
}
ActionInterval* FadeIn::clone() const {
return FadeIn::create(duration_);
}
ActionInterval* FadeIn::reverse() const {
return FadeOut::create(duration_);
}
// FadeOut
FadeOut* FadeOut::create(float duration) {
auto* action = new FadeOut();
action->duration_ = duration;
return action;
}
void FadeOut::onStart() {
startOpacity_ = target_->getOpacity();
target_->setOpacity(1.0f);
}
void FadeOut::onUpdate(float progress) {
target_->setOpacity(1.0f - progress);
}
ActionInterval* FadeOut::clone() const {
return FadeOut::create(duration_);
}
ActionInterval* FadeOut::reverse() const {
return FadeIn::create(duration_);
}
// FadeTo
FadeTo* FadeTo::create(float duration, float opacity) {
auto* action = new FadeTo();
action->duration_ = duration;
action->endOpacity_ = opacity;
return action;
}
void FadeTo::onStart() {
startOpacity_ = target_->getOpacity();
deltaOpacity_ = endOpacity_ - startOpacity_;
}
void FadeTo::onUpdate(float progress) {
target_->setOpacity(startOpacity_ + deltaOpacity_ * progress);
}
ActionInterval* FadeTo::clone() const {
return FadeTo::create(duration_, endOpacity_);
}
ActionInterval* FadeTo::reverse() const {
return FadeTo::create(duration_, startOpacity_);
}
// ============================================================================
// 闪烁动作
// ============================================================================
Blink* Blink::create(float duration, int times) {
auto* action = new Blink();
action->duration_ = duration;
action->times_ = times;
return action;
}
void Blink::onStart() {
originalVisible_ = target_->isVisible();
currentTimes_ = 0;
}
void Blink::onUpdate(float progress) {
float slice = 1.0f / times_;
float m = fmodf(progress, slice);
target_->setVisible(m > slice / 2.0f);
}
ActionInterval* Blink::clone() const {
return Blink::create(duration_, times_);
}
ActionInterval* Blink::reverse() const {
return Blink::create(duration_, times_);
}
// ============================================================================
// 色调动作
// ============================================================================
TintTo* TintTo::create(float duration, uint8_t red, uint8_t green, uint8_t blue) {
auto* action = new TintTo();
action->duration_ = duration;
action->endColor_ = Color3B(red, green, blue);
return action;
}
void TintTo::onStart() {
startColor_ = target_->getColor();
deltaColor_ = Color3B(
static_cast<int16_t>(endColor_.r) - static_cast<int16_t>(startColor_.r),
static_cast<int16_t>(endColor_.g) - static_cast<int16_t>(startColor_.g),
static_cast<int16_t>(endColor_.b) - static_cast<int16_t>(startColor_.b)
);
}
void TintTo::onUpdate(float progress) {
Color3B newColor(
static_cast<uint8_t>(startColor_.r + deltaColor_.r * progress),
static_cast<uint8_t>(startColor_.g + deltaColor_.g * progress),
static_cast<uint8_t>(startColor_.b + deltaColor_.b * progress)
);
target_->setColor(newColor);
}
ActionInterval* TintTo::clone() const {
return TintTo::create(duration_, endColor_.r, endColor_.g, endColor_.b);
}
ActionInterval* TintTo::reverse() const {
return TintTo::create(duration_, startColor_.r, startColor_.g, startColor_.b);
}
// TintBy
TintBy* TintBy::create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue) {
auto* action = new TintBy();
action->duration_ = duration;
action->deltaR_ = deltaRed;
action->deltaG_ = deltaGreen;
action->deltaB_ = deltaBlue;
return action;
}
void TintBy::onStart() {
startColor_ = target_->getColor();
}
void TintBy::onUpdate(float progress) {
Color3B newColor(
static_cast<uint8_t>(startColor_.r + deltaR_ * progress),
static_cast<uint8_t>(startColor_.g + deltaG_ * progress),
static_cast<uint8_t>(startColor_.b + deltaB_ * progress)
);
target_->setColor(newColor);
}
ActionInterval* TintBy::clone() const {
return TintBy::create(duration_, deltaR_, deltaG_, deltaB_);
}
ActionInterval* TintBy::reverse() const {
return TintBy::create(duration_, -deltaR_, -deltaG_, -deltaB_);
}
// ============================================================================
// 组合动作
// ============================================================================
Sequence* Sequence::create(ActionInterval* action1, ...) {
std::vector<ActionInterval*> actions;
actions.push_back(action1);
va_list args;
va_start(args, action1);
ActionInterval* action = nullptr;
while ((action = va_arg(args, ActionInterval*)) != nullptr) {
actions.push_back(action);
}
va_end(args);
return create(actions);
}
Sequence* Sequence::create(const std::vector<ActionInterval*>& actions) {
auto* seq = new Sequence();
seq->duration_ = 0.0f;
for (auto* action : actions) {
if (action) {
seq->actions_.push_back(action);
seq->duration_ += action->getDuration();
}
}
return seq;
}
Sequence::~Sequence() {
for (auto* action : actions_) {
delete action;
}
}
void Sequence::onStart() {
currentIndex_ = 0;
split_ = 0.0f;
last_ = -1.0f;
if (!actions_.empty()) {
actions_[0]->startWithTarget(target_);
}
}
void Sequence::onUpdate(float progress) {
float newTime = progress * duration_;
if (newTime < last_) {
for (auto* action : actions_) {
action->stop();
}
}
last_ = newTime;
float foundSplit = 0.0f;
size_t found = 0;
for (size_t i = 0; i < actions_.size(); ++i) {
foundSplit += actions_[i]->getDuration();
if (foundSplit > newTime) {
found = i;
break;
} else if (foundSplit == newTime) {
found = i + 1;
break;
}
}
if (found != currentIndex_) {
if (currentIndex_ < actions_.size()) {
actions_[currentIndex_]->update(1.0f);
}
if (found < actions_.size()) {
actions_[found]->startWithTarget(target_);
}
currentIndex_ = found;
}
if (currentIndex_ < actions_.size()) {
float localTime = newTime - (foundSplit - actions_[currentIndex_]->getDuration());
float localProgress = actions_[currentIndex_]->getDuration() > 0.0f
? localTime / actions_[currentIndex_]->getDuration() : 1.0f;
actions_[currentIndex_]->update(localProgress);
}
}
ActionInterval* Sequence::clone() const {
std::vector<ActionInterval*> cloned;
for (auto* action : actions_) {
cloned.push_back(action->clone());
}
return Sequence::create(cloned);
}
ActionInterval* Sequence::reverse() const {
std::vector<ActionInterval*> rev;
for (auto it = actions_.rbegin(); it != actions_.rend(); ++it) {
rev.push_back((*it)->reverse());
}
return Sequence::create(rev);
}
// Spawn
Spawn* Spawn::create(ActionInterval* action1, ...) {
std::vector<ActionInterval*> actions;
actions.push_back(action1);
va_list args;
va_start(args, action1);
ActionInterval* action = nullptr;
while ((action = va_arg(args, ActionInterval*)) != nullptr) {
actions.push_back(action);
}
va_end(args);
return create(actions);
}
Spawn* Spawn::create(const std::vector<ActionInterval*>& actions) {
auto* spawn = new Spawn();
spawn->duration_ = 0.0f;
for (auto* action : actions) {
if (action) {
spawn->actions_.push_back(action);
spawn->duration_ = std::max(spawn->duration_, action->getDuration());
}
}
return spawn;
}
Spawn::~Spawn() {
for (auto* action : actions_) {
delete action;
}
}
void Spawn::onStart() {
for (auto* action : actions_) {
action->startWithTarget(target_);
}
}
void Spawn::onUpdate(float progress) {
for (auto* action : actions_) {
float localProgress = action->getDuration() > 0.0f
? std::min(1.0f, (progress * duration_) / action->getDuration())
: 1.0f;
action->update(localProgress);
}
}
ActionInterval* Spawn::clone() const {
std::vector<ActionInterval*> cloned;
for (auto* action : actions_) {
cloned.push_back(action->clone());
}
return Spawn::create(cloned);
}
ActionInterval* Spawn::reverse() const {
std::vector<ActionInterval*> rev;
for (auto* action : actions_) {
rev.push_back(action->reverse());
}
return Spawn::create(rev);
}
// Repeat
Repeat* Repeat::create(ActionInterval* action, int times) {
auto* repeat = new Repeat();
repeat->innerAction_ = action;
repeat->times_ = times;
repeat->duration_ = action->getDuration() * times;
return repeat;
}
ActionInterval* Repeat::clone() const {
return Repeat::create(innerAction_->clone(), times_);
}
ActionInterval* Repeat::reverse() const {
return Repeat::create(innerAction_->reverse(), times_);
}
bool Repeat::isDone() const {
return currentTimes_ >= times_;
}
void Repeat::onStart() {
currentTimes_ = 0;
innerAction_->startWithTarget(target_);
}
void Repeat::onUpdate(float progress) {
float t = progress * times_;
int current = static_cast<int>(t);
if (current > currentTimes_) {
innerAction_->update(1.0f);
currentTimes_++;
if (currentTimes_ < times_) {
innerAction_->startWithTarget(target_);
}
}
if (currentTimes_ < times_) {
innerAction_->update(t - current);
}
}
// RepeatForever
RepeatForever* RepeatForever::create(ActionInterval* action) {
auto* repeat = new RepeatForever();
repeat->innerAction_ = action;
repeat->duration_ = action->getDuration();
return repeat;
}
ActionInterval* RepeatForever::clone() const {
return RepeatForever::create(innerAction_->clone());
}
ActionInterval* RepeatForever::reverse() const {
return RepeatForever::create(innerAction_->reverse());
}
bool RepeatForever::isDone() const {
return false;
}
void RepeatForever::onStart() {
innerAction_->startWithTarget(target_);
}
void RepeatForever::onUpdate(float progress) {
innerAction_->update(progress);
if (innerAction_->isDone()) {
innerAction_->startWithTarget(target_);
elapsed_ = 0.0f;
}
}
// DelayTime
DelayTime* DelayTime::create(float duration) {
auto* delay = new DelayTime();
delay->duration_ = duration;
return delay;
}
ActionInterval* DelayTime::clone() const {
return DelayTime::create(duration_);
}
ActionInterval* DelayTime::reverse() const {
return DelayTime::create(duration_);
}
// ReverseTime
ReverseTime* ReverseTime::create(ActionInterval* action) {
auto* rev = new ReverseTime();
rev->innerAction_ = action;
rev->duration_ = action->getDuration();
return rev;
}
ReverseTime::~ReverseTime() {
delete innerAction_;
}
ActionInterval* ReverseTime::clone() const {
return ReverseTime::create(innerAction_->clone());
}
ActionInterval* ReverseTime::reverse() const {
return innerAction_->clone();
}
void ReverseTime::onStart() {
innerAction_->startWithTarget(target_);
}
void ReverseTime::onUpdate(float progress) {
innerAction_->update(1.0f - progress);
}
} // namespace extra2d

View File

@ -0,0 +1,244 @@
#include "extra2d/action/action_manager.h"
#include "extra2d/scene/node.h"
#include "extra2d/utils/logger.h"
namespace extra2d {
ActionManager* ActionManager::instance_ = nullptr;
ActionManager::ActionManager() {}
ActionManager::~ActionManager() {
removeAllActions();
}
ActionManager* ActionManager::getInstance() {
if (!instance_) {
instance_ = new ActionManager();
}
return instance_;
}
void ActionManager::destroyInstance() {
if (instance_) {
delete instance_;
instance_ = nullptr;
}
}
void ActionManager::addAction(Action* action, Node* target, bool paused) {
if (!action || !target) {
return;
}
auto it = targets_.find(target);
if (it == targets_.end()) {
ActionElement element;
element.target = target;
element.paused = paused;
targets_[target] = element;
it = targets_.find(target);
}
auto& element = it->second;
element.actions.push_back(action);
action->startWithTarget(target);
}
void ActionManager::removeAction(Action* action) {
if (!action) {
return;
}
Node* target = action->getOriginalTarget();
if (!target) {
return;
}
auto it = targets_.find(target);
if (it == targets_.end()) {
return;
}
auto& element = it->second;
for (size_t i = 0; i < element.actions.size(); ++i) {
if (element.actions[i] == action) {
removeActionAt(i, element);
break;
}
}
}
void ActionManager::removeActionByTag(int tag, Node* target) {
if (!target) {
return;
}
auto it = targets_.find(target);
if (it == targets_.end()) {
return;
}
auto& element = it->second;
for (size_t i = 0; i < element.actions.size(); ++i) {
if (element.actions[i]->getTag() == tag) {
removeActionAt(i, element);
break;
}
}
}
void ActionManager::removeActionsByFlags(unsigned int flags, Node* target) {
if (!target) {
return;
}
auto it = targets_.find(target);
if (it == targets_.end()) {
return;
}
auto& element = it->second;
for (int i = static_cast<int>(element.actions.size()) - 1; i >= 0; --i) {
if (element.actions[i]->getFlags() & flags) {
removeActionAt(i, element);
}
}
}
void ActionManager::removeAllActionsFromTarget(Node* target) {
if (!target) {
return;
}
auto it = targets_.find(target);
if (it == targets_.end()) {
return;
}
auto& element = it->second;
for (auto* action : element.actions) {
action->stop();
deleteAction(action);
}
element.actions.clear();
targets_.erase(it);
}
void ActionManager::removeAllActions() {
for (auto& pair : targets_) {
auto& element = pair.second;
for (auto* action : element.actions) {
action->stop();
deleteAction(action);
}
}
targets_.clear();
}
Action* ActionManager::getActionByTag(int tag, Node* target) {
if (!target) {
return nullptr;
}
auto it = targets_.find(target);
if (it == targets_.end()) {
return nullptr;
}
auto& element = it->second;
for (auto* action : element.actions) {
if (action->getTag() == tag) {
return action;
}
}
return nullptr;
}
size_t ActionManager::getActionCount(Node* target) const {
auto it = targets_.find(target);
if (it == targets_.end()) {
return 0;
}
return it->second.actions.size();
}
void ActionManager::pauseTarget(Node* target) {
auto it = targets_.find(target);
if (it != targets_.end()) {
it->second.paused = true;
}
}
void ActionManager::resumeTarget(Node* target) {
auto it = targets_.find(target);
if (it != targets_.end()) {
it->second.paused = false;
}
}
bool ActionManager::isPaused(Node* target) const {
auto it = targets_.find(target);
if (it == targets_.end()) {
return false;
}
return it->second.paused;
}
void ActionManager::update(float dt) {
for (auto it = targets_.begin(); it != targets_.end(); ) {
auto& element = it->second;
Node* target = element.target;
if (!element.paused && target) {
element.actionIndex = 0;
while (static_cast<size_t>(element.actionIndex) < element.actions.size()) {
element.currentAction = element.actions[element.actionIndex];
element.currentActionSalvaged = false;
if (element.currentAction->getState() != ActionState::Paused) {
element.currentAction->step(dt);
}
if (element.currentActionSalvaged) {
deleteAction(element.currentAction);
} else if (element.currentAction->isDone()) {
element.currentAction->stop();
deleteAction(element.currentAction);
element.actions.erase(element.actions.begin() + element.actionIndex);
continue;
}
element.actionIndex++;
}
}
if (element.actions.empty()) {
it = targets_.erase(it);
} else {
++it;
}
}
}
void ActionManager::removeActionAt(size_t index, ActionElement& element) {
if (index >= element.actions.size()) {
return;
}
Action* action = element.actions[index];
if (action == element.currentAction) {
element.currentActionSalvaged = true;
}
action->stop();
deleteAction(action);
element.actions.erase(element.actions.begin() + index);
}
void ActionManager::deleteAction(Action* action) {
if (action) {
delete action;
}
}
} // namespace extra2d

View File

@ -0,0 +1,172 @@
#include "extra2d/action/action_special.h"
#include "extra2d/scene/node.h"
namespace extra2d {
// ============================================================================
// Speed
// ============================================================================
Speed* Speed::create(ActionInterval* action, float speed) {
auto* speedAction = new Speed();
speedAction->innerAction_ = action;
speedAction->speed_ = speed;
return speedAction;
}
Speed::~Speed() {
delete innerAction_;
}
void Speed::startWithTarget(Node* target) {
Action::startWithTarget(target);
innerAction_->startWithTarget(target);
}
void Speed::stop() {
innerAction_->stop();
Action::stop();
}
void Speed::step(float dt) {
if (state_ != ActionState::Running) {
return;
}
innerAction_->step(dt * speed_);
if (innerAction_->isDone()) {
state_ = ActionState::Completed;
onComplete();
}
}
bool Speed::isDone() const {
return innerAction_->isDone();
}
Action* Speed::clone() const {
return Speed::create(innerAction_->clone(), speed_);
}
Action* Speed::reverse() const {
return Speed::create(innerAction_->reverse(), speed_);
}
// ============================================================================
// Follow
// ============================================================================
Follow* Follow::create(Node* followedNode) {
return create(followedNode, Rect::Zero());
}
Follow* Follow::create(Node* followedNode, const Rect& boundary) {
auto* follow = new Follow();
follow->followedNode_ = followedNode;
follow->boundary_ = boundary;
follow->boundarySet_ = (boundary != Rect::Zero());
return follow;
}
Follow::~Follow() {
followedNode_ = nullptr;
}
void Follow::startWithTarget(Node* target) {
Action::startWithTarget(target);
if (target && followedNode_) {
halfScreenSize_ = Vec2(0, 0);
fullScreenSize_ = Vec2(0, 0);
if (boundarySet_) {
leftBoundary_ = Vec2(boundary_.origin.x, 0);
rightBoundary_ = Vec2(boundary_.origin.x + boundary_.size.width, 0);
topBoundary_ = Vec2(0, boundary_.origin.y);
bottomBoundary_ = Vec2(0, boundary_.origin.y + boundary_.size.height);
}
}
}
void Follow::stop() {
followedNode_ = nullptr;
Action::stop();
}
void Follow::step(float dt) {
(void)dt;
if (state_ != ActionState::Running || !followedNode_ || !target_) {
return;
}
Vec2 pos = followedNode_->getPosition();
if (boundarySet_) {
pos.x = std::clamp(pos.x, leftBoundary_.x, rightBoundary_.x);
pos.y = std::clamp(pos.y, bottomBoundary_.y, topBoundary_.y);
}
target_->setPosition(pos);
}
bool Follow::isDone() const {
return followedNode_ == nullptr || !followedNode_->isRunning();
}
Action* Follow::clone() const {
return Follow::create(followedNode_, boundary_);
}
Action* Follow::reverse() const {
return Follow::create(followedNode_, boundary_);
}
// ============================================================================
// TargetedAction
// ============================================================================
TargetedAction* TargetedAction::create(Node* target, FiniteTimeAction* action) {
auto* targeted = new TargetedAction();
targeted->targetNode_ = target;
targeted->innerAction_ = action;
return targeted;
}
TargetedAction::~TargetedAction() {
delete innerAction_;
}
void TargetedAction::startWithTarget(Node* target) {
Action::startWithTarget(target);
if (targetNode_) {
innerAction_->startWithTarget(targetNode_);
}
}
void TargetedAction::stop() {
innerAction_->stop();
Action::stop();
}
void TargetedAction::step(float dt) {
if (state_ != ActionState::Running) {
return;
}
innerAction_->step(dt);
if (innerAction_->isDone()) {
state_ = ActionState::Completed;
onComplete();
}
}
bool TargetedAction::isDone() const {
return innerAction_->isDone();
}
Action* TargetedAction::clone() const {
return TargetedAction::create(targetNode_, innerAction_->clone());
}
Action* TargetedAction::reverse() const {
return TargetedAction::create(targetNode_, innerAction_->reverse());
}
} // namespace extra2d

View File

@ -1,405 +0,0 @@
#include "extra2d/action/actions.h"
#include "extra2d/scene/node.h"
#include <algorithm>
namespace extra2d {
// IntervalAction
IntervalAction::IntervalAction(float duration) { duration_ = duration; }
bool IntervalAction::isDone() const { return elapsed_ >= duration_; }
// InstantAction
InstantAction::InstantAction() { duration_ = 0.0f; }
bool InstantAction::isDone() const { return true; }
// MoveBy
MoveBy::MoveBy(float duration, const Vec2 &delta)
: IntervalAction(duration), delta_(delta) {}
void MoveBy::onStart() { startPosition_ = target_->getPosition(); }
void MoveBy::onUpdate(float progress) {
Vec2 newPos = startPosition_ + delta_ * progress;
target_->setPosition(newPos);
}
Action *MoveBy::clone() const { return new MoveBy(duration_, delta_); }
Action *MoveBy::reverse() const { return new MoveBy(duration_, -delta_); }
// MoveTo
MoveTo::MoveTo(float duration, const Vec2 &position)
: IntervalAction(duration), endPosition_(position) {}
void MoveTo::onStart() {
startPosition_ = target_->getPosition();
delta_ = endPosition_ - startPosition_;
}
void MoveTo::onUpdate(float progress) {
Vec2 newPos = startPosition_ + delta_ * progress;
target_->setPosition(newPos);
}
Action *MoveTo::clone() const { return new MoveTo(duration_, endPosition_); }
Action *MoveTo::reverse() const {
return new MoveTo(duration_, startPosition_);
}
// ScaleBy
ScaleBy::ScaleBy(float duration, float scale)
: IntervalAction(duration), deltaScale_(scale - 1.0f, scale - 1.0f) {}
ScaleBy::ScaleBy(float duration, float scaleX, float scaleY)
: IntervalAction(duration), deltaScale_(scaleX - 1.0f, scaleY - 1.0f) {}
ScaleBy::ScaleBy(float duration, const Vec2 &scale)
: IntervalAction(duration), deltaScale_(scale.x - 1.0f, scale.y - 1.0f) {}
void ScaleBy::onStart() { startScale_ = target_->getScale(); }
void ScaleBy::onUpdate(float progress) {
Vec2 newScale = startScale_ + deltaScale_ * progress;
target_->setScale(newScale);
}
Action *ScaleBy::clone() const {
return new ScaleBy(duration_, Vec2(startScale_.x + deltaScale_.x,
startScale_.y + deltaScale_.y));
}
Action *ScaleBy::reverse() const {
return new ScaleBy(duration_, Vec2(startScale_.x - deltaScale_.x,
startScale_.y - deltaScale_.y));
}
// ScaleTo
ScaleTo::ScaleTo(float duration, float scale)
: IntervalAction(duration), endScale_(scale, scale) {}
ScaleTo::ScaleTo(float duration, float scaleX, float scaleY)
: IntervalAction(duration), endScale_(scaleX, scaleY) {}
ScaleTo::ScaleTo(float duration, const Vec2 &scale)
: IntervalAction(duration), endScale_(scale) {}
void ScaleTo::onStart() {
startScale_ = target_->getScale();
delta_ = endScale_ - startScale_;
}
void ScaleTo::onUpdate(float progress) {
Vec2 newScale = startScale_ + delta_ * progress;
target_->setScale(newScale);
}
Action *ScaleTo::clone() const { return new ScaleTo(duration_, endScale_); }
Action *ScaleTo::reverse() const { return new ScaleTo(duration_, startScale_); }
// RotateBy
RotateBy::RotateBy(float duration, float deltaAngle)
: IntervalAction(duration), deltaAngle_(deltaAngle) {}
RotateBy::RotateBy(float duration, float deltaAngleX, float deltaAngleY)
: IntervalAction(duration), deltaAngle_(deltaAngleX) {
(void)deltaAngleY;
}
void RotateBy::onStart() { startAngle_ = target_->getRotation(); }
void RotateBy::onUpdate(float progress) {
float newAngle = startAngle_ + deltaAngle_ * progress;
target_->setRotation(newAngle);
}
Action *RotateBy::clone() const { return new RotateBy(duration_, deltaAngle_); }
Action *RotateBy::reverse() const {
return new RotateBy(duration_, -deltaAngle_);
}
// RotateTo
RotateTo::RotateTo(float duration, float angle)
: IntervalAction(duration), endAngle_(angle) {}
RotateTo::RotateTo(float duration, float angleX, float angleY)
: IntervalAction(duration), endAngle_(angleX) {
(void)angleY;
}
void RotateTo::onStart() {
startAngle_ = target_->getRotation();
deltaAngle_ = endAngle_ - startAngle_;
// Shortest path
if (deltaAngle_ > 180.0f)
deltaAngle_ -= 360.0f;
if (deltaAngle_ < -180.0f)
deltaAngle_ += 360.0f;
}
void RotateTo::onUpdate(float progress) {
float newAngle = startAngle_ + deltaAngle_ * progress;
target_->setRotation(newAngle);
}
Action *RotateTo::clone() const { return new RotateTo(duration_, endAngle_); }
Action *RotateTo::reverse() const {
return new RotateTo(duration_, startAngle_);
}
// FadeIn
FadeIn::FadeIn(float duration) : IntervalAction(duration) {}
void FadeIn::onStart() {
startOpacity_ = target_->getOpacity();
target_->setOpacity(0.0f);
}
void FadeIn::onUpdate(float progress) { target_->setOpacity(progress); }
Action *FadeIn::clone() const { return new FadeIn(duration_); }
Action *FadeIn::reverse() const { return new FadeOut(duration_); }
// FadeOut
FadeOut::FadeOut(float duration) : IntervalAction(duration) {}
void FadeOut::onStart() {
startOpacity_ = target_->getOpacity();
target_->setOpacity(1.0f);
}
void FadeOut::onUpdate(float progress) { target_->setOpacity(1.0f - progress); }
Action *FadeOut::clone() const { return new FadeOut(duration_); }
Action *FadeOut::reverse() const { return new FadeIn(duration_); }
// FadeTo
FadeTo::FadeTo(float duration, float opacity)
: IntervalAction(duration), endOpacity_(opacity) {}
void FadeTo::onStart() {
startOpacity_ = target_->getOpacity();
deltaOpacity_ = endOpacity_ - startOpacity_;
}
void FadeTo::onUpdate(float progress) {
target_->setOpacity(startOpacity_ + deltaOpacity_ * progress);
}
Action *FadeTo::clone() const { return new FadeTo(duration_, endOpacity_); }
Action *FadeTo::reverse() const { return new FadeTo(duration_, startOpacity_); }
// Sequence
Sequence::Sequence(const std::vector<Action *> &actions)
: IntervalAction(0.0f) {
for (auto *action : actions) {
if (action) {
actions_.push_back(action->clone());
duration_ += action->getDuration();
}
}
}
Sequence::~Sequence() {
for (auto *action : actions_)
delete action;
}
void Sequence::onStart() {
currentIndex_ = 0;
split_ = 0.0f;
last_ = -1.0f;
if (!actions_.empty()) {
actions_[0]->start(target_);
}
}
void Sequence::onUpdate(float progress) {
int found = 0;
float newTime = progress * duration_;
if (newTime < last_) {
// Rewind
for (auto *action : actions_) {
action->stop();
}
}
last_ = newTime;
for (size_t i = 0; i < actions_.size(); ++i) {
split_ += actions_[i]->getDuration();
if (split_ > newTime) {
found = static_cast<int>(i);
break;
} else if (split_ == newTime) {
found = static_cast<int>(i) + 1;
break;
}
}
if (found != currentIndex_) {
if (currentIndex_ >= 0 &&
currentIndex_ < static_cast<int>(actions_.size())) {
actions_[currentIndex_]->update(actions_[currentIndex_]->getDuration());
}
if (found >= 0 && found < static_cast<int>(actions_.size())) {
actions_[found]->start(target_);
}
currentIndex_ = found;
}
if (currentIndex_ >= 0 && currentIndex_ < static_cast<int>(actions_.size())) {
float localProgress = 0.0f;
if (actions_[currentIndex_]->getDuration() > 0.0f) {
localProgress =
(newTime - (split_ - actions_[currentIndex_]->getDuration())) /
actions_[currentIndex_]->getDuration();
}
actions_[currentIndex_]->step(actions_[currentIndex_]->getDuration() *
localProgress);
}
}
Action *Sequence::clone() const { return new Sequence(actions_); }
Action *Sequence::reverse() const {
std::vector<Action *> rev;
for (auto it = actions_.rbegin(); it != actions_.rend(); ++it) {
rev.push_back((*it)->reverse());
}
return new Sequence(rev);
}
// Spawn
Spawn::Spawn(const std::vector<Action *> &actions) : IntervalAction(0.0f) {
for (auto *action : actions) {
if (action) {
actions_.push_back(action->clone());
duration_ = std::max(duration_, action->getDuration());
}
}
}
Spawn::~Spawn() {
for (auto *action : actions_)
delete action;
}
void Spawn::onStart() {
for (auto *action : actions_) {
action->start(target_);
}
}
void Spawn::onUpdate(float progress) {
for (auto *action : actions_) {
float localProgress = 0.0f;
if (action->getDuration() > 0.0f) {
localProgress =
std::min(1.0f, (progress * duration_) / action->getDuration());
} else {
localProgress = 1.0f;
}
action->step(action->getDuration() * localProgress);
}
}
Action *Spawn::clone() const { return new Spawn(actions_); }
Action *Spawn::reverse() const {
std::vector<Action *> rev;
for (auto *action : actions_) {
rev.push_back(action->reverse());
}
return new Spawn(rev);
}
// Loop
Loop::Loop(Action *action, int times)
: action_(action ? action->clone() : nullptr), times_(times),
currentTimes_(0) {
if (action_) {
duration_ = times < 0 ? -1.0f : action_->getDuration() * times;
}
}
Loop::~Loop() { delete action_; }
bool Loop::isDone() const {
if (times_ < 0)
return false;
return currentTimes_ >= times_;
}
void Loop::onStart() {
currentTimes_ = 0;
if (action_) {
action_->start(target_);
}
}
void Loop::onUpdate(float progress) {
if (!action_)
return;
float actionDuration = action_->getDuration();
float dt = progress * duration_ - elapsed_;
while (dt > 0.0f) {
float localProgress = std::min(1.0f, dt / actionDuration);
action_->step(actionDuration * localProgress);
if (action_->isDone()) {
currentTimes_++;
if (times_ > 0 && currentTimes_ >= times_)
break;
action_->restart();
}
dt -= actionDuration;
}
}
Action *Loop::clone() const { return new Loop(action_, times_); }
Action *Loop::reverse() const {
return new Loop(action_ ? action_->reverse() : nullptr, times_);
}
// Delay
Delay::Delay(float duration) : IntervalAction(duration) {}
void Delay::onUpdate(float progress) {
// No update needed, just wait
}
Action *Delay::clone() const { return new Delay(duration_); }
Action *Delay::reverse() const { return new Delay(duration_); }
// CallFunc
CallFunc::CallFunc(Callback callback) : callback_(std::move(callback)) {}
void CallFunc::onUpdate(float progress) {
(void)progress;
if (callback_)
callback_();
}
Action *CallFunc::clone() const { return new CallFunc(callback_); }
Action *CallFunc::reverse() const { return new CallFunc(callback_); }
} // namespace extra2d

View File

@ -1,62 +1,104 @@
#include "extra2d/action/ease.h"
#include <cmath>
#include <extra2d/action/ease.h>
namespace extra2d {
// Linear
float easeLinear(float t) { return t; }
// Quadratic
float easeInQuad(float t) { return t * t; }
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
float easeOutQuad(float t) { return 1.0f - (1.0f - t) * (1.0f - t); }
// ============================================================================
// 线性缓动
// ============================================================================
float easeLinear(float t) {
return t;
}
// ============================================================================
// 二次缓动 (Quad)
// ============================================================================
float easeInQuad(float t) {
return t * t;
}
float easeOutQuad(float t) {
return 1.0f - (1.0f - t) * (1.0f - t);
}
float easeInOutQuad(float t) {
return t < 0.5f ? 2.0f * t * t
: 1.0f - std::pow(-2.0f * t + 2.0f, 2.0f) / 2.0f;
return t < 0.5f ? 2.0f * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 2.0f) / 2.0f;
}
// Cubic
float easeInCubic(float t) { return t * t * t; }
// ============================================================================
// 三次缓动 (Cubic)
// ============================================================================
float easeOutCubic(float t) { return 1.0f - std::pow(1.0f - t, 3.0f); }
float easeInCubic(float t) {
return t * t * t;
}
float easeOutCubic(float t) {
return 1.0f - std::pow(1.0f - t, 3.0f);
}
float easeInOutCubic(float t) {
return t < 0.5f ? 4.0f * t * t * t
: 1.0f - std::pow(-2.0f * t + 2.0f, 3.0f) / 2.0f;
return t < 0.5f ? 4.0f * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 3.0f) / 2.0f;
}
// Quartic
float easeInQuart(float t) { return t * t * t * t; }
// ============================================================================
// 四次缓动 (Quart)
// ============================================================================
float easeOutQuart(float t) { return 1.0f - std::pow(1.0f - t, 4.0f); }
float easeInQuart(float t) {
return t * t * t * t;
}
float easeOutQuart(float t) {
return 1.0f - std::pow(1.0f - t, 4.0f);
}
float easeInOutQuart(float t) {
return t < 0.5f ? 8.0f * t * t * t * t
: 1.0f - std::pow(-2.0f * t + 2.0f, 4.0f) / 2.0f;
return t < 0.5f ? 8.0f * t * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 4.0f) / 2.0f;
}
// Quintic
float easeInQuint(float t) { return t * t * t * t * t; }
// ============================================================================
// 五次缓动 (Quint)
// ============================================================================
float easeOutQuint(float t) { return 1.0f - std::pow(1.0f - t, 5.0f); }
float easeInQuint(float t) {
return t * t * t * t * t;
}
float easeOutQuint(float t) {
return 1.0f - std::pow(1.0f - t, 5.0f);
}
float easeInOutQuint(float t) {
return t < 0.5f ? 16.0f * t * t * t * t * t
: 1.0f - std::pow(-2.0f * t + 2.0f, 5.0f) / 2.0f;
return t < 0.5f ? 16.0f * t * t * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 5.0f) / 2.0f;
}
// Sine
// ============================================================================
// 正弦缓动 (Sine)
// ============================================================================
float easeInSine(float t) {
return 1.0f - std::cos((t * 3.14159265359f) / 2.0f);
return 1.0f - std::cos((t * M_PI) / 2.0f);
}
float easeOutSine(float t) { return std::sin((t * 3.14159265359f) / 2.0f); }
float easeOutSine(float t) {
return std::sin((t * M_PI) / 2.0f);
}
float easeInOutSine(float t) {
return -(std::cos(3.14159265359f * t) - 1.0f) / 2.0f;
return -(std::cos(M_PI * t) - 1.0f) / 2.0f;
}
// Exponential
// ============================================================================
// 指数缓动 (Exponential)
// ============================================================================
float easeInExpo(float t) {
return t == 0.0f ? 0.0f : std::pow(2.0f, 10.0f * (t - 1.0f));
}
@ -66,16 +108,20 @@ float easeOutExpo(float t) {
}
float easeInOutExpo(float t) {
if (t == 0.0f)
return 0.0f;
if (t == 1.0f)
return 1.0f;
return t < 0.5f ? std::pow(2.0f, 20.0f * t - 10.0f) / 2.0f
if (t == 0.0f) return 0.0f;
if (t == 1.0f) return 1.0f;
return t < 0.5f
? std::pow(2.0f, 20.0f * t - 10.0f) / 2.0f
: (2.0f - std::pow(2.0f, -20.0f * t + 10.0f)) / 2.0f;
}
// Circular
float easeInCirc(float t) { return 1.0f - std::sqrt(1.0f - std::pow(t, 2.0f)); }
// ============================================================================
// 圆形缓动 (Circular)
// ============================================================================
float easeInCirc(float t) {
return 1.0f - std::sqrt(1.0f - std::pow(t, 2.0f));
}
float easeOutCirc(float t) {
return std::sqrt(1.0f - std::pow(t - 1.0f, 2.0f));
@ -84,11 +130,13 @@ float easeOutCirc(float t) {
float easeInOutCirc(float t) {
return t < 0.5f
? (1.0f - std::sqrt(1.0f - std::pow(2.0f * t, 2.0f))) / 2.0f
: (std::sqrt(1.0f - std::pow(-2.0f * t + 2.0f, 2.0f)) + 1.0f) /
2.0f;
: (std::sqrt(1.0f - std::pow(-2.0f * t + 2.0f, 2.0f)) + 1.0f) / 2.0f;
}
// Back
// ============================================================================
// 回震缓动 (Back)
// ============================================================================
float easeInBack(float t) {
const float c1 = 1.70158f;
const float c3 = c1 + 1.0f;
@ -106,48 +154,40 @@ float easeInOutBack(float t) {
const float c2 = c1 * 1.525f;
return t < 0.5f
? (std::pow(2.0f * t, 2.0f) * ((c2 + 1.0f) * 2.0f * t - c2)) / 2.0f
: (std::pow(2.0f * t - 2.0f, 2.0f) *
((c2 + 1.0f) * (t * 2.0f - 2.0f) + c2) +
2.0f) /
2.0f;
: (std::pow(2.0f * t - 2.0f, 2.0f) * ((c2 + 1.0f) * (t * 2.0f - 2.0f) + c2) + 2.0f) / 2.0f;
}
// Elastic
// ============================================================================
// 弹性缓动 (Elastic)
// ============================================================================
float easeInElastic(float t) {
const float c4 = (2.0f * 3.14159265359f) / 3.0f;
if (t == 0.0f)
return 0.0f;
if (t == 1.0f)
return 1.0f;
return -std::pow(2.0f, 10.0f * t - 10.0f) *
std::sin((t * 10.0f - 10.75f) * c4);
const float c4 = (2.0f * M_PI) / 3.0f;
if (t == 0.0f) return 0.0f;
if (t == 1.0f) return 1.0f;
return -std::pow(2.0f, 10.0f * t - 10.0f) * std::sin((t * 10.0f - 10.75f) * c4);
}
float easeOutElastic(float t) {
const float c4 = (2.0f * 3.14159265359f) / 3.0f;
if (t == 0.0f)
return 0.0f;
if (t == 1.0f)
return 1.0f;
const float c4 = (2.0f * M_PI) / 3.0f;
if (t == 0.0f) return 0.0f;
if (t == 1.0f) return 1.0f;
return std::pow(2.0f, -10.0f * t) * std::sin((t * 10.0f - 0.75f) * c4) + 1.0f;
}
float easeInOutElastic(float t) {
const float c5 = (2.0f * 3.14159265359f) / 4.5f;
if (t == 0.0f)
return 0.0f;
if (t == 1.0f)
return 1.0f;
return t < 0.5f ? -(std::pow(2.0f, 20.0f * t - 10.0f) *
std::sin((20.0f * t - 11.125f) * c5)) /
2.0f
: (std::pow(2.0f, -20.0f * t + 10.0f) *
std::sin((20.0f * t - 11.125f) * c5)) /
2.0f +
1.0f;
const float c5 = (2.0f * M_PI) / 4.5f;
if (t == 0.0f) return 0.0f;
if (t == 1.0f) return 1.0f;
return t < 0.5f
? -(std::pow(2.0f, 20.0f * t - 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f
: (std::pow(2.0f, -20.0f * t + 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f + 1.0f;
}
// Bounce
// ============================================================================
// 弹跳缓动 (Bounce)
// ============================================================================
namespace {
float easeOutBounceInternal(float t) {
const float n1 = 7.5625f;
@ -166,14 +206,20 @@ float easeOutBounceInternal(float t) {
return n1 * t * t + 0.984375f;
}
}
} // namespace
}
float easeInBounce(float t) { return 1.0f - easeOutBounceInternal(1.0f - t); }
float easeInBounce(float t) {
return 1.0f - easeOutBounceInternal(1.0f - t);
}
float easeOutBounce(float t) { return easeOutBounceInternal(t); }
float easeOutBounce(float t) {
return easeOutBounceInternal(t);
}
float easeInOutBounce(float t) {
return t < 0.5f ? (1.0f - easeOutBounceInternal(1.0f - 2.0f * t)) / 2.0f
return t < 0.5f
? (1.0f - easeOutBounceInternal(1.0f - 2.0f * t)) / 2.0f
: (1.0f + easeOutBounceInternal(2.0f * t - 1.0f)) / 2.0f;
}
} // namespace extra2d

View File

@ -0,0 +1,9 @@
#include "extra2d/action/finite_time_action.h"
namespace extra2d {
FiniteTimeAction::FiniteTimeAction(float duration)
: duration_(duration) {
}
} // namespace extra2d

View File

@ -1,3 +1,4 @@
#include <extra2d/action/action_manager.h>
#include <extra2d/app/application.h>
#include <extra2d/audio/audio_engine.h>
#include <extra2d/event/event_dispatcher.h>
@ -13,9 +14,9 @@
#include <extra2d/utils/object_pool.h>
#include <extra2d/utils/timer.h>
#include <chrono>
#include <thread>
#include <time.h>
#ifdef __SWITCH__
#include <switch.h>
@ -74,7 +75,8 @@ bool Application::init(const AppConfig &config) {
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem",
rc);
}
// ========================================
@ -340,6 +342,9 @@ void Application::mainLoop() {
}
void Application::update() {
// Update action manager (single update per frame)
ActionManager::getInstance()->update(deltaTime_);
if (timerManager_) {
timerManager_->update(deltaTime_);
}

View File

@ -1,6 +1,7 @@
#include <algorithm>
#include <cmath>
#include <extra2d/action/action.h>
#include <extra2d/action/action_manager.h>
#include <extra2d/graphics/render_command.h>
#include <extra2d/scene/node.h>
#include <extra2d/scene/scene.h>
@ -13,6 +14,7 @@ Node::Node() = default;
Node::~Node() {
removeAllChildren();
stopAllActions();
ActionManager::getInstance()->removeAllActionsFromTarget(this);
}
void Node::addChild(Ptr<Node> child) {
@ -201,6 +203,12 @@ void Node::setOpacity(float opacity) {
void Node::setVisible(bool visible) { visible_ = visible; }
void Node::setColor(const Color3B& color) { color_ = color; }
void Node::setFlipX(bool flipX) { flipX_ = flipX; }
void Node::setFlipY(bool flipY) { flipY_ = flipY; }
void Node::setZOrder(int zOrder) {
if (zOrder_ != zOrder) {
zOrder_ = zOrder;
@ -332,17 +340,6 @@ void Node::onExit() {
void Node::onUpdate(float dt) {
onUpdateNode(dt);
// Update actions
for (auto it = actions_.begin(); it != actions_.end();) {
auto &action = *it;
action->update(dt);
if (action->isDone()) {
it = actions_.erase(it);
} else {
++it;
}
}
// Update children
for (auto &child : children_) {
child->onUpdate(dt);
@ -406,74 +403,44 @@ void Node::updateSpatialIndex() {
}
}
void Node::runAction(Ptr<Action> action) {
// ============================================================================
// 动作系统 - 新接口
// ============================================================================
Action* Node::runAction(Action* action) {
if (!action) {
return;
return nullptr;
}
action->start(this);
int tag = action->getTag();
if (tag != -1) {
// 有 tag 的 Action 存入哈希表O(1) 查找
// 如果已存在相同 tag 的 Action先停止它
auto it = actionByTag_.find(tag);
if (it != actionByTag_.end()) {
// 从 vector 中移除旧的 Action
auto oldAction = it->second;
auto vecIt = std::find(actions_.begin(), actions_.end(), oldAction);
if (vecIt != actions_.end()) {
actions_.erase(vecIt);
}
}
actionByTag_[tag] = action;
}
actions_.push_back(action);
ActionManager::getInstance()->addAction(action, this);
return action;
}
void Node::stopAllActions() {
actions_.clear();
actionByTag_.clear();
ActionManager::getInstance()->removeAllActionsFromTarget(this);
}
void Node::stopAction(Ptr<Action> action) {
if (!action) {
return;
}
// 从 vector 中移除
auto it = std::find(actions_.begin(), actions_.end(), action);
if (it != actions_.end()) {
// 如果有 tag从哈希表中也移除
int tag = action->getTag();
if (tag != -1) {
actionByTag_.erase(tag);
}
actions_.erase(it);
}
void Node::stopAction(Action* action) {
ActionManager::getInstance()->removeAction(action);
}
void Node::stopActionByTag(int tag) {
auto it = actionByTag_.find(tag);
if (it != actionByTag_.end()) {
auto action = it->second;
// 从 vector 中移除
auto vecIt = std::find(actions_.begin(), actions_.end(), action);
if (vecIt != actions_.end()) {
actions_.erase(vecIt);
}
actionByTag_.erase(it);
}
ActionManager::getInstance()->removeActionByTag(tag, this);
}
Ptr<Action> Node::getActionByTag(int tag) const {
// O(1) 哈希查找
auto it = actionByTag_.find(tag);
if (it != actionByTag_.end()) {
return it->second;
void Node::stopActionsByFlags(unsigned int flags) {
ActionManager::getInstance()->removeActionsByFlags(flags, this);
}
return nullptr;
Action* Node::getActionByTag(int tag) {
return ActionManager::getInstance()->getActionByTag(tag, this);
}
size_t Node::getActionCount() const {
return ActionManager::getInstance()->getActionCount(const_cast<Node*>(this));
}
bool Node::isRunningActions() const {
return getActionCount() > 0;
}
void Node::update(float dt) { onUpdate(dt); }

View File

@ -0,0 +1,598 @@
# 动作系统 (Action System)
Extra2D 的动作系统提供了一套完整的动画解决方案,参考 Cocos2d-x 的设计模式,支持丰富的动作类型和缓动效果。
## 目录
1. [概述](#概述)
2. [核心概念](#核心概念)
3. [基本动作](#基本动作)
4. [组合动作](#组合动作)
5. [缓动动作](#缓动动作)
6. [特殊动作](#特殊动作)
7. [瞬时动作](#瞬时动作)
8. [动作管理](#动作管理)
9. [完整示例](#完整示例)
---
## 概述
动作系统用于在节点上创建动画效果,通过修改节点的属性(位置、缩放、旋转、透明度等)实现各种动画。
### 类层次结构
```
Action (基类)
├── FiniteTimeAction (有限时间动作)
│ ├── ActionInterval (时间间隔动作)
│ │ ├── MoveBy/MoveTo
│ │ ├── JumpBy/JumpTo
│ │ ├── BezierBy/BezierTo
│ │ ├── ScaleBy/ScaleTo
│ │ ├── RotateBy/RotateTo
│ │ ├── FadeIn/FadeOut/FadeTo
│ │ ├── Blink
│ │ ├── TintBy/TintTo
│ │ ├── Sequence
│ │ ├── Spawn
│ │ ├── Repeat/RepeatForever
│ │ ├── DelayTime
│ │ ├── ReverseTime
│ │ └── ActionEase (缓动动作基类)
│ └── ActionInstant (瞬时动作)
│ ├── CallFunc/CallFuncN
│ ├── Place
│ ├── FlipX/FlipY
│ ├── Show/Hide
│ ├── ToggleVisibility
│ └── RemoveSelf
├── Speed (速度控制)
├── Follow (跟随动作)
└── TargetedAction (目标动作)
```
---
## 核心概念
### 创建动作
所有动作都使用静态工厂方法 `create()` 创建:
```cpp
// 创建移动动作
auto move = MoveTo::create(2.0f, Vec2(100, 100));
// 创建缩放动作
auto scale = ScaleTo::create(1.0f, 2.0f);
// 创建旋转动作
auto rotate = RotateBy::create(3.0f, 360.0f);
```
### 运行动作
通过节点的 `runAction()` 方法运行动作:
```cpp
sprite->runAction(move);
```
### 动作完成回调
使用 `setCompletionCallback()` 设置动作完成时的回调:
```cpp
auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setCompletionCallback([]() {
E2D_LOG_INFO("动作完成!");
});
sprite->runAction(action);
```
---
## 基本动作
### 移动动作
```cpp
// MoveTo - 移动到指定位置
auto moveTo = MoveTo::create(2.0f, Vec2(100, 100));
// MoveBy - 相对移动
auto moveBy = MoveBy::create(2.0f, Vec2(50, 0)); // 向右移动 50 像素
```
### 跳跃动作
```cpp
// JumpTo - 跳跃到指定位置
auto jumpTo = JumpTo::create(2.0f, Vec2(100, 100), 50, 3); // 跳到 (100,100),高度 50跳 3 次
// JumpBy - 相对跳跃
auto jumpBy = JumpBy::create(2.0f, Vec2(100, 0), 50, 3); // 向右跳跃 100 像素
```
### 贝塞尔曲线动作
```cpp
BezierConfig bezier;
bezier.controlPoint1 = Vec2(100, 200);
bezier.controlPoint2 = Vec2(200, 100);
bezier.endPosition = Vec2(300, 150);
// BezierTo - 贝塞尔曲线移动到指定位置
auto bezierTo = BezierTo::create(3.0f, bezier);
// BezierBy - 相对贝塞尔曲线移动
auto bezierBy = BezierBy::create(3.0f, bezier);
```
### 缩放动作
```cpp
// ScaleTo - 缩放到指定比例
auto scaleTo = ScaleTo::create(1.0f, 2.0f); // 缩放到 2 倍
auto scaleToXY = ScaleTo::create(1.0f, 2.0f, 1.5f); // X 缩放 2 倍Y 缩放 1.5 倍
// ScaleBy - 相对缩放
auto scaleBy = ScaleBy::create(1.0f, 0.5f); // 缩小一半
```
### 旋转动作
```cpp
// RotateTo - 旋转到指定角度
auto rotateTo = RotateTo::create(2.0f, 90.0f); // 旋转到 90 度
// RotateBy - 相对旋转
auto rotateBy = RotateBy::create(2.0f, 360.0f); // 旋转 360 度
```
### 淡入淡出动作
```cpp
// FadeIn - 淡入(透明度从 0 到 1
auto fadeIn = FadeIn::create(1.0f);
// FadeOut - 淡出(透明度从 1 到 0
auto fadeOut = FadeOut::create(1.0f);
// FadeTo - 淡入到指定透明度
auto fadeTo = FadeTo::create(1.0f, 0.5f); // 淡入到 50% 透明度
```
### 闪烁动作
```cpp
// Blink - 闪烁
auto blink = Blink::create(2.0f, 5); // 2 秒内闪烁 5 次
```
### 色调动作
```cpp
// TintTo - 变化到指定颜色
auto tintTo = TintTo::create(1.0f, 255, 0, 0); // 变为红色
// TintBy - 相对颜色变化
auto tintBy = TintBy::create(1.0f, 50, 0, 0); // 红色通道增加 50
```
---
## 组合动作
### Sequence - 序列动作
按顺序依次执行多个动作:
```cpp
auto sequence = Sequence::create(
MoveTo::create(1.0f, Vec2(100, 100)),
DelayTime::create(0.5f),
FadeOut::create(1.0f),
CallFunc::create([]() { E2D_LOG_INFO("完成"); }),
nullptr // 必须以 nullptr 结尾
);
sprite->runAction(sequence);
```
### Spawn - 并行动作
同时执行多个动作:
```cpp
auto spawn = Spawn::create(
MoveTo::create(2.0f, Vec2(200, 200)),
RotateBy::create(2.0f, 360),
FadeIn::create(2.0f),
nullptr
);
sprite->runAction(spawn);
```
### Repeat - 重复动作
```cpp
// Repeat - 重复指定次数
auto repeat = Repeat::create(
Sequence::create(
MoveBy::create(0.5f, Vec2(50, 0)),
MoveBy::create(0.5f, Vec2(-50, 0)),
nullptr
),
5 // 重复 5 次
);
// RepeatForever - 永久重复
auto repeatForever = RepeatForever::create(
RotateBy::create(1.0f, 360)
);
sprite->runAction(repeatForever);
```
### DelayTime - 延时动作
```cpp
auto sequence = Sequence::create(
MoveTo::create(1.0f, Vec2(100, 0)),
DelayTime::create(1.0f), // 延时 1 秒
MoveTo::create(1.0f, Vec2(200, 0)),
nullptr
);
```
### ReverseTime - 反向动作
```cpp
auto move = MoveBy::create(1.0f, Vec2(100, 0));
auto reverse = ReverseTime::create(move->clone());
// 反向执行,相当于 MoveBy(1.0f, Vec2(-100, 0))
```
---
## 缓动动作
缓动动作使用装饰器模式包装其他动作,实现各种缓动效果。
### 使用缓动
```cpp
// 创建基础动作
auto move = MoveTo::create(3.0f, Vec2(500, 300));
// 用缓动包装
auto easeMove = EaseElasticOut::create(move);
sprite->runAction(easeMove);
```
### 可用缓动类型
| 缓动类 | 效果 |
|--------|------|
| `EaseQuadIn/Out/InOut` | 二次缓动 |
| `EaseCubicIn/Out/InOut` | 三次缓动 |
| `EaseQuartIn/Out/InOut` | 四次缓动 |
| `EaseQuintIn/Out/InOut` | 五次缓动 |
| `EaseSineIn/Out/InOut` | 正弦缓动 |
| `EaseExponentialIn/Out/InOut` | 指数缓动 |
| `EaseCircleIn/Out/InOut` | 圆形缓动 |
| `EaseBackIn/Out/InOut` | 回震缓动 |
| `EaseElasticIn/Out/InOut` | 弹性缓动 |
| `EaseBounceIn/Out/InOut` | 弹跳缓动 |
### 缓动效果说明
- **In**: 开始时慢,逐渐加速
- **Out**: 开始时快,逐渐减速
- **InOut**: 开始和结束时慢,中间快
### 示例
```cpp
// 弹性缓动
auto jump = JumpBy::create(2.0f, Vec2(200, 0), 100, 1);
auto elasticJump = EaseElasticOut::create(jump, 0.5f);
sprite->runAction(elasticJump);
// 弹跳缓动
auto scale = ScaleTo::create(1.0f, 2.0f);
auto bounceScale = EaseBounceOut::create(scale);
sprite->runAction(bounceScale);
// 指数缓动
auto move = MoveTo::create(2.0f, Vec2(300, 200));
auto expoMove = EaseExponentialInOut::create(move);
sprite->runAction(expoMove);
```
### 自定义缓动
```cpp
// 使用自定义缓动函数
auto customEase = EaseCustom::create(move, [](float t) {
// 自定义缓动函数
return t * t * (3.0f - 2.0f * t); // smoothstep
});
```
---
## 特殊动作
### Speed - 速度控制
动态控制动作的播放速度:
```cpp
auto move = MoveTo::create(5.0f, Vec2(500, 300));
auto speed = Speed::create(move, 0.5f); // 半速播放
sprite->runAction(speed);
// 运行时调整速度
speed->setSpeed(2.0f); // 2 倍速
```
### Follow - 跟随动作
使节点跟随另一个节点移动(常用于相机):
```cpp
// 无边界跟随
auto follow = Follow::create(player);
// 带边界跟随
Rect boundary(0, 0, 2000, 2000); // 世界边界
auto followWithBoundary = Follow::create(player, boundary);
camera->runAction(follow);
```
### TargetedAction - 目标动作
在一个节点上运行动作,但作用于另一个节点:
```cpp
// 在 spriteA 上运行,但影响 spriteB
auto targeted = TargetedAction::create(spriteB, MoveTo::create(2.0f, Vec2(100, 100)));
spriteA->runAction(targeted);
```
---
## 瞬时动作
瞬时动作立即完成,没有动画过程。
### CallFunc - 回调动作
```cpp
// 无参数回调
auto callFunc = CallFunc::create([]() {
E2D_LOG_INFO("回调执行");
});
// 带节点参数的回调
auto callFuncN = CallFuncN::create([](Node* node) {
E2D_LOG_INFO("节点: %p", node);
});
```
### Place - 放置动作
```cpp
// 立即放置到指定位置
auto place = Place::create(Vec2(100, 100));
```
### FlipX/FlipY - 翻转动作
```cpp
auto flipX = FlipX::create(true); // 水平翻转
auto flipY = FlipY::create(true); // 垂直翻转
```
### Show/Hide - 显示/隐藏动作
```cpp
auto show = Show::create(); // 显示
auto hide = Hide::create(); // 隐藏
auto toggle = ToggleVisibility::create(); // 切换可见性
```
### RemoveSelf - 移除自身
```cpp
// 从父节点移除
auto removeSelf = RemoveSelf::create();
```
---
## 动作管理
### 停止动作
```cpp
// 停止所有动作
sprite->stopAllActions();
// 停止指定动作
sprite->stopAction(action);
// 根据标签停止动作
sprite->stopActionByTag(1);
// 根据标志位停止动作
sprite->stopActionsByFlags(0x01);
```
### 查询动作
```cpp
// 获取动作数量
size_t count = sprite->getActionCount();
// 检查是否有动作在运行
bool running = sprite->isRunningActions();
// 根据标签获取动作
Action* action = sprite->getActionByTag(1);
```
### 动作标签和标志位
```cpp
auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setTag(1); // 设置标签
action->setFlags(0x01); // 设置标志位
sprite->runAction(action);
```
---
## 完整示例
### 示例 1角色移动动画
```cpp
void Player::moveTo(const Vec2& target) {
// 停止当前移动
stopActionByTag(TAG_MOVE);
// 创建移动动作
auto move = MoveTo::create(1.0f, target);
move->setTag(TAG_MOVE);
// 添加缓动效果
auto easeMove = EaseQuadInOut::create(move);
runAction(easeMove);
}
```
### 示例 2UI 弹出动画
```cpp
void UIPanel::show() {
// 初始状态
setScale(0.0f);
setOpacity(0.0f);
setVisible(true);
// 并行动画:缩放 + 淡入
auto spawn = Spawn::create(
EaseBackOut::create(ScaleTo::create(0.3f, 1.0f)),
FadeIn::create(0.3f),
nullptr
);
runAction(spawn);
}
```
### 示例 3游戏结束动画
```cpp
void GameOverLayer::playAnimation() {
// 从屏幕底部滑入
setPosition(Vec2(screenWidth / 2, screenHeight));
auto moveUp = MoveBy::create(1.0f, Vec2(0, -screenHeight));
moveUp->setCompletionCallback([this]() {
// 动画完成后启用按钮
restartBtn_->setEnabled(true);
});
// 添加缓动效果
auto easeMove = EaseQuadOut::create(moveUp);
runAction(easeMove);
}
```
### 示例 4复杂序列动画
```cpp
void Enemy::playDeathAnimation() {
auto sequence = Sequence::create(
// 闪烁
Blink::create(0.5f, 3),
// 放大
ScaleTo::create(0.2f, 1.5f),
// 淡出
FadeOut::create(0.3f),
// 移除自身
RemoveSelf::create(),
nullptr
);
runAction(sequence);
}
```
### 示例 5循环动画
```cpp
void Coin::startFloating() {
// 上下浮动
auto floatUp = MoveBy::create(0.5f, Vec2(0, 10));
auto floatDown = floatUp->reverse();
auto floatSequence = Sequence::create(
EaseSineInOut::create(floatUp),
EaseSineInOut::create(floatDown),
nullptr
);
// 永久循环
auto floatForever = RepeatForever::create(floatSequence);
floatForever->setTag(TAG_FLOAT);
runAction(floatForever);
}
```
---
## 性能优化建议
1. **复用动作**:对于频繁使用的动作,可以 `clone()` 复用
2. **合理使用标签**:便于管理和停止特定动作
3. **避免过多并发动作**:大量节点同时运行动作会影响性能
4. **及时停止不需要的动作**:节点销毁前调用 `stopAllActions()`
---
## API 参考
### Action 基类
| 方法 | 说明 |
|------|------|
| `isDone()` | 检查动作是否完成 |
| `startWithTarget(node)` | 启动动作 |
| `stop()` | 停止动作 |
| `pause()` | 暂停动作 |
| `resume()` | 恢复动作 |
| `clone()` | 克隆动作 |
| `reverse()` | 创建反向动作 |
| `getTag()` / `setTag()` | 获取/设置标签 |
| `getFlags()` / `setFlags()` | 获取/设置标志位 |
| `setCompletionCallback()` | 设置完成回调 |
### Node 动作接口
| 方法 | 说明 |
|------|------|
| `runAction(action)` | 运行动作 |
| `stopAllActions()` | 停止所有动作 |
| `stopAction(action)` | 停止指定动作 |
| `stopActionByTag(tag)` | 根据标签停止动作 |
| `stopActionsByFlags(flags)` | 根据标志位停止动作 |
| `getActionByTag(tag)` | 根据标签获取动作 |
| `getActionCount()` | 获取动作数量 |
| `isRunningActions()` | 检查是否有动作运行 |

View File

@ -0,0 +1,678 @@
# Extra2D 动作系统重构规格文档
## 一、概述
本文档描述了 Extra2D 动作系统的重构方案,参考 Cocos2d-x 的动作系统设计模式,实现一个更加完善、易用、功能丰富的动作系统。
## 二、现有系统分析
### 2.1 当前类层次结构
```
Action (基类)
├── IntervalAction (有限时间动作基类)
│ ├── MoveBy/MoveTo
│ ├── ScaleBy/ScaleTo
│ ├── RotateBy/RotateTo
│ ├── FadeIn/FadeOut/FadeTo
│ ├── Sequence
│ ├── Spawn
│ ├── Delay
│ └── Loop
└── InstantAction (瞬时动作基类)
└── CallFunc
```
### 2.2 与 Cocos2d-x 的差异
| 特性 | Extra2D 当前 | Cocos2d-x | 差异说明 |
|------|-------------|-----------|---------|
| 类层次 | Action -> IntervalAction/InstantAction | Action -> FiniteTimeAction -> ActionInterval/ActionInstant | 缺少 FiniteTimeAction 中间层 |
| 动作管理 | Node 内部管理 | ActionManager 单例集中管理 | 缺少集中式管理 |
| 缓动动作 | EaseAction 静态方法 | ActionEase 类系(装饰器模式) | 不够灵活,无法组合 |
| 速度控制 | Action::speed_ 成员 | Speed 动作包装器 | 缺少动态速度控制 |
| 跟随动作 | 无 | Follow 类 | 缺失 |
| 跳跃动作 | 无 | JumpBy/JumpTo | 缺失 |
| 贝塞尔动作 | 无 | BezierBy/BezierTo | 缺失 |
| 闪烁动作 | 无 | Blink | 缺失 |
| 色调动作 | 无 | TintBy/TintTo | 缺失 |
| 重复动作 | Loop | Repeat/RepeatForever | 命名和功能差异 |
| 反向动作 | reverse() 方法 | reverse() 方法 | 相同 |
| 克隆动作 | clone() 方法 | clone() 方法 | 相同 |
## 三、重构目标
### 3.1 核心目标
1. **完善类层次结构** - 添加 FiniteTimeAction 中间层
2. **集中式动作管理** - 实现 ActionManager 单例
3. **装饰器模式缓动** - 实现 ActionEase 类系
4. **丰富动作类型** - 添加跳跃、贝塞尔、闪烁、色调等动作
5. **统一API风格** - 与 Cocos2d-x API 保持一致
### 3.2 设计原则
1. **组合优于继承** - 使用装饰器模式实现缓动和速度控制
2. **单一职责** - ActionManager 只负责动作调度
3. **开放封闭** - 易于扩展新动作类型
4. **接口隔离** - 不同类型动作提供不同接口
## 四、新类层次结构
```
Action (基类)
├── FiniteTimeAction (有限时间动作基类)
│ ├── ActionInterval (时间间隔动作)
│ │ ├── MoveBy/MoveTo
│ │ ├── MoveBy3D/MoveTo3D (新增)
│ │ ├── JumpBy/JumpTo (新增)
│ │ ├── BezierBy/BezierTo (新增)
│ │ ├── ScaleBy/ScaleTo
│ │ ├── RotateBy/RotateTo
│ │ ├── FadeIn/FadeOut/FadeTo
│ │ ├── Blink (新增)
│ │ ├── TintBy/TintTo (新增)
│ │ ├── Sequence
│ │ ├── Spawn
│ │ ├── DelayTime
│ │ ├── Repeat (重命名自 Loop)
│ │ ├── RepeatForever (新增)
│ │ ├── ReverseTime (新增)
│ │ └── ActionEase (缓动动作基类,新增)
│ │ ├── EaseIn/EaseOut/EaseInOut
│ │ ├── EaseSineIn/EaseSineOut/EaseSineInOut
│ │ ├── EaseExponentialIn/EaseExponentialOut/EaseExponentialInOut
│ │ ├── EaseBounceIn/EaseBounceOut/EaseBounceInOut
│ │ ├── EaseElasticIn/EaseElasticOut/EaseElasticInOut
│ │ ├── EaseBackIn/EaseBackOut/EaseBackInOut
│ │ └── EaseBezier (新增)
│ └── ActionInstant (瞬时动作)
│ ├── CallFunc
│ ├── CallFuncN (新增)
│ ├── Place (新增)
│ ├── FlipX/FlipY (新增)
│ ├── Show/Hide (新增)
│ ├── ToggleVisibility (新增)
│ └── RemoveSelf (新增)
├── Speed (速度控制动作,新增)
├── Follow (跟随动作,新增)
└── TargetedAction (目标动作,新增)
```
## 五、核心类设计
### 5.1 Action 基类
```cpp
class Action {
public:
virtual ~Action() = default;
// 核心接口
virtual bool isDone() const;
virtual void startWithTarget(Node* target);
virtual void stop();
virtual void step(float dt);
virtual void update(float time);
// 克隆和反向
virtual Action* clone() const = 0;
virtual Action* reverse() const = 0;
// 属性访问
Node* getTarget() const;
Node* getOriginalTarget() const;
int getTag() const;
void setTag(int tag);
unsigned int getFlags() const;
void setFlags(unsigned int flags);
protected:
Node* target_ = nullptr;
Node* originalTarget_ = nullptr;
int tag_ = -1;
unsigned int flags_ = 0;
};
```
### 5.2 FiniteTimeAction 中间层
```cpp
class FiniteTimeAction : public Action {
public:
float getDuration() const { return duration_; }
void setDuration(float duration) { duration_ = duration; }
virtual FiniteTimeAction* clone() const = 0;
virtual FiniteTimeAction* reverse() const = 0;
protected:
float duration_ = 0.0f;
};
```
### 5.3 ActionInterval 时间间隔动作
```cpp
class ActionInterval : public FiniteTimeAction {
public:
float getElapsed() const { return elapsed_; }
bool isDone() const override;
void startWithTarget(Node* target) override;
void step(float dt) override;
void setAmplitudeRate(float amp) { amplitudeRate_ = amp; }
float getAmplitudeRate() const { return amplitudeRate_; }
protected:
float elapsed_ = 0.0f;
bool firstTick_ = true;
float amplitudeRate_ = 1.0f;
EaseFunction easeFunc_ = nullptr; // 内置缓动函数
};
```
### 5.4 ActionInstant 瞬时动作
```cpp
class ActionInstant : public FiniteTimeAction {
public:
bool isDone() const override { return true; }
void step(float dt) override;
protected:
bool done_ = false;
};
```
### 5.5 ActionManager 动作管理器
```cpp
class ActionManager {
public:
static ActionManager* getInstance();
// 动作管理
void addAction(Action* action, Node* target, bool paused = false);
void removeAction(Action* action);
void removeActionByTag(int tag, Node* target);
void removeAllActionsFromTarget(Node* target);
void removeAllActions();
// 查询
Action* getActionByTag(int tag, Node* target);
size_t getActionCount(Node* target) const;
// 暂停/恢复
void pauseTarget(Node* target);
void resumeTarget(Node* target);
bool isPaused(Node* target) const;
// 更新(每帧调用)
void update(float dt);
private:
struct ActionElement {
std::vector<Action*> actions;
Node* target;
bool paused;
int actionIndex;
Action* currentAction;
bool currentActionSalvaged;
};
std::unordered_map<Node*, ActionElement> targets_;
static ActionManager* instance_;
};
```
### 5.6 ActionEase 缓动动作基类
```cpp
class ActionEase : public ActionInterval {
public:
static ActionEase* create(ActionInterval* action);
ActionInterval* getInnerAction() const { return innerAction_; }
void startWithTarget(Node* target) override;
void stop() override;
void update(float time) override;
Action* reverse() const override;
Action* clone() const override;
protected:
ActionInterval* innerAction_ = nullptr;
};
```
### 5.7 Speed 速度控制动作
```cpp
class Speed : public Action {
public:
static Speed* create(ActionInterval* action, float speed);
float getSpeed() const { return speed_; }
void setSpeed(float speed) { speed_ = speed; }
void startWithTarget(Node* target) override;
void stop() override;
void step(float dt) override;
bool isDone() const override;
Action* reverse() const override;
Action* clone() const override;
protected:
ActionInterval* innerAction_ = nullptr;
float speed_ = 1.0f;
};
```
## 六、新增动作类型
### 6.1 JumpBy/JumpTo 跳跃动作
```cpp
class JumpBy : public ActionInterval {
public:
static JumpBy* create(float duration, const Vec2& position,
float height, int jumps);
protected:
Vec2 startPosition_;
Vec2 delta_;
float height_;
int jumps_;
};
class JumpTo : public JumpBy {
// 继承实现,目标位置版本
};
```
### 6.2 BezierBy/BezierTo 贝塞尔动作
```cpp
struct BezierConfig {
Vec2 controlPoint1;
Vec2 controlPoint2;
Vec2 endPosition;
};
class BezierBy : public ActionInterval {
public:
static BezierBy* create(float duration, const BezierConfig& config);
protected:
BezierConfig config_;
Vec2 startPosition_;
};
```
### 6.3 Blink 闪烁动作
```cpp
class Blink : public ActionInterval {
public:
static Blink* create(float duration, int times);
protected:
int times_;
int currentTimes_;
bool originalVisible_;
};
```
### 6.4 TintBy/TintTo 色调动作
```cpp
class TintTo : public ActionInterval {
public:
static TintTo* create(float duration,
uint8_t red, uint8_t green, uint8_t blue);
protected:
Color3B startColor_;
Color3B endColor_;
Color3B deltaColor_;
};
```
### 6.5 Follow 跟随动作
```cpp
class Follow : public Action {
public:
static Follow* create(Node* followedNode,
const Rect& boundary = Rect::ZERO);
bool isDone() const override;
void step(float dt) override;
protected:
Node* followedNode_ = nullptr;
Rect boundary_;
bool boundarySet_ = false;
};
```
### 6.6 瞬时动作扩展
```cpp
// 放置到指定位置
class Place : public ActionInstant {
public:
static Place* create(const Vec2& position);
};
// X/Y轴翻转
class FlipX : public ActionInstant {
public:
static FlipX* create(bool flipX);
};
class FlipY : public ActionInstant {
public:
static FlipY* create(bool flipY);
};
// 显示/隐藏
class Show : public ActionInstant {
public:
static Show* create();
};
class Hide : public ActionInstant {
public:
static Hide* create();
};
// 切换可见性
class ToggleVisibility : public ActionInstant {
public:
static ToggleVisibility* create();
};
// 移除自身
class RemoveSelf : public ActionInstant {
public:
static RemoveSelf* create();
};
// 带Node参数的回调
class CallFuncN : public ActionInstant {
public:
static CallFuncN* create(const std::function<void(Node*)>& func);
};
```
## 七、缓动动作完整实现
### 7.1 通用缓动包装器
```cpp
// 通用缓动包装器
class EaseCustom : public ActionEase {
public:
static EaseCustom* create(ActionInterval* action,
EaseFunction easeFunc);
};
// 指数缓动
class EaseExponentialIn : public ActionEase { /* ... */ };
class EaseExponentialOut : public ActionEase { /* ... */ };
class EaseExponentialInOut : public ActionEase { /* ... */ };
// 正弦缓动
class EaseSineIn : public ActionEase { /* ... */ };
class EaseSineOut : public ActionEase { /* ... */ };
class EaseSineInOut : public ActionEase { /* ... */ };
// 弹性缓动
class EaseElasticIn : public ActionEase {
public:
static EaseElasticIn* create(ActionInterval* action, float period = 0.3f);
};
class EaseElasticOut : public ActionEase { /* ... */ };
class EaseElasticInOut : public ActionEase { /* ... */ };
// 弹跳缓动
class EaseBounceIn : public ActionEase { /* ... */ };
class EaseBounceOut : public ActionEase { /* ... */ };
class EaseBounceInOut : public ActionEase { /* ... */ };
// 回震缓动
class EaseBackIn : public ActionEase { /* ... */ };
class EaseBackOut : public ActionEase { /* ... */ };
class EaseBackInOut : public ActionEase { /* ... */ };
// 贝塞尔缓动(自定义曲线)
class EaseBezier : public ActionEase {
public:
static EaseBezier* create(ActionInterval* action);
void setBezierParamer(float p0, float p1, float p2, float p3);
};
```
## 八、Node 类接口更新
### 8.1 动作相关接口
```cpp
class Node {
public:
// 运行动作
Action* runAction(Action* action);
// 停止动作
void stopAllActions();
void stopAction(Action* action);
void stopActionByTag(int tag);
void stopActionsByFlags(unsigned int flags);
// 获取动作
Action* getActionByTag(int tag);
size_t getNumberOfRunningActions() const;
// 暂停/恢复所有动作
void pauseAllActions();
void resumeAllActions();
// 检查是否有动作在运行
bool isRunningActions() const;
};
```
## 九、使用示例
### 9.1 基本动作
```cpp
// 移动
auto moveTo = MoveTo::create(2.0f, Vec2(100, 100));
sprite->runAction(moveTo);
// 跳跃
auto jump = JumpBy::create(2.0f, Vec2(200, 0), 100, 3);
sprite->runAction(jump);
// 贝塞尔曲线
BezierConfig bezier;
bezier.controlPoint1 = Vec2(100, 200);
bezier.controlPoint2 = Vec2(200, 100);
bezier.endPosition = Vec2(300, 150);
auto bezierAction = BezierTo::create(3.0f, bezier);
sprite->runAction(bezierAction);
```
### 9.2 组合动作
```cpp
// 序列动作
auto seq = Sequence::create(
MoveTo::create(1.0f, Vec2(100, 100)),
DelayTime::create(0.5f),
FadeOut::create(1.0f),
CallFunc::create([](){ log("Done!"); }),
nullptr
);
sprite->runAction(seq);
// 并行动作
auto spawn = Spawn::create(
MoveTo::create(2.0f, Vec2(200, 200)),
RotateBy::create(2.0f, 360),
FadeIn::create(2.0f),
nullptr
);
sprite->runAction(spawn);
// 重复动作
auto repeat = Repeat::create(MoveBy::create(1.0f, Vec2(50, 0)), 5);
sprite->runAction(repeat);
// 永久重复
auto repeatForever = RepeatForever::create(
Sequence::create(
MoveBy::create(1.0f, Vec2(100, 0)),
MoveBy::create(1.0f, Vec2(-100, 0)),
nullptr
)
);
sprite->runAction(repeatForever);
```
### 9.3 缓动动作
```cpp
// 使用缓动包装器
auto move = MoveTo::create(3.0f, Vec2(500, 300));
auto easeMove = EaseElasticOut::create(move, 0.5f);
sprite->runAction(easeMove);
// 指数缓动
auto jump = JumpBy::create(2.0f, Vec2(200, 0), 100, 1);
auto easeJump = EaseExponentialOut::create(jump);
sprite->runAction(easeJump);
// 弹跳缓动
auto scale = ScaleTo::create(1.0f, 2.0f);
auto bounceScale = EaseBounceOut::create(scale);
sprite->runAction(bounceScale);
```
### 9.4 速度控制
```cpp
// 慢动作回放
auto action = MoveTo::create(5.0f, Vec2(500, 300));
auto speed = Speed::create(action, 0.5f); // 半速
sprite->runAction(speed);
// 动态调整速度
speed->setSpeed(2.0f); // 2倍速
```
### 9.5 跟随动作
```cpp
// 相机跟随玩家
auto follow = Follow::create(player, Rect(0, 0, 2000, 2000));
camera->runAction(follow);
```
## 十、文件结构
```
Extra2D/include/extra2d/action/
├── action.h # Action 基类
├── finite_time_action.h # FiniteTimeAction 中间层
├── action_interval.h # ActionInterval 及其子类
├── action_instant.h # ActionInstant 及其子类
├── action_ease.h # 缓动动作类系
├── action_manager.h # ActionManager
├── ease_functions.h # 缓动函数
└── action_factory.h # 动作工厂(可选)
Extra2D/src/action/
├── action.cpp
├── finite_time_action.cpp
├── action_interval.cpp
├── action_instant.cpp
├── action_ease.cpp
├── action_manager.cpp
└── ease_functions.cpp
```
## 十一、实现优先级
### 第一阶段(核心重构)
1. Action 基类重构
2. FiniteTimeAction 中间层
3. ActionInterval 重构
4. ActionInstant 重构
5. ActionManager 实现
### 第二阶段(新增动作)
1. JumpBy/JumpTo
2. BezierBy/BezierTo
3. Blink
4. TintBy/TintTo
5. Follow
6. Speed
### 第三阶段(缓动系统)
1. ActionEase 基类
2. 各类缓动包装器
3. EaseCustom 自定义缓动
### 第四阶段(瞬时动作扩展)
1. Place
2. FlipX/FlipY
3. Show/Hide
4. ToggleVisibility
5. RemoveSelf
6. CallFuncN
## 十二、兼容性考虑
### 12.1 API 变更
- 动作创建方式改为 `create()` 静态工厂方法
- `Loop` 重命名为 `Repeat`
- `Delay` 重命名为 `DelayTime`
- 动作管理由 `ActionManager` 集中处理
### 12.2 迁移指南
```cpp
// 旧写法
auto action = std::make_shared<MoveTo>(2.0f, Vec2(100, 100));
sprite->runAction(action);
// 新写法(推荐)
auto action = MoveTo::create(2.0f, Vec2(100, 100));
sprite->runAction(action);
// 旧写法(缓动)
auto action = EaseAction::create(MoveTo::create(2.0f, Vec2(100, 100)), easeInQuad);
// 新写法(缓动)
auto action = EaseQuadIn::create(MoveTo::create(2.0f, Vec2(100, 100)));
```
## 十三、性能优化
1. **对象池集成** - 动作对象使用对象池管理
2. **批量更新** - ActionManager 统一调度减少调用开销
3. **延迟删除** - 动作完成时标记删除,统一清理
4. **缓存友好** - 连续存储同一类型动作
## 十四、测试计划
1. 单元测试:每个动作类型的独立测试
2. 集成测试:组合动作测试
3. 性能测试:大量动作并发测试
4. 内存测试:动作对象生命周期测试

View File

@ -44,8 +44,7 @@ void GameOverLayer::onEnter() {
initButtons();
// 创建向上移动的动画(从屏幕底部移动到正常位置)
auto moveAction = extra2d::makePtr<extra2d::MoveBy>(
1.0f, extra2d::Vec2(0.0f, -screenHeight));
auto moveAction = extra2d::MoveBy::create(1.0f, extra2d::Vec2(0.0f, -screenHeight));
moveAction->setCompletionCallback([this]() {
animationDone_ = true;
if (restartBtn_)