refactor(action): 重构动作系统架构并添加新功能
重构动作系统核心架构,主要变更包括: 1. 将动作系统拆分为多个模块化头文件 2. 新增动作管理器实现集中管理 3. 添加瞬时动作、间隔动作和特殊动作类 4. 实现完整的缓动动画系统 5. 优化节点动作接口与性能 新增功能: 1. 支持颜色和翻转动画 2. 添加回调动作和节点管理动作 3. 实现跟随和速度控制等特殊动作 4. 提供30+种缓动函数支持 BREAKING CHANGE: 动作系统API不兼容旧版本,需更新相关调用代码
This commit is contained in:
parent
3ffcd692b6
commit
f02b368dc9
|
|
@ -1,76 +1,172 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
|
||||
enum class ActionState { Idle, Running, Paused, Completed };
|
||||
/**
|
||||
* @brief 动作状态枚举
|
||||
*/
|
||||
enum class ActionState {
|
||||
Idle,
|
||||
Running,
|
||||
Paused,
|
||||
Completed
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 动作基类
|
||||
*
|
||||
* 所有动作的基类,定义了动作的核心接口。
|
||||
* 动作用于修改 Node 的属性,实现动画效果。
|
||||
*/
|
||||
class Action {
|
||||
public:
|
||||
using ProgressCallback = std::function<void(float)>;
|
||||
using CompletionCallback = std::function<void()>;
|
||||
|
||||
Action();
|
||||
virtual ~Action() = default;
|
||||
|
||||
Action(const Action &) = delete;
|
||||
Action &operator=(const Action &) = delete;
|
||||
Action(Action &&) = default;
|
||||
Action &operator=(Action &&) = default;
|
||||
Action(const Action&) = delete;
|
||||
Action& operator=(const Action&) = delete;
|
||||
Action(Action&&) = default;
|
||||
Action& operator=(Action&&) = default;
|
||||
|
||||
virtual void start(Node *target);
|
||||
/**
|
||||
* @brief 检查动作是否完成
|
||||
* @return true 如果动作已完成
|
||||
*/
|
||||
virtual bool isDone() const;
|
||||
|
||||
/**
|
||||
* @brief 使用目标节点启动动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
virtual void startWithTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 停止动作
|
||||
*/
|
||||
virtual void stop();
|
||||
virtual void update(float dt);
|
||||
|
||||
/**
|
||||
* @brief 每帧调用的步进函数
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
virtual void step(float dt);
|
||||
|
||||
virtual bool isDone() const = 0;
|
||||
virtual Action *clone() const = 0;
|
||||
virtual Action *reverse() const = 0;
|
||||
/**
|
||||
* @brief 更新动作状态
|
||||
* @param time 归一化时间 [0, 1]
|
||||
*/
|
||||
virtual void update(float time);
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
virtual Action* clone() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
virtual Action* reverse() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 暂停动作
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复动作
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief 重启动作
|
||||
*/
|
||||
void restart();
|
||||
|
||||
/**
|
||||
* @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_; }
|
||||
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; }
|
||||
/**
|
||||
* @brief 获取标签
|
||||
* @return 标签值
|
||||
*/
|
||||
int getTag() const { return tag_; }
|
||||
|
||||
protected:
|
||||
virtual void onStart() {}
|
||||
virtual void onUpdate(float progress) = 0;
|
||||
virtual void onComplete() {}
|
||||
/**
|
||||
* @brief 设置标签
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void setTag(int tag) { tag_ = tag; }
|
||||
|
||||
/**
|
||||
* @brief 获取标志位
|
||||
* @return 标志位
|
||||
*/
|
||||
unsigned int getFlags() const { return flags_; }
|
||||
|
||||
/**
|
||||
* @brief 设置标志位
|
||||
* @param flags 标志位
|
||||
*/
|
||||
void setFlags(unsigned int flags) { flags_ = flags; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 动作开始时调用
|
||||
*/
|
||||
virtual void onStart() {}
|
||||
|
||||
/**
|
||||
* @brief 动作完成时调用
|
||||
*/
|
||||
virtual void onComplete() {
|
||||
if (completionCallback_) {
|
||||
completionCallback_();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置动作完成状态
|
||||
*/
|
||||
void setDone() { state_ = ActionState::Completed; }
|
||||
|
||||
Node *target_ = nullptr;
|
||||
Node *originalTarget_ = nullptr;
|
||||
Node* target_ = nullptr;
|
||||
Node* originalTarget_ = nullptr;
|
||||
ActionState state_ = ActionState::Idle;
|
||||
float elapsed_ = 0.0f;
|
||||
float duration_ = 0.0f;
|
||||
float speed_ = 1.0f;
|
||||
int tag_ = -1;
|
||||
ProgressCallback progressCallback_;
|
||||
unsigned int flags_ = 0;
|
||||
CompletionCallback completionCallback_;
|
||||
};
|
||||
|
||||
using ActionPtr = std::unique_ptr<Action>;
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -0,0 +1,344 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/action/ease.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 缓动动作基类
|
||||
*
|
||||
* 使用装饰器模式包装其他动作,实现缓动效果。
|
||||
*/
|
||||
class ActionEase : public ActionInterval {
|
||||
public:
|
||||
virtual ~ActionEase();
|
||||
|
||||
/**
|
||||
* @brief 获取内部动作
|
||||
* @return 内部动作指针
|
||||
*/
|
||||
ActionInterval* getInnerAction() const { return innerAction_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void update(float time) override;
|
||||
ActionInterval* clone() const override = 0;
|
||||
ActionInterval* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
ActionEase() = default;
|
||||
bool initWithAction(ActionInterval* action);
|
||||
void onUpdate(float progress) override {}
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 指数缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseExponentialIn : public ActionEase {
|
||||
public:
|
||||
static EaseExponentialIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseExponentialOut : public ActionEase {
|
||||
public:
|
||||
static EaseExponentialOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseExponentialInOut : public ActionEase {
|
||||
public:
|
||||
static EaseExponentialInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 正弦缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseSineIn : public ActionEase {
|
||||
public:
|
||||
static EaseSineIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseSineOut : public ActionEase {
|
||||
public:
|
||||
static EaseSineOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseSineInOut : public ActionEase {
|
||||
public:
|
||||
static EaseSineInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 弹性缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseElasticIn : public ActionEase {
|
||||
public:
|
||||
static EaseElasticIn* create(ActionInterval* action, float period = 0.3f);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
float period_ = 0.3f;
|
||||
};
|
||||
|
||||
class EaseElasticOut : public ActionEase {
|
||||
public:
|
||||
static EaseElasticOut* create(ActionInterval* action, float period = 0.3f);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
float period_ = 0.3f;
|
||||
};
|
||||
|
||||
class EaseElasticInOut : public ActionEase {
|
||||
public:
|
||||
static EaseElasticInOut* create(ActionInterval* action, float period = 0.3f);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
float period_ = 0.3f;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 弹跳缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseBounceIn : public ActionEase {
|
||||
public:
|
||||
static EaseBounceIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBounceOut : public ActionEase {
|
||||
public:
|
||||
static EaseBounceOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBounceInOut : public ActionEase {
|
||||
public:
|
||||
static EaseBounceInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 回震缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseBackIn : public ActionEase {
|
||||
public:
|
||||
static EaseBackIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBackOut : public ActionEase {
|
||||
public:
|
||||
static EaseBackOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseBackInOut : public ActionEase {
|
||||
public:
|
||||
static EaseBackInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 二次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseQuadIn : public ActionEase {
|
||||
public:
|
||||
static EaseQuadIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuadOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuadOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuadInOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuadInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 三次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseCubicIn : public ActionEase {
|
||||
public:
|
||||
static EaseCubicIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCubicOut : public ActionEase {
|
||||
public:
|
||||
static EaseCubicOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCubicInOut : public ActionEase {
|
||||
public:
|
||||
static EaseCubicInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 四次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseQuartIn : public ActionEase {
|
||||
public:
|
||||
static EaseQuartIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuartOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuartOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuartInOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuartInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 五次缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseQuintIn : public ActionEase {
|
||||
public:
|
||||
static EaseQuintIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuintOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuintOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseQuintInOut : public ActionEase {
|
||||
public:
|
||||
static EaseQuintInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 圆形缓动
|
||||
// ============================================================================
|
||||
|
||||
class EaseCircleIn : public ActionEase {
|
||||
public:
|
||||
static EaseCircleIn* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCircleOut : public ActionEase {
|
||||
public:
|
||||
static EaseCircleOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
class EaseCircleInOut : public ActionEase {
|
||||
public:
|
||||
static EaseCircleInOut* create(ActionInterval* action);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义缓动
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 自定义缓动动作
|
||||
*/
|
||||
class EaseCustom : public ActionEase {
|
||||
public:
|
||||
static EaseCustom* create(ActionInterval* action, EaseFunction easeFunc);
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
void update(float time) override;
|
||||
|
||||
protected:
|
||||
EaseFunction easeFunc_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/finite_time_action.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 瞬时动作基类
|
||||
*
|
||||
* 瞬间完成的动作,中间没有任何动画效果。
|
||||
* 继承自 FiniteTimeAction,持续时间为 0。
|
||||
*/
|
||||
class ActionInstant : public FiniteTimeAction {
|
||||
public:
|
||||
ActionInstant();
|
||||
virtual ~ActionInstant() = default;
|
||||
|
||||
/**
|
||||
* @brief 检查动作是否完成
|
||||
* @return 总是返回 true
|
||||
*/
|
||||
bool isDone() const override;
|
||||
|
||||
/**
|
||||
* @brief 使用目标节点启动动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void startWithTarget(Node* target) override;
|
||||
|
||||
/**
|
||||
* @brief 每帧调用的步进函数
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
void step(float dt) override;
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
ActionInstant* clone() const override = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
ActionInstant* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 执行瞬时动作
|
||||
*/
|
||||
virtual void execute() = 0;
|
||||
|
||||
bool done_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action_instant.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 回调动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 无参数回调动作
|
||||
*/
|
||||
class CallFunc : public ActionInstant {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
static CallFunc* create(const Callback& callback);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 带节点参数的回调动作
|
||||
*/
|
||||
class CallFuncN : public ActionInstant {
|
||||
public:
|
||||
using Callback = std::function<void(Node*)>;
|
||||
|
||||
static CallFuncN* create(const Callback& callback);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 位置动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 瞬间放置到指定位置
|
||||
*/
|
||||
class Place : public ActionInstant {
|
||||
public:
|
||||
static Place* create(const Vec2& position);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
Vec2 position_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 翻转动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief X轴翻转动作
|
||||
*/
|
||||
class FlipX : public ActionInstant {
|
||||
public:
|
||||
static FlipX* create(bool flipX);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
bool flipX_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Y轴翻转动作
|
||||
*/
|
||||
class FlipY : public ActionInstant {
|
||||
public:
|
||||
static FlipY* create(bool flipY);
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
|
||||
bool flipY_ = false;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 可见性动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 显示动作
|
||||
*/
|
||||
class Show : public ActionInstant {
|
||||
public:
|
||||
static Show* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 隐藏动作
|
||||
*/
|
||||
class Hide : public ActionInstant {
|
||||
public:
|
||||
static Hide* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 切换可见性动作
|
||||
*/
|
||||
class ToggleVisibility : public ActionInstant {
|
||||
public:
|
||||
static ToggleVisibility* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 节点管理动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 移除自身动作
|
||||
*/
|
||||
class RemoveSelf : public ActionInstant {
|
||||
public:
|
||||
static RemoveSelf* create();
|
||||
|
||||
ActionInstant* clone() const override;
|
||||
ActionInstant* reverse() const override;
|
||||
|
||||
protected:
|
||||
void execute() override;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/finite_time_action.h>
|
||||
#include <extra2d/action/ease.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 时间间隔动作基类
|
||||
*
|
||||
* 在指定时间内完成的动作,中间会有动画效果。
|
||||
* 继承自 FiniteTimeAction。
|
||||
*/
|
||||
class ActionInterval : public FiniteTimeAction {
|
||||
public:
|
||||
ActionInterval() = default;
|
||||
explicit ActionInterval(float duration);
|
||||
virtual ~ActionInterval() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取已流逝时间
|
||||
* @return 已流逝时间(秒)
|
||||
*/
|
||||
float getElapsed() const { return elapsed_; }
|
||||
|
||||
/**
|
||||
* @brief 检查动作是否完成
|
||||
* @return true 如果动作已完成
|
||||
*/
|
||||
bool isDone() const override;
|
||||
|
||||
/**
|
||||
* @brief 使用目标节点启动动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void startWithTarget(Node* target) override;
|
||||
|
||||
/**
|
||||
* @brief 停止动作
|
||||
*/
|
||||
void stop() override;
|
||||
|
||||
/**
|
||||
* @brief 每帧调用的步进函数
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
void step(float dt) override;
|
||||
|
||||
/**
|
||||
* @brief 设置振幅比率(用于缓动)
|
||||
* @param amp 振幅比率
|
||||
*/
|
||||
void setAmplitudeRate(float amp) { amplitudeRate_ = amp; }
|
||||
|
||||
/**
|
||||
* @brief 获取振幅比率
|
||||
* @return 振幅比率
|
||||
*/
|
||||
float getAmplitudeRate() const { return amplitudeRate_; }
|
||||
|
||||
/**
|
||||
* @brief 设置内置缓动函数
|
||||
* @param easeFunc 缓动函数
|
||||
*/
|
||||
void setEaseFunction(EaseFunction easeFunc) { easeFunc_ = easeFunc; }
|
||||
|
||||
/**
|
||||
* @brief 获取内置缓动函数
|
||||
* @return 缓动函数
|
||||
*/
|
||||
EaseFunction getEaseFunction() const { return easeFunc_; }
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
ActionInterval* clone() const override = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
ActionInterval* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 动作开始时调用
|
||||
*/
|
||||
virtual void onStart() {}
|
||||
|
||||
/**
|
||||
* @brief 动作更新时调用
|
||||
* @param progress 归一化进度 [0, 1]
|
||||
*/
|
||||
virtual void onUpdate(float progress) = 0;
|
||||
|
||||
float elapsed_ = 0.0f;
|
||||
bool firstTick_ = true;
|
||||
float amplitudeRate_ = 1.0f;
|
||||
EaseFunction easeFunc_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,469 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 移动动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对移动动作
|
||||
*/
|
||||
class MoveBy : public ActionInterval {
|
||||
public:
|
||||
static MoveBy* create(float duration, const Vec2& delta);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 delta_;
|
||||
Vec2 startPosition_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对移动动作
|
||||
*/
|
||||
class MoveTo : public ActionInterval {
|
||||
public:
|
||||
static MoveTo* create(float duration, const Vec2& position);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 endPosition_;
|
||||
Vec2 startPosition_;
|
||||
Vec2 delta_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 跳跃动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对跳跃动作
|
||||
*/
|
||||
class JumpBy : public ActionInterval {
|
||||
public:
|
||||
static JumpBy* create(float duration, const Vec2& position, float height, int jumps);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 startPosition_;
|
||||
Vec2 delta_;
|
||||
float height_ = 0.0f;
|
||||
int jumps_ = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对跳跃动作
|
||||
*/
|
||||
class JumpTo : public JumpBy {
|
||||
public:
|
||||
static JumpTo* create(float duration, const Vec2& position, float height, int jumps);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
|
||||
Vec2 endPosition_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 贝塞尔曲线动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 贝塞尔曲线配置
|
||||
*/
|
||||
struct BezierConfig {
|
||||
Vec2 controlPoint1;
|
||||
Vec2 controlPoint2;
|
||||
Vec2 endPosition;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相对贝塞尔曲线动作
|
||||
*/
|
||||
class BezierBy : public ActionInterval {
|
||||
public:
|
||||
static BezierBy* create(float duration, const BezierConfig& config);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
static float bezierat(float a, float b, float c, float d, float t);
|
||||
|
||||
BezierConfig config_;
|
||||
Vec2 startPosition_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对贝塞尔曲线动作
|
||||
*/
|
||||
class BezierTo : public BezierBy {
|
||||
public:
|
||||
static BezierTo* create(float duration, const BezierConfig& config);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
|
||||
BezierConfig originalConfig_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 缩放动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对缩放动作
|
||||
*/
|
||||
class ScaleBy : public ActionInterval {
|
||||
public:
|
||||
static ScaleBy* create(float duration, float scale);
|
||||
static ScaleBy* create(float duration, float scaleX, float scaleY);
|
||||
static ScaleBy* create(float duration, const Vec2& scale);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 deltaScale_;
|
||||
Vec2 startScale_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对缩放动作
|
||||
*/
|
||||
class ScaleTo : public ActionInterval {
|
||||
public:
|
||||
static ScaleTo* create(float duration, float scale);
|
||||
static ScaleTo* create(float duration, float scaleX, float scaleY);
|
||||
static ScaleTo* create(float duration, const Vec2& scale);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Vec2 endScale_;
|
||||
Vec2 startScale_;
|
||||
Vec2 delta_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 旋转动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 相对旋转动作
|
||||
*/
|
||||
class RotateBy : public ActionInterval {
|
||||
public:
|
||||
static RotateBy* create(float duration, float deltaAngle);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float deltaAngle_ = 0.0f;
|
||||
float startAngle_ = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绝对旋转动作
|
||||
*/
|
||||
class RotateTo : public ActionInterval {
|
||||
public:
|
||||
static RotateTo* create(float duration, float angle);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float endAngle_ = 0.0f;
|
||||
float startAngle_ = 0.0f;
|
||||
float deltaAngle_ = 0.0f;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 淡入淡出动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 淡入动作
|
||||
*/
|
||||
class FadeIn : public ActionInterval {
|
||||
public:
|
||||
static FadeIn* create(float duration);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float startOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 淡出动作
|
||||
*/
|
||||
class FadeOut : public ActionInterval {
|
||||
public:
|
||||
static FadeOut* create(float duration);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float startOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 淡入到指定透明度动作
|
||||
*/
|
||||
class FadeTo : public ActionInterval {
|
||||
public:
|
||||
static FadeTo* create(float duration, float opacity);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
float endOpacity_ = 0.0f;
|
||||
float startOpacity_ = 0.0f;
|
||||
float deltaOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 闪烁动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 闪烁动作
|
||||
*/
|
||||
class Blink : public ActionInterval {
|
||||
public:
|
||||
static Blink* create(float duration, int times);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
int times_ = 1;
|
||||
int currentTimes_ = 0;
|
||||
bool originalVisible_ = true;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 色调动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 色调变化动作
|
||||
*/
|
||||
class TintTo : public ActionInterval {
|
||||
public:
|
||||
static TintTo* create(float duration, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Color3B startColor_;
|
||||
Color3B endColor_;
|
||||
Color3B deltaColor_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相对色调变化动作
|
||||
*/
|
||||
class TintBy : public ActionInterval {
|
||||
public:
|
||||
static TintBy* create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
Color3B startColor_;
|
||||
int16_t deltaR_ = 0;
|
||||
int16_t deltaG_ = 0;
|
||||
int16_t deltaB_ = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 组合动作
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 序列动作
|
||||
*/
|
||||
class Sequence : public ActionInterval {
|
||||
public:
|
||||
static Sequence* create(ActionInterval* action1, ...);
|
||||
static Sequence* create(const std::vector<ActionInterval*>& actions);
|
||||
|
||||
~Sequence();
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
std::vector<ActionInterval*> actions_;
|
||||
size_t currentIndex_ = 0;
|
||||
float split_ = 0.0f;
|
||||
float last_ = -1.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 并行动作
|
||||
*/
|
||||
class Spawn : public ActionInterval {
|
||||
public:
|
||||
static Spawn* create(ActionInterval* action1, ...);
|
||||
static Spawn* create(const std::vector<ActionInterval*>& actions);
|
||||
|
||||
~Spawn();
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
std::vector<ActionInterval*> actions_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 重复动作
|
||||
*/
|
||||
class Repeat : public ActionInterval {
|
||||
public:
|
||||
static Repeat* create(ActionInterval* action, int times);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
bool isDone() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
int times_ = 1;
|
||||
int currentTimes_ = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 永久重复动作
|
||||
*/
|
||||
class RepeatForever : public ActionInterval {
|
||||
public:
|
||||
static RepeatForever* create(ActionInterval* action);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
bool isDone() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 延时动作
|
||||
*/
|
||||
class DelayTime : public ActionInterval {
|
||||
public:
|
||||
static DelayTime* create(float duration);
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onUpdate(float progress) override {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 反向时间动作
|
||||
*/
|
||||
class ReverseTime : public ActionInterval {
|
||||
public:
|
||||
static ReverseTime* create(ActionInterval* action);
|
||||
|
||||
~ReverseTime();
|
||||
|
||||
ActionInterval* clone() const override;
|
||||
ActionInterval* reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 动作管理器
|
||||
*
|
||||
* 单例模式,集中管理所有动作的调度和生命周期。
|
||||
* 负责动作的添加、移除、暂停、恢复和更新。
|
||||
*/
|
||||
class ActionManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return ActionManager 实例指针
|
||||
*/
|
||||
static ActionManager* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 销毁单例实例
|
||||
*/
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* @brief 添加动作到管理器
|
||||
* @param action 动作指针
|
||||
* @param target 目标节点
|
||||
* @param paused 是否暂停
|
||||
*/
|
||||
void addAction(Action* action, Node* target, bool paused = false);
|
||||
|
||||
/**
|
||||
* @brief 移除指定动作
|
||||
* @param action 动作指针
|
||||
*/
|
||||
void removeAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 根据标签移除动作
|
||||
* @param tag 标签值
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void removeActionByTag(int tag, Node* target);
|
||||
|
||||
/**
|
||||
* @brief 根据标志位移除动作
|
||||
* @param flags 标志位
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void removeActionsByFlags(unsigned int flags, Node* target);
|
||||
|
||||
/**
|
||||
* @brief 移除目标节点的所有动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void removeAllActionsFromTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 移除所有动作
|
||||
*/
|
||||
void removeAllActions();
|
||||
|
||||
/**
|
||||
* @brief 根据标签获取动作
|
||||
* @param tag 标签值
|
||||
* @param target 目标节点
|
||||
* @return 动作指针,未找到返回 nullptr
|
||||
*/
|
||||
Action* getActionByTag(int tag, Node* target);
|
||||
|
||||
/**
|
||||
* @brief 获取目标节点的动作数量
|
||||
* @param target 目标节点
|
||||
* @return 动作数量
|
||||
*/
|
||||
size_t getActionCount(Node* target) const;
|
||||
|
||||
/**
|
||||
* @brief 暂停目标节点的所有动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void pauseTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 恢复目标节点的所有动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void resumeTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 检查目标节点是否暂停
|
||||
* @param target 目标节点
|
||||
* @return true 如果暂停
|
||||
*/
|
||||
bool isPaused(Node* target) const;
|
||||
|
||||
/**
|
||||
* @brief 更新所有动作(每帧调用)
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
void update(float dt);
|
||||
|
||||
private:
|
||||
ActionManager();
|
||||
~ActionManager();
|
||||
|
||||
struct ActionElement {
|
||||
std::vector<Action*> actions;
|
||||
Node* target = nullptr;
|
||||
bool paused = false;
|
||||
int actionIndex = 0;
|
||||
Action* currentAction = nullptr;
|
||||
bool currentActionSalvaged = false;
|
||||
};
|
||||
|
||||
using ActionMap = std::unordered_map<Node*, ActionElement>;
|
||||
|
||||
void removeActionAt(size_t index, ActionElement& element);
|
||||
void deleteAction(Action* action);
|
||||
|
||||
ActionMap targets_;
|
||||
static ActionManager* instance_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action.h>
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 速度控制动作
|
||||
*
|
||||
* 包装其他动作,实现动态速度控制。
|
||||
* 可以在运行时调整动作的播放速度。
|
||||
*/
|
||||
class Speed : public Action {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建速度控制动作
|
||||
* @param action 内部动作
|
||||
* @param speed 速度倍率(1.0 为正常速度)
|
||||
* @return 动作指针
|
||||
*/
|
||||
static Speed* create(ActionInterval* action, float speed);
|
||||
|
||||
~Speed();
|
||||
|
||||
/**
|
||||
* @brief 获取速度倍率
|
||||
* @return 速度倍率
|
||||
*/
|
||||
float getSpeed() const { return speed_; }
|
||||
|
||||
/**
|
||||
* @brief 设置速度倍率
|
||||
* @param speed 速度倍率
|
||||
*/
|
||||
void setSpeed(float speed) { speed_ = speed; }
|
||||
|
||||
/**
|
||||
* @brief 获取内部动作
|
||||
* @return 内部动作指针
|
||||
*/
|
||||
ActionInterval* getInnerAction() const { return innerAction_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void step(float dt) override;
|
||||
bool isDone() const override;
|
||||
Action* clone() const override;
|
||||
Action* reverse() const override;
|
||||
|
||||
protected:
|
||||
Speed() = default;
|
||||
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
float speed_ = 1.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 跟随动作
|
||||
*
|
||||
* 使节点跟随另一个节点移动。
|
||||
* 常用于相机跟随玩家。
|
||||
*/
|
||||
class Follow : public Action {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建跟随动作
|
||||
* @param followedNode 被跟随的节点
|
||||
* @return 动作指针
|
||||
*/
|
||||
static Follow* create(Node* followedNode);
|
||||
|
||||
/**
|
||||
* @brief 创建带边界的跟随动作
|
||||
* @param followedNode 被跟随的节点
|
||||
* @param boundary 边界矩形
|
||||
* @return 动作指针
|
||||
*/
|
||||
static Follow* create(Node* followedNode, const Rect& boundary);
|
||||
|
||||
~Follow();
|
||||
|
||||
/**
|
||||
* @brief 获取被跟随的节点
|
||||
* @return 被跟随的节点指针
|
||||
*/
|
||||
Node* getFollowedNode() const { return followedNode_; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否设置了边界
|
||||
* @return true 如果设置了边界
|
||||
*/
|
||||
bool isBoundarySet() const { return boundarySet_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void step(float dt) override;
|
||||
bool isDone() const override;
|
||||
Action* clone() const override;
|
||||
Action* reverse() const override;
|
||||
|
||||
protected:
|
||||
Follow() = default;
|
||||
|
||||
Node* followedNode_ = nullptr;
|
||||
Rect boundary_;
|
||||
bool boundarySet_ = false;
|
||||
Vec2 halfScreenSize_;
|
||||
Vec2 fullScreenSize_;
|
||||
Vec2 leftBoundary_;
|
||||
Vec2 rightBoundary_;
|
||||
Vec2 topBoundary_;
|
||||
Vec2 bottomBoundary_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 目标动作
|
||||
*
|
||||
* 允许在一个节点上运行动作,但目标为另一个节点。
|
||||
*/
|
||||
class TargetedAction : public Action {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建目标动作
|
||||
* @param target 目标节点
|
||||
* @param action 要运行的动作
|
||||
* @return 动作指针
|
||||
*/
|
||||
static TargetedAction* create(Node* target, FiniteTimeAction* action);
|
||||
|
||||
~TargetedAction();
|
||||
|
||||
/**
|
||||
* @brief 获取目标节点
|
||||
* @return 目标节点指针
|
||||
*/
|
||||
Node* getTargetNode() const { return targetNode_; }
|
||||
|
||||
/**
|
||||
* @brief 设置目标节点
|
||||
* @param target 目标节点
|
||||
*/
|
||||
void setTargetNode(Node* target) { targetNode_ = target; }
|
||||
|
||||
/**
|
||||
* @brief 获取内部动作
|
||||
* @return 内部动作指针
|
||||
*/
|
||||
FiniteTimeAction* getAction() const { return innerAction_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void step(float dt) override;
|
||||
bool isDone() const override;
|
||||
Action* clone() const override;
|
||||
Action* reverse() const override;
|
||||
|
||||
protected:
|
||||
TargetedAction() = default;
|
||||
|
||||
Node* targetNode_ = nullptr;
|
||||
FiniteTimeAction* innerAction_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "extra2d/action/action.h"
|
||||
#include "extra2d/core/math_types.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
// Interval Action Base
|
||||
class IntervalAction : public Action {
|
||||
public:
|
||||
explicit IntervalAction(float duration);
|
||||
bool isDone() const override;
|
||||
};
|
||||
|
||||
// Instant Action Base
|
||||
class InstantAction : public Action {
|
||||
public:
|
||||
InstantAction();
|
||||
bool isDone() const override;
|
||||
};
|
||||
|
||||
// Move Actions
|
||||
class MoveBy : public IntervalAction {
|
||||
public:
|
||||
MoveBy(float duration, const Vec2 &delta);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
Vec2 delta_;
|
||||
Vec2 startPosition_;
|
||||
};
|
||||
|
||||
class MoveTo : public IntervalAction {
|
||||
public:
|
||||
MoveTo(float duration, const Vec2 &position);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
Vec2 endPosition_;
|
||||
Vec2 startPosition_;
|
||||
Vec2 delta_;
|
||||
};
|
||||
|
||||
// Scale Actions
|
||||
class ScaleBy : public IntervalAction {
|
||||
public:
|
||||
ScaleBy(float duration, float scale);
|
||||
ScaleBy(float duration, float scaleX, float scaleY);
|
||||
ScaleBy(float duration, const Vec2 &scale);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
Vec2 deltaScale_;
|
||||
Vec2 startScale_;
|
||||
};
|
||||
|
||||
class ScaleTo : public IntervalAction {
|
||||
public:
|
||||
ScaleTo(float duration, float scale);
|
||||
ScaleTo(float duration, float scaleX, float scaleY);
|
||||
ScaleTo(float duration, const Vec2 &scale);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
Vec2 endScale_;
|
||||
Vec2 startScale_;
|
||||
Vec2 delta_;
|
||||
};
|
||||
|
||||
// Rotate Actions
|
||||
class RotateBy : public IntervalAction {
|
||||
public:
|
||||
RotateBy(float duration, float deltaAngle);
|
||||
RotateBy(float duration, float deltaAngleX, float deltaAngleY);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
float deltaAngle_ = 0.0f;
|
||||
float startAngle_ = 0.0f;
|
||||
};
|
||||
|
||||
class RotateTo : public IntervalAction {
|
||||
public:
|
||||
RotateTo(float duration, float angle);
|
||||
RotateTo(float duration, float angleX, float angleY);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
float endAngle_ = 0.0f;
|
||||
float startAngle_ = 0.0f;
|
||||
float deltaAngle_ = 0.0f;
|
||||
};
|
||||
|
||||
// Fade Actions
|
||||
class FadeIn : public IntervalAction {
|
||||
public:
|
||||
explicit FadeIn(float duration);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
float startOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
class FadeOut : public IntervalAction {
|
||||
public:
|
||||
explicit FadeOut(float duration);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
float startOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
class FadeTo : public IntervalAction {
|
||||
public:
|
||||
FadeTo(float duration, float opacity);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
float endOpacity_ = 0.0f;
|
||||
float startOpacity_ = 0.0f;
|
||||
float deltaOpacity_ = 0.0f;
|
||||
};
|
||||
|
||||
// Composite Actions
|
||||
class Sequence : public IntervalAction {
|
||||
public:
|
||||
Sequence(const std::vector<Action *> &actions);
|
||||
~Sequence();
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
std::vector<Action *> actions_;
|
||||
int currentIndex_ = 0;
|
||||
float split_ = 0.0f;
|
||||
float last_ = 0.0f;
|
||||
};
|
||||
|
||||
class Spawn : public IntervalAction {
|
||||
public:
|
||||
Spawn(const std::vector<Action *> &actions);
|
||||
~Spawn();
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
std::vector<Action *> actions_;
|
||||
};
|
||||
|
||||
// Loop Action
|
||||
class Loop : public Action {
|
||||
public:
|
||||
Loop(Action *action, int times = -1);
|
||||
~Loop();
|
||||
|
||||
bool isDone() const override;
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onStart() override;
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
Action *action_ = nullptr;
|
||||
int times_ = 1;
|
||||
int currentTimes_ = 0;
|
||||
};
|
||||
|
||||
// Delay Action
|
||||
class Delay : public IntervalAction {
|
||||
public:
|
||||
explicit Delay(float duration);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onUpdate(float progress) override;
|
||||
};
|
||||
|
||||
// CallFunc Action
|
||||
class CallFunc : public InstantAction {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
explicit CallFunc(Callback callback);
|
||||
|
||||
Action *clone() const override;
|
||||
Action *reverse() const override;
|
||||
|
||||
protected:
|
||||
void onUpdate(float progress) override;
|
||||
|
||||
private:
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
// Helper functions
|
||||
inline Sequence *sequence(const std::vector<Action *> &actions) {
|
||||
return new Sequence(actions);
|
||||
}
|
||||
inline Spawn *spawn(const std::vector<Action *> &actions) {
|
||||
return new Spawn(actions);
|
||||
}
|
||||
inline Loop *loop(Action *action, int times = -1) {
|
||||
return new Loop(action, times);
|
||||
}
|
||||
inline Delay *delay(float duration) { return new Delay(duration); }
|
||||
inline CallFunc *callFunc(CallFunc::Callback callback) {
|
||||
return new CallFunc(std::move(callback));
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/action/action.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 有限时间动作基类
|
||||
*
|
||||
* 所有具有持续时间的动作的基类。
|
||||
* 继承自 Action,添加了持续时间属性。
|
||||
*/
|
||||
class FiniteTimeAction : public Action {
|
||||
public:
|
||||
FiniteTimeAction() = default;
|
||||
explicit FiniteTimeAction(float duration);
|
||||
virtual ~FiniteTimeAction() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取动作持续时间
|
||||
* @return 持续时间(秒)
|
||||
*/
|
||||
float getDuration() const { return duration_; }
|
||||
|
||||
/**
|
||||
* @brief 设置动作持续时间
|
||||
* @param duration 持续时间(秒)
|
||||
*/
|
||||
void setDuration(float duration) { duration_ = duration; }
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
FiniteTimeAction* clone() const override = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
FiniteTimeAction* reverse() const override = 0;
|
||||
|
||||
protected:
|
||||
float duration_ = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -6,6 +6,40 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/// RGB 颜色(字节,每通道 0-255)
|
||||
struct Color3B {
|
||||
uint8_t r = 255;
|
||||
uint8_t g = 255;
|
||||
uint8_t b = 255;
|
||||
|
||||
constexpr Color3B() = default;
|
||||
constexpr Color3B(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {}
|
||||
|
||||
constexpr bool operator==(const Color3B& other) const {
|
||||
return r == other.r && g == other.g && b == other.b;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const Color3B& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
Color3B operator+(const Color3B& other) const {
|
||||
return Color3B(
|
||||
static_cast<uint8_t>(std::min(255, static_cast<int>(r) + other.r)),
|
||||
static_cast<uint8_t>(std::min(255, static_cast<int>(g) + other.g)),
|
||||
static_cast<uint8_t>(std::min(255, static_cast<int>(b) + other.b))
|
||||
);
|
||||
}
|
||||
|
||||
Color3B operator-(const Color3B& other) const {
|
||||
return Color3B(
|
||||
static_cast<uint8_t>(std::max(0, static_cast<int>(r) - other.r)),
|
||||
static_cast<uint8_t>(std::max(0, static_cast<int>(g) - other.g)),
|
||||
static_cast<uint8_t>(std::max(0, static_cast<int>(b) - other.b))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/// RGBA 颜色(浮点数,每通道 0.0 - 1.0)
|
||||
struct Color {
|
||||
float r = 0.0f;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,14 @@
|
|||
|
||||
// Action
|
||||
#include <extra2d/action/action.h>
|
||||
#include <extra2d/action/actions.h>
|
||||
#include <extra2d/action/finite_time_action.h>
|
||||
#include <extra2d/action/action_interval.h>
|
||||
#include <extra2d/action/action_instant.h>
|
||||
#include <extra2d/action/action_interval_actions.h>
|
||||
#include <extra2d/action/action_instant_actions.h>
|
||||
#include <extra2d/action/action_ease.h>
|
||||
#include <extra2d/action/action_special.h>
|
||||
#include <extra2d/action/action_manager.h>
|
||||
#include <extra2d/action/ease.h>
|
||||
|
||||
// Event
|
||||
|
|
|
|||
|
|
@ -76,6 +76,25 @@ public:
|
|||
void setVisible(bool visible);
|
||||
bool isVisible() const { return visible_; }
|
||||
|
||||
/**
|
||||
* @brief 设置颜色
|
||||
* @param color RGB颜色
|
||||
*/
|
||||
void setColor(const Color3B& color);
|
||||
Color3B getColor() const { return color_; }
|
||||
|
||||
/**
|
||||
* @brief 设置X轴翻转
|
||||
*/
|
||||
void setFlipX(bool flipX);
|
||||
bool isFlipX() const { return flipX_; }
|
||||
|
||||
/**
|
||||
* @brief 设置Y轴翻转
|
||||
*/
|
||||
void setFlipY(bool flipY);
|
||||
bool isFlipY() const { return flipY_; }
|
||||
|
||||
void setZOrder(int zOrder);
|
||||
int getZOrder() const { return zOrder_; }
|
||||
|
||||
|
|
@ -139,12 +158,54 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 动作系统
|
||||
// ------------------------------------------------------------------------
|
||||
void runAction(Ptr<Action> action);
|
||||
/**
|
||||
* @brief 运行动作
|
||||
* @param action 动作指针(所有权转移)
|
||||
* @return 动作指针
|
||||
*/
|
||||
Action* runAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 停止所有动作
|
||||
*/
|
||||
void stopAllActions();
|
||||
void stopAction(Ptr<Action> action);
|
||||
|
||||
/**
|
||||
* @brief 停止指定动作
|
||||
* @param action 动作指针
|
||||
*/
|
||||
void stopAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 根据标签停止动作
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void stopActionByTag(int tag);
|
||||
Ptr<Action> getActionByTag(int tag) const;
|
||||
size_t getActionCount() const { return actions_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 根据标志位停止动作
|
||||
* @param flags 标志位
|
||||
*/
|
||||
void stopActionsByFlags(unsigned int flags);
|
||||
|
||||
/**
|
||||
* @brief 根据标签获取动作
|
||||
* @param tag 标签值
|
||||
* @return 动作指针,未找到返回 nullptr
|
||||
*/
|
||||
Action* getActionByTag(int tag);
|
||||
|
||||
/**
|
||||
* @brief 获取运行中的动作数量
|
||||
* @return 动作数量
|
||||
*/
|
||||
size_t getActionCount() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否有动作在运行
|
||||
* @return true 如果有动作在运行
|
||||
*/
|
||||
bool isRunningActions() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 事件系统
|
||||
|
|
@ -198,14 +259,10 @@ private:
|
|||
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
|
||||
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
|
||||
|
||||
// 4. 动作系统(使用 unordered_map 加速 tag 查找)
|
||||
std::unordered_map<int, Ptr<Action>> actionByTag_; // 56 bytes
|
||||
std::vector<Ptr<Action>> actions_; // 24 bytes(无 tag 的 Action)
|
||||
|
||||
// 5. 事件分发器
|
||||
// 4. 事件分发器
|
||||
EventDispatcher eventDispatcher_; // 大小取决于实现
|
||||
|
||||
// 6. 父节点引用
|
||||
// 5. 父节点引用
|
||||
WeakPtr<Node> parent_; // 16 bytes
|
||||
|
||||
// 7. 变换属性(按访问频率分组)
|
||||
|
|
@ -221,10 +278,17 @@ private:
|
|||
float rotation_ = 0.0f; // 4 bytes
|
||||
float opacity_ = 1.0f; // 4 bytes
|
||||
|
||||
// 10. 整数属性
|
||||
// 10. 颜色属性
|
||||
Color3B color_ = Color3B(255, 255, 255); // 3 bytes
|
||||
|
||||
// 11. 整数属性
|
||||
int zOrder_ = 0; // 4 bytes
|
||||
int tag_ = -1; // 4 bytes
|
||||
|
||||
// 12. 布尔属性
|
||||
bool flipX_ = false; // 1 byte
|
||||
bool flipY_ = false; // 1 byte
|
||||
|
||||
// 11. 场景指针
|
||||
Scene *scene_ = nullptr; // 8 bytes
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@
|
|||
#include "extra2d/scene/node.h"
|
||||
|
||||
namespace extra2d {
|
||||
Action::Action() : elapsed_(0.0f), duration_(0.0f), speed_(1.0f), tag_(-1) {}
|
||||
|
||||
void Action::start(Node *target) {
|
||||
Action::Action() : tag_(-1), flags_(0) {}
|
||||
|
||||
bool Action::isDone() const {
|
||||
return state_ == ActionState::Completed;
|
||||
}
|
||||
|
||||
void Action::startWithTarget(Node* target) {
|
||||
target_ = target;
|
||||
originalTarget_ = target;
|
||||
elapsed_ = 0.0f;
|
||||
state_ = ActionState::Running;
|
||||
onStart();
|
||||
}
|
||||
|
|
@ -17,52 +21,29 @@ void Action::stop() {
|
|||
state_ = ActionState::Completed;
|
||||
}
|
||||
|
||||
void Action::update(float dt) {
|
||||
if (state_ != ActionState::Running)
|
||||
return;
|
||||
|
||||
step(dt);
|
||||
|
||||
if (isDone()) {
|
||||
state_ = ActionState::Completed;
|
||||
onComplete();
|
||||
if (completionCallback_)
|
||||
completionCallback_();
|
||||
}
|
||||
void Action::step(float dt) {
|
||||
(void)dt;
|
||||
}
|
||||
|
||||
void Action::step(float dt) {
|
||||
if (state_ != ActionState::Running)
|
||||
return;
|
||||
|
||||
elapsed_ += dt * speed_;
|
||||
|
||||
float progress = 0.0f;
|
||||
if (duration_ > 0.0f) {
|
||||
progress = std::min(1.0f, elapsed_ / duration_);
|
||||
} else {
|
||||
progress = 1.0f;
|
||||
}
|
||||
|
||||
if (progressCallback_)
|
||||
progressCallback_(progress);
|
||||
|
||||
onUpdate(progress);
|
||||
void Action::update(float time) {
|
||||
(void)time;
|
||||
}
|
||||
|
||||
void Action::pause() {
|
||||
if (state_ == ActionState::Running)
|
||||
if (state_ == ActionState::Running) {
|
||||
state_ = ActionState::Paused;
|
||||
}
|
||||
}
|
||||
|
||||
void Action::resume() {
|
||||
if (state_ == ActionState::Paused)
|
||||
if (state_ == ActionState::Paused) {
|
||||
state_ = ActionState::Running;
|
||||
}
|
||||
}
|
||||
|
||||
void Action::restart() {
|
||||
elapsed_ = 0.0f;
|
||||
state_ = ActionState::Running;
|
||||
onStart();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -0,0 +1,683 @@
|
|||
#include "extra2d/action/action_ease.h"
|
||||
#include <cmath>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
#endif
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// ActionEase 基类
|
||||
// ============================================================================
|
||||
|
||||
ActionEase::~ActionEase() {
|
||||
delete innerAction_;
|
||||
}
|
||||
|
||||
bool ActionEase::initWithAction(ActionInterval* action) {
|
||||
if (!action) {
|
||||
return false;
|
||||
}
|
||||
innerAction_ = action;
|
||||
duration_ = action->getDuration();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ActionEase::startWithTarget(Node* target) {
|
||||
ActionInterval::startWithTarget(target);
|
||||
innerAction_->startWithTarget(target);
|
||||
}
|
||||
|
||||
void ActionEase::stop() {
|
||||
innerAction_->stop();
|
||||
ActionInterval::stop();
|
||||
}
|
||||
|
||||
void ActionEase::update(float time) {
|
||||
innerAction_->update(time);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 指数缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseExponentialIn* EaseExponentialIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseExponentialIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseExponentialIn::update(float time) {
|
||||
innerAction_->update(easeInExpo(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseExponentialIn::clone() const {
|
||||
return EaseExponentialIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseExponentialIn::reverse() const {
|
||||
return EaseExponentialOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseExponentialOut* EaseExponentialOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseExponentialOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseExponentialOut::update(float time) {
|
||||
innerAction_->update(easeOutExpo(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseExponentialOut::clone() const {
|
||||
return EaseExponentialOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseExponentialOut::reverse() const {
|
||||
return EaseExponentialIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseExponentialInOut* EaseExponentialInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseExponentialInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseExponentialInOut::update(float time) {
|
||||
innerAction_->update(easeInOutExpo(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseExponentialInOut::clone() const {
|
||||
return EaseExponentialInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseExponentialInOut::reverse() const {
|
||||
return EaseExponentialInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 正弦缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseSineIn* EaseSineIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseSineIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseSineIn::update(float time) {
|
||||
innerAction_->update(easeInSine(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseSineIn::clone() const {
|
||||
return EaseSineIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseSineIn::reverse() const {
|
||||
return EaseSineOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseSineOut* EaseSineOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseSineOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseSineOut::update(float time) {
|
||||
innerAction_->update(easeOutSine(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseSineOut::clone() const {
|
||||
return EaseSineOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseSineOut::reverse() const {
|
||||
return EaseSineIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseSineInOut* EaseSineInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseSineInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseSineInOut::update(float time) {
|
||||
innerAction_->update(easeInOutSine(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseSineInOut::clone() const {
|
||||
return EaseSineInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseSineInOut::reverse() const {
|
||||
return EaseSineInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 弹性缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseElasticIn* EaseElasticIn::create(ActionInterval* action, float period) {
|
||||
auto* ease = new EaseElasticIn();
|
||||
ease->initWithAction(action);
|
||||
ease->period_ = period;
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseElasticIn::update(float time) {
|
||||
float newT = 0.0f;
|
||||
if (time == 0.0f || time == 1.0f) {
|
||||
newT = time;
|
||||
} else {
|
||||
float s = period_ / 4.0f;
|
||||
time = time - 1.0f;
|
||||
newT = -std::pow(2.0f, 10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_);
|
||||
}
|
||||
innerAction_->update(newT);
|
||||
}
|
||||
|
||||
ActionInterval* EaseElasticIn::clone() const {
|
||||
return EaseElasticIn::create(innerAction_->clone(), period_);
|
||||
}
|
||||
|
||||
ActionInterval* EaseElasticIn::reverse() const {
|
||||
return EaseElasticOut::create(innerAction_->reverse(), period_);
|
||||
}
|
||||
|
||||
EaseElasticOut* EaseElasticOut::create(ActionInterval* action, float period) {
|
||||
auto* ease = new EaseElasticOut();
|
||||
ease->initWithAction(action);
|
||||
ease->period_ = period;
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseElasticOut::update(float time) {
|
||||
float newT = 0.0f;
|
||||
if (time == 0.0f || time == 1.0f) {
|
||||
newT = time;
|
||||
} else {
|
||||
float s = period_ / 4.0f;
|
||||
newT = std::pow(2.0f, -10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_) + 1.0f;
|
||||
}
|
||||
innerAction_->update(newT);
|
||||
}
|
||||
|
||||
ActionInterval* EaseElasticOut::clone() const {
|
||||
return EaseElasticOut::create(innerAction_->clone(), period_);
|
||||
}
|
||||
|
||||
ActionInterval* EaseElasticOut::reverse() const {
|
||||
return EaseElasticIn::create(innerAction_->reverse(), period_);
|
||||
}
|
||||
|
||||
EaseElasticInOut* EaseElasticInOut::create(ActionInterval* action, float period) {
|
||||
auto* ease = new EaseElasticInOut();
|
||||
ease->initWithAction(action);
|
||||
ease->period_ = period;
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseElasticInOut::update(float time) {
|
||||
float newT = 0.0f;
|
||||
if (time == 0.0f || time == 1.0f) {
|
||||
newT = time;
|
||||
} else {
|
||||
time = time * 2.0f;
|
||||
if (period_ == 0.0f) {
|
||||
period_ = 0.3f * 1.5f;
|
||||
}
|
||||
float s = period_ / 4.0f;
|
||||
if (time < 1.0f) {
|
||||
time -= 1.0f;
|
||||
newT = -0.5f * std::pow(2.0f, 10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_);
|
||||
} else {
|
||||
time -= 1.0f;
|
||||
newT = std::pow(2.0f, -10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_) * 0.5f + 1.0f;
|
||||
}
|
||||
}
|
||||
innerAction_->update(newT);
|
||||
}
|
||||
|
||||
ActionInterval* EaseElasticInOut::clone() const {
|
||||
return EaseElasticInOut::create(innerAction_->clone(), period_);
|
||||
}
|
||||
|
||||
ActionInterval* EaseElasticInOut::reverse() const {
|
||||
return EaseElasticInOut::create(innerAction_->reverse(), period_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 弹跳缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseBounceIn* EaseBounceIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseBounceIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseBounceIn::update(float time) {
|
||||
innerAction_->update(easeInBounce(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseBounceIn::clone() const {
|
||||
return EaseBounceIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseBounceIn::reverse() const {
|
||||
return EaseBounceOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseBounceOut* EaseBounceOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseBounceOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseBounceOut::update(float time) {
|
||||
innerAction_->update(easeOutBounce(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseBounceOut::clone() const {
|
||||
return EaseBounceOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseBounceOut::reverse() const {
|
||||
return EaseBounceIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseBounceInOut* EaseBounceInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseBounceInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseBounceInOut::update(float time) {
|
||||
innerAction_->update(easeInOutBounce(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseBounceInOut::clone() const {
|
||||
return EaseBounceInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseBounceInOut::reverse() const {
|
||||
return EaseBounceInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 回震缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseBackIn* EaseBackIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseBackIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseBackIn::update(float time) {
|
||||
innerAction_->update(easeInBack(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseBackIn::clone() const {
|
||||
return EaseBackIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseBackIn::reverse() const {
|
||||
return EaseBackOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseBackOut* EaseBackOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseBackOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseBackOut::update(float time) {
|
||||
innerAction_->update(easeOutBack(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseBackOut::clone() const {
|
||||
return EaseBackOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseBackOut::reverse() const {
|
||||
return EaseBackIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseBackInOut* EaseBackInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseBackInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseBackInOut::update(float time) {
|
||||
innerAction_->update(easeInOutBack(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseBackInOut::clone() const {
|
||||
return EaseBackInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseBackInOut::reverse() const {
|
||||
return EaseBackInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 二次缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseQuadIn* EaseQuadIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuadIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuadIn::update(float time) {
|
||||
innerAction_->update(easeInQuad(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuadIn::clone() const {
|
||||
return EaseQuadIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuadIn::reverse() const {
|
||||
return EaseQuadOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseQuadOut* EaseQuadOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuadOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuadOut::update(float time) {
|
||||
innerAction_->update(easeOutQuad(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuadOut::clone() const {
|
||||
return EaseQuadOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuadOut::reverse() const {
|
||||
return EaseQuadIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseQuadInOut* EaseQuadInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuadInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuadInOut::update(float time) {
|
||||
innerAction_->update(easeInOutQuad(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuadInOut::clone() const {
|
||||
return EaseQuadInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuadInOut::reverse() const {
|
||||
return EaseQuadInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 三次缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseCubicIn* EaseCubicIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseCubicIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseCubicIn::update(float time) {
|
||||
innerAction_->update(easeInCubic(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseCubicIn::clone() const {
|
||||
return EaseCubicIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseCubicIn::reverse() const {
|
||||
return EaseCubicOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseCubicOut* EaseCubicOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseCubicOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseCubicOut::update(float time) {
|
||||
innerAction_->update(easeOutCubic(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseCubicOut::clone() const {
|
||||
return EaseCubicOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseCubicOut::reverse() const {
|
||||
return EaseCubicIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseCubicInOut* EaseCubicInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseCubicInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseCubicInOut::update(float time) {
|
||||
innerAction_->update(easeInOutCubic(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseCubicInOut::clone() const {
|
||||
return EaseCubicInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseCubicInOut::reverse() const {
|
||||
return EaseCubicInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 四次缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseQuartIn* EaseQuartIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuartIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuartIn::update(float time) {
|
||||
innerAction_->update(easeInQuart(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuartIn::clone() const {
|
||||
return EaseQuartIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuartIn::reverse() const {
|
||||
return EaseQuartOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseQuartOut* EaseQuartOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuartOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuartOut::update(float time) {
|
||||
innerAction_->update(easeOutQuart(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuartOut::clone() const {
|
||||
return EaseQuartOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuartOut::reverse() const {
|
||||
return EaseQuartIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseQuartInOut* EaseQuartInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuartInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuartInOut::update(float time) {
|
||||
innerAction_->update(easeInOutQuart(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuartInOut::clone() const {
|
||||
return EaseQuartInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuartInOut::reverse() const {
|
||||
return EaseQuartInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 五次缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseQuintIn* EaseQuintIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuintIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuintIn::update(float time) {
|
||||
innerAction_->update(easeInQuint(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuintIn::clone() const {
|
||||
return EaseQuintIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuintIn::reverse() const {
|
||||
return EaseQuintOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseQuintOut* EaseQuintOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuintOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuintOut::update(float time) {
|
||||
innerAction_->update(easeOutQuint(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuintOut::clone() const {
|
||||
return EaseQuintOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuintOut::reverse() const {
|
||||
return EaseQuintIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseQuintInOut* EaseQuintInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseQuintInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseQuintInOut::update(float time) {
|
||||
innerAction_->update(easeInOutQuint(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuintInOut::clone() const {
|
||||
return EaseQuintInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseQuintInOut::reverse() const {
|
||||
return EaseQuintInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 圆形缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseCircleIn* EaseCircleIn::create(ActionInterval* action) {
|
||||
auto* ease = new EaseCircleIn();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseCircleIn::update(float time) {
|
||||
innerAction_->update(easeInCirc(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseCircleIn::clone() const {
|
||||
return EaseCircleIn::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseCircleIn::reverse() const {
|
||||
return EaseCircleOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseCircleOut* EaseCircleOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseCircleOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseCircleOut::update(float time) {
|
||||
innerAction_->update(easeOutCirc(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseCircleOut::clone() const {
|
||||
return EaseCircleOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseCircleOut::reverse() const {
|
||||
return EaseCircleIn::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
EaseCircleInOut* EaseCircleInOut::create(ActionInterval* action) {
|
||||
auto* ease = new EaseCircleInOut();
|
||||
ease->initWithAction(action);
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseCircleInOut::update(float time) {
|
||||
innerAction_->update(easeInOutCirc(time));
|
||||
}
|
||||
|
||||
ActionInterval* EaseCircleInOut::clone() const {
|
||||
return EaseCircleInOut::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* EaseCircleInOut::reverse() const {
|
||||
return EaseCircleInOut::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 自定义缓动
|
||||
// ============================================================================
|
||||
|
||||
EaseCustom* EaseCustom::create(ActionInterval* action, EaseFunction easeFunc) {
|
||||
auto* ease = new EaseCustom();
|
||||
ease->initWithAction(action);
|
||||
ease->easeFunc_ = easeFunc;
|
||||
return ease;
|
||||
}
|
||||
|
||||
void EaseCustom::update(float time) {
|
||||
if (easeFunc_) {
|
||||
innerAction_->update(easeFunc_(time));
|
||||
} else {
|
||||
innerAction_->update(time);
|
||||
}
|
||||
}
|
||||
|
||||
ActionInterval* EaseCustom::clone() const {
|
||||
return EaseCustom::create(innerAction_->clone(), easeFunc_);
|
||||
}
|
||||
|
||||
ActionInterval* EaseCustom::reverse() const {
|
||||
return EaseCustom::create(innerAction_->reverse(), easeFunc_);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,787 @@
|
|||
#include "extra2d/action/action_interval_actions.h"
|
||||
#include "extra2d/scene/node.h"
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 移动动作
|
||||
// ============================================================================
|
||||
|
||||
MoveBy* MoveBy::create(float duration, const Vec2& delta) {
|
||||
auto* action = new MoveBy();
|
||||
action->duration_ = duration;
|
||||
action->delta_ = delta;
|
||||
return action;
|
||||
}
|
||||
|
||||
void MoveBy::onStart() {
|
||||
startPosition_ = target_->getPosition();
|
||||
}
|
||||
|
||||
void MoveBy::onUpdate(float progress) {
|
||||
Vec2 newPos = startPosition_ + delta_ * progress;
|
||||
target_->setPosition(newPos);
|
||||
}
|
||||
|
||||
ActionInterval* MoveBy::clone() const {
|
||||
return MoveBy::create(duration_, delta_);
|
||||
}
|
||||
|
||||
ActionInterval* MoveBy::reverse() const {
|
||||
return MoveBy::create(duration_, -delta_);
|
||||
}
|
||||
|
||||
// MoveTo
|
||||
MoveTo* MoveTo::create(float duration, const Vec2& position) {
|
||||
auto* action = new MoveTo();
|
||||
action->duration_ = duration;
|
||||
action->endPosition_ = position;
|
||||
return action;
|
||||
}
|
||||
|
||||
void MoveTo::onStart() {
|
||||
startPosition_ = target_->getPosition();
|
||||
delta_ = endPosition_ - startPosition_;
|
||||
}
|
||||
|
||||
void MoveTo::onUpdate(float progress) {
|
||||
Vec2 newPos = startPosition_ + delta_ * progress;
|
||||
target_->setPosition(newPos);
|
||||
}
|
||||
|
||||
ActionInterval* MoveTo::clone() const {
|
||||
return MoveTo::create(duration_, endPosition_);
|
||||
}
|
||||
|
||||
ActionInterval* MoveTo::reverse() const {
|
||||
return MoveTo::create(duration_, startPosition_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 跳跃动作
|
||||
// ============================================================================
|
||||
|
||||
JumpBy* JumpBy::create(float duration, const Vec2& position, float height, int jumps) {
|
||||
auto* action = new JumpBy();
|
||||
action->duration_ = duration;
|
||||
action->delta_ = position;
|
||||
action->height_ = height;
|
||||
action->jumps_ = jumps;
|
||||
return action;
|
||||
}
|
||||
|
||||
void JumpBy::onStart() {
|
||||
startPosition_ = target_->getPosition();
|
||||
}
|
||||
|
||||
void JumpBy::onUpdate(float progress) {
|
||||
float frac = (progress * jumps_) - static_cast<int>(progress * jumps_);
|
||||
float y = height_ * 4.0f * frac * (1.0f - frac);
|
||||
y += delta_.y * progress;
|
||||
float x = delta_.x * progress;
|
||||
target_->setPosition(startPosition_ + Vec2(x, y));
|
||||
}
|
||||
|
||||
ActionInterval* JumpBy::clone() const {
|
||||
return JumpBy::create(duration_, delta_, height_, jumps_);
|
||||
}
|
||||
|
||||
ActionInterval* JumpBy::reverse() const {
|
||||
return JumpBy::create(duration_, -delta_, height_, jumps_);
|
||||
}
|
||||
|
||||
// JumpTo
|
||||
JumpTo* JumpTo::create(float duration, const Vec2& position, float height, int jumps) {
|
||||
auto* action = new JumpTo();
|
||||
action->duration_ = duration;
|
||||
action->endPosition_ = position;
|
||||
action->height_ = height;
|
||||
action->jumps_ = jumps;
|
||||
return action;
|
||||
}
|
||||
|
||||
void JumpTo::onStart() {
|
||||
JumpBy::onStart();
|
||||
delta_ = endPosition_ - startPosition_;
|
||||
}
|
||||
|
||||
ActionInterval* JumpTo::clone() const {
|
||||
return JumpTo::create(duration_, endPosition_, height_, jumps_);
|
||||
}
|
||||
|
||||
ActionInterval* JumpTo::reverse() const {
|
||||
return JumpTo::create(duration_, startPosition_, height_, jumps_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 贝塞尔曲线动作
|
||||
// ============================================================================
|
||||
|
||||
BezierBy* BezierBy::create(float duration, const BezierConfig& config) {
|
||||
auto* action = new BezierBy();
|
||||
action->duration_ = duration;
|
||||
action->config_ = config;
|
||||
return action;
|
||||
}
|
||||
|
||||
void BezierBy::onStart() {
|
||||
startPosition_ = target_->getPosition();
|
||||
}
|
||||
|
||||
void BezierBy::onUpdate(float progress) {
|
||||
float xa = startPosition_.x;
|
||||
float xb = config_.controlPoint1.x;
|
||||
float xc = config_.controlPoint2.x;
|
||||
float xd = config_.endPosition.x;
|
||||
|
||||
float ya = startPosition_.y;
|
||||
float yb = config_.controlPoint1.y;
|
||||
float yc = config_.controlPoint2.y;
|
||||
float yd = config_.endPosition.y;
|
||||
|
||||
float x = bezierat(xa, xb, xc, xd, progress);
|
||||
float y = bezierat(ya, yb, yc, yd, progress);
|
||||
|
||||
target_->setPosition(Vec2(x, y));
|
||||
}
|
||||
|
||||
ActionInterval* BezierBy::clone() const {
|
||||
return BezierBy::create(duration_, config_);
|
||||
}
|
||||
|
||||
ActionInterval* BezierBy::reverse() const {
|
||||
BezierConfig rev;
|
||||
rev.controlPoint1 = config_.controlPoint2 + config_.endPosition;
|
||||
rev.controlPoint2 = config_.controlPoint1 + config_.endPosition;
|
||||
rev.endPosition = config_.endPosition;
|
||||
return BezierBy::create(duration_, rev);
|
||||
}
|
||||
|
||||
float BezierBy::bezierat(float a, float b, float c, float d, float t) {
|
||||
return (powf(1 - t, 3) * a +
|
||||
3.0f * t * powf(1 - t, 2) * b +
|
||||
3.0f * t * t * (1 - t) * c +
|
||||
t * t * t * d);
|
||||
}
|
||||
|
||||
// BezierTo
|
||||
BezierTo* BezierTo::create(float duration, const BezierConfig& config) {
|
||||
auto* action = new BezierTo();
|
||||
action->duration_ = duration;
|
||||
action->originalConfig_ = config;
|
||||
return action;
|
||||
}
|
||||
|
||||
void BezierTo::onStart() {
|
||||
BezierBy::onStart();
|
||||
config_.controlPoint1 = originalConfig_.controlPoint1 - startPosition_;
|
||||
config_.controlPoint2 = originalConfig_.controlPoint2 - startPosition_;
|
||||
config_.endPosition = originalConfig_.endPosition - startPosition_;
|
||||
}
|
||||
|
||||
ActionInterval* BezierTo::clone() const {
|
||||
return BezierTo::create(duration_, originalConfig_);
|
||||
}
|
||||
|
||||
ActionInterval* BezierTo::reverse() const {
|
||||
BezierConfig rev;
|
||||
rev.controlPoint1 = originalConfig_.controlPoint2;
|
||||
rev.controlPoint2 = originalConfig_.controlPoint1;
|
||||
rev.endPosition = startPosition_;
|
||||
return BezierTo::create(duration_, rev);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 缩放动作
|
||||
// ============================================================================
|
||||
|
||||
ScaleBy* ScaleBy::create(float duration, float scale) {
|
||||
return create(duration, scale, scale);
|
||||
}
|
||||
|
||||
ScaleBy* ScaleBy::create(float duration, float scaleX, float scaleY) {
|
||||
auto* action = new ScaleBy();
|
||||
action->duration_ = duration;
|
||||
action->deltaScale_ = Vec2(scaleX - 1.0f, scaleY - 1.0f);
|
||||
return action;
|
||||
}
|
||||
|
||||
ScaleBy* ScaleBy::create(float duration, const Vec2& scale) {
|
||||
return create(duration, scale.x, scale.y);
|
||||
}
|
||||
|
||||
void ScaleBy::onStart() {
|
||||
startScale_ = target_->getScale();
|
||||
}
|
||||
|
||||
void ScaleBy::onUpdate(float progress) {
|
||||
Vec2 newScale = startScale_ + deltaScale_ * progress;
|
||||
target_->setScale(newScale);
|
||||
}
|
||||
|
||||
ActionInterval* ScaleBy::clone() const {
|
||||
return ScaleBy::create(duration_, Vec2(startScale_.x + deltaScale_.x, startScale_.y + deltaScale_.y));
|
||||
}
|
||||
|
||||
ActionInterval* ScaleBy::reverse() const {
|
||||
return ScaleBy::create(duration_, Vec2(startScale_.x - deltaScale_.x, startScale_.y - deltaScale_.y));
|
||||
}
|
||||
|
||||
// ScaleTo
|
||||
ScaleTo* ScaleTo::create(float duration, float scale) {
|
||||
return create(duration, scale, scale);
|
||||
}
|
||||
|
||||
ScaleTo* ScaleTo::create(float duration, float scaleX, float scaleY) {
|
||||
auto* action = new ScaleTo();
|
||||
action->duration_ = duration;
|
||||
action->endScale_ = Vec2(scaleX, scaleY);
|
||||
return action;
|
||||
}
|
||||
|
||||
ScaleTo* ScaleTo::create(float duration, const Vec2& scale) {
|
||||
return create(duration, scale.x, scale.y);
|
||||
}
|
||||
|
||||
void ScaleTo::onStart() {
|
||||
startScale_ = target_->getScale();
|
||||
delta_ = endScale_ - startScale_;
|
||||
}
|
||||
|
||||
void ScaleTo::onUpdate(float progress) {
|
||||
Vec2 newScale = startScale_ + delta_ * progress;
|
||||
target_->setScale(newScale);
|
||||
}
|
||||
|
||||
ActionInterval* ScaleTo::clone() const {
|
||||
return ScaleTo::create(duration_, endScale_);
|
||||
}
|
||||
|
||||
ActionInterval* ScaleTo::reverse() const {
|
||||
return ScaleTo::create(duration_, startScale_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 旋转动作
|
||||
// ============================================================================
|
||||
|
||||
RotateBy* RotateBy::create(float duration, float deltaAngle) {
|
||||
auto* action = new RotateBy();
|
||||
action->duration_ = duration;
|
||||
action->deltaAngle_ = deltaAngle;
|
||||
return action;
|
||||
}
|
||||
|
||||
void RotateBy::onStart() {
|
||||
startAngle_ = target_->getRotation();
|
||||
}
|
||||
|
||||
void RotateBy::onUpdate(float progress) {
|
||||
float newAngle = startAngle_ + deltaAngle_ * progress;
|
||||
target_->setRotation(newAngle);
|
||||
}
|
||||
|
||||
ActionInterval* RotateBy::clone() const {
|
||||
return RotateBy::create(duration_, deltaAngle_);
|
||||
}
|
||||
|
||||
ActionInterval* RotateBy::reverse() const {
|
||||
return RotateBy::create(duration_, -deltaAngle_);
|
||||
}
|
||||
|
||||
// RotateTo
|
||||
RotateTo* RotateTo::create(float duration, float angle) {
|
||||
auto* action = new RotateTo();
|
||||
action->duration_ = duration;
|
||||
action->endAngle_ = angle;
|
||||
return action;
|
||||
}
|
||||
|
||||
void RotateTo::onStart() {
|
||||
startAngle_ = target_->getRotation();
|
||||
deltaAngle_ = endAngle_ - startAngle_;
|
||||
|
||||
if (deltaAngle_ > 180.0f) deltaAngle_ -= 360.0f;
|
||||
if (deltaAngle_ < -180.0f) deltaAngle_ += 360.0f;
|
||||
}
|
||||
|
||||
void RotateTo::onUpdate(float progress) {
|
||||
float newAngle = startAngle_ + deltaAngle_ * progress;
|
||||
target_->setRotation(newAngle);
|
||||
}
|
||||
|
||||
ActionInterval* RotateTo::clone() const {
|
||||
return RotateTo::create(duration_, endAngle_);
|
||||
}
|
||||
|
||||
ActionInterval* RotateTo::reverse() const {
|
||||
return RotateTo::create(duration_, startAngle_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 淡入淡出动作
|
||||
// ============================================================================
|
||||
|
||||
FadeIn* FadeIn::create(float duration) {
|
||||
auto* action = new FadeIn();
|
||||
action->duration_ = duration;
|
||||
return action;
|
||||
}
|
||||
|
||||
void FadeIn::onStart() {
|
||||
startOpacity_ = target_->getOpacity();
|
||||
target_->setOpacity(0.0f);
|
||||
}
|
||||
|
||||
void FadeIn::onUpdate(float progress) {
|
||||
target_->setOpacity(progress);
|
||||
}
|
||||
|
||||
ActionInterval* FadeIn::clone() const {
|
||||
return FadeIn::create(duration_);
|
||||
}
|
||||
|
||||
ActionInterval* FadeIn::reverse() const {
|
||||
return FadeOut::create(duration_);
|
||||
}
|
||||
|
||||
// FadeOut
|
||||
FadeOut* FadeOut::create(float duration) {
|
||||
auto* action = new FadeOut();
|
||||
action->duration_ = duration;
|
||||
return action;
|
||||
}
|
||||
|
||||
void FadeOut::onStart() {
|
||||
startOpacity_ = target_->getOpacity();
|
||||
target_->setOpacity(1.0f);
|
||||
}
|
||||
|
||||
void FadeOut::onUpdate(float progress) {
|
||||
target_->setOpacity(1.0f - progress);
|
||||
}
|
||||
|
||||
ActionInterval* FadeOut::clone() const {
|
||||
return FadeOut::create(duration_);
|
||||
}
|
||||
|
||||
ActionInterval* FadeOut::reverse() const {
|
||||
return FadeIn::create(duration_);
|
||||
}
|
||||
|
||||
// FadeTo
|
||||
FadeTo* FadeTo::create(float duration, float opacity) {
|
||||
auto* action = new FadeTo();
|
||||
action->duration_ = duration;
|
||||
action->endOpacity_ = opacity;
|
||||
return action;
|
||||
}
|
||||
|
||||
void FadeTo::onStart() {
|
||||
startOpacity_ = target_->getOpacity();
|
||||
deltaOpacity_ = endOpacity_ - startOpacity_;
|
||||
}
|
||||
|
||||
void FadeTo::onUpdate(float progress) {
|
||||
target_->setOpacity(startOpacity_ + deltaOpacity_ * progress);
|
||||
}
|
||||
|
||||
ActionInterval* FadeTo::clone() const {
|
||||
return FadeTo::create(duration_, endOpacity_);
|
||||
}
|
||||
|
||||
ActionInterval* FadeTo::reverse() const {
|
||||
return FadeTo::create(duration_, startOpacity_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 闪烁动作
|
||||
// ============================================================================
|
||||
|
||||
Blink* Blink::create(float duration, int times) {
|
||||
auto* action = new Blink();
|
||||
action->duration_ = duration;
|
||||
action->times_ = times;
|
||||
return action;
|
||||
}
|
||||
|
||||
void Blink::onStart() {
|
||||
originalVisible_ = target_->isVisible();
|
||||
currentTimes_ = 0;
|
||||
}
|
||||
|
||||
void Blink::onUpdate(float progress) {
|
||||
float slice = 1.0f / times_;
|
||||
float m = fmodf(progress, slice);
|
||||
target_->setVisible(m > slice / 2.0f);
|
||||
}
|
||||
|
||||
ActionInterval* Blink::clone() const {
|
||||
return Blink::create(duration_, times_);
|
||||
}
|
||||
|
||||
ActionInterval* Blink::reverse() const {
|
||||
return Blink::create(duration_, times_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 色调动作
|
||||
// ============================================================================
|
||||
|
||||
TintTo* TintTo::create(float duration, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
auto* action = new TintTo();
|
||||
action->duration_ = duration;
|
||||
action->endColor_ = Color3B(red, green, blue);
|
||||
return action;
|
||||
}
|
||||
|
||||
void TintTo::onStart() {
|
||||
startColor_ = target_->getColor();
|
||||
deltaColor_ = Color3B(
|
||||
static_cast<int16_t>(endColor_.r) - static_cast<int16_t>(startColor_.r),
|
||||
static_cast<int16_t>(endColor_.g) - static_cast<int16_t>(startColor_.g),
|
||||
static_cast<int16_t>(endColor_.b) - static_cast<int16_t>(startColor_.b)
|
||||
);
|
||||
}
|
||||
|
||||
void TintTo::onUpdate(float progress) {
|
||||
Color3B newColor(
|
||||
static_cast<uint8_t>(startColor_.r + deltaColor_.r * progress),
|
||||
static_cast<uint8_t>(startColor_.g + deltaColor_.g * progress),
|
||||
static_cast<uint8_t>(startColor_.b + deltaColor_.b * progress)
|
||||
);
|
||||
target_->setColor(newColor);
|
||||
}
|
||||
|
||||
ActionInterval* TintTo::clone() const {
|
||||
return TintTo::create(duration_, endColor_.r, endColor_.g, endColor_.b);
|
||||
}
|
||||
|
||||
ActionInterval* TintTo::reverse() const {
|
||||
return TintTo::create(duration_, startColor_.r, startColor_.g, startColor_.b);
|
||||
}
|
||||
|
||||
// TintBy
|
||||
TintBy* TintBy::create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue) {
|
||||
auto* action = new TintBy();
|
||||
action->duration_ = duration;
|
||||
action->deltaR_ = deltaRed;
|
||||
action->deltaG_ = deltaGreen;
|
||||
action->deltaB_ = deltaBlue;
|
||||
return action;
|
||||
}
|
||||
|
||||
void TintBy::onStart() {
|
||||
startColor_ = target_->getColor();
|
||||
}
|
||||
|
||||
void TintBy::onUpdate(float progress) {
|
||||
Color3B newColor(
|
||||
static_cast<uint8_t>(startColor_.r + deltaR_ * progress),
|
||||
static_cast<uint8_t>(startColor_.g + deltaG_ * progress),
|
||||
static_cast<uint8_t>(startColor_.b + deltaB_ * progress)
|
||||
);
|
||||
target_->setColor(newColor);
|
||||
}
|
||||
|
||||
ActionInterval* TintBy::clone() const {
|
||||
return TintBy::create(duration_, deltaR_, deltaG_, deltaB_);
|
||||
}
|
||||
|
||||
ActionInterval* TintBy::reverse() const {
|
||||
return TintBy::create(duration_, -deltaR_, -deltaG_, -deltaB_);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 组合动作
|
||||
// ============================================================================
|
||||
|
||||
Sequence* Sequence::create(ActionInterval* action1, ...) {
|
||||
std::vector<ActionInterval*> actions;
|
||||
actions.push_back(action1);
|
||||
|
||||
va_list args;
|
||||
va_start(args, action1);
|
||||
ActionInterval* action = nullptr;
|
||||
while ((action = va_arg(args, ActionInterval*)) != nullptr) {
|
||||
actions.push_back(action);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return create(actions);
|
||||
}
|
||||
|
||||
Sequence* Sequence::create(const std::vector<ActionInterval*>& actions) {
|
||||
auto* seq = new Sequence();
|
||||
seq->duration_ = 0.0f;
|
||||
|
||||
for (auto* action : actions) {
|
||||
if (action) {
|
||||
seq->actions_.push_back(action);
|
||||
seq->duration_ += action->getDuration();
|
||||
}
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
Sequence::~Sequence() {
|
||||
for (auto* action : actions_) {
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::onStart() {
|
||||
currentIndex_ = 0;
|
||||
split_ = 0.0f;
|
||||
last_ = -1.0f;
|
||||
|
||||
if (!actions_.empty()) {
|
||||
actions_[0]->startWithTarget(target_);
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::onUpdate(float progress) {
|
||||
float newTime = progress * duration_;
|
||||
|
||||
if (newTime < last_) {
|
||||
for (auto* action : actions_) {
|
||||
action->stop();
|
||||
}
|
||||
}
|
||||
last_ = newTime;
|
||||
|
||||
float foundSplit = 0.0f;
|
||||
size_t found = 0;
|
||||
|
||||
for (size_t i = 0; i < actions_.size(); ++i) {
|
||||
foundSplit += actions_[i]->getDuration();
|
||||
if (foundSplit > newTime) {
|
||||
found = i;
|
||||
break;
|
||||
} else if (foundSplit == newTime) {
|
||||
found = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found != currentIndex_) {
|
||||
if (currentIndex_ < actions_.size()) {
|
||||
actions_[currentIndex_]->update(1.0f);
|
||||
}
|
||||
if (found < actions_.size()) {
|
||||
actions_[found]->startWithTarget(target_);
|
||||
}
|
||||
currentIndex_ = found;
|
||||
}
|
||||
|
||||
if (currentIndex_ < actions_.size()) {
|
||||
float localTime = newTime - (foundSplit - actions_[currentIndex_]->getDuration());
|
||||
float localProgress = actions_[currentIndex_]->getDuration() > 0.0f
|
||||
? localTime / actions_[currentIndex_]->getDuration() : 1.0f;
|
||||
actions_[currentIndex_]->update(localProgress);
|
||||
}
|
||||
}
|
||||
|
||||
ActionInterval* Sequence::clone() const {
|
||||
std::vector<ActionInterval*> cloned;
|
||||
for (auto* action : actions_) {
|
||||
cloned.push_back(action->clone());
|
||||
}
|
||||
return Sequence::create(cloned);
|
||||
}
|
||||
|
||||
ActionInterval* Sequence::reverse() const {
|
||||
std::vector<ActionInterval*> rev;
|
||||
for (auto it = actions_.rbegin(); it != actions_.rend(); ++it) {
|
||||
rev.push_back((*it)->reverse());
|
||||
}
|
||||
return Sequence::create(rev);
|
||||
}
|
||||
|
||||
// Spawn
|
||||
Spawn* Spawn::create(ActionInterval* action1, ...) {
|
||||
std::vector<ActionInterval*> actions;
|
||||
actions.push_back(action1);
|
||||
|
||||
va_list args;
|
||||
va_start(args, action1);
|
||||
ActionInterval* action = nullptr;
|
||||
while ((action = va_arg(args, ActionInterval*)) != nullptr) {
|
||||
actions.push_back(action);
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return create(actions);
|
||||
}
|
||||
|
||||
Spawn* Spawn::create(const std::vector<ActionInterval*>& actions) {
|
||||
auto* spawn = new Spawn();
|
||||
spawn->duration_ = 0.0f;
|
||||
|
||||
for (auto* action : actions) {
|
||||
if (action) {
|
||||
spawn->actions_.push_back(action);
|
||||
spawn->duration_ = std::max(spawn->duration_, action->getDuration());
|
||||
}
|
||||
}
|
||||
return spawn;
|
||||
}
|
||||
|
||||
Spawn::~Spawn() {
|
||||
for (auto* action : actions_) {
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
||||
void Spawn::onStart() {
|
||||
for (auto* action : actions_) {
|
||||
action->startWithTarget(target_);
|
||||
}
|
||||
}
|
||||
|
||||
void Spawn::onUpdate(float progress) {
|
||||
for (auto* action : actions_) {
|
||||
float localProgress = action->getDuration() > 0.0f
|
||||
? std::min(1.0f, (progress * duration_) / action->getDuration())
|
||||
: 1.0f;
|
||||
action->update(localProgress);
|
||||
}
|
||||
}
|
||||
|
||||
ActionInterval* Spawn::clone() const {
|
||||
std::vector<ActionInterval*> cloned;
|
||||
for (auto* action : actions_) {
|
||||
cloned.push_back(action->clone());
|
||||
}
|
||||
return Spawn::create(cloned);
|
||||
}
|
||||
|
||||
ActionInterval* Spawn::reverse() const {
|
||||
std::vector<ActionInterval*> rev;
|
||||
for (auto* action : actions_) {
|
||||
rev.push_back(action->reverse());
|
||||
}
|
||||
return Spawn::create(rev);
|
||||
}
|
||||
|
||||
// Repeat
|
||||
Repeat* Repeat::create(ActionInterval* action, int times) {
|
||||
auto* repeat = new Repeat();
|
||||
repeat->innerAction_ = action;
|
||||
repeat->times_ = times;
|
||||
repeat->duration_ = action->getDuration() * times;
|
||||
return repeat;
|
||||
}
|
||||
|
||||
ActionInterval* Repeat::clone() const {
|
||||
return Repeat::create(innerAction_->clone(), times_);
|
||||
}
|
||||
|
||||
ActionInterval* Repeat::reverse() const {
|
||||
return Repeat::create(innerAction_->reverse(), times_);
|
||||
}
|
||||
|
||||
bool Repeat::isDone() const {
|
||||
return currentTimes_ >= times_;
|
||||
}
|
||||
|
||||
void Repeat::onStart() {
|
||||
currentTimes_ = 0;
|
||||
innerAction_->startWithTarget(target_);
|
||||
}
|
||||
|
||||
void Repeat::onUpdate(float progress) {
|
||||
float t = progress * times_;
|
||||
int current = static_cast<int>(t);
|
||||
|
||||
if (current > currentTimes_) {
|
||||
innerAction_->update(1.0f);
|
||||
currentTimes_++;
|
||||
if (currentTimes_ < times_) {
|
||||
innerAction_->startWithTarget(target_);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTimes_ < times_) {
|
||||
innerAction_->update(t - current);
|
||||
}
|
||||
}
|
||||
|
||||
// RepeatForever
|
||||
RepeatForever* RepeatForever::create(ActionInterval* action) {
|
||||
auto* repeat = new RepeatForever();
|
||||
repeat->innerAction_ = action;
|
||||
repeat->duration_ = action->getDuration();
|
||||
return repeat;
|
||||
}
|
||||
|
||||
ActionInterval* RepeatForever::clone() const {
|
||||
return RepeatForever::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* RepeatForever::reverse() const {
|
||||
return RepeatForever::create(innerAction_->reverse());
|
||||
}
|
||||
|
||||
bool RepeatForever::isDone() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RepeatForever::onStart() {
|
||||
innerAction_->startWithTarget(target_);
|
||||
}
|
||||
|
||||
void RepeatForever::onUpdate(float progress) {
|
||||
innerAction_->update(progress);
|
||||
if (innerAction_->isDone()) {
|
||||
innerAction_->startWithTarget(target_);
|
||||
elapsed_ = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// DelayTime
|
||||
DelayTime* DelayTime::create(float duration) {
|
||||
auto* delay = new DelayTime();
|
||||
delay->duration_ = duration;
|
||||
return delay;
|
||||
}
|
||||
|
||||
ActionInterval* DelayTime::clone() const {
|
||||
return DelayTime::create(duration_);
|
||||
}
|
||||
|
||||
ActionInterval* DelayTime::reverse() const {
|
||||
return DelayTime::create(duration_);
|
||||
}
|
||||
|
||||
// ReverseTime
|
||||
ReverseTime* ReverseTime::create(ActionInterval* action) {
|
||||
auto* rev = new ReverseTime();
|
||||
rev->innerAction_ = action;
|
||||
rev->duration_ = action->getDuration();
|
||||
return rev;
|
||||
}
|
||||
|
||||
ReverseTime::~ReverseTime() {
|
||||
delete innerAction_;
|
||||
}
|
||||
|
||||
ActionInterval* ReverseTime::clone() const {
|
||||
return ReverseTime::create(innerAction_->clone());
|
||||
}
|
||||
|
||||
ActionInterval* ReverseTime::reverse() const {
|
||||
return innerAction_->clone();
|
||||
}
|
||||
|
||||
void ReverseTime::onStart() {
|
||||
innerAction_->startWithTarget(target_);
|
||||
}
|
||||
|
||||
void ReverseTime::onUpdate(float progress) {
|
||||
innerAction_->update(1.0f - progress);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
#include "extra2d/action/action_manager.h"
|
||||
#include "extra2d/scene/node.h"
|
||||
#include "extra2d/utils/logger.h"
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
ActionManager* ActionManager::instance_ = nullptr;
|
||||
|
||||
ActionManager::ActionManager() {}
|
||||
|
||||
ActionManager::~ActionManager() {
|
||||
removeAllActions();
|
||||
}
|
||||
|
||||
ActionManager* ActionManager::getInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new ActionManager();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void ActionManager::destroyInstance() {
|
||||
if (instance_) {
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::addAction(Action* action, Node* target, bool paused) {
|
||||
if (!action || !target) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
ActionElement element;
|
||||
element.target = target;
|
||||
element.paused = paused;
|
||||
targets_[target] = element;
|
||||
it = targets_.find(target);
|
||||
}
|
||||
|
||||
auto& element = it->second;
|
||||
element.actions.push_back(action);
|
||||
action->startWithTarget(target);
|
||||
}
|
||||
|
||||
void ActionManager::removeAction(Action* action) {
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node* target = action->getOriginalTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& element = it->second;
|
||||
for (size_t i = 0; i < element.actions.size(); ++i) {
|
||||
if (element.actions[i] == action) {
|
||||
removeActionAt(i, element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::removeActionByTag(int tag, Node* target) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& element = it->second;
|
||||
for (size_t i = 0; i < element.actions.size(); ++i) {
|
||||
if (element.actions[i]->getTag() == tag) {
|
||||
removeActionAt(i, element);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::removeActionsByFlags(unsigned int flags, Node* target) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& element = it->second;
|
||||
for (int i = static_cast<int>(element.actions.size()) - 1; i >= 0; --i) {
|
||||
if (element.actions[i]->getFlags() & flags) {
|
||||
removeActionAt(i, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::removeAllActionsFromTarget(Node* target) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& element = it->second;
|
||||
for (auto* action : element.actions) {
|
||||
action->stop();
|
||||
deleteAction(action);
|
||||
}
|
||||
element.actions.clear();
|
||||
targets_.erase(it);
|
||||
}
|
||||
|
||||
void ActionManager::removeAllActions() {
|
||||
for (auto& pair : targets_) {
|
||||
auto& element = pair.second;
|
||||
for (auto* action : element.actions) {
|
||||
action->stop();
|
||||
deleteAction(action);
|
||||
}
|
||||
}
|
||||
targets_.clear();
|
||||
}
|
||||
|
||||
Action* ActionManager::getActionByTag(int tag, Node* target) {
|
||||
if (!target) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& element = it->second;
|
||||
for (auto* action : element.actions) {
|
||||
if (action->getTag() == tag) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t ActionManager::getActionCount(Node* target) const {
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
return 0;
|
||||
}
|
||||
return it->second.actions.size();
|
||||
}
|
||||
|
||||
void ActionManager::pauseTarget(Node* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it != targets_.end()) {
|
||||
it->second.paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::resumeTarget(Node* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it != targets_.end()) {
|
||||
it->second.paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ActionManager::isPaused(Node* target) const {
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) {
|
||||
return false;
|
||||
}
|
||||
return it->second.paused;
|
||||
}
|
||||
|
||||
void ActionManager::update(float dt) {
|
||||
for (auto it = targets_.begin(); it != targets_.end(); ) {
|
||||
auto& element = it->second;
|
||||
Node* target = element.target;
|
||||
|
||||
if (!element.paused && target) {
|
||||
element.actionIndex = 0;
|
||||
while (static_cast<size_t>(element.actionIndex) < element.actions.size()) {
|
||||
element.currentAction = element.actions[element.actionIndex];
|
||||
element.currentActionSalvaged = false;
|
||||
|
||||
if (element.currentAction->getState() != ActionState::Paused) {
|
||||
element.currentAction->step(dt);
|
||||
}
|
||||
|
||||
if (element.currentActionSalvaged) {
|
||||
deleteAction(element.currentAction);
|
||||
} else if (element.currentAction->isDone()) {
|
||||
element.currentAction->stop();
|
||||
deleteAction(element.currentAction);
|
||||
element.actions.erase(element.actions.begin() + element.actionIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
element.actionIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (element.actions.empty()) {
|
||||
it = targets_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::removeActionAt(size_t index, ActionElement& element) {
|
||||
if (index >= element.actions.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Action* action = element.actions[index];
|
||||
if (action == element.currentAction) {
|
||||
element.currentActionSalvaged = true;
|
||||
}
|
||||
action->stop();
|
||||
deleteAction(action);
|
||||
element.actions.erase(element.actions.begin() + index);
|
||||
}
|
||||
|
||||
void ActionManager::deleteAction(Action* action) {
|
||||
if (action) {
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -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
|
||||
|
|
@ -1,405 +0,0 @@
|
|||
#include "extra2d/action/actions.h"
|
||||
#include "extra2d/scene/node.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
// IntervalAction
|
||||
IntervalAction::IntervalAction(float duration) { duration_ = duration; }
|
||||
|
||||
bool IntervalAction::isDone() const { return elapsed_ >= duration_; }
|
||||
|
||||
// InstantAction
|
||||
InstantAction::InstantAction() { duration_ = 0.0f; }
|
||||
|
||||
bool InstantAction::isDone() const { return true; }
|
||||
|
||||
// MoveBy
|
||||
MoveBy::MoveBy(float duration, const Vec2 &delta)
|
||||
: IntervalAction(duration), delta_(delta) {}
|
||||
|
||||
void MoveBy::onStart() { startPosition_ = target_->getPosition(); }
|
||||
|
||||
void MoveBy::onUpdate(float progress) {
|
||||
Vec2 newPos = startPosition_ + delta_ * progress;
|
||||
target_->setPosition(newPos);
|
||||
}
|
||||
|
||||
Action *MoveBy::clone() const { return new MoveBy(duration_, delta_); }
|
||||
|
||||
Action *MoveBy::reverse() const { return new MoveBy(duration_, -delta_); }
|
||||
|
||||
// MoveTo
|
||||
MoveTo::MoveTo(float duration, const Vec2 &position)
|
||||
: IntervalAction(duration), endPosition_(position) {}
|
||||
|
||||
void MoveTo::onStart() {
|
||||
startPosition_ = target_->getPosition();
|
||||
delta_ = endPosition_ - startPosition_;
|
||||
}
|
||||
|
||||
void MoveTo::onUpdate(float progress) {
|
||||
Vec2 newPos = startPosition_ + delta_ * progress;
|
||||
target_->setPosition(newPos);
|
||||
}
|
||||
|
||||
Action *MoveTo::clone() const { return new MoveTo(duration_, endPosition_); }
|
||||
|
||||
Action *MoveTo::reverse() const {
|
||||
return new MoveTo(duration_, startPosition_);
|
||||
}
|
||||
|
||||
// ScaleBy
|
||||
ScaleBy::ScaleBy(float duration, float scale)
|
||||
: IntervalAction(duration), deltaScale_(scale - 1.0f, scale - 1.0f) {}
|
||||
|
||||
ScaleBy::ScaleBy(float duration, float scaleX, float scaleY)
|
||||
: IntervalAction(duration), deltaScale_(scaleX - 1.0f, scaleY - 1.0f) {}
|
||||
|
||||
ScaleBy::ScaleBy(float duration, const Vec2 &scale)
|
||||
: IntervalAction(duration), deltaScale_(scale.x - 1.0f, scale.y - 1.0f) {}
|
||||
|
||||
void ScaleBy::onStart() { startScale_ = target_->getScale(); }
|
||||
|
||||
void ScaleBy::onUpdate(float progress) {
|
||||
Vec2 newScale = startScale_ + deltaScale_ * progress;
|
||||
target_->setScale(newScale);
|
||||
}
|
||||
|
||||
Action *ScaleBy::clone() const {
|
||||
return new ScaleBy(duration_, Vec2(startScale_.x + deltaScale_.x,
|
||||
startScale_.y + deltaScale_.y));
|
||||
}
|
||||
|
||||
Action *ScaleBy::reverse() const {
|
||||
return new ScaleBy(duration_, Vec2(startScale_.x - deltaScale_.x,
|
||||
startScale_.y - deltaScale_.y));
|
||||
}
|
||||
|
||||
// ScaleTo
|
||||
ScaleTo::ScaleTo(float duration, float scale)
|
||||
: IntervalAction(duration), endScale_(scale, scale) {}
|
||||
|
||||
ScaleTo::ScaleTo(float duration, float scaleX, float scaleY)
|
||||
: IntervalAction(duration), endScale_(scaleX, scaleY) {}
|
||||
|
||||
ScaleTo::ScaleTo(float duration, const Vec2 &scale)
|
||||
: IntervalAction(duration), endScale_(scale) {}
|
||||
|
||||
void ScaleTo::onStart() {
|
||||
startScale_ = target_->getScale();
|
||||
delta_ = endScale_ - startScale_;
|
||||
}
|
||||
|
||||
void ScaleTo::onUpdate(float progress) {
|
||||
Vec2 newScale = startScale_ + delta_ * progress;
|
||||
target_->setScale(newScale);
|
||||
}
|
||||
|
||||
Action *ScaleTo::clone() const { return new ScaleTo(duration_, endScale_); }
|
||||
|
||||
Action *ScaleTo::reverse() const { return new ScaleTo(duration_, startScale_); }
|
||||
|
||||
// RotateBy
|
||||
RotateBy::RotateBy(float duration, float deltaAngle)
|
||||
: IntervalAction(duration), deltaAngle_(deltaAngle) {}
|
||||
|
||||
RotateBy::RotateBy(float duration, float deltaAngleX, float deltaAngleY)
|
||||
: IntervalAction(duration), deltaAngle_(deltaAngleX) {
|
||||
(void)deltaAngleY;
|
||||
}
|
||||
|
||||
void RotateBy::onStart() { startAngle_ = target_->getRotation(); }
|
||||
|
||||
void RotateBy::onUpdate(float progress) {
|
||||
float newAngle = startAngle_ + deltaAngle_ * progress;
|
||||
target_->setRotation(newAngle);
|
||||
}
|
||||
|
||||
Action *RotateBy::clone() const { return new RotateBy(duration_, deltaAngle_); }
|
||||
|
||||
Action *RotateBy::reverse() const {
|
||||
return new RotateBy(duration_, -deltaAngle_);
|
||||
}
|
||||
|
||||
// RotateTo
|
||||
RotateTo::RotateTo(float duration, float angle)
|
||||
: IntervalAction(duration), endAngle_(angle) {}
|
||||
|
||||
RotateTo::RotateTo(float duration, float angleX, float angleY)
|
||||
: IntervalAction(duration), endAngle_(angleX) {
|
||||
(void)angleY;
|
||||
}
|
||||
|
||||
void RotateTo::onStart() {
|
||||
startAngle_ = target_->getRotation();
|
||||
deltaAngle_ = endAngle_ - startAngle_;
|
||||
|
||||
// Shortest path
|
||||
if (deltaAngle_ > 180.0f)
|
||||
deltaAngle_ -= 360.0f;
|
||||
if (deltaAngle_ < -180.0f)
|
||||
deltaAngle_ += 360.0f;
|
||||
}
|
||||
|
||||
void RotateTo::onUpdate(float progress) {
|
||||
float newAngle = startAngle_ + deltaAngle_ * progress;
|
||||
target_->setRotation(newAngle);
|
||||
}
|
||||
|
||||
Action *RotateTo::clone() const { return new RotateTo(duration_, endAngle_); }
|
||||
|
||||
Action *RotateTo::reverse() const {
|
||||
return new RotateTo(duration_, startAngle_);
|
||||
}
|
||||
|
||||
// FadeIn
|
||||
FadeIn::FadeIn(float duration) : IntervalAction(duration) {}
|
||||
|
||||
void FadeIn::onStart() {
|
||||
startOpacity_ = target_->getOpacity();
|
||||
target_->setOpacity(0.0f);
|
||||
}
|
||||
|
||||
void FadeIn::onUpdate(float progress) { target_->setOpacity(progress); }
|
||||
|
||||
Action *FadeIn::clone() const { return new FadeIn(duration_); }
|
||||
|
||||
Action *FadeIn::reverse() const { return new FadeOut(duration_); }
|
||||
|
||||
// FadeOut
|
||||
FadeOut::FadeOut(float duration) : IntervalAction(duration) {}
|
||||
|
||||
void FadeOut::onStart() {
|
||||
startOpacity_ = target_->getOpacity();
|
||||
target_->setOpacity(1.0f);
|
||||
}
|
||||
|
||||
void FadeOut::onUpdate(float progress) { target_->setOpacity(1.0f - progress); }
|
||||
|
||||
Action *FadeOut::clone() const { return new FadeOut(duration_); }
|
||||
|
||||
Action *FadeOut::reverse() const { return new FadeIn(duration_); }
|
||||
|
||||
// FadeTo
|
||||
FadeTo::FadeTo(float duration, float opacity)
|
||||
: IntervalAction(duration), endOpacity_(opacity) {}
|
||||
|
||||
void FadeTo::onStart() {
|
||||
startOpacity_ = target_->getOpacity();
|
||||
deltaOpacity_ = endOpacity_ - startOpacity_;
|
||||
}
|
||||
|
||||
void FadeTo::onUpdate(float progress) {
|
||||
target_->setOpacity(startOpacity_ + deltaOpacity_ * progress);
|
||||
}
|
||||
|
||||
Action *FadeTo::clone() const { return new FadeTo(duration_, endOpacity_); }
|
||||
|
||||
Action *FadeTo::reverse() const { return new FadeTo(duration_, startOpacity_); }
|
||||
|
||||
// Sequence
|
||||
Sequence::Sequence(const std::vector<Action *> &actions)
|
||||
: IntervalAction(0.0f) {
|
||||
for (auto *action : actions) {
|
||||
if (action) {
|
||||
actions_.push_back(action->clone());
|
||||
duration_ += action->getDuration();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sequence::~Sequence() {
|
||||
for (auto *action : actions_)
|
||||
delete action;
|
||||
}
|
||||
|
||||
void Sequence::onStart() {
|
||||
currentIndex_ = 0;
|
||||
split_ = 0.0f;
|
||||
last_ = -1.0f;
|
||||
|
||||
if (!actions_.empty()) {
|
||||
actions_[0]->start(target_);
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::onUpdate(float progress) {
|
||||
int found = 0;
|
||||
float newTime = progress * duration_;
|
||||
|
||||
if (newTime < last_) {
|
||||
// Rewind
|
||||
for (auto *action : actions_) {
|
||||
action->stop();
|
||||
}
|
||||
}
|
||||
|
||||
last_ = newTime;
|
||||
|
||||
for (size_t i = 0; i < actions_.size(); ++i) {
|
||||
split_ += actions_[i]->getDuration();
|
||||
|
||||
if (split_ > newTime) {
|
||||
found = static_cast<int>(i);
|
||||
break;
|
||||
} else if (split_ == newTime) {
|
||||
found = static_cast<int>(i) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found != currentIndex_) {
|
||||
if (currentIndex_ >= 0 &&
|
||||
currentIndex_ < static_cast<int>(actions_.size())) {
|
||||
actions_[currentIndex_]->update(actions_[currentIndex_]->getDuration());
|
||||
}
|
||||
|
||||
if (found >= 0 && found < static_cast<int>(actions_.size())) {
|
||||
actions_[found]->start(target_);
|
||||
}
|
||||
|
||||
currentIndex_ = found;
|
||||
}
|
||||
|
||||
if (currentIndex_ >= 0 && currentIndex_ < static_cast<int>(actions_.size())) {
|
||||
float localProgress = 0.0f;
|
||||
if (actions_[currentIndex_]->getDuration() > 0.0f) {
|
||||
localProgress =
|
||||
(newTime - (split_ - actions_[currentIndex_]->getDuration())) /
|
||||
actions_[currentIndex_]->getDuration();
|
||||
}
|
||||
actions_[currentIndex_]->step(actions_[currentIndex_]->getDuration() *
|
||||
localProgress);
|
||||
}
|
||||
}
|
||||
|
||||
Action *Sequence::clone() const { return new Sequence(actions_); }
|
||||
|
||||
Action *Sequence::reverse() const {
|
||||
std::vector<Action *> rev;
|
||||
for (auto it = actions_.rbegin(); it != actions_.rend(); ++it) {
|
||||
rev.push_back((*it)->reverse());
|
||||
}
|
||||
return new Sequence(rev);
|
||||
}
|
||||
|
||||
// Spawn
|
||||
Spawn::Spawn(const std::vector<Action *> &actions) : IntervalAction(0.0f) {
|
||||
for (auto *action : actions) {
|
||||
if (action) {
|
||||
actions_.push_back(action->clone());
|
||||
duration_ = std::max(duration_, action->getDuration());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spawn::~Spawn() {
|
||||
for (auto *action : actions_)
|
||||
delete action;
|
||||
}
|
||||
|
||||
void Spawn::onStart() {
|
||||
for (auto *action : actions_) {
|
||||
action->start(target_);
|
||||
}
|
||||
}
|
||||
|
||||
void Spawn::onUpdate(float progress) {
|
||||
for (auto *action : actions_) {
|
||||
float localProgress = 0.0f;
|
||||
if (action->getDuration() > 0.0f) {
|
||||
localProgress =
|
||||
std::min(1.0f, (progress * duration_) / action->getDuration());
|
||||
} else {
|
||||
localProgress = 1.0f;
|
||||
}
|
||||
action->step(action->getDuration() * localProgress);
|
||||
}
|
||||
}
|
||||
|
||||
Action *Spawn::clone() const { return new Spawn(actions_); }
|
||||
|
||||
Action *Spawn::reverse() const {
|
||||
std::vector<Action *> rev;
|
||||
for (auto *action : actions_) {
|
||||
rev.push_back(action->reverse());
|
||||
}
|
||||
return new Spawn(rev);
|
||||
}
|
||||
|
||||
// Loop
|
||||
Loop::Loop(Action *action, int times)
|
||||
: action_(action ? action->clone() : nullptr), times_(times),
|
||||
currentTimes_(0) {
|
||||
if (action_) {
|
||||
duration_ = times < 0 ? -1.0f : action_->getDuration() * times;
|
||||
}
|
||||
}
|
||||
|
||||
Loop::~Loop() { delete action_; }
|
||||
|
||||
bool Loop::isDone() const {
|
||||
if (times_ < 0)
|
||||
return false;
|
||||
return currentTimes_ >= times_;
|
||||
}
|
||||
|
||||
void Loop::onStart() {
|
||||
currentTimes_ = 0;
|
||||
if (action_) {
|
||||
action_->start(target_);
|
||||
}
|
||||
}
|
||||
|
||||
void Loop::onUpdate(float progress) {
|
||||
if (!action_)
|
||||
return;
|
||||
|
||||
float actionDuration = action_->getDuration();
|
||||
float dt = progress * duration_ - elapsed_;
|
||||
|
||||
while (dt > 0.0f) {
|
||||
float localProgress = std::min(1.0f, dt / actionDuration);
|
||||
action_->step(actionDuration * localProgress);
|
||||
|
||||
if (action_->isDone()) {
|
||||
currentTimes_++;
|
||||
if (times_ > 0 && currentTimes_ >= times_)
|
||||
break;
|
||||
action_->restart();
|
||||
}
|
||||
|
||||
dt -= actionDuration;
|
||||
}
|
||||
}
|
||||
|
||||
Action *Loop::clone() const { return new Loop(action_, times_); }
|
||||
|
||||
Action *Loop::reverse() const {
|
||||
return new Loop(action_ ? action_->reverse() : nullptr, times_);
|
||||
}
|
||||
|
||||
// Delay
|
||||
Delay::Delay(float duration) : IntervalAction(duration) {}
|
||||
|
||||
void Delay::onUpdate(float progress) {
|
||||
// No update needed, just wait
|
||||
}
|
||||
|
||||
Action *Delay::clone() const { return new Delay(duration_); }
|
||||
|
||||
Action *Delay::reverse() const { return new Delay(duration_); }
|
||||
|
||||
// CallFunc
|
||||
CallFunc::CallFunc(Callback callback) : callback_(std::move(callback)) {}
|
||||
|
||||
void CallFunc::onUpdate(float progress) {
|
||||
(void)progress;
|
||||
if (callback_)
|
||||
callback_();
|
||||
}
|
||||
|
||||
Action *CallFunc::clone() const { return new CallFunc(callback_); }
|
||||
|
||||
Action *CallFunc::reverse() const { return new CallFunc(callback_); }
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,62 +1,104 @@
|
|||
#include "extra2d/action/ease.h"
|
||||
#include <cmath>
|
||||
#include <extra2d/action/ease.h>
|
||||
|
||||
namespace extra2d {
|
||||
// Linear
|
||||
float easeLinear(float t) { return t; }
|
||||
|
||||
// Quadratic
|
||||
float easeInQuad(float t) { return t * t; }
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846f
|
||||
#endif
|
||||
|
||||
float easeOutQuad(float t) { return 1.0f - (1.0f - t) * (1.0f - t); }
|
||||
// ============================================================================
|
||||
// 线性缓动
|
||||
// ============================================================================
|
||||
|
||||
float easeLinear(float t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 二次缓动 (Quad)
|
||||
// ============================================================================
|
||||
|
||||
float easeInQuad(float t) {
|
||||
return t * t;
|
||||
}
|
||||
|
||||
float easeOutQuad(float t) {
|
||||
return 1.0f - (1.0f - t) * (1.0f - t);
|
||||
}
|
||||
|
||||
float easeInOutQuad(float t) {
|
||||
return t < 0.5f ? 2.0f * t * t
|
||||
: 1.0f - std::pow(-2.0f * t + 2.0f, 2.0f) / 2.0f;
|
||||
return t < 0.5f ? 2.0f * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 2.0f) / 2.0f;
|
||||
}
|
||||
|
||||
// Cubic
|
||||
float easeInCubic(float t) { return t * t * t; }
|
||||
// ============================================================================
|
||||
// 三次缓动 (Cubic)
|
||||
// ============================================================================
|
||||
|
||||
float easeOutCubic(float t) { return 1.0f - std::pow(1.0f - t, 3.0f); }
|
||||
float easeInCubic(float t) {
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
float easeOutCubic(float t) {
|
||||
return 1.0f - std::pow(1.0f - t, 3.0f);
|
||||
}
|
||||
|
||||
float easeInOutCubic(float t) {
|
||||
return t < 0.5f ? 4.0f * t * t * t
|
||||
: 1.0f - std::pow(-2.0f * t + 2.0f, 3.0f) / 2.0f;
|
||||
return t < 0.5f ? 4.0f * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 3.0f) / 2.0f;
|
||||
}
|
||||
|
||||
// Quartic
|
||||
float easeInQuart(float t) { return t * t * t * t; }
|
||||
// ============================================================================
|
||||
// 四次缓动 (Quart)
|
||||
// ============================================================================
|
||||
|
||||
float easeOutQuart(float t) { return 1.0f - std::pow(1.0f - t, 4.0f); }
|
||||
float easeInQuart(float t) {
|
||||
return t * t * t * t;
|
||||
}
|
||||
|
||||
float easeOutQuart(float t) {
|
||||
return 1.0f - std::pow(1.0f - t, 4.0f);
|
||||
}
|
||||
|
||||
float easeInOutQuart(float t) {
|
||||
return t < 0.5f ? 8.0f * t * t * t * t
|
||||
: 1.0f - std::pow(-2.0f * t + 2.0f, 4.0f) / 2.0f;
|
||||
return t < 0.5f ? 8.0f * t * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 4.0f) / 2.0f;
|
||||
}
|
||||
|
||||
// Quintic
|
||||
float easeInQuint(float t) { return t * t * t * t * t; }
|
||||
// ============================================================================
|
||||
// 五次缓动 (Quint)
|
||||
// ============================================================================
|
||||
|
||||
float easeOutQuint(float t) { return 1.0f - std::pow(1.0f - t, 5.0f); }
|
||||
float easeInQuint(float t) {
|
||||
return t * t * t * t * t;
|
||||
}
|
||||
|
||||
float easeOutQuint(float t) {
|
||||
return 1.0f - std::pow(1.0f - t, 5.0f);
|
||||
}
|
||||
|
||||
float easeInOutQuint(float t) {
|
||||
return t < 0.5f ? 16.0f * t * t * t * t * t
|
||||
: 1.0f - std::pow(-2.0f * t + 2.0f, 5.0f) / 2.0f;
|
||||
return t < 0.5f ? 16.0f * t * t * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 5.0f) / 2.0f;
|
||||
}
|
||||
|
||||
// Sine
|
||||
// ============================================================================
|
||||
// 正弦缓动 (Sine)
|
||||
// ============================================================================
|
||||
|
||||
float easeInSine(float t) {
|
||||
return 1.0f - std::cos((t * 3.14159265359f) / 2.0f);
|
||||
return 1.0f - std::cos((t * M_PI) / 2.0f);
|
||||
}
|
||||
|
||||
float easeOutSine(float t) { return std::sin((t * 3.14159265359f) / 2.0f); }
|
||||
float easeOutSine(float t) {
|
||||
return std::sin((t * M_PI) / 2.0f);
|
||||
}
|
||||
|
||||
float easeInOutSine(float t) {
|
||||
return -(std::cos(3.14159265359f * t) - 1.0f) / 2.0f;
|
||||
return -(std::cos(M_PI * t) - 1.0f) / 2.0f;
|
||||
}
|
||||
|
||||
// Exponential
|
||||
// ============================================================================
|
||||
// 指数缓动 (Exponential)
|
||||
// ============================================================================
|
||||
|
||||
float easeInExpo(float t) {
|
||||
return t == 0.0f ? 0.0f : std::pow(2.0f, 10.0f * (t - 1.0f));
|
||||
}
|
||||
|
|
@ -66,16 +108,20 @@ float easeOutExpo(float t) {
|
|||
}
|
||||
|
||||
float easeInOutExpo(float t) {
|
||||
if (t == 0.0f)
|
||||
return 0.0f;
|
||||
if (t == 1.0f)
|
||||
return 1.0f;
|
||||
return t < 0.5f ? std::pow(2.0f, 20.0f * t - 10.0f) / 2.0f
|
||||
if (t == 0.0f) return 0.0f;
|
||||
if (t == 1.0f) return 1.0f;
|
||||
return t < 0.5f
|
||||
? std::pow(2.0f, 20.0f * t - 10.0f) / 2.0f
|
||||
: (2.0f - std::pow(2.0f, -20.0f * t + 10.0f)) / 2.0f;
|
||||
}
|
||||
|
||||
// Circular
|
||||
float easeInCirc(float t) { return 1.0f - std::sqrt(1.0f - std::pow(t, 2.0f)); }
|
||||
// ============================================================================
|
||||
// 圆形缓动 (Circular)
|
||||
// ============================================================================
|
||||
|
||||
float easeInCirc(float t) {
|
||||
return 1.0f - std::sqrt(1.0f - std::pow(t, 2.0f));
|
||||
}
|
||||
|
||||
float easeOutCirc(float t) {
|
||||
return std::sqrt(1.0f - std::pow(t - 1.0f, 2.0f));
|
||||
|
|
@ -84,11 +130,13 @@ float easeOutCirc(float t) {
|
|||
float easeInOutCirc(float t) {
|
||||
return t < 0.5f
|
||||
? (1.0f - std::sqrt(1.0f - std::pow(2.0f * t, 2.0f))) / 2.0f
|
||||
: (std::sqrt(1.0f - std::pow(-2.0f * t + 2.0f, 2.0f)) + 1.0f) /
|
||||
2.0f;
|
||||
: (std::sqrt(1.0f - std::pow(-2.0f * t + 2.0f, 2.0f)) + 1.0f) / 2.0f;
|
||||
}
|
||||
|
||||
// Back
|
||||
// ============================================================================
|
||||
// 回震缓动 (Back)
|
||||
// ============================================================================
|
||||
|
||||
float easeInBack(float t) {
|
||||
const float c1 = 1.70158f;
|
||||
const float c3 = c1 + 1.0f;
|
||||
|
|
@ -106,48 +154,40 @@ float easeInOutBack(float t) {
|
|||
const float c2 = c1 * 1.525f;
|
||||
return t < 0.5f
|
||||
? (std::pow(2.0f * t, 2.0f) * ((c2 + 1.0f) * 2.0f * t - c2)) / 2.0f
|
||||
: (std::pow(2.0f * t - 2.0f, 2.0f) *
|
||||
((c2 + 1.0f) * (t * 2.0f - 2.0f) + c2) +
|
||||
2.0f) /
|
||||
2.0f;
|
||||
: (std::pow(2.0f * t - 2.0f, 2.0f) * ((c2 + 1.0f) * (t * 2.0f - 2.0f) + c2) + 2.0f) / 2.0f;
|
||||
}
|
||||
|
||||
// Elastic
|
||||
// ============================================================================
|
||||
// 弹性缓动 (Elastic)
|
||||
// ============================================================================
|
||||
|
||||
float easeInElastic(float t) {
|
||||
const float c4 = (2.0f * 3.14159265359f) / 3.0f;
|
||||
if (t == 0.0f)
|
||||
return 0.0f;
|
||||
if (t == 1.0f)
|
||||
return 1.0f;
|
||||
return -std::pow(2.0f, 10.0f * t - 10.0f) *
|
||||
std::sin((t * 10.0f - 10.75f) * c4);
|
||||
const float c4 = (2.0f * M_PI) / 3.0f;
|
||||
if (t == 0.0f) return 0.0f;
|
||||
if (t == 1.0f) return 1.0f;
|
||||
return -std::pow(2.0f, 10.0f * t - 10.0f) * std::sin((t * 10.0f - 10.75f) * c4);
|
||||
}
|
||||
|
||||
float easeOutElastic(float t) {
|
||||
const float c4 = (2.0f * 3.14159265359f) / 3.0f;
|
||||
if (t == 0.0f)
|
||||
return 0.0f;
|
||||
if (t == 1.0f)
|
||||
return 1.0f;
|
||||
const float c4 = (2.0f * M_PI) / 3.0f;
|
||||
if (t == 0.0f) return 0.0f;
|
||||
if (t == 1.0f) return 1.0f;
|
||||
return std::pow(2.0f, -10.0f * t) * std::sin((t * 10.0f - 0.75f) * c4) + 1.0f;
|
||||
}
|
||||
|
||||
float easeInOutElastic(float t) {
|
||||
const float c5 = (2.0f * 3.14159265359f) / 4.5f;
|
||||
if (t == 0.0f)
|
||||
return 0.0f;
|
||||
if (t == 1.0f)
|
||||
return 1.0f;
|
||||
return t < 0.5f ? -(std::pow(2.0f, 20.0f * t - 10.0f) *
|
||||
std::sin((20.0f * t - 11.125f) * c5)) /
|
||||
2.0f
|
||||
: (std::pow(2.0f, -20.0f * t + 10.0f) *
|
||||
std::sin((20.0f * t - 11.125f) * c5)) /
|
||||
2.0f +
|
||||
1.0f;
|
||||
const float c5 = (2.0f * M_PI) / 4.5f;
|
||||
if (t == 0.0f) return 0.0f;
|
||||
if (t == 1.0f) return 1.0f;
|
||||
return t < 0.5f
|
||||
? -(std::pow(2.0f, 20.0f * t - 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f
|
||||
: (std::pow(2.0f, -20.0f * t + 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f + 1.0f;
|
||||
}
|
||||
|
||||
// Bounce
|
||||
// ============================================================================
|
||||
// 弹跳缓动 (Bounce)
|
||||
// ============================================================================
|
||||
|
||||
namespace {
|
||||
float easeOutBounceInternal(float t) {
|
||||
const float n1 = 7.5625f;
|
||||
|
|
@ -166,14 +206,20 @@ float easeOutBounceInternal(float t) {
|
|||
return n1 * t * t + 0.984375f;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
}
|
||||
|
||||
float easeInBounce(float t) { return 1.0f - easeOutBounceInternal(1.0f - t); }
|
||||
float easeInBounce(float t) {
|
||||
return 1.0f - easeOutBounceInternal(1.0f - t);
|
||||
}
|
||||
|
||||
float easeOutBounce(float t) { return easeOutBounceInternal(t); }
|
||||
float easeOutBounce(float t) {
|
||||
return easeOutBounceInternal(t);
|
||||
}
|
||||
|
||||
float easeInOutBounce(float t) {
|
||||
return t < 0.5f ? (1.0f - easeOutBounceInternal(1.0f - 2.0f * t)) / 2.0f
|
||||
return t < 0.5f
|
||||
? (1.0f - easeOutBounceInternal(1.0f - 2.0f * t)) / 2.0f
|
||||
: (1.0f + easeOutBounceInternal(2.0f * t - 1.0f)) / 2.0f;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#include "extra2d/action/finite_time_action.h"
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
FiniteTimeAction::FiniteTimeAction(float duration)
|
||||
: duration_(duration) {
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include <extra2d/action/action_manager.h>
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/audio/audio_engine.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
|
|
@ -13,9 +14,9 @@
|
|||
#include <extra2d/utils/object_pool.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
|
|
@ -74,7 +75,8 @@ bool Application::init(const AppConfig &config) {
|
|||
if (R_SUCCEEDED(rc)) {
|
||||
E2D_LOG_INFO("RomFS initialized successfully");
|
||||
} else {
|
||||
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
|
||||
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem",
|
||||
rc);
|
||||
}
|
||||
|
||||
// ========================================
|
||||
|
|
@ -171,7 +173,7 @@ bool Application::init(const AppConfig &config) {
|
|||
void Application::prewarmObjectPools() {
|
||||
E2D_LOG_INFO("Prewarming object pools...");
|
||||
|
||||
auto& poolManager = ObjectPoolManager::getInstance();
|
||||
auto &poolManager = ObjectPoolManager::getInstance();
|
||||
|
||||
// 预热常用类型的对象池
|
||||
// 这些池会在首次使用时自动预热,但提前预热可以避免运行时延迟
|
||||
|
|
@ -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_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <extra2d/action/action.h>
|
||||
#include <extra2d/action/action_manager.h>
|
||||
#include <extra2d/graphics/render_command.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
|
|
@ -13,6 +14,7 @@ Node::Node() = default;
|
|||
Node::~Node() {
|
||||
removeAllChildren();
|
||||
stopAllActions();
|
||||
ActionManager::getInstance()->removeAllActionsFromTarget(this);
|
||||
}
|
||||
|
||||
void Node::addChild(Ptr<Node> child) {
|
||||
|
|
@ -201,6 +203,12 @@ void Node::setOpacity(float opacity) {
|
|||
|
||||
void Node::setVisible(bool visible) { visible_ = visible; }
|
||||
|
||||
void Node::setColor(const Color3B& color) { color_ = color; }
|
||||
|
||||
void Node::setFlipX(bool flipX) { flipX_ = flipX; }
|
||||
|
||||
void Node::setFlipY(bool flipY) { flipY_ = flipY; }
|
||||
|
||||
void Node::setZOrder(int zOrder) {
|
||||
if (zOrder_ != zOrder) {
|
||||
zOrder_ = zOrder;
|
||||
|
|
@ -332,17 +340,6 @@ void Node::onExit() {
|
|||
void Node::onUpdate(float dt) {
|
||||
onUpdateNode(dt);
|
||||
|
||||
// Update actions
|
||||
for (auto it = actions_.begin(); it != actions_.end();) {
|
||||
auto &action = *it;
|
||||
action->update(dt);
|
||||
if (action->isDone()) {
|
||||
it = actions_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Update children
|
||||
for (auto &child : children_) {
|
||||
child->onUpdate(dt);
|
||||
|
|
@ -406,74 +403,44 @@ void Node::updateSpatialIndex() {
|
|||
}
|
||||
}
|
||||
|
||||
void Node::runAction(Ptr<Action> action) {
|
||||
// ============================================================================
|
||||
// 动作系统 - 新接口
|
||||
// ============================================================================
|
||||
|
||||
Action* Node::runAction(Action* action) {
|
||||
if (!action) {
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
action->start(this);
|
||||
|
||||
int tag = action->getTag();
|
||||
if (tag != -1) {
|
||||
// 有 tag 的 Action 存入哈希表,O(1) 查找
|
||||
// 如果已存在相同 tag 的 Action,先停止它
|
||||
auto it = actionByTag_.find(tag);
|
||||
if (it != actionByTag_.end()) {
|
||||
// 从 vector 中移除旧的 Action
|
||||
auto oldAction = it->second;
|
||||
auto vecIt = std::find(actions_.begin(), actions_.end(), oldAction);
|
||||
if (vecIt != actions_.end()) {
|
||||
actions_.erase(vecIt);
|
||||
}
|
||||
}
|
||||
actionByTag_[tag] = action;
|
||||
}
|
||||
|
||||
actions_.push_back(action);
|
||||
ActionManager::getInstance()->addAction(action, this);
|
||||
return action;
|
||||
}
|
||||
|
||||
void Node::stopAllActions() {
|
||||
actions_.clear();
|
||||
actionByTag_.clear();
|
||||
ActionManager::getInstance()->removeAllActionsFromTarget(this);
|
||||
}
|
||||
|
||||
void Node::stopAction(Ptr<Action> action) {
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从 vector 中移除
|
||||
auto it = std::find(actions_.begin(), actions_.end(), action);
|
||||
if (it != actions_.end()) {
|
||||
// 如果有 tag,从哈希表中也移除
|
||||
int tag = action->getTag();
|
||||
if (tag != -1) {
|
||||
actionByTag_.erase(tag);
|
||||
}
|
||||
actions_.erase(it);
|
||||
}
|
||||
void Node::stopAction(Action* action) {
|
||||
ActionManager::getInstance()->removeAction(action);
|
||||
}
|
||||
|
||||
void Node::stopActionByTag(int tag) {
|
||||
auto it = actionByTag_.find(tag);
|
||||
if (it != actionByTag_.end()) {
|
||||
auto action = it->second;
|
||||
// 从 vector 中移除
|
||||
auto vecIt = std::find(actions_.begin(), actions_.end(), action);
|
||||
if (vecIt != actions_.end()) {
|
||||
actions_.erase(vecIt);
|
||||
}
|
||||
actionByTag_.erase(it);
|
||||
}
|
||||
ActionManager::getInstance()->removeActionByTag(tag, this);
|
||||
}
|
||||
|
||||
Ptr<Action> Node::getActionByTag(int tag) const {
|
||||
// O(1) 哈希查找
|
||||
auto it = actionByTag_.find(tag);
|
||||
if (it != actionByTag_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
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<Node*>(this));
|
||||
}
|
||||
|
||||
bool Node::isRunningActions() const {
|
||||
return getActionCount() > 0;
|
||||
}
|
||||
|
||||
void Node::update(float dt) { onUpdate(dt); }
|
||||
|
|
|
|||
|
|
@ -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()` | 检查是否有动作运行 |
|
||||
|
|
@ -0,0 +1,678 @@
|
|||
# Extra2D 动作系统重构规格文档
|
||||
|
||||
## 一、概述
|
||||
|
||||
本文档描述了 Extra2D 动作系统的重构方案,参考 Cocos2d-x 的动作系统设计模式,实现一个更加完善、易用、功能丰富的动作系统。
|
||||
|
||||
## 二、现有系统分析
|
||||
|
||||
### 2.1 当前类层次结构
|
||||
|
||||
```
|
||||
Action (基类)
|
||||
├── IntervalAction (有限时间动作基类)
|
||||
│ ├── MoveBy/MoveTo
|
||||
│ ├── ScaleBy/ScaleTo
|
||||
│ ├── RotateBy/RotateTo
|
||||
│ ├── FadeIn/FadeOut/FadeTo
|
||||
│ ├── Sequence
|
||||
│ ├── Spawn
|
||||
│ ├── Delay
|
||||
│ └── Loop
|
||||
└── InstantAction (瞬时动作基类)
|
||||
└── CallFunc
|
||||
```
|
||||
|
||||
### 2.2 与 Cocos2d-x 的差异
|
||||
|
||||
| 特性 | Extra2D 当前 | Cocos2d-x | 差异说明 |
|
||||
|------|-------------|-----------|---------|
|
||||
| 类层次 | Action -> IntervalAction/InstantAction | Action -> FiniteTimeAction -> ActionInterval/ActionInstant | 缺少 FiniteTimeAction 中间层 |
|
||||
| 动作管理 | Node 内部管理 | ActionManager 单例集中管理 | 缺少集中式管理 |
|
||||
| 缓动动作 | EaseAction 静态方法 | ActionEase 类系(装饰器模式) | 不够灵活,无法组合 |
|
||||
| 速度控制 | Action::speed_ 成员 | Speed 动作包装器 | 缺少动态速度控制 |
|
||||
| 跟随动作 | 无 | Follow 类 | 缺失 |
|
||||
| 跳跃动作 | 无 | JumpBy/JumpTo | 缺失 |
|
||||
| 贝塞尔动作 | 无 | BezierBy/BezierTo | 缺失 |
|
||||
| 闪烁动作 | 无 | Blink | 缺失 |
|
||||
| 色调动作 | 无 | TintBy/TintTo | 缺失 |
|
||||
| 重复动作 | Loop | Repeat/RepeatForever | 命名和功能差异 |
|
||||
| 反向动作 | reverse() 方法 | reverse() 方法 | 相同 |
|
||||
| 克隆动作 | clone() 方法 | clone() 方法 | 相同 |
|
||||
|
||||
## 三、重构目标
|
||||
|
||||
### 3.1 核心目标
|
||||
|
||||
1. **完善类层次结构** - 添加 FiniteTimeAction 中间层
|
||||
2. **集中式动作管理** - 实现 ActionManager 单例
|
||||
3. **装饰器模式缓动** - 实现 ActionEase 类系
|
||||
4. **丰富动作类型** - 添加跳跃、贝塞尔、闪烁、色调等动作
|
||||
5. **统一API风格** - 与 Cocos2d-x API 保持一致
|
||||
|
||||
### 3.2 设计原则
|
||||
|
||||
1. **组合优于继承** - 使用装饰器模式实现缓动和速度控制
|
||||
2. **单一职责** - ActionManager 只负责动作调度
|
||||
3. **开放封闭** - 易于扩展新动作类型
|
||||
4. **接口隔离** - 不同类型动作提供不同接口
|
||||
|
||||
## 四、新类层次结构
|
||||
|
||||
```
|
||||
Action (基类)
|
||||
├── FiniteTimeAction (有限时间动作基类)
|
||||
│ ├── ActionInterval (时间间隔动作)
|
||||
│ │ ├── MoveBy/MoveTo
|
||||
│ │ ├── MoveBy3D/MoveTo3D (新增)
|
||||
│ │ ├── JumpBy/JumpTo (新增)
|
||||
│ │ ├── BezierBy/BezierTo (新增)
|
||||
│ │ ├── ScaleBy/ScaleTo
|
||||
│ │ ├── RotateBy/RotateTo
|
||||
│ │ ├── FadeIn/FadeOut/FadeTo
|
||||
│ │ ├── Blink (新增)
|
||||
│ │ ├── TintBy/TintTo (新增)
|
||||
│ │ ├── Sequence
|
||||
│ │ ├── Spawn
|
||||
│ │ ├── DelayTime
|
||||
│ │ ├── Repeat (重命名自 Loop)
|
||||
│ │ ├── RepeatForever (新增)
|
||||
│ │ ├── ReverseTime (新增)
|
||||
│ │ └── ActionEase (缓动动作基类,新增)
|
||||
│ │ ├── EaseIn/EaseOut/EaseInOut
|
||||
│ │ ├── EaseSineIn/EaseSineOut/EaseSineInOut
|
||||
│ │ ├── EaseExponentialIn/EaseExponentialOut/EaseExponentialInOut
|
||||
│ │ ├── EaseBounceIn/EaseBounceOut/EaseBounceInOut
|
||||
│ │ ├── EaseElasticIn/EaseElasticOut/EaseElasticInOut
|
||||
│ │ ├── EaseBackIn/EaseBackOut/EaseBackInOut
|
||||
│ │ └── EaseBezier (新增)
|
||||
│ └── ActionInstant (瞬时动作)
|
||||
│ ├── CallFunc
|
||||
│ ├── CallFuncN (新增)
|
||||
│ ├── Place (新增)
|
||||
│ ├── FlipX/FlipY (新增)
|
||||
│ ├── Show/Hide (新增)
|
||||
│ ├── ToggleVisibility (新增)
|
||||
│ └── RemoveSelf (新增)
|
||||
├── Speed (速度控制动作,新增)
|
||||
├── Follow (跟随动作,新增)
|
||||
└── TargetedAction (目标动作,新增)
|
||||
```
|
||||
|
||||
## 五、核心类设计
|
||||
|
||||
### 5.1 Action 基类
|
||||
|
||||
```cpp
|
||||
class Action {
|
||||
public:
|
||||
virtual ~Action() = default;
|
||||
|
||||
// 核心接口
|
||||
virtual bool isDone() const;
|
||||
virtual void startWithTarget(Node* target);
|
||||
virtual void stop();
|
||||
virtual void step(float dt);
|
||||
virtual void update(float time);
|
||||
|
||||
// 克隆和反向
|
||||
virtual Action* clone() const = 0;
|
||||
virtual Action* reverse() const = 0;
|
||||
|
||||
// 属性访问
|
||||
Node* getTarget() const;
|
||||
Node* getOriginalTarget() const;
|
||||
int getTag() const;
|
||||
void setTag(int tag);
|
||||
unsigned int getFlags() const;
|
||||
void setFlags(unsigned int flags);
|
||||
|
||||
protected:
|
||||
Node* target_ = nullptr;
|
||||
Node* originalTarget_ = nullptr;
|
||||
int tag_ = -1;
|
||||
unsigned int flags_ = 0;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.2 FiniteTimeAction 中间层
|
||||
|
||||
```cpp
|
||||
class FiniteTimeAction : public Action {
|
||||
public:
|
||||
float getDuration() const { return duration_; }
|
||||
void setDuration(float duration) { duration_ = duration; }
|
||||
|
||||
virtual FiniteTimeAction* clone() const = 0;
|
||||
virtual FiniteTimeAction* reverse() const = 0;
|
||||
|
||||
protected:
|
||||
float duration_ = 0.0f;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.3 ActionInterval 时间间隔动作
|
||||
|
||||
```cpp
|
||||
class ActionInterval : public FiniteTimeAction {
|
||||
public:
|
||||
float getElapsed() const { return elapsed_; }
|
||||
bool isDone() const override;
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void step(float dt) override;
|
||||
|
||||
void setAmplitudeRate(float amp) { amplitudeRate_ = amp; }
|
||||
float getAmplitudeRate() const { return amplitudeRate_; }
|
||||
|
||||
protected:
|
||||
float elapsed_ = 0.0f;
|
||||
bool firstTick_ = true;
|
||||
float amplitudeRate_ = 1.0f;
|
||||
EaseFunction easeFunc_ = nullptr; // 内置缓动函数
|
||||
};
|
||||
```
|
||||
|
||||
### 5.4 ActionInstant 瞬时动作
|
||||
|
||||
```cpp
|
||||
class ActionInstant : public FiniteTimeAction {
|
||||
public:
|
||||
bool isDone() const override { return true; }
|
||||
void step(float dt) override;
|
||||
|
||||
protected:
|
||||
bool done_ = false;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.5 ActionManager 动作管理器
|
||||
|
||||
```cpp
|
||||
class ActionManager {
|
||||
public:
|
||||
static ActionManager* getInstance();
|
||||
|
||||
// 动作管理
|
||||
void addAction(Action* action, Node* target, bool paused = false);
|
||||
void removeAction(Action* action);
|
||||
void removeActionByTag(int tag, Node* target);
|
||||
void removeAllActionsFromTarget(Node* target);
|
||||
void removeAllActions();
|
||||
|
||||
// 查询
|
||||
Action* getActionByTag(int tag, Node* target);
|
||||
size_t getActionCount(Node* target) const;
|
||||
|
||||
// 暂停/恢复
|
||||
void pauseTarget(Node* target);
|
||||
void resumeTarget(Node* target);
|
||||
bool isPaused(Node* target) const;
|
||||
|
||||
// 更新(每帧调用)
|
||||
void update(float dt);
|
||||
|
||||
private:
|
||||
struct ActionElement {
|
||||
std::vector<Action*> actions;
|
||||
Node* target;
|
||||
bool paused;
|
||||
int actionIndex;
|
||||
Action* currentAction;
|
||||
bool currentActionSalvaged;
|
||||
};
|
||||
|
||||
std::unordered_map<Node*, ActionElement> targets_;
|
||||
static ActionManager* instance_;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.6 ActionEase 缓动动作基类
|
||||
|
||||
```cpp
|
||||
class ActionEase : public ActionInterval {
|
||||
public:
|
||||
static ActionEase* create(ActionInterval* action);
|
||||
|
||||
ActionInterval* getInnerAction() const { return innerAction_; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void update(float time) override;
|
||||
Action* reverse() const override;
|
||||
Action* clone() const override;
|
||||
|
||||
protected:
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.7 Speed 速度控制动作
|
||||
|
||||
```cpp
|
||||
class Speed : public Action {
|
||||
public:
|
||||
static Speed* create(ActionInterval* action, float speed);
|
||||
|
||||
float getSpeed() const { return speed_; }
|
||||
void setSpeed(float speed) { speed_ = speed; }
|
||||
|
||||
void startWithTarget(Node* target) override;
|
||||
void stop() override;
|
||||
void step(float dt) override;
|
||||
bool isDone() const override;
|
||||
Action* reverse() const override;
|
||||
Action* clone() const override;
|
||||
|
||||
protected:
|
||||
ActionInterval* innerAction_ = nullptr;
|
||||
float speed_ = 1.0f;
|
||||
};
|
||||
```
|
||||
|
||||
## 六、新增动作类型
|
||||
|
||||
### 6.1 JumpBy/JumpTo 跳跃动作
|
||||
|
||||
```cpp
|
||||
class JumpBy : public ActionInterval {
|
||||
public:
|
||||
static JumpBy* create(float duration, const Vec2& position,
|
||||
float height, int jumps);
|
||||
|
||||
protected:
|
||||
Vec2 startPosition_;
|
||||
Vec2 delta_;
|
||||
float height_;
|
||||
int jumps_;
|
||||
};
|
||||
|
||||
class JumpTo : public JumpBy {
|
||||
// 继承实现,目标位置版本
|
||||
};
|
||||
```
|
||||
|
||||
### 6.2 BezierBy/BezierTo 贝塞尔动作
|
||||
|
||||
```cpp
|
||||
struct BezierConfig {
|
||||
Vec2 controlPoint1;
|
||||
Vec2 controlPoint2;
|
||||
Vec2 endPosition;
|
||||
};
|
||||
|
||||
class BezierBy : public ActionInterval {
|
||||
public:
|
||||
static BezierBy* create(float duration, const BezierConfig& config);
|
||||
|
||||
protected:
|
||||
BezierConfig config_;
|
||||
Vec2 startPosition_;
|
||||
};
|
||||
```
|
||||
|
||||
### 6.3 Blink 闪烁动作
|
||||
|
||||
```cpp
|
||||
class Blink : public ActionInterval {
|
||||
public:
|
||||
static Blink* create(float duration, int times);
|
||||
|
||||
protected:
|
||||
int times_;
|
||||
int currentTimes_;
|
||||
bool originalVisible_;
|
||||
};
|
||||
```
|
||||
|
||||
### 6.4 TintBy/TintTo 色调动作
|
||||
|
||||
```cpp
|
||||
class TintTo : public ActionInterval {
|
||||
public:
|
||||
static TintTo* create(float duration,
|
||||
uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
protected:
|
||||
Color3B startColor_;
|
||||
Color3B endColor_;
|
||||
Color3B deltaColor_;
|
||||
};
|
||||
```
|
||||
|
||||
### 6.5 Follow 跟随动作
|
||||
|
||||
```cpp
|
||||
class Follow : public Action {
|
||||
public:
|
||||
static Follow* create(Node* followedNode,
|
||||
const Rect& boundary = Rect::ZERO);
|
||||
|
||||
bool isDone() const override;
|
||||
void step(float dt) override;
|
||||
|
||||
protected:
|
||||
Node* followedNode_ = nullptr;
|
||||
Rect boundary_;
|
||||
bool boundarySet_ = false;
|
||||
};
|
||||
```
|
||||
|
||||
### 6.6 瞬时动作扩展
|
||||
|
||||
```cpp
|
||||
// 放置到指定位置
|
||||
class Place : public ActionInstant {
|
||||
public:
|
||||
static Place* create(const Vec2& position);
|
||||
};
|
||||
|
||||
// X/Y轴翻转
|
||||
class FlipX : public ActionInstant {
|
||||
public:
|
||||
static FlipX* create(bool flipX);
|
||||
};
|
||||
|
||||
class FlipY : public ActionInstant {
|
||||
public:
|
||||
static FlipY* create(bool flipY);
|
||||
};
|
||||
|
||||
// 显示/隐藏
|
||||
class Show : public ActionInstant {
|
||||
public:
|
||||
static Show* create();
|
||||
};
|
||||
|
||||
class Hide : public ActionInstant {
|
||||
public:
|
||||
static Hide* create();
|
||||
};
|
||||
|
||||
// 切换可见性
|
||||
class ToggleVisibility : public ActionInstant {
|
||||
public:
|
||||
static ToggleVisibility* create();
|
||||
};
|
||||
|
||||
// 移除自身
|
||||
class RemoveSelf : public ActionInstant {
|
||||
public:
|
||||
static RemoveSelf* create();
|
||||
};
|
||||
|
||||
// 带Node参数的回调
|
||||
class CallFuncN : public ActionInstant {
|
||||
public:
|
||||
static CallFuncN* create(const std::function<void(Node*)>& func);
|
||||
};
|
||||
```
|
||||
|
||||
## 七、缓动动作完整实现
|
||||
|
||||
### 7.1 通用缓动包装器
|
||||
|
||||
```cpp
|
||||
// 通用缓动包装器
|
||||
class EaseCustom : public ActionEase {
|
||||
public:
|
||||
static EaseCustom* create(ActionInterval* action,
|
||||
EaseFunction easeFunc);
|
||||
};
|
||||
|
||||
// 指数缓动
|
||||
class EaseExponentialIn : public ActionEase { /* ... */ };
|
||||
class EaseExponentialOut : public ActionEase { /* ... */ };
|
||||
class EaseExponentialInOut : public ActionEase { /* ... */ };
|
||||
|
||||
// 正弦缓动
|
||||
class EaseSineIn : public ActionEase { /* ... */ };
|
||||
class EaseSineOut : public ActionEase { /* ... */ };
|
||||
class EaseSineInOut : public ActionEase { /* ... */ };
|
||||
|
||||
// 弹性缓动
|
||||
class EaseElasticIn : public ActionEase {
|
||||
public:
|
||||
static EaseElasticIn* create(ActionInterval* action, float period = 0.3f);
|
||||
};
|
||||
class EaseElasticOut : public ActionEase { /* ... */ };
|
||||
class EaseElasticInOut : public ActionEase { /* ... */ };
|
||||
|
||||
// 弹跳缓动
|
||||
class EaseBounceIn : public ActionEase { /* ... */ };
|
||||
class EaseBounceOut : public ActionEase { /* ... */ };
|
||||
class EaseBounceInOut : public ActionEase { /* ... */ };
|
||||
|
||||
// 回震缓动
|
||||
class EaseBackIn : public ActionEase { /* ... */ };
|
||||
class EaseBackOut : public ActionEase { /* ... */ };
|
||||
class EaseBackInOut : public ActionEase { /* ... */ };
|
||||
|
||||
// 贝塞尔缓动(自定义曲线)
|
||||
class EaseBezier : public ActionEase {
|
||||
public:
|
||||
static EaseBezier* create(ActionInterval* action);
|
||||
void setBezierParamer(float p0, float p1, float p2, float p3);
|
||||
};
|
||||
```
|
||||
|
||||
## 八、Node 类接口更新
|
||||
|
||||
### 8.1 动作相关接口
|
||||
|
||||
```cpp
|
||||
class Node {
|
||||
public:
|
||||
// 运行动作
|
||||
Action* runAction(Action* action);
|
||||
|
||||
// 停止动作
|
||||
void stopAllActions();
|
||||
void stopAction(Action* action);
|
||||
void stopActionByTag(int tag);
|
||||
void stopActionsByFlags(unsigned int flags);
|
||||
|
||||
// 获取动作
|
||||
Action* getActionByTag(int tag);
|
||||
size_t getNumberOfRunningActions() const;
|
||||
|
||||
// 暂停/恢复所有动作
|
||||
void pauseAllActions();
|
||||
void resumeAllActions();
|
||||
|
||||
// 检查是否有动作在运行
|
||||
bool isRunningActions() const;
|
||||
};
|
||||
```
|
||||
|
||||
## 九、使用示例
|
||||
|
||||
### 9.1 基本动作
|
||||
|
||||
```cpp
|
||||
// 移动
|
||||
auto moveTo = MoveTo::create(2.0f, Vec2(100, 100));
|
||||
sprite->runAction(moveTo);
|
||||
|
||||
// 跳跃
|
||||
auto jump = JumpBy::create(2.0f, Vec2(200, 0), 100, 3);
|
||||
sprite->runAction(jump);
|
||||
|
||||
// 贝塞尔曲线
|
||||
BezierConfig bezier;
|
||||
bezier.controlPoint1 = Vec2(100, 200);
|
||||
bezier.controlPoint2 = Vec2(200, 100);
|
||||
bezier.endPosition = Vec2(300, 150);
|
||||
auto bezierAction = BezierTo::create(3.0f, bezier);
|
||||
sprite->runAction(bezierAction);
|
||||
```
|
||||
|
||||
### 9.2 组合动作
|
||||
|
||||
```cpp
|
||||
// 序列动作
|
||||
auto seq = Sequence::create(
|
||||
MoveTo::create(1.0f, Vec2(100, 100)),
|
||||
DelayTime::create(0.5f),
|
||||
FadeOut::create(1.0f),
|
||||
CallFunc::create([](){ log("Done!"); }),
|
||||
nullptr
|
||||
);
|
||||
sprite->runAction(seq);
|
||||
|
||||
// 并行动作
|
||||
auto spawn = Spawn::create(
|
||||
MoveTo::create(2.0f, Vec2(200, 200)),
|
||||
RotateBy::create(2.0f, 360),
|
||||
FadeIn::create(2.0f),
|
||||
nullptr
|
||||
);
|
||||
sprite->runAction(spawn);
|
||||
|
||||
// 重复动作
|
||||
auto repeat = Repeat::create(MoveBy::create(1.0f, Vec2(50, 0)), 5);
|
||||
sprite->runAction(repeat);
|
||||
|
||||
// 永久重复
|
||||
auto repeatForever = RepeatForever::create(
|
||||
Sequence::create(
|
||||
MoveBy::create(1.0f, Vec2(100, 0)),
|
||||
MoveBy::create(1.0f, Vec2(-100, 0)),
|
||||
nullptr
|
||||
)
|
||||
);
|
||||
sprite->runAction(repeatForever);
|
||||
```
|
||||
|
||||
### 9.3 缓动动作
|
||||
|
||||
```cpp
|
||||
// 使用缓动包装器
|
||||
auto move = MoveTo::create(3.0f, Vec2(500, 300));
|
||||
auto easeMove = EaseElasticOut::create(move, 0.5f);
|
||||
sprite->runAction(easeMove);
|
||||
|
||||
// 指数缓动
|
||||
auto jump = JumpBy::create(2.0f, Vec2(200, 0), 100, 1);
|
||||
auto easeJump = EaseExponentialOut::create(jump);
|
||||
sprite->runAction(easeJump);
|
||||
|
||||
// 弹跳缓动
|
||||
auto scale = ScaleTo::create(1.0f, 2.0f);
|
||||
auto bounceScale = EaseBounceOut::create(scale);
|
||||
sprite->runAction(bounceScale);
|
||||
```
|
||||
|
||||
### 9.4 速度控制
|
||||
|
||||
```cpp
|
||||
// 慢动作回放
|
||||
auto action = MoveTo::create(5.0f, Vec2(500, 300));
|
||||
auto speed = Speed::create(action, 0.5f); // 半速
|
||||
sprite->runAction(speed);
|
||||
|
||||
// 动态调整速度
|
||||
speed->setSpeed(2.0f); // 2倍速
|
||||
```
|
||||
|
||||
### 9.5 跟随动作
|
||||
|
||||
```cpp
|
||||
// 相机跟随玩家
|
||||
auto follow = Follow::create(player, Rect(0, 0, 2000, 2000));
|
||||
camera->runAction(follow);
|
||||
```
|
||||
|
||||
## 十、文件结构
|
||||
|
||||
```
|
||||
Extra2D/include/extra2d/action/
|
||||
├── action.h # Action 基类
|
||||
├── finite_time_action.h # FiniteTimeAction 中间层
|
||||
├── action_interval.h # ActionInterval 及其子类
|
||||
├── action_instant.h # ActionInstant 及其子类
|
||||
├── action_ease.h # 缓动动作类系
|
||||
├── action_manager.h # ActionManager
|
||||
├── ease_functions.h # 缓动函数
|
||||
└── action_factory.h # 动作工厂(可选)
|
||||
|
||||
Extra2D/src/action/
|
||||
├── action.cpp
|
||||
├── finite_time_action.cpp
|
||||
├── action_interval.cpp
|
||||
├── action_instant.cpp
|
||||
├── action_ease.cpp
|
||||
├── action_manager.cpp
|
||||
└── ease_functions.cpp
|
||||
```
|
||||
|
||||
## 十一、实现优先级
|
||||
|
||||
### 第一阶段(核心重构)
|
||||
1. Action 基类重构
|
||||
2. FiniteTimeAction 中间层
|
||||
3. ActionInterval 重构
|
||||
4. ActionInstant 重构
|
||||
5. ActionManager 实现
|
||||
|
||||
### 第二阶段(新增动作)
|
||||
1. JumpBy/JumpTo
|
||||
2. BezierBy/BezierTo
|
||||
3. Blink
|
||||
4. TintBy/TintTo
|
||||
5. Follow
|
||||
6. Speed
|
||||
|
||||
### 第三阶段(缓动系统)
|
||||
1. ActionEase 基类
|
||||
2. 各类缓动包装器
|
||||
3. EaseCustom 自定义缓动
|
||||
|
||||
### 第四阶段(瞬时动作扩展)
|
||||
1. Place
|
||||
2. FlipX/FlipY
|
||||
3. Show/Hide
|
||||
4. ToggleVisibility
|
||||
5. RemoveSelf
|
||||
6. CallFuncN
|
||||
|
||||
## 十二、兼容性考虑
|
||||
|
||||
### 12.1 API 变更
|
||||
|
||||
- 动作创建方式改为 `create()` 静态工厂方法
|
||||
- `Loop` 重命名为 `Repeat`
|
||||
- `Delay` 重命名为 `DelayTime`
|
||||
- 动作管理由 `ActionManager` 集中处理
|
||||
|
||||
### 12.2 迁移指南
|
||||
|
||||
```cpp
|
||||
// 旧写法
|
||||
auto action = std::make_shared<MoveTo>(2.0f, Vec2(100, 100));
|
||||
sprite->runAction(action);
|
||||
|
||||
// 新写法(推荐)
|
||||
auto action = MoveTo::create(2.0f, Vec2(100, 100));
|
||||
sprite->runAction(action);
|
||||
|
||||
// 旧写法(缓动)
|
||||
auto action = EaseAction::create(MoveTo::create(2.0f, Vec2(100, 100)), easeInQuad);
|
||||
|
||||
// 新写法(缓动)
|
||||
auto action = EaseQuadIn::create(MoveTo::create(2.0f, Vec2(100, 100)));
|
||||
```
|
||||
|
||||
## 十三、性能优化
|
||||
|
||||
1. **对象池集成** - 动作对象使用对象池管理
|
||||
2. **批量更新** - ActionManager 统一调度减少调用开销
|
||||
3. **延迟删除** - 动作完成时标记删除,统一清理
|
||||
4. **缓存友好** - 连续存储同一类型动作
|
||||
|
||||
## 十四、测试计划
|
||||
|
||||
1. 单元测试:每个动作类型的独立测试
|
||||
2. 集成测试:组合动作测试
|
||||
3. 性能测试:大量动作并发测试
|
||||
4. 内存测试:动作对象生命周期测试
|
||||
|
|
@ -44,8 +44,7 @@ void GameOverLayer::onEnter() {
|
|||
initButtons();
|
||||
|
||||
// 创建向上移动的动画(从屏幕底部移动到正常位置)
|
||||
auto moveAction = extra2d::makePtr<extra2d::MoveBy>(
|
||||
1.0f, extra2d::Vec2(0.0f, -screenHeight));
|
||||
auto moveAction = extra2d::MoveBy::create(1.0f, extra2d::Vec2(0.0f, -screenHeight));
|
||||
moveAction->setCompletionCallback([this]() {
|
||||
animationDone_ = true;
|
||||
if (restartBtn_)
|
||||
|
|
|
|||
Loading…
Reference in New Issue