diff --git a/Extra2D/include/extra2d/action/action.h b/Extra2D/include/extra2d/action/action.h index 5c4428f..8fbbae5 100644 --- a/Extra2D/include/extra2d/action/action.h +++ b/Extra2D/include/extra2d/action/action.h @@ -1,76 +1,172 @@ #pragma once +#include #include -#include namespace extra2d { + class Node; -enum class ActionState { Idle, Running, Paused, Completed }; - -class Action { -public: - using ProgressCallback = std::function; - using CompletionCallback = std::function; - - Action(); - virtual ~Action() = default; - - Action(const Action &) = delete; - Action &operator=(const Action &) = delete; - Action(Action &&) = default; - Action &operator=(Action &&) = default; - - virtual void start(Node *target); - virtual void stop(); - virtual void update(float dt); - virtual void step(float dt); - - virtual bool isDone() const = 0; - virtual Action *clone() const = 0; - virtual Action *reverse() const = 0; - - void pause(); - void resume(); - void restart(); - - ActionState getState() const { return state_; } - float getElapsed() const { return elapsed_; } - float getDuration() const { return duration_; } - Node *getTarget() const { return target_; } - Node *getOriginalTarget() const { return originalTarget_; } - - void setDuration(float duration) { duration_ = duration; } - void setSpeed(float speed) { speed_ = speed; } - float getSpeed() const { return speed_; } - - void setProgressCallback(ProgressCallback callback) { - progressCallback_ = std::move(callback); - } - void setCompletionCallback(CompletionCallback callback) { - completionCallback_ = std::move(callback); - } - - void setTag(int tag) { tag_ = tag; } - int getTag() const { return tag_; } - -protected: - virtual void onStart() {} - virtual void onUpdate(float progress) = 0; - virtual void onComplete() {} - - 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_; - CompletionCallback completionCallback_; +/** + * @brief 动作状态枚举 + */ +enum class ActionState { + Idle, + Running, + Paused, + Completed +}; + +/** + * @brief 动作基类 + * + * 所有动作的基类,定义了动作的核心接口。 + * 动作用于修改 Node 的属性,实现动画效果。 + */ +class Action { +public: + using CompletionCallback = std::function; + + Action(); + virtual ~Action() = default; + + Action(const Action&) = delete; + Action& operator=(const Action&) = delete; + Action(Action&&) = default; + Action& operator=(Action&&) = default; + + /** + * @brief 检查动作是否完成 + * @return true 如果动作已完成 + */ + virtual bool isDone() const; + + /** + * @brief 使用目标节点启动动作 + * @param target 目标节点 + */ + virtual void startWithTarget(Node* target); + + /** + * @brief 停止动作 + */ + virtual void stop(); + + /** + * @brief 每帧调用的步进函数 + * @param dt 帧时间间隔 + */ + virtual void step(float dt); + + /** + * @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(); + + /** + * @brief 设置完成回调 + * @param callback 回调函数 + */ + void setCompletionCallback(const CompletionCallback& callback) { + completionCallback_ = callback; + } + + /** + * @brief 获取目标节点 + * @return 目标节点指针 + */ + Node* getTarget() const { return target_; } + + /** + * @brief 获取原始目标节点 + * @return 原始目标节点指针 + */ + Node* getOriginalTarget() const { return originalTarget_; } + + /** + * @brief 获取动作状态 + * @return 当前状态 + */ + ActionState getState() const { return state_; } + + /** + * @brief 获取标签 + * @return 标签值 + */ + int getTag() const { return tag_; } + + /** + * @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; + int tag_ = -1; + unsigned int flags_ = 0; + CompletionCallback completionCallback_; }; -using ActionPtr = std::unique_ptr; } // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_ease.h b/Extra2D/include/extra2d/action/action_ease.h new file mode 100644 index 0000000..f6bb6dd --- /dev/null +++ b/Extra2D/include/extra2d/action/action_ease.h @@ -0,0 +1,344 @@ +#pragma once + +#include +#include + +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 diff --git a/Extra2D/include/extra2d/action/action_instant.h b/Extra2D/include/extra2d/action/action_instant.h new file mode 100644 index 0000000..af2e45d --- /dev/null +++ b/Extra2D/include/extra2d/action/action_instant.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +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 diff --git a/Extra2D/include/extra2d/action/action_instant_actions.h b/Extra2D/include/extra2d/action/action_instant_actions.h new file mode 100644 index 0000000..c0a7563 --- /dev/null +++ b/Extra2D/include/extra2d/action/action_instant_actions.h @@ -0,0 +1,169 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 回调动作 +// ============================================================================ + +/** + * @brief 无参数回调动作 + */ +class CallFunc : public ActionInstant { +public: + using Callback = std::function; + + 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; + + 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 diff --git a/Extra2D/include/extra2d/action/action_interval.h b/Extra2D/include/extra2d/action/action_interval.h new file mode 100644 index 0000000..194e751 --- /dev/null +++ b/Extra2D/include/extra2d/action/action_interval.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include + +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 diff --git a/Extra2D/include/extra2d/action/action_interval_actions.h b/Extra2D/include/extra2d/action/action_interval_actions.h new file mode 100644 index 0000000..df369c5 --- /dev/null +++ b/Extra2D/include/extra2d/action/action_interval_actions.h @@ -0,0 +1,469 @@ +#pragma once + +#include +#include +#include +#include +#include + +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& actions); + + ~Sequence(); + + ActionInterval* clone() const override; + ActionInterval* reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + + std::vector 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& actions); + + ~Spawn(); + + ActionInterval* clone() const override; + ActionInterval* reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + + std::vector 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 diff --git a/Extra2D/include/extra2d/action/action_manager.h b/Extra2D/include/extra2d/action/action_manager.h new file mode 100644 index 0000000..9acd14d --- /dev/null +++ b/Extra2D/include/extra2d/action/action_manager.h @@ -0,0 +1,130 @@ +#pragma once + +#include +#include +#include +#include + +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 actions; + Node* target = nullptr; + bool paused = false; + int actionIndex = 0; + Action* currentAction = nullptr; + bool currentActionSalvaged = false; + }; + + using ActionMap = std::unordered_map; + + void removeActionAt(size_t index, ActionElement& element); + void deleteAction(Action* action); + + ActionMap targets_; + static ActionManager* instance_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_special.h b/Extra2D/include/extra2d/action/action_special.h new file mode 100644 index 0000000..b40527e --- /dev/null +++ b/Extra2D/include/extra2d/action/action_special.h @@ -0,0 +1,166 @@ +#pragma once + +#include +#include +#include + +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 diff --git a/Extra2D/include/extra2d/action/actions.h b/Extra2D/include/extra2d/action/actions.h deleted file mode 100644 index 9dd956c..0000000 --- a/Extra2D/include/extra2d/action/actions.h +++ /dev/null @@ -1,278 +0,0 @@ -#pragma once - -#include "extra2d/action/action.h" -#include "extra2d/core/math_types.h" -#include -#include - -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 &actions); - ~Sequence(); - - Action *clone() const override; - Action *reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - -private: - std::vector actions_; - int currentIndex_ = 0; - float split_ = 0.0f; - float last_ = 0.0f; -}; - -class Spawn : public IntervalAction { -public: - Spawn(const std::vector &actions); - ~Spawn(); - - Action *clone() const override; - Action *reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - -private: - std::vector 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; - - 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 &actions) { - return new Sequence(actions); -} -inline Spawn *spawn(const std::vector &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 diff --git a/Extra2D/include/extra2d/action/ease.h b/Extra2D/include/extra2d/action/ease.h index d91b48f..842fe2f 100644 --- a/Extra2D/include/extra2d/action/ease.h +++ b/Extra2D/include/extra2d/action/ease.h @@ -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 diff --git a/Extra2D/include/extra2d/action/finite_time_action.h b/Extra2D/include/extra2d/action/finite_time_action.h new file mode 100644 index 0000000..7601e5f --- /dev/null +++ b/Extra2D/include/extra2d/action/finite_time_action.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +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 diff --git a/Extra2D/include/extra2d/core/color.h b/Extra2D/include/extra2d/core/color.h index 9f853b1..6797137 100644 --- a/Extra2D/include/extra2d/core/color.h +++ b/Extra2D/include/extra2d/core/color.h @@ -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(std::min(255, static_cast(r) + other.r)), + static_cast(std::min(255, static_cast(g) + other.g)), + static_cast(std::min(255, static_cast(b) + other.b)) + ); + } + + Color3B operator-(const Color3B& other) const { + return Color3B( + static_cast(std::max(0, static_cast(r) - other.r)), + static_cast(std::max(0, static_cast(g) - other.g)), + static_cast(std::max(0, static_cast(b) - other.b)) + ); + } +}; + /// RGBA 颜色(浮点数,每通道 0.0 - 1.0) struct Color { float r = 0.0f; diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index 293647e..476a636 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -66,7 +66,14 @@ // Action #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include // Event diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h index 4a29013..8924e12 100644 --- a/Extra2D/include/extra2d/scene/node.h +++ b/Extra2D/include/extra2d/scene/node.h @@ -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); + /** + * @brief 运行动作 + * @param action 动作指针(所有权转移) + * @return 动作指针 + */ + Action* runAction(Action* action); + + /** + * @brief 停止所有动作 + */ void stopAllActions(); - void stopAction(Ptr action); + + /** + * @brief 停止指定动作 + * @param action 动作指针 + */ + void stopAction(Action* action); + + /** + * @brief 根据标签停止动作 + * @param tag 标签值 + */ void stopActionByTag(int tag); - Ptr 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> nameIndex_; // 56 bytes std::unordered_map> tagIndex_; // 56 bytes - // 4. 动作系统(使用 unordered_map 加速 tag 查找) - std::unordered_map> actionByTag_; // 56 bytes - std::vector> actions_; // 24 bytes(无 tag 的 Action) - - // 5. 事件分发器 + // 4. 事件分发器 EventDispatcher eventDispatcher_; // 大小取决于实现 - // 6. 父节点引用 + // 5. 父节点引用 WeakPtr 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 diff --git a/Extra2D/src/action/action.cpp b/Extra2D/src/action/action.cpp index a8ddb79..e770896 100644 --- a/Extra2D/src/action/action.cpp +++ b/Extra2D/src/action/action.cpp @@ -2,67 +2,48 @@ #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) { - target_ = target; - originalTarget_ = target; - elapsed_ = 0.0f; - state_ = ActionState::Running; - onStart(); +Action::Action() : tag_(-1), flags_(0) {} + +bool Action::isDone() const { + return state_ == ActionState::Completed; +} + +void Action::startWithTarget(Node* target) { + target_ = target; + originalTarget_ = target; + state_ = ActionState::Running; + onStart(); } void Action::stop() { - target_ = nullptr; - state_ = ActionState::Completed; -} - -void Action::update(float dt) { - if (state_ != ActionState::Running) - return; - - step(dt); - - if (isDone()) { + target_ = nullptr; state_ = ActionState::Completed; - onComplete(); - if (completionCallback_) - completionCallback_(); - } } void Action::step(float dt) { - if (state_ != ActionState::Running) - return; + (void)dt; +} - elapsed_ += dt * speed_; - - float progress = 0.0f; - if (duration_ > 0.0f) { - progress = std::min(1.0f, elapsed_ / duration_); - } else { - progress = 1.0f; - } - - if (progressCallback_) - progressCallback_(progress); - - onUpdate(progress); +void Action::update(float time) { + (void)time; } void Action::pause() { - if (state_ == ActionState::Running) - state_ = ActionState::Paused; + if (state_ == ActionState::Running) { + state_ = ActionState::Paused; + } } void Action::resume() { - if (state_ == ActionState::Paused) - state_ = ActionState::Running; + if (state_ == ActionState::Paused) { + state_ = ActionState::Running; + } } void Action::restart() { - elapsed_ = 0.0f; - state_ = ActionState::Running; - onStart(); + state_ = ActionState::Running; + onStart(); } + } // namespace extra2d diff --git a/Extra2D/src/action/action_ease.cpp b/Extra2D/src/action/action_ease.cpp new file mode 100644 index 0000000..e2eec8b --- /dev/null +++ b/Extra2D/src/action/action_ease.cpp @@ -0,0 +1,683 @@ +#include "extra2d/action/action_ease.h" +#include + +#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 diff --git a/Extra2D/src/action/action_instant.cpp b/Extra2D/src/action/action_instant.cpp new file mode 100644 index 0000000..7cd8ad1 --- /dev/null +++ b/Extra2D/src/action/action_instant.cpp @@ -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 diff --git a/Extra2D/src/action/action_instant_actions.cpp b/Extra2D/src/action/action_instant_actions.cpp new file mode 100644 index 0000000..7afcae3 --- /dev/null +++ b/Extra2D/src/action/action_instant_actions.cpp @@ -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 diff --git a/Extra2D/src/action/action_interval.cpp b/Extra2D/src/action/action_interval.cpp new file mode 100644 index 0000000..5643020 --- /dev/null +++ b/Extra2D/src/action/action_interval.cpp @@ -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 diff --git a/Extra2D/src/action/action_interval_actions.cpp b/Extra2D/src/action/action_interval_actions.cpp new file mode 100644 index 0000000..f986c99 --- /dev/null +++ b/Extra2D/src/action/action_interval_actions.cpp @@ -0,0 +1,787 @@ +#include "extra2d/action/action_interval_actions.h" +#include "extra2d/scene/node.h" +#include +#include + +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(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(endColor_.r) - static_cast(startColor_.r), + static_cast(endColor_.g) - static_cast(startColor_.g), + static_cast(endColor_.b) - static_cast(startColor_.b) + ); +} + +void TintTo::onUpdate(float progress) { + Color3B newColor( + static_cast(startColor_.r + deltaColor_.r * progress), + static_cast(startColor_.g + deltaColor_.g * progress), + static_cast(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(startColor_.r + deltaR_ * progress), + static_cast(startColor_.g + deltaG_ * progress), + static_cast(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 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& 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 cloned; + for (auto* action : actions_) { + cloned.push_back(action->clone()); + } + return Sequence::create(cloned); +} + +ActionInterval* Sequence::reverse() const { + std::vector 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 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& 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 cloned; + for (auto* action : actions_) { + cloned.push_back(action->clone()); + } + return Spawn::create(cloned); +} + +ActionInterval* Spawn::reverse() const { + std::vector 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(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 diff --git a/Extra2D/src/action/action_manager.cpp b/Extra2D/src/action/action_manager.cpp new file mode 100644 index 0000000..cbecbf8 --- /dev/null +++ b/Extra2D/src/action/action_manager.cpp @@ -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(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(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 diff --git a/Extra2D/src/action/action_special.cpp b/Extra2D/src/action/action_special.cpp new file mode 100644 index 0000000..a3df981 --- /dev/null +++ b/Extra2D/src/action/action_special.cpp @@ -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 diff --git a/Extra2D/src/action/actions.cpp b/Extra2D/src/action/actions.cpp deleted file mode 100644 index a94d244..0000000 --- a/Extra2D/src/action/actions.cpp +++ /dev/null @@ -1,405 +0,0 @@ -#include "extra2d/action/actions.h" -#include "extra2d/scene/node.h" -#include - -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 &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(i); - break; - } else if (split_ == newTime) { - found = static_cast(i) + 1; - break; - } - } - - if (found != currentIndex_) { - if (currentIndex_ >= 0 && - currentIndex_ < static_cast(actions_.size())) { - actions_[currentIndex_]->update(actions_[currentIndex_]->getDuration()); - } - - if (found >= 0 && found < static_cast(actions_.size())) { - actions_[found]->start(target_); - } - - currentIndex_ = found; - } - - if (currentIndex_ >= 0 && currentIndex_ < static_cast(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 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 &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 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 diff --git a/Extra2D/src/action/ease.cpp b/Extra2D/src/action/ease.cpp index 4a819fb..aa382b9 100644 --- a/Extra2D/src/action/ease.cpp +++ b/Extra2D/src/action/ease.cpp @@ -1,179 +1,225 @@ +#include "extra2d/action/ease.h" #include -#include 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)); + return t == 0.0f ? 0.0f : std::pow(2.0f, 10.0f * (t - 1.0f)); } float easeOutExpo(float t) { - return t == 1.0f ? 1.0f : 1.0f - std::pow(2.0f, -10.0f * t); + return t == 1.0f ? 1.0f : 1.0f - std::pow(2.0f, -10.0f * 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 - : (2.0f - 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)); + return std::sqrt(1.0f - std::pow(t - 1.0f, 2.0f)); } 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; + 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; } -// Back +// ============================================================================ +// 回震缓动 (Back) +// ============================================================================ + float easeInBack(float t) { - const float c1 = 1.70158f; - const float c3 = c1 + 1.0f; - return c3 * t * t * t - c1 * t * t; + const float c1 = 1.70158f; + const float c3 = c1 + 1.0f; + return c3 * t * t * t - c1 * t * t; } float easeOutBack(float t) { - const float c1 = 1.70158f; - const float c3 = c1 + 1.0f; - return 1.0f + c3 * std::pow(t - 1.0f, 3.0f) + c1 * std::pow(t - 1.0f, 2.0f); + const float c1 = 1.70158f; + const float c3 = c1 + 1.0f; + return 1.0f + c3 * std::pow(t - 1.0f, 3.0f) + c1 * std::pow(t - 1.0f, 2.0f); } float easeInOutBack(float t) { - const float c1 = 1.70158f; - 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; + const float c1 = 1.70158f; + 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; } -// 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; - return std::pow(2.0f, -10.0f * t) * std::sin((t * 10.0f - 0.75f) * c4) + 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; - const float d1 = 2.75f; + const float n1 = 7.5625f; + const float d1 = 2.75f; - if (t < 1.0f / d1) { - return n1 * t * t; - } else if (t < 2.0f / d1) { - t -= 1.5f / d1; - return n1 * t * t + 0.75f; - } else if (t < 2.5f / d1) { - t -= 2.25f / d1; - return n1 * t * t + 0.9375f; - } else { - t -= 2.625f / d1; - return n1 * t * t + 0.984375f; - } + if (t < 1.0f / d1) { + return n1 * t * t; + } else if (t < 2.0f / d1) { + t -= 1.5f / d1; + return n1 * t * t + 0.75f; + } else if (t < 2.5f / d1) { + t -= 2.25f / d1; + return n1 * t * t + 0.9375f; + } else { + t -= 2.625f / d1; + 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 - : (1.0f + easeOutBounceInternal(2.0f * t - 1.0f)) / 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 diff --git a/Extra2D/src/action/finite_time_action.cpp b/Extra2D/src/action/finite_time_action.cpp new file mode 100644 index 0000000..41d6a7e --- /dev/null +++ b/Extra2D/src/action/finite_time_action.cpp @@ -0,0 +1,9 @@ +#include "extra2d/action/finite_time_action.h" + +namespace extra2d { + +FiniteTimeAction::FiniteTimeAction(float duration) + : duration_(duration) { +} + +} // namespace extra2d diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index f166cd6..dbec1a4 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -13,9 +14,9 @@ #include #include + #include #include -#include #ifdef __SWITCH__ #include @@ -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); } // ======================================== @@ -98,7 +100,7 @@ bool Application::init(const AppConfig &config) { winConfig.height = config.height; if (platform == PlatformType::Switch) { winConfig.fullscreen = true; - winConfig.fullscreenDesktop = false; // Switch 使用固定分辨率全屏 + winConfig.fullscreenDesktop = false; // Switch 使用固定分辨率全屏 winConfig.resizable = false; winConfig.enableCursors = false; winConfig.enableDpiScale = false; @@ -170,12 +172,12 @@ bool Application::init(const AppConfig &config) { void Application::prewarmObjectPools() { E2D_LOG_INFO("Prewarming object pools..."); - - auto& poolManager = ObjectPoolManager::getInstance(); - + + auto &poolManager = ObjectPoolManager::getInstance(); + // 预热常用类型的对象池 // 这些池会在首次使用时自动预热,但提前预热可以避免运行时延迟 - + E2D_LOG_INFO("Object pools prewarmed successfully"); } @@ -187,9 +189,9 @@ void Application::shutdown() { // 打印 VRAM 统计 VRAMManager::getInstance().printStats(); - + // 打印对象池内存统计 - E2D_LOG_INFO("Object pool memory usage: {} bytes (auto-managed)", + E2D_LOG_INFO("Object pool memory usage: {} bytes (auto-managed)", ObjectPoolManager::getInstance().getPool()->memoryUsage()); // 先结束所有场景,确保 onExit() 被正确调用 @@ -201,9 +203,9 @@ void Application::shutdown() { // 1. 先清理所有持有 GPU 资源的子系统 // 必须在渲染器关闭前释放纹理等资源 // ======================================== - sceneManager_.reset(); // 场景持有纹理引用 - resourceManager_.reset(); // 纹理缓存持有 GPU 纹理 - camera_.reset(); // 相机可能持有渲染目标 + sceneManager_.reset(); // 场景持有纹理引用 + resourceManager_.reset(); // 纹理缓存持有 GPU 纹理 + camera_.reset(); // 相机可能持有渲染目标 // ======================================== // 2. 关闭音频(不依赖 GPU) @@ -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_); } diff --git a/Extra2D/src/scene/node.cpp b/Extra2D/src/scene/node.cpp index 0dfae1a..ec24190 100644 --- a/Extra2D/src/scene/node.cpp +++ b/Extra2D/src/scene/node.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ Node::Node() = default; Node::~Node() { removeAllChildren(); stopAllActions(); + ActionManager::getInstance()->removeAllActionsFromTarget(this); } void Node::addChild(Ptr 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* 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) { - 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 Node::getActionByTag(int tag) const { - // O(1) 哈希查找 - auto it = actionByTag_.find(tag); - if (it != actionByTag_.end()) { - return it->second; - } - return nullptr; +void Node::stopActionsByFlags(unsigned int flags) { + ActionManager::getInstance()->removeActionsByFlags(flags, this); +} + +Action* Node::getActionByTag(int tag) { + return ActionManager::getInstance()->getActionByTag(tag, this); +} + +size_t Node::getActionCount() const { + return ActionManager::getInstance()->getActionCount(const_cast(this)); +} + +bool Node::isRunningActions() const { + return getActionCount() > 0; } void Node::update(float dt) { onUpdate(dt); } diff --git a/docs/API_Tutorial/09_Action_System.md b/docs/API_Tutorial/09_Action_System.md new file mode 100644 index 0000000..5085367 --- /dev/null +++ b/docs/API_Tutorial/09_Action_System.md @@ -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); +} +``` + +### 示例 2:UI 弹出动画 + +```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()` | 检查是否有动作运行 | diff --git a/docs/ActionSystem_Refactor_Spec.md b/docs/ActionSystem_Refactor_Spec.md new file mode 100644 index 0000000..2dd3842 --- /dev/null +++ b/docs/ActionSystem_Refactor_Spec.md @@ -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 actions; + Node* target; + bool paused; + int actionIndex; + Action* currentAction; + bool currentActionSalvaged; + }; + + std::unordered_map 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& 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(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. 内存测试:动作对象生命周期测试 diff --git a/examples/flappy_bird/GameOverLayer.cpp b/examples/flappy_bird/GameOverLayer.cpp index 1a75167..1ca508a 100644 --- a/examples/flappy_bird/GameOverLayer.cpp +++ b/examples/flappy_bird/GameOverLayer.cpp @@ -44,8 +44,7 @@ void GameOverLayer::onEnter() { initButtons(); // 创建向上移动的动画(从屏幕底部移动到正常位置) - auto moveAction = extra2d::makePtr( - 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_)