Compare commits
58 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
e011cea090 | |
|
|
d5cb194552 | |
|
|
44e0d65f10 | |
|
|
e0b0a7883d | |
|
|
97be1b746a | |
|
|
717112c437 | |
|
|
ec6ced9db2 | |
|
|
8cd883ede7 | |
|
|
91e3e8fe57 | |
|
|
9041833430 | |
|
|
b4a3d6b14b | |
|
|
d387532738 | |
|
|
f7e4f89cca | |
|
|
46ec1c665f | |
|
|
3b827149ba | |
|
|
92be7d9d18 | |
|
|
b4be0d84f8 | |
|
|
46393fd027 | |
|
|
bbdc1435ce | |
|
|
bdf78f5eca | |
|
|
6717015e28 | |
|
|
fb11f2a71e | |
|
|
f9be301dae | |
|
|
ebf73a4492 | |
|
|
418d2c8f92 | |
|
|
e68ce87638 | |
|
|
5a3d0cd9de | |
|
|
0761b55864 | |
|
|
8abf58e3d5 | |
|
|
5ef1873a44 | |
|
|
71eeeac033 | |
|
|
3b031666ae | |
|
|
24b86b4916 | |
|
|
ea081b9dd3 | |
|
|
d81f0c1e45 | |
|
|
b4a55239aa | |
|
|
4afd52cc82 | |
|
|
60ef7ab63f | |
|
|
c84aab70ed | |
|
|
00d709fcc8 | |
|
|
aec444f2b5 | |
|
|
e52c117830 | |
|
|
f41600306e | |
|
|
98bca638d0 | |
|
|
353a222c62 | |
|
|
7b1e1299e0 | |
|
|
bd3cebf0b5 | |
|
|
ea5ecd383f | |
|
|
377ec373b0 | |
|
|
0f520c8e37 | |
|
|
fa9ee0e2a7 | |
|
|
a6c1f66fff | |
|
|
5039b1d9fc | |
|
|
f9c8f080eb | |
|
|
f08a0bf583 | |
|
|
97220e9417 | |
|
|
a2b142eb99 | |
|
|
7c7d9cca92 |
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git diff:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +71,6 @@ cmake-build-*/
|
|||
# --------------------------------------------
|
||||
/packages/
|
||||
/deps/
|
||||
/third_party/
|
||||
/vendor/
|
||||
|
||||
# --------------------------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "third_party/tbb"]
|
||||
path = third_party/tbb
|
||||
url = https://github.com/oneapi-src/oneTBB.git
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
|
||||
/**
|
||||
* @brief 动作状态枚举
|
||||
*/
|
||||
enum class ActionState {
|
||||
Idle,
|
||||
Running,
|
||||
Paused,
|
||||
Completed
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 动作基类
|
||||
*
|
||||
* 所有动作的基类,定义了动作的核心接口。
|
||||
* 动作用于修改 Node 的属性,实现动画效果。
|
||||
*/
|
||||
class Action {
|
||||
public:
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief 检查动作是否完成
|
||||
* @return true 如果动作已完成
|
||||
*/
|
||||
virtual bool isDone() const;
|
||||
|
||||
/**
|
||||
* @brief 使用目标节点启动动作
|
||||
* @param target 目标节点
|
||||
*/
|
||||
virtual void startWithTarget(Node* target);
|
||||
|
||||
/**
|
||||
* @brief 停止动作
|
||||
*/
|
||||
virtual void stop();
|
||||
|
||||
/**
|
||||
* @brief 每帧调用的步进函数
|
||||
* @param dt 帧时间间隔
|
||||
*/
|
||||
virtual void step(float dt);
|
||||
|
||||
/**
|
||||
* @brief 更新动作状态
|
||||
* @param time 归一化时间 [0, 1]
|
||||
*/
|
||||
virtual void update(float time);
|
||||
|
||||
/**
|
||||
* @brief 克隆动作
|
||||
* @return 动作的深拷贝
|
||||
*/
|
||||
virtual Action* clone() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建反向动作
|
||||
* @return 反向动作
|
||||
*/
|
||||
virtual Action* reverse() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 暂停动作
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复动作
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief 重启动作
|
||||
*/
|
||||
void restart();
|
||||
|
||||
/**
|
||||
* @brief 设置完成回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void setCompletionCallback(const CompletionCallback& callback) {
|
||||
completionCallback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取目标节点
|
||||
* @return 目标节点指针
|
||||
*/
|
||||
Node* getTarget() const { return target_; }
|
||||
|
||||
/**
|
||||
* @brief 获取原始目标节点
|
||||
* @return 原始目标节点指针
|
||||
*/
|
||||
Node* getOriginalTarget() const { return originalTarget_; }
|
||||
|
||||
/**
|
||||
* @brief 获取动作状态
|
||||
* @return 当前状态
|
||||
*/
|
||||
ActionState getState() const { return state_; }
|
||||
|
||||
/**
|
||||
* @brief 获取标签
|
||||
* @return 标签值
|
||||
*/
|
||||
int getTag() const { return tag_; }
|
||||
|
||||
/**
|
||||
* @brief 设置标签
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void setTag(int tag) { tag_ = tag; }
|
||||
|
||||
/**
|
||||
* @brief 获取标志位
|
||||
* @return 标志位
|
||||
*/
|
||||
unsigned int getFlags() const { return flags_; }
|
||||
|
||||
/**
|
||||
* @brief 设置标志位
|
||||
* @param flags 标志位
|
||||
*/
|
||||
void setFlags(unsigned int flags) { flags_ = flags; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 动作开始时调用
|
||||
*/
|
||||
virtual void onStart() {}
|
||||
|
||||
/**
|
||||
* @brief 动作完成时调用
|
||||
*/
|
||||
virtual void onComplete() {
|
||||
if (completionCallback_) {
|
||||
completionCallback_();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置动作完成状态
|
||||
*/
|
||||
void setDone() { state_ = ActionState::Completed; }
|
||||
|
||||
Node* target_ = nullptr;
|
||||
Node* originalTarget_ = nullptr;
|
||||
ActionState state_ = ActionState::Idle;
|
||||
int tag_ = -1;
|
||||
unsigned int flags_ = 0;
|
||||
CompletionCallback completionCallback_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,344 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,469 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
#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,101 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 缓动函数类型
|
||||
*/
|
||||
using EaseFunction = float (*)(float);
|
||||
|
||||
// ============================================================================
|
||||
// 线性缓动
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 线性缓动(无缓动)
|
||||
* @param t 归一化时间 [0, 1]
|
||||
* @return 缓动后的值
|
||||
*/
|
||||
float easeLinear(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 二次缓动 (Quad)
|
||||
// ============================================================================
|
||||
|
||||
float easeInQuad(float t);
|
||||
float easeOutQuad(float t);
|
||||
float easeInOutQuad(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 三次缓动 (Cubic)
|
||||
// ============================================================================
|
||||
|
||||
float easeInCubic(float t);
|
||||
float easeOutCubic(float t);
|
||||
float easeInOutCubic(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 四次缓动 (Quart)
|
||||
// ============================================================================
|
||||
|
||||
float easeInQuart(float t);
|
||||
float easeOutQuart(float t);
|
||||
float easeInOutQuart(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 五次缓动 (Quint)
|
||||
// ============================================================================
|
||||
|
||||
float easeInQuint(float t);
|
||||
float easeOutQuint(float t);
|
||||
float easeInOutQuint(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 正弦缓动 (Sine)
|
||||
// ============================================================================
|
||||
|
||||
float easeInSine(float t);
|
||||
float easeOutSine(float t);
|
||||
float easeInOutSine(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 指数缓动 (Exponential)
|
||||
// ============================================================================
|
||||
|
||||
float easeInExpo(float t);
|
||||
float easeOutExpo(float t);
|
||||
float easeInOutExpo(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 圆形缓动 (Circular)
|
||||
// ============================================================================
|
||||
|
||||
float easeInCirc(float t);
|
||||
float easeOutCirc(float t);
|
||||
float easeInOutCirc(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 回震缓动 (Back)
|
||||
// ============================================================================
|
||||
|
||||
float easeInBack(float t);
|
||||
float easeOutBack(float t);
|
||||
float easeInOutBack(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 弹性缓动 (Elastic)
|
||||
// ============================================================================
|
||||
|
||||
float easeInElastic(float t);
|
||||
float easeOutElastic(float t);
|
||||
float easeInOutElastic(float t);
|
||||
|
||||
// ============================================================================
|
||||
// 弹跳缓动 (Bounce)
|
||||
// ============================================================================
|
||||
|
||||
float easeInBounce(float t);
|
||||
float easeOutBounce(float t);
|
||||
float easeInOutBounce(float t);
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// ALS 图层信息
|
||||
// ============================================================================
|
||||
struct AlsLayerInfo {
|
||||
std::string aniPath; // 子动画的 ANI 文件路径
|
||||
int zOrder = 0; // 层级顺序
|
||||
Vec2 offset; // 层偏移
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ALS 解析结果
|
||||
// ============================================================================
|
||||
struct AlsParseResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
std::vector<AlsLayerInfo> layers;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// AlsParser - ALS 复合动画文件解析器
|
||||
// 解析 .als 文件获取多层动画的图层信息
|
||||
// ============================================================================
|
||||
class AlsParser {
|
||||
public:
|
||||
AlsParser() = default;
|
||||
|
||||
/// 从文件解析
|
||||
AlsParseResult parse(const std::string &filePath);
|
||||
|
||||
/// 从内存内容解析
|
||||
AlsParseResult parseFromMemory(const std::string &content,
|
||||
const std::string &basePath = "");
|
||||
|
||||
/// 设置基础路径
|
||||
void setBasePath(const std::string &basePath) { basePath_ = basePath; }
|
||||
|
||||
private:
|
||||
std::string basePath_;
|
||||
|
||||
std::string resolvePath(const std::string &relativePath) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <extra2d/animation/ani_parser.h>
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// DNF ANI 二进制格式中的节点类型枚举
|
||||
// ============================================================================
|
||||
enum class AniNodeType : uint16_t {
|
||||
Loop = 0,
|
||||
Shadow = 1,
|
||||
Coord = 3,
|
||||
ImageRate = 7,
|
||||
ImageRotate = 8,
|
||||
RGBA = 9,
|
||||
Interpolation = 10,
|
||||
GraphicEffect = 11,
|
||||
Delay = 12,
|
||||
DamageType = 13,
|
||||
DamageBox = 14,
|
||||
AttackBox = 15,
|
||||
PlaySound = 16,
|
||||
Preload = 17,
|
||||
Spectrum = 18,
|
||||
SetFlag = 23,
|
||||
FlipType = 24,
|
||||
LoopStart = 25,
|
||||
LoopEnd = 26,
|
||||
Clip = 27,
|
||||
Operation = 28,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// AniBinaryParser - ANI 二进制格式解析器
|
||||
// 参考 DNF-Porting 的 PvfAnimation 实现
|
||||
// ============================================================================
|
||||
class AniBinaryParser {
|
||||
public:
|
||||
AniBinaryParser() = default;
|
||||
|
||||
/// 从二进制数据解析
|
||||
AniParseResult parse(const uint8_t *data, size_t length);
|
||||
|
||||
/// 从文件解析
|
||||
AniParseResult parseFromFile(const std::string &filePath);
|
||||
|
||||
/// 设置路径替换回调
|
||||
void setPathResolver(PathResolveCallback callback) {
|
||||
pathResolver_ = std::move(callback);
|
||||
}
|
||||
|
||||
/// 设置基础路径
|
||||
void setBasePath(const std::string &basePath) { basePath_ = basePath; }
|
||||
|
||||
private:
|
||||
PathResolveCallback pathResolver_;
|
||||
std::string basePath_;
|
||||
|
||||
std::string resolvePath(const std::string &relativePath) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// ANI 文件解析结果
|
||||
// ============================================================================
|
||||
struct AniParseResult {
|
||||
bool success = false;
|
||||
std::string errorMessage;
|
||||
Ptr<AnimationClip> clip;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// AniParser - ANI 脚本文件解析器
|
||||
// 将原始 ANI 文件格式解析为 AnimationClip 数据
|
||||
// ============================================================================
|
||||
class AniParser {
|
||||
public:
|
||||
AniParser() = default;
|
||||
|
||||
/// 从文件解析
|
||||
AniParseResult parse(const std::string &filePath);
|
||||
|
||||
/// 从内存内容解析
|
||||
AniParseResult parseFromMemory(const std::string &content,
|
||||
const std::string &basePath = "");
|
||||
|
||||
/// 设置路径替换回调(对应原始 AdditionalOptions)
|
||||
void setPathResolver(PathResolveCallback callback) {
|
||||
pathResolver_ = std::move(callback);
|
||||
}
|
||||
|
||||
/// 设置基础路径(用于解析相对路径)
|
||||
void setBasePath(const std::string &basePath) { basePath_ = basePath; }
|
||||
|
||||
private:
|
||||
PathResolveCallback pathResolver_;
|
||||
std::string basePath_;
|
||||
|
||||
std::string resolvePath(const std::string &relativePath) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/animation_controller.h>
|
||||
#include <extra2d/scene/sprite.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimatedSprite - 动画精灵节点
|
||||
// 将 AnimationController 与 Sprite 渲染桥接,接入场景图
|
||||
// ============================================================================
|
||||
class AnimatedSprite : public Sprite {
|
||||
public:
|
||||
AnimatedSprite();
|
||||
~AnimatedSprite() override = default;
|
||||
|
||||
// ------ 静态工厂 ------
|
||||
static Ptr<AnimatedSprite> create();
|
||||
static Ptr<AnimatedSprite> create(Ptr<AnimationClip> clip);
|
||||
static Ptr<AnimatedSprite> create(const std::string &aniFilePath);
|
||||
|
||||
// ------ 动画绑定 ------
|
||||
void setAnimationClip(Ptr<AnimationClip> clip);
|
||||
void loadAnimation(const std::string &aniFilePath);
|
||||
Ptr<AnimationClip> getAnimationClip() const;
|
||||
|
||||
// ------ 动画字典 ------
|
||||
void addAnimation(const std::string &name, Ptr<AnimationClip> clip);
|
||||
void play(const std::string &name, bool loop = true);
|
||||
bool hasAnimation(const std::string &name) const;
|
||||
Ptr<AnimationClip> getAnimation(const std::string &name) const;
|
||||
const std::string &getCurrentAnimationName() const;
|
||||
|
||||
// ------ 播放控制(委托 controller_)------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
bool isStopped() const;
|
||||
|
||||
// ------ 属性控制 ------
|
||||
void setLooping(bool loop);
|
||||
bool isLooping() const;
|
||||
void setPlaybackSpeed(float speed);
|
||||
float getPlaybackSpeed() const;
|
||||
|
||||
// ------ 帧控制 ------
|
||||
void setFrameIndex(size_t index);
|
||||
size_t getCurrentFrameIndex() const;
|
||||
size_t getTotalFrames() const;
|
||||
void nextFrame();
|
||||
void prevFrame();
|
||||
|
||||
// ------ 帧范围限制 ------
|
||||
/// 设置帧播放范围(用于精灵图动画,限制在指定范围内循环)
|
||||
/// @param start 起始帧索引(包含)
|
||||
/// @param end 结束帧索引(包含),-1表示不限制
|
||||
void setFrameRange(int start, int end = -1);
|
||||
|
||||
/// 获取当前帧范围
|
||||
/// @return pair<起始帧, 结束帧>,结束帧为-1表示不限制
|
||||
std::pair<int, int> getFrameRange() const;
|
||||
|
||||
/// 清除帧范围限制(恢复播放所有帧)
|
||||
void clearFrameRange();
|
||||
|
||||
/// 检查是否设置了帧范围限制
|
||||
bool hasFrameRange() const;
|
||||
|
||||
// ------ 回调 ------
|
||||
void setCompletionCallback(AnimationController::CompletionCallback cb);
|
||||
void setKeyframeCallback(AnimationController::KeyframeCallback cb);
|
||||
void setSoundTriggerCallback(AnimationController::SoundTriggerCallback cb);
|
||||
|
||||
// ------ 碰撞盒访问(当前帧)------
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentDamageBoxes() const;
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentAttackBoxes() const;
|
||||
|
||||
// ------ 帧变换控制 ------
|
||||
/// 设置是否由动画帧数据覆盖节点的 position/scale/rotation
|
||||
/// ANI 动画需要开启(默认),精灵图动画应关闭
|
||||
void setApplyFrameTransform(bool apply) { applyFrameTransform_ = apply; }
|
||||
bool isApplyFrameTransform() const { return applyFrameTransform_; }
|
||||
|
||||
// ------ 自动播放 ------
|
||||
void setAutoPlay(bool autoPlay) { autoPlay_ = autoPlay; }
|
||||
bool isAutoPlay() const { return autoPlay_; }
|
||||
|
||||
// ------ 直接控制器访问 ------
|
||||
AnimationController &getController() { return controller_; }
|
||||
const AnimationController &getController() const { return controller_; }
|
||||
|
||||
protected:
|
||||
void onUpdate(float dt) override;
|
||||
void onEnter() override;
|
||||
|
||||
private:
|
||||
AnimationController controller_;
|
||||
bool autoPlay_ = false;
|
||||
bool applyFrameTransform_ = true;
|
||||
|
||||
// 动画字典
|
||||
std::unordered_map<std::string, Ptr<AnimationClip>> animations_;
|
||||
std::string currentAnimationName_;
|
||||
static const std::string emptyString_;
|
||||
|
||||
// 帧范围限制(用于精灵图动画)
|
||||
int frameRangeStart_ = 0; // 起始帧索引
|
||||
int frameRangeEnd_ = -1; // 结束帧索引,-1表示不限制
|
||||
|
||||
// 空碰撞盒列表(用于无帧时返回引用)
|
||||
static const std::vector<std::array<int32_t, 6>> emptyBoxes_;
|
||||
|
||||
void applyFrame(const AnimationFrame &frame);
|
||||
void onFrameChanged(size_t oldIdx, size_t newIdx,
|
||||
const AnimationFrame &frame);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 路径替换回调(对应原始 AdditionalOptions)
|
||||
using PathResolveCallback = std::function<std::string(const std::string &)>;
|
||||
|
||||
// ============================================================================
|
||||
// AnimationCache - 动画片段全局缓存(借鉴 Cocos AnimationCache)
|
||||
// 同一 ANI 文件只解析一次,后续直接复用数据
|
||||
// ============================================================================
|
||||
class AnimationCache {
|
||||
public:
|
||||
static AnimationCache &getInstance() {
|
||||
static AnimationCache instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ------ 加载与获取 ------
|
||||
|
||||
/// 从文件加载(自动缓存),已缓存则直接返回
|
||||
/// 注意:实际的 ANI 解析逻辑在 AniParser 中实现
|
||||
/// 此方法在 animation_cache.cpp 中实现,依赖 AniParser
|
||||
Ptr<AnimationClip> loadClip(const std::string &aniFilePath);
|
||||
|
||||
/// 从缓存获取(不触发加载)
|
||||
Ptr<AnimationClip> getClip(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = clips_.find(name);
|
||||
if (it != clips_.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// 手动添加到缓存
|
||||
void addClip(Ptr<AnimationClip> clip, const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
clips_[name] = std::move(clip);
|
||||
}
|
||||
|
||||
// ------ 缓存管理 ------
|
||||
|
||||
bool has(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return clips_.find(name) != clips_.end();
|
||||
}
|
||||
|
||||
void removeClip(const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
clips_.erase(name);
|
||||
}
|
||||
|
||||
/// 移除未被外部引用的动画片段
|
||||
void removeUnusedClips() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto it = clips_.begin(); it != clips_.end();) {
|
||||
if (it->second.use_count() == 1) {
|
||||
it = clips_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
clips_.clear();
|
||||
}
|
||||
|
||||
size_t count() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return clips_.size();
|
||||
}
|
||||
|
||||
// ------ 路径配置 ------
|
||||
void setPathResolver(PathResolveCallback resolver) {
|
||||
pathResolver_ = std::move(resolver);
|
||||
}
|
||||
|
||||
PathResolveCallback getPathResolver() const { return pathResolver_; }
|
||||
|
||||
private:
|
||||
AnimationCache() = default;
|
||||
~AnimationCache() = default;
|
||||
AnimationCache(const AnimationCache &) = delete;
|
||||
AnimationCache &operator=(const AnimationCache &) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, Ptr<AnimationClip>> clips_;
|
||||
PathResolveCallback pathResolver_;
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_ANIMATION_CACHE() ::extra2d::AnimationCache::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/animation/sprite_frame_cache.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimationClip - 动画片段(纯数据,可复用)
|
||||
// 借鉴 Cocos:一份 AnimationClip 可被多个 AnimationNode 同时使用
|
||||
// ============================================================================
|
||||
class AnimationClip {
|
||||
public:
|
||||
AnimationClip() = default;
|
||||
|
||||
explicit AnimationClip(const std::string &name) : name_(name) {}
|
||||
|
||||
// ------ 帧管理 ------
|
||||
void addFrame(const AnimationFrame &frame) { frames_.push_back(frame); }
|
||||
|
||||
void addFrame(AnimationFrame &&frame) { frames_.push_back(std::move(frame)); }
|
||||
|
||||
void insertFrame(size_t index, const AnimationFrame &frame) {
|
||||
assert(index <= frames_.size());
|
||||
frames_.insert(frames_.begin() + static_cast<ptrdiff_t>(index), frame);
|
||||
}
|
||||
|
||||
void removeFrame(size_t index) {
|
||||
assert(index < frames_.size());
|
||||
frames_.erase(frames_.begin() + static_cast<ptrdiff_t>(index));
|
||||
}
|
||||
|
||||
void clearFrames() { frames_.clear(); }
|
||||
|
||||
const AnimationFrame &getFrame(size_t index) const {
|
||||
assert(index < frames_.size());
|
||||
return frames_[index];
|
||||
}
|
||||
|
||||
AnimationFrame &getFrame(size_t index) {
|
||||
assert(index < frames_.size());
|
||||
return frames_[index];
|
||||
}
|
||||
|
||||
size_t getFrameCount() const { return frames_.size(); }
|
||||
bool empty() const { return frames_.empty(); }
|
||||
|
||||
// ------ 全局属性(对应原始 AnimationFlag)------
|
||||
FramePropertySet &globalProperties() { return globalProperties_; }
|
||||
const FramePropertySet &globalProperties() const { return globalProperties_; }
|
||||
|
||||
bool isLooping() const {
|
||||
return globalProperties_.getOr<bool>(FramePropertyKey::Loop, false);
|
||||
}
|
||||
|
||||
void setLooping(bool loop) { globalProperties_.withLoop(loop); }
|
||||
|
||||
// ------ 时间信息 ------
|
||||
float getTotalDuration() const {
|
||||
float total = 0.0f;
|
||||
for (const auto &frame : frames_) {
|
||||
total += frame.delay;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// ------ 预计算最大帧尺寸 ------
|
||||
Size getMaxFrameSize() const {
|
||||
Size maxSize;
|
||||
for (const auto &frame : frames_) {
|
||||
if (frame.spriteFrame && frame.spriteFrame->isValid()) {
|
||||
const auto &rect = frame.spriteFrame->getRect();
|
||||
if (rect.size.width > maxSize.width)
|
||||
maxSize.width = rect.size.width;
|
||||
if (rect.size.height > maxSize.height)
|
||||
maxSize.height = rect.size.height;
|
||||
}
|
||||
}
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
// ------ 元数据 ------
|
||||
void setName(const std::string &name) { name_ = name; }
|
||||
const std::string &getName() const { return name_; }
|
||||
|
||||
void setSourcePath(const std::string &path) { sourcePath_ = path; }
|
||||
const std::string &getSourcePath() const { return sourcePath_; }
|
||||
|
||||
// ------ 静态工厂 ------
|
||||
static Ptr<AnimationClip> create(const std::string &name = "") {
|
||||
return makePtr<AnimationClip>(name);
|
||||
}
|
||||
|
||||
/// 从精灵图网格创建(所有帧按顺序)
|
||||
static Ptr<AnimationClip> createFromGrid(Ptr<Texture> texture, int frameWidth,
|
||||
int frameHeight,
|
||||
float frameDurationMs = 100.0f,
|
||||
int frameCount = -1, int spacing = 0,
|
||||
int margin = 0) {
|
||||
if (!texture)
|
||||
return nullptr;
|
||||
|
||||
int texW = texture->getWidth();
|
||||
int texH = texture->getHeight();
|
||||
int usableW = texW - 2 * margin;
|
||||
int usableH = texH - 2 * margin;
|
||||
int cols = (usableW + spacing) / (frameWidth + spacing);
|
||||
int rows = (usableH + spacing) / (frameHeight + spacing);
|
||||
int total = (frameCount > 0) ? frameCount : cols * rows;
|
||||
|
||||
auto clip = makePtr<AnimationClip>();
|
||||
for (int i = 0; i < total; ++i) {
|
||||
int col = i % cols;
|
||||
int row = i / cols;
|
||||
if (row >= rows)
|
||||
break;
|
||||
|
||||
// 翻转行顺序:精灵图第0行在顶部,但OpenGL纹理V坐标从底部开始
|
||||
// 所以将行索引翻转,使第0行对应纹理底部(V=1.0),第3行对应纹理顶部(V=0.0)
|
||||
int flippedRow = (rows - 1) - row;
|
||||
|
||||
Rect rect(
|
||||
static_cast<float>(margin + col * (frameWidth + spacing)),
|
||||
static_cast<float>(margin + flippedRow * (frameHeight + spacing)),
|
||||
static_cast<float>(frameWidth), static_cast<float>(frameHeight));
|
||||
|
||||
auto sf = SpriteFrame::create(texture, rect);
|
||||
AnimationFrame frame;
|
||||
frame.spriteFrame = std::move(sf);
|
||||
frame.delay = frameDurationMs;
|
||||
clip->addFrame(std::move(frame));
|
||||
}
|
||||
return clip;
|
||||
}
|
||||
|
||||
/// 从精灵图网格创建(指定帧索引列表)
|
||||
static Ptr<AnimationClip>
|
||||
createFromGridIndices(Ptr<Texture> texture, int frameWidth, int frameHeight,
|
||||
const std::vector<int> &frameIndices,
|
||||
float frameDurationMs = 100.0f, int spacing = 0,
|
||||
int margin = 0) {
|
||||
if (!texture)
|
||||
return nullptr;
|
||||
|
||||
int texW = texture->getWidth();
|
||||
int texH = texture->getHeight();
|
||||
int usableW = texW - 2 * margin;
|
||||
int usableH = texH - 2 * margin;
|
||||
int cols = (usableW + spacing) / (frameWidth + spacing);
|
||||
int rows = (usableH + spacing) / (frameHeight + spacing);
|
||||
|
||||
auto clip = makePtr<AnimationClip>();
|
||||
for (int idx : frameIndices) {
|
||||
int col = idx % cols;
|
||||
int row = idx / cols;
|
||||
|
||||
// 翻转行顺序:精灵图第0行在顶部,但OpenGL纹理V坐标从底部开始
|
||||
int flippedRow = (rows - 1) - row;
|
||||
|
||||
Rect rect(
|
||||
static_cast<float>(margin + col * (frameWidth + spacing)),
|
||||
static_cast<float>(margin + flippedRow * (frameHeight + spacing)),
|
||||
static_cast<float>(frameWidth), static_cast<float>(frameHeight));
|
||||
|
||||
auto sf = SpriteFrame::create(texture, rect);
|
||||
AnimationFrame frame;
|
||||
frame.spriteFrame = std::move(sf);
|
||||
frame.delay = frameDurationMs;
|
||||
clip->addFrame(std::move(frame));
|
||||
}
|
||||
return clip;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string sourcePath_;
|
||||
std::vector<AnimationFrame> frames_;
|
||||
FramePropertySet globalProperties_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/animation/interpolation_engine.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 动画播放状态
|
||||
// ============================================================================
|
||||
enum class AnimPlayState : uint8 { Stopped, Playing, Paused };
|
||||
|
||||
// ============================================================================
|
||||
// AnimationController - 动画播放控制器
|
||||
// 借鉴 Cocos Creator 的 AnimationState:纯播放逻辑,不持有渲染资源
|
||||
// ============================================================================
|
||||
class AnimationController {
|
||||
public:
|
||||
// 回调类型定义
|
||||
using FrameChangeCallback = std::function<void(size_t oldIdx, size_t newIdx,
|
||||
const AnimationFrame &frame)>;
|
||||
using KeyframeCallback = std::function<void(int flagIndex)>;
|
||||
using SoundTriggerCallback = std::function<void(const std::string &path)>;
|
||||
using CompletionCallback = std::function<void()>;
|
||||
|
||||
AnimationController() = default;
|
||||
|
||||
// ------ 绑定动画数据 ------
|
||||
void setClip(Ptr<AnimationClip> clip);
|
||||
Ptr<AnimationClip> getClip() const { return clip_; }
|
||||
|
||||
// ------ 播放控制 ------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
|
||||
// ------ 帧控制 ------
|
||||
void setFrameIndex(size_t index);
|
||||
void nextFrame();
|
||||
void prevFrame();
|
||||
|
||||
// ------ 核心更新(每帧调用)------
|
||||
void update(float dt);
|
||||
|
||||
// ------ 状态查询 ------
|
||||
AnimPlayState getState() const { return state_; }
|
||||
bool isPlaying() const { return state_ == AnimPlayState::Playing; }
|
||||
bool isPaused() const { return state_ == AnimPlayState::Paused; }
|
||||
bool isStopped() const { return state_ == AnimPlayState::Stopped; }
|
||||
|
||||
size_t getCurrentFrameIndex() const { return currentFrameIndex_; }
|
||||
size_t getTotalFrames() const;
|
||||
const AnimationFrame &getCurrentFrame() const;
|
||||
|
||||
float getPlaybackSpeed() const { return playbackSpeed_; }
|
||||
void setPlaybackSpeed(float speed) { playbackSpeed_ = speed; }
|
||||
|
||||
bool isLooping() const;
|
||||
void setLooping(bool loop);
|
||||
|
||||
// ------ 插值状态 ------
|
||||
float getInterpolationFactor() const { return interpolationFactor_; }
|
||||
bool isInterpolating() const { return interpolating_; }
|
||||
|
||||
// ------ 回调注册 ------
|
||||
void setFrameChangeCallback(FrameChangeCallback cb) {
|
||||
onFrameChange_ = std::move(cb);
|
||||
}
|
||||
void setKeyframeCallback(KeyframeCallback cb) { onKeyframe_ = std::move(cb); }
|
||||
void setSoundTriggerCallback(SoundTriggerCallback cb) {
|
||||
onSoundTrigger_ = std::move(cb);
|
||||
}
|
||||
void setCompletionCallback(CompletionCallback cb) {
|
||||
onComplete_ = std::move(cb);
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<AnimationClip> clip_;
|
||||
AnimPlayState state_ = AnimPlayState::Stopped;
|
||||
|
||||
size_t currentFrameIndex_ = 0;
|
||||
float accumulatedTime_ = 0.0f; // 当前帧已累积时间 (ms)
|
||||
float playbackSpeed_ = 1.0f;
|
||||
bool loopOverride_ = false; // 外部循环覆盖值
|
||||
bool hasLoopOverride_ = false; // 是否使用外部循环覆盖
|
||||
|
||||
// 插值状态
|
||||
bool interpolating_ = false;
|
||||
float interpolationFactor_ = 0.0f;
|
||||
|
||||
// 回调
|
||||
FrameChangeCallback onFrameChange_;
|
||||
KeyframeCallback onKeyframe_;
|
||||
SoundTriggerCallback onSoundTrigger_;
|
||||
CompletionCallback onComplete_;
|
||||
|
||||
// 内部方法
|
||||
void advanceFrame(size_t newIndex);
|
||||
void processFrameProperties(const AnimationFrame &frame);
|
||||
void updateInterpolation();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Node;
|
||||
|
||||
// ============================================================================
|
||||
// 动画事件类型
|
||||
// ============================================================================
|
||||
enum class AnimationEventType : uint32_t {
|
||||
FrameChanged = 0x2001, // 帧切换
|
||||
KeyframeHit = 0x2002, // 关键帧触发
|
||||
SoundTrigger = 0x2003, // 音效触发
|
||||
AnimationStart = 0x2004, // 动画开始播放
|
||||
AnimationEnd = 0x2005, // 动画播放结束
|
||||
AnimationLoop = 0x2006, // 动画循环一轮
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 动画事件数据
|
||||
// ============================================================================
|
||||
struct AnimationEvent {
|
||||
AnimationEventType type;
|
||||
size_t frameIndex = 0;
|
||||
size_t previousFrameIndex = 0;
|
||||
int keyframeFlag = -1;
|
||||
std::string soundPath;
|
||||
Node *source = nullptr;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 动画事件回调类型
|
||||
// ============================================================================
|
||||
using AnimationEventCallback = std::function<void(const AnimationEvent &)>;
|
||||
using KeyframeHitCallback = std::function<void(int flagIndex)>;
|
||||
using AnimationCompleteCallback = std::function<void()>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <extra2d/animation/frame_property.h>
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimationFrame - 单帧数据
|
||||
// 引用 SpriteFrame 而非直接持有纹理(借鉴 Cocos 模式)
|
||||
// 通过 FramePropertySet 支持不固定数据(ANI Flag 系统增强版)
|
||||
// ============================================================================
|
||||
struct AnimationFrame {
|
||||
// ------ 核心数据(固定部分)------
|
||||
Ptr<SpriteFrame> spriteFrame; // 精灵帧引用(Cocos 模式)
|
||||
std::string texturePath; // 原始图片路径(用于解析时定位资源)
|
||||
int textureIndex = 0; // 精灵图集索引
|
||||
Vec2 offset; // 位置偏移
|
||||
float delay = 100.0f; // 帧延迟(毫秒)
|
||||
|
||||
// ------ 碰撞盒数据(DNF ANI 格式)------
|
||||
std::vector<std::array<int32_t, 6>> damageBoxes; // 伤害碰撞盒
|
||||
std::vector<std::array<int32_t, 6>> attackBoxes; // 攻击碰撞盒
|
||||
|
||||
// ------ 不固定数据(属性集合)------
|
||||
FramePropertySet properties; // 类型安全的 Flag 系统
|
||||
|
||||
// ------ 便捷方法 ------
|
||||
bool hasTexture() const {
|
||||
return spriteFrame != nullptr && spriteFrame->isValid();
|
||||
}
|
||||
|
||||
bool hasInterpolation() const {
|
||||
return properties.getOr<bool>(FramePropertyKey::Interpolation, false);
|
||||
}
|
||||
|
||||
bool hasKeyframeCallback() const {
|
||||
return properties.has(FramePropertyKey::SetFlag);
|
||||
}
|
||||
|
||||
int getKeyframeIndex() const {
|
||||
return properties.getOr<int>(FramePropertyKey::SetFlag, -1);
|
||||
}
|
||||
|
||||
Vec2 getEffectiveScale() const {
|
||||
return properties.getOr<Vec2>(FramePropertyKey::ImageRate, Vec2::One());
|
||||
}
|
||||
|
||||
float getEffectiveRotation() const {
|
||||
return properties.getOr<float>(FramePropertyKey::ImageRotate, 0.0f);
|
||||
}
|
||||
|
||||
Color getEffectiveColor() const {
|
||||
return properties.getOr<Color>(FramePropertyKey::ColorTint, Colors::White);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/animation/animation_controller.h>
|
||||
#include <extra2d/animation/animation_event.h>
|
||||
#include <extra2d/animation/frame_renderer.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// AnimationNode - 动画节点(继承 Node)
|
||||
// 使用 FrameRenderer 单渲染器策略,不依赖 Sprite 基类
|
||||
// 适用于需要独立渲染控制的动画(如特效、复合动画图层)
|
||||
// ============================================================================
|
||||
class AnimationNode : public Node {
|
||||
public:
|
||||
AnimationNode();
|
||||
~AnimationNode() override = default;
|
||||
|
||||
// ------ 静态工厂(Cocos 风格)------
|
||||
static Ptr<AnimationNode> create();
|
||||
static Ptr<AnimationNode> create(Ptr<AnimationClip> clip);
|
||||
static Ptr<AnimationNode> create(const std::string &aniFilePath);
|
||||
|
||||
// ------ 动画数据 ------
|
||||
void setClip(Ptr<AnimationClip> clip);
|
||||
Ptr<AnimationClip> getClip() const;
|
||||
bool loadFromFile(const std::string &aniFilePath);
|
||||
|
||||
// ------ 播放控制 ------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
bool isStopped() const;
|
||||
|
||||
void setPlaybackSpeed(float speed);
|
||||
float getPlaybackSpeed() const;
|
||||
void setLooping(bool loop);
|
||||
bool isLooping() const;
|
||||
|
||||
// ------ 帧控制 ------
|
||||
void setFrameIndex(size_t index);
|
||||
size_t getCurrentFrameIndex() const;
|
||||
size_t getTotalFrames() const;
|
||||
|
||||
// ------ 事件回调 ------
|
||||
void setKeyframeCallback(KeyframeHitCallback callback);
|
||||
void setCompletionCallback(AnimationCompleteCallback callback);
|
||||
void
|
||||
setFrameChangeCallback(AnimationController::FrameChangeCallback callback);
|
||||
void addEventListener(AnimationEventCallback callback);
|
||||
|
||||
// ------ 视觉属性 ------
|
||||
void setTintColor(const Color &color);
|
||||
Color getTintColor() const { return tintColor_; }
|
||||
void setFlipX(bool flip) { flipX_ = flip; }
|
||||
void setFlipY(bool flip) { flipY_ = flip; }
|
||||
bool isFlipX() const { return flipX_; }
|
||||
bool isFlipY() const { return flipY_; }
|
||||
|
||||
// ------ 自动播放 ------
|
||||
void setAutoPlay(bool autoPlay) { autoPlay_ = autoPlay; }
|
||||
bool isAutoPlay() const { return autoPlay_; }
|
||||
|
||||
// ------ 碰撞盒访问 ------
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentDamageBoxes() const;
|
||||
const std::vector<std::array<int32_t, 6>> &getCurrentAttackBoxes() const;
|
||||
|
||||
// ------ 查询 ------
|
||||
Size getMaxFrameSize() const;
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
// ------ 直接访问 ------
|
||||
AnimationController &getController() { return controller_; }
|
||||
const AnimationController &getController() const { return controller_; }
|
||||
FrameRenderer &getFrameRenderer() { return frameRenderer_; }
|
||||
const FrameRenderer &getFrameRenderer() const { return frameRenderer_; }
|
||||
|
||||
protected:
|
||||
void onUpdate(float dt) override;
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
|
||||
private:
|
||||
AnimationController controller_;
|
||||
FrameRenderer frameRenderer_;
|
||||
Color tintColor_ = Colors::White;
|
||||
bool flipX_ = false;
|
||||
bool flipY_ = false;
|
||||
bool autoPlay_ = false;
|
||||
std::vector<AnimationEventCallback> eventListeners_;
|
||||
|
||||
static const std::vector<std::array<int32_t, 6>> emptyBoxes_;
|
||||
|
||||
void setupControllerCallbacks();
|
||||
void dispatchEvent(const AnimationEvent &event);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/als_parser.h>
|
||||
#include <extra2d/animation/animation_event.h>
|
||||
#include <extra2d/animation/animation_node.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// CompositeAnimation - ALS 多层复合动画节点
|
||||
// 管理多个 AnimationNode 图层,统一控制播放
|
||||
// 对应 DNF 的 ALS 格式(多层动画叠加)
|
||||
// ============================================================================
|
||||
class CompositeAnimation : public Node {
|
||||
public:
|
||||
CompositeAnimation() = default;
|
||||
~CompositeAnimation() override = default;
|
||||
|
||||
// ------ 静态工厂 ------
|
||||
static Ptr<CompositeAnimation> create();
|
||||
static Ptr<CompositeAnimation> create(const std::string &alsFilePath);
|
||||
|
||||
// ------ 加载 ------
|
||||
bool loadFromFile(const std::string &alsFilePath);
|
||||
|
||||
// ------ 图层管理 ------
|
||||
void addLayer(Ptr<AnimationNode> node, int zOrder = 0);
|
||||
void removeLayer(size_t index);
|
||||
Ptr<AnimationNode> getLayer(size_t index) const;
|
||||
Ptr<AnimationNode> getMainLayer() const;
|
||||
size_t getLayerCount() const;
|
||||
|
||||
// ------ 统一播放控制 ------
|
||||
void play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
void reset();
|
||||
void setPlaybackSpeed(float speed);
|
||||
void setLooping(bool loop);
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isStopped() const;
|
||||
|
||||
// ------ 事件回调(绑定到主图层)------
|
||||
void setKeyframeCallback(KeyframeHitCallback callback);
|
||||
void setCompletionCallback(AnimationCompleteCallback callback);
|
||||
void addEventListener(AnimationEventCallback callback);
|
||||
|
||||
// ------ 视觉属性(应用到所有图层)------
|
||||
void setTintColor(const Color &color);
|
||||
void setFlipX(bool flip);
|
||||
void setFlipY(bool flip);
|
||||
|
||||
private:
|
||||
struct LayerEntry {
|
||||
Ptr<AnimationNode> node;
|
||||
int zOrder = 0;
|
||||
};
|
||||
std::vector<LayerEntry> layers_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <any>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 帧属性键 - 强类型枚举替代原始 ANI 的字符串键
|
||||
// ============================================================================
|
||||
enum class FramePropertyKey : uint32 {
|
||||
// 事件触发
|
||||
SetFlag = 0x0001, // int: 关键帧回调索引
|
||||
PlaySound = 0x0002, // string: 音效路径
|
||||
|
||||
// 变换属性
|
||||
ImageRate = 0x0010, // Vec2: 缩放比例
|
||||
ImageRotate = 0x0011, // float: 旋转角度(度)
|
||||
ImageOffset = 0x0012, // Vec2: 额外位置偏移
|
||||
|
||||
// 视觉效果
|
||||
BlendLinearDodge = 0x0020, // bool: 线性减淡
|
||||
BlendAdditive = 0x0021, // bool: 加法混合
|
||||
ColorTint = 0x0022, // Color: RGBA 颜色
|
||||
|
||||
// 控制标记
|
||||
Interpolation = 0x0030, // bool: 启用到下一帧的插值
|
||||
Loop = 0x0031, // bool: 全局循环标记
|
||||
|
||||
// DNF ANI 扩展属性
|
||||
DamageType = 0x0040, // int: 伤害类型 (0=Normal, 1=SuperArmor, 2=Unbreakable)
|
||||
Shadow = 0x0041, // bool: 阴影
|
||||
FlipType = 0x0042, // int: 翻转类型 (1=Horizon, 2=Vertical, 3=All)
|
||||
Coord = 0x0043, // int: 坐标系
|
||||
LoopStart = 0x0044, // bool: 循环起始标记
|
||||
LoopEnd = 0x0045, // int: 循环结束帧数
|
||||
GraphicEffect = 0x0046, // int: 图形特效类型
|
||||
ClipRegion = 0x0047, // vector<int>: 裁剪区域 [4个int16]
|
||||
|
||||
// 用户自定义扩展区间 (0x1000+)
|
||||
UserDefined = 0x1000,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 帧属性值 - variant 多态值(优化版本)
|
||||
// 使用紧凑存储,常用小类型直接内联,大类型使用索引引用
|
||||
// ============================================================================
|
||||
|
||||
// 前向声明
|
||||
struct FramePropertyValue;
|
||||
|
||||
// 属性存储类型枚举
|
||||
enum class PropertyValueType : uint8_t {
|
||||
Empty = 0,
|
||||
Bool = 1,
|
||||
Int = 2,
|
||||
Float = 3,
|
||||
Vec2 = 4,
|
||||
Color = 5,
|
||||
String = 6, // 字符串使用索引引用
|
||||
IntVector = 7, // vector<int> 使用索引引用
|
||||
};
|
||||
|
||||
// 紧凑的属性值结构(16字节)
|
||||
struct FramePropertyValue {
|
||||
PropertyValueType type = PropertyValueType::Empty;
|
||||
uint8_t padding[3] = {0};
|
||||
|
||||
// 使用结构体包装非平凡类型,使其可以在union中使用
|
||||
struct Vec2Storage {
|
||||
float x, y;
|
||||
Vec2Storage() = default;
|
||||
Vec2Storage(const Vec2& v) : x(v.x), y(v.y) {}
|
||||
operator Vec2() const { return Vec2(x, y); }
|
||||
};
|
||||
|
||||
struct ColorStorage {
|
||||
float r, g, b, a;
|
||||
ColorStorage() = default;
|
||||
ColorStorage(const Color& c) : r(c.r), g(c.g), b(c.b), a(c.a) {}
|
||||
operator Color() const { return Color(r, g, b, a); }
|
||||
};
|
||||
|
||||
union Data {
|
||||
bool boolValue;
|
||||
int intValue;
|
||||
float floatValue;
|
||||
Vec2Storage vec2Value;
|
||||
ColorStorage colorValue;
|
||||
uint32_t stringIndex; // 字符串池索引
|
||||
uint32_t vectorIndex; // vector池索引
|
||||
|
||||
Data() : intValue(0) {} // 默认构造函数
|
||||
~Data() {} // 析构函数
|
||||
} data;
|
||||
|
||||
FramePropertyValue() : type(PropertyValueType::Empty) {}
|
||||
explicit FramePropertyValue(bool v) : type(PropertyValueType::Bool) { data.boolValue = v; }
|
||||
explicit FramePropertyValue(int v) : type(PropertyValueType::Int) { data.intValue = v; }
|
||||
explicit FramePropertyValue(float v) : type(PropertyValueType::Float) { data.floatValue = v; }
|
||||
explicit FramePropertyValue(const Vec2& v) : type(PropertyValueType::Vec2) { data.vec2Value = v; }
|
||||
explicit FramePropertyValue(const Color& v) : type(PropertyValueType::Color) { data.colorValue = v; }
|
||||
|
||||
bool isInline() const {
|
||||
return type <= PropertyValueType::Color;
|
||||
}
|
||||
|
||||
bool isString() const { return type == PropertyValueType::String; }
|
||||
bool isIntVector() const { return type == PropertyValueType::IntVector; }
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// FramePropertyKey 的 hash 支持
|
||||
// ============================================================================
|
||||
struct FramePropertyKeyHash {
|
||||
size_t operator()(FramePropertyKey key) const noexcept {
|
||||
return std::hash<uint32>{}(static_cast<uint32>(key));
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// FramePropertySet - 单帧属性集合(优化版本)
|
||||
// 使用紧凑存储和线性探测哈希表,提高缓存命中率
|
||||
// ============================================================================
|
||||
class FramePropertySet {
|
||||
public:
|
||||
FramePropertySet() = default;
|
||||
|
||||
// ------ 设置属性 ------
|
||||
void set(FramePropertyKey key, FramePropertyValue value);
|
||||
void set(FramePropertyKey key, bool value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, int value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, float value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, const Vec2& value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, const Color& value) { set(key, FramePropertyValue(value)); }
|
||||
void set(FramePropertyKey key, const std::string& value);
|
||||
void set(FramePropertyKey key, const std::vector<int>& value);
|
||||
|
||||
void setCustom(const std::string &key, std::any value);
|
||||
|
||||
// ------ 类型安全获取 ------
|
||||
template <typename T> std::optional<T> get(FramePropertyKey key) const;
|
||||
|
||||
template <typename T>
|
||||
T getOr(FramePropertyKey key, const T &defaultValue) const {
|
||||
auto result = get<T>(key);
|
||||
return result.value_or(defaultValue);
|
||||
}
|
||||
|
||||
std::optional<std::any> getCustom(const std::string &key) const;
|
||||
|
||||
// ------ 查询 ------
|
||||
bool has(FramePropertyKey key) const;
|
||||
bool hasCustom(const std::string &key) const;
|
||||
bool empty() const { return properties_.empty() && customProperties_.empty(); }
|
||||
size_t count() const { return properties_.size() + customProperties_.size(); }
|
||||
|
||||
// ------ 移除 ------
|
||||
void remove(FramePropertyKey key);
|
||||
void removeCustom(const std::string &key);
|
||||
void clear();
|
||||
|
||||
// ------ 迭代 ------
|
||||
using PropertyMap = std::unordered_map<FramePropertyKey, FramePropertyValue,
|
||||
FramePropertyKeyHash>;
|
||||
const PropertyMap &properties() const { return properties_; }
|
||||
|
||||
// ------ 链式 API ------
|
||||
FramePropertySet &withSetFlag(int index);
|
||||
FramePropertySet &withPlaySound(const std::string &path);
|
||||
FramePropertySet &withImageRate(const Vec2 &scale);
|
||||
FramePropertySet &withImageRotate(float degrees);
|
||||
FramePropertySet &withColorTint(const Color &color);
|
||||
FramePropertySet &withInterpolation(bool enabled = true);
|
||||
FramePropertySet &withBlendLinearDodge(bool enabled = true);
|
||||
FramePropertySet &withLoop(bool enabled = true);
|
||||
|
||||
private:
|
||||
PropertyMap properties_;
|
||||
std::unordered_map<std::string, std::any> customProperties_;
|
||||
|
||||
// 字符串池和vector池,用于存储大对象
|
||||
mutable std::vector<std::string> stringPool_;
|
||||
mutable std::vector<std::vector<int>> vectorPool_;
|
||||
mutable uint32_t nextStringIndex_ = 0;
|
||||
mutable uint32_t nextVectorIndex_ = 0;
|
||||
|
||||
uint32_t allocateString(const std::string& str);
|
||||
uint32_t allocateVector(const std::vector<int>& vec);
|
||||
const std::string* getString(uint32_t index) const;
|
||||
const std::vector<int>* getVector(uint32_t index) const;
|
||||
};
|
||||
|
||||
// 模板特化声明
|
||||
template <> std::optional<bool> FramePropertySet::get<bool>(FramePropertyKey key) const;
|
||||
template <> std::optional<int> FramePropertySet::get<int>(FramePropertyKey key) const;
|
||||
template <> std::optional<float> FramePropertySet::get<float>(FramePropertyKey key) const;
|
||||
template <> std::optional<Vec2> FramePropertySet::get<Vec2>(FramePropertyKey key) const;
|
||||
template <> std::optional<Color> FramePropertySet::get<Color>(FramePropertyKey key) const;
|
||||
template <> std::optional<std::string> FramePropertySet::get<std::string>(FramePropertyKey key) const;
|
||||
template <> std::optional<std::vector<int>> FramePropertySet::get<std::vector<int>>(FramePropertyKey key) const;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/animation/interpolation_engine.h>
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <extra2d/animation/sprite_frame_cache.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// FrameRenderer - 帧渲染器
|
||||
// 单渲染器 + SpriteFrame 引用策略,替代 N帧=N个Sprite 的旧设计
|
||||
// 负责预加载帧的 SpriteFrame、渲染当前帧、处理混合模式
|
||||
// ============================================================================
|
||||
class FrameRenderer {
|
||||
public:
|
||||
FrameRenderer() = default;
|
||||
|
||||
// ------ 预加载 ------
|
||||
// 解析所有帧的 SpriteFrame(通过 SpriteFrameCache)
|
||||
bool preloadFrames(const std::vector<AnimationFrame> &frames);
|
||||
void releaseFrames();
|
||||
|
||||
// ------ 渲染当前帧 ------
|
||||
void renderFrame(RenderBackend &renderer, const AnimationFrame &frame,
|
||||
size_t frameIndex, const Vec2 &position, float nodeOpacity,
|
||||
const Color &tintColor, bool flipX, bool flipY);
|
||||
|
||||
// ------ 渲染插值帧 ------
|
||||
void renderInterpolated(RenderBackend &renderer,
|
||||
const AnimationFrame &fromFrame, size_t fromIndex,
|
||||
const InterpolatedProperties &props,
|
||||
const Vec2 &position, float nodeOpacity,
|
||||
const Color &tintColor, bool flipX, bool flipY);
|
||||
|
||||
// ------ 混合模式映射 ------
|
||||
static BlendMode mapBlendMode(const FramePropertySet &props);
|
||||
|
||||
// ------ 查询 ------
|
||||
Ptr<SpriteFrame> getSpriteFrame(size_t frameIndex) const;
|
||||
Size getMaxFrameSize() const { return maxFrameSize_; }
|
||||
bool isLoaded() const { return !spriteFrames_.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<Ptr<SpriteFrame>> spriteFrames_;
|
||||
Size maxFrameSize_;
|
||||
|
||||
void drawSpriteFrame(RenderBackend &renderer, Ptr<SpriteFrame> sf,
|
||||
const Vec2 &position, const Vec2 &offset,
|
||||
const Vec2 &scale, float rotation, float opacity,
|
||||
const Color &tint, bool flipX, bool flipY,
|
||||
BlendMode blend);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 插值结果 - 两帧之间的插值后属性
|
||||
// ============================================================================
|
||||
struct InterpolatedProperties {
|
||||
Vec2 position;
|
||||
Vec2 scale = Vec2::One();
|
||||
float rotation = 0.0f;
|
||||
Color color = Colors::White;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 插值曲线类型
|
||||
// ============================================================================
|
||||
enum class InterpolationCurve : uint8 {
|
||||
Linear, // 线性(原始系统的 uniform velocity)
|
||||
EaseIn, // 缓入
|
||||
EaseOut, // 缓出
|
||||
EaseInOut, // 缓入缓出
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// InterpolationEngine - 帧间属性插值计算(静态方法,无状态)
|
||||
// 独立于 AnimationController,可复用于其他系统
|
||||
// ============================================================================
|
||||
class InterpolationEngine {
|
||||
public:
|
||||
/// 核心插值计算:根据 t 因子 (0~1) 计算两帧之间的插值属性
|
||||
static InterpolatedProperties
|
||||
interpolate(const AnimationFrame &from, const AnimationFrame &to, float t,
|
||||
InterpolationCurve curve = InterpolationCurve::Linear) {
|
||||
float curvedT = applyCurve(t, curve);
|
||||
|
||||
InterpolatedProperties result;
|
||||
result.position = lerpPosition(from, to, curvedT);
|
||||
result.scale = lerpScale(from, to, curvedT);
|
||||
result.rotation = lerpRotation(from, to, curvedT);
|
||||
result.color = lerpColor(from, to, curvedT);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 位置插值
|
||||
static Vec2 lerpPosition(const AnimationFrame &from, const AnimationFrame &to,
|
||||
float t) {
|
||||
return Vec2::lerp(from.offset, to.offset, t);
|
||||
}
|
||||
|
||||
/// 缩放插值
|
||||
static Vec2 lerpScale(const AnimationFrame &from, const AnimationFrame &to,
|
||||
float t) {
|
||||
Vec2 fromScale = from.getEffectiveScale();
|
||||
Vec2 toScale = to.getEffectiveScale();
|
||||
return Vec2::lerp(fromScale, toScale, t);
|
||||
}
|
||||
|
||||
/// 旋转插值
|
||||
static float lerpRotation(const AnimationFrame &from,
|
||||
const AnimationFrame &to, float t) {
|
||||
float fromRot = from.getEffectiveRotation();
|
||||
float toRot = to.getEffectiveRotation();
|
||||
return math::lerp(fromRot, toRot, t);
|
||||
}
|
||||
|
||||
/// 颜色插值
|
||||
static Color lerpColor(const AnimationFrame &from, const AnimationFrame &to,
|
||||
float t) {
|
||||
Color fromColor = from.getEffectiveColor();
|
||||
Color toColor = to.getEffectiveColor();
|
||||
return Color::lerp(fromColor, toColor, t);
|
||||
}
|
||||
|
||||
/// 应用曲线函数
|
||||
static float applyCurve(float t, InterpolationCurve curve) {
|
||||
t = math::clamp(t, 0.0f, 1.0f);
|
||||
|
||||
switch (curve) {
|
||||
case InterpolationCurve::Linear:
|
||||
return t;
|
||||
|
||||
case InterpolationCurve::EaseIn:
|
||||
return t * t;
|
||||
|
||||
case InterpolationCurve::EaseOut:
|
||||
return t * (2.0f - t);
|
||||
|
||||
case InterpolationCurve::EaseInOut:
|
||||
if (t < 0.5f)
|
||||
return 2.0f * t * t;
|
||||
else
|
||||
return -1.0f + (4.0f - 2.0f * t) * t;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// SpriteFrame - 精灵帧(纹理 + 区域 + 偏移的中间抽象)
|
||||
// 借鉴 Cocos2d-x SpriteFrame:解耦纹理物理存储与逻辑帧
|
||||
// 一个纹理图集可包含多个 SpriteFrame,减少纹理切换提升渲染性能
|
||||
// ============================================================================
|
||||
class SpriteFrame {
|
||||
public:
|
||||
SpriteFrame() = default;
|
||||
|
||||
SpriteFrame(Ptr<Texture> texture, const Rect &rect)
|
||||
: texture_(std::move(texture)), rect_(rect), originalSize_(rect.size) {}
|
||||
|
||||
SpriteFrame(Ptr<Texture> texture, const Rect &rect, const Vec2 &offset,
|
||||
const Size &originalSize)
|
||||
: texture_(std::move(texture)), rect_(rect), offset_(offset),
|
||||
originalSize_(originalSize) {}
|
||||
|
||||
// ------ 静态创建 ------
|
||||
static Ptr<SpriteFrame> create(Ptr<Texture> texture, const Rect &rect) {
|
||||
return makePtr<SpriteFrame>(std::move(texture), rect);
|
||||
}
|
||||
|
||||
static Ptr<SpriteFrame> create(Ptr<Texture> texture, const Rect &rect,
|
||||
const Vec2 &offset, const Size &originalSize) {
|
||||
return makePtr<SpriteFrame>(std::move(texture), rect, offset, originalSize);
|
||||
}
|
||||
|
||||
// ------ 纹理信息 ------
|
||||
void setTexture(Ptr<Texture> texture) { texture_ = std::move(texture); }
|
||||
Ptr<Texture> getTexture() const { return texture_; }
|
||||
|
||||
// ------ 矩形区域(在纹理图集中的位置)------
|
||||
void setRect(const Rect &rect) { rect_ = rect; }
|
||||
const Rect &getRect() const { return rect_; }
|
||||
|
||||
// ------ 偏移(图集打包时的裁剪偏移)------
|
||||
void setOffset(const Vec2 &offset) { offset_ = offset; }
|
||||
const Vec2 &getOffset() const { return offset_; }
|
||||
|
||||
// ------ 原始尺寸(裁剪前的完整尺寸)------
|
||||
void setOriginalSize(const Size &size) { originalSize_ = size; }
|
||||
const Size &getOriginalSize() const { return originalSize_; }
|
||||
|
||||
// ------ 旋转标志(图集工具可能旋转90度)------
|
||||
void setRotated(bool rotated) { rotated_ = rotated; }
|
||||
bool isRotated() const { return rotated_; }
|
||||
|
||||
// ------ 名称(用于缓存索引)------
|
||||
void setName(const std::string &name) { name_ = name; }
|
||||
const std::string &getName() const { return name_; }
|
||||
|
||||
// ------ 有效性检查 ------
|
||||
bool isValid() const { return texture_ != nullptr; }
|
||||
|
||||
private:
|
||||
Ptr<Texture> texture_;
|
||||
Rect rect_;
|
||||
Vec2 offset_;
|
||||
Size originalSize_;
|
||||
bool rotated_ = false;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/resource/resource_manager.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// SpriteFrameCache - 精灵帧全局缓存(借鉴 Cocos SpriteFrameCache)
|
||||
// 全局单例管理所有精灵帧,避免重复创建,支持图集自动切割
|
||||
// ============================================================================
|
||||
class SpriteFrameCache {
|
||||
public:
|
||||
static SpriteFrameCache &getInstance() {
|
||||
static SpriteFrameCache instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ------ 添加帧 ------
|
||||
|
||||
/// 添加单个精灵帧
|
||||
void addSpriteFrame(Ptr<SpriteFrame> frame, const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_[name] = std::move(frame);
|
||||
}
|
||||
|
||||
/// 从纹理和矩形区域创建并添加帧
|
||||
void addSpriteFrameFromTexture(Ptr<Texture> texture, const Rect &rect,
|
||||
const std::string &name) {
|
||||
auto frame = SpriteFrame::create(std::move(texture), rect);
|
||||
frame->setName(name);
|
||||
addSpriteFrame(std::move(frame), name);
|
||||
}
|
||||
|
||||
/// 从纹理图集批量切割添加(等宽等高网格)
|
||||
void addSpriteFramesFromGrid(const std::string &texturePath, int frameWidth,
|
||||
int frameHeight, int frameCount = -1,
|
||||
int spacing = 0, int margin = 0) {
|
||||
auto texture = loadTextureFromFile(texturePath);
|
||||
if (!texture)
|
||||
return;
|
||||
addSpriteFramesFromGrid(texture, texturePath, frameWidth, frameHeight,
|
||||
frameCount, spacing, margin);
|
||||
}
|
||||
|
||||
/// 从纹理对象批量切割添加(等宽等高网格,无需走 TexturePool)
|
||||
void addSpriteFramesFromGrid(Ptr<Texture> texture,
|
||||
const std::string &keyPrefix, int frameWidth,
|
||||
int frameHeight, int frameCount = -1,
|
||||
int spacing = 0, int margin = 0) {
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
int texW = texture->getWidth();
|
||||
int texH = texture->getHeight();
|
||||
int usableW = texW - 2 * margin;
|
||||
int usableH = texH - 2 * margin;
|
||||
int cols = (usableW + spacing) / (frameWidth + spacing);
|
||||
int rows = (usableH + spacing) / (frameHeight + spacing);
|
||||
int total = (frameCount > 0) ? frameCount : cols * rows;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (int i = 0; i < total; ++i) {
|
||||
int col = i % cols;
|
||||
int row = i / cols;
|
||||
if (row >= rows)
|
||||
break;
|
||||
|
||||
Rect rect(static_cast<float>(margin + col * (frameWidth + spacing)),
|
||||
static_cast<float>(margin + row * (frameHeight + spacing)),
|
||||
static_cast<float>(frameWidth),
|
||||
static_cast<float>(frameHeight));
|
||||
|
||||
std::string name = keyPrefix + "#" + std::to_string(i);
|
||||
auto frame = SpriteFrame::create(texture, rect);
|
||||
frame->setName(name);
|
||||
frames_[name] = std::move(frame);
|
||||
}
|
||||
}
|
||||
|
||||
// ------ 获取帧 ------
|
||||
|
||||
/// 按名称获取
|
||||
Ptr<SpriteFrame> getSpriteFrame(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = frames_.find(name);
|
||||
if (it != frames_.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// 通过路径+索引获取或创建(ANI 格式的定位方式)
|
||||
Ptr<SpriteFrame> getOrCreateFromFile(const std::string &texturePath,
|
||||
int index = 0) {
|
||||
std::string key = texturePath + "#" + std::to_string(index);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = frames_.find(key);
|
||||
if (it != frames_.end())
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// 缓存未命中,加载纹理并创建 SpriteFrame
|
||||
auto texture = loadTextureFromFile(texturePath);
|
||||
if (!texture)
|
||||
return nullptr;
|
||||
|
||||
// 默认整张纹理作为一帧(index=0),或用整张纹理
|
||||
Rect rect(0.0f, 0.0f, static_cast<float>(texture->getWidth()),
|
||||
static_cast<float>(texture->getHeight()));
|
||||
|
||||
auto frame = SpriteFrame::create(texture, rect);
|
||||
frame->setName(key);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_[key] = frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
// ------ 缓存管理 ------
|
||||
|
||||
bool has(const std::string &name) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return frames_.find(name) != frames_.end();
|
||||
}
|
||||
|
||||
void removeSpriteFrame(const std::string &name) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_.erase(name);
|
||||
}
|
||||
|
||||
/// 移除未被外部引用的精灵帧(use_count == 1 表示仅缓存自身持有)
|
||||
void removeUnusedSpriteFrames() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto it = frames_.begin(); it != frames_.end();) {
|
||||
if (it->second.use_count() == 1) {
|
||||
it = frames_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
frames_.clear();
|
||||
}
|
||||
|
||||
size_t count() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return frames_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
SpriteFrameCache() = default;
|
||||
~SpriteFrameCache() = default;
|
||||
SpriteFrameCache(const SpriteFrameCache &) = delete;
|
||||
SpriteFrameCache &operator=(const SpriteFrameCache &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载纹理(使用 ResourceManager 的 LRU 缓存)
|
||||
* @param filepath 纹理文件路径
|
||||
* @return 纹理对象,失败返回nullptr
|
||||
*/
|
||||
Ptr<Texture> loadTextureFromFile(const std::string &filepath) {
|
||||
// 使用 ResourceManager 的纹理缓存机制
|
||||
// 这样可以享受 LRU 缓存、自动清理和缓存统计等功能
|
||||
auto &resources = ResourceManager::getInstance();
|
||||
|
||||
// 先检查缓存中是否已有该纹理
|
||||
auto texture = resources.getTexture(filepath);
|
||||
if (texture) {
|
||||
E2D_TRACE("SpriteFrameCache: 使用缓存纹理: {}", filepath);
|
||||
return texture;
|
||||
}
|
||||
|
||||
// 缓存未命中,通过 ResourceManager 加载
|
||||
texture = resources.loadTexture(filepath);
|
||||
if (!texture) {
|
||||
E2D_ERROR("SpriteFrameCache: 加载纹理失败: {}", filepath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
E2D_TRACE("SpriteFrameCache: 加载新纹理: {}", filepath);
|
||||
return texture;
|
||||
}
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, Ptr<SpriteFrame>> frames_;
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_SPRITE_FRAME_CACHE() ::extra2d::SpriteFrameCache::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/string.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/platform/window.h>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Input;
|
||||
class AudioEngine;
|
||||
class SceneManager;
|
||||
class ResourceManager;
|
||||
class TimerManager;
|
||||
class EventQueue;
|
||||
class EventDispatcher;
|
||||
class Camera;
|
||||
|
||||
// ============================================================================
|
||||
// Application 配置
|
||||
// ============================================================================
|
||||
|
||||
enum class PlatformType {
|
||||
Auto = 0,
|
||||
PC,
|
||||
Switch
|
||||
};
|
||||
|
||||
struct AppConfig {
|
||||
std::string title = "Easy2D Application";
|
||||
int width = 800;
|
||||
int height = 600;
|
||||
bool fullscreen = false;
|
||||
bool resizable = true;
|
||||
bool vsync = true;
|
||||
int fpsLimit = 0;
|
||||
BackendType renderBackend = BackendType::OpenGL;
|
||||
int msaaSamples = 0;
|
||||
PlatformType platform = PlatformType::Auto;
|
||||
// 窗口高级配置
|
||||
bool enableCursors = true; // 是否启用光标
|
||||
bool enableDpiScale = false; // 是否启用DPI缩放
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Application 单例 - 应用主控
|
||||
// ============================================================================
|
||||
class Application {
|
||||
public:
|
||||
// Meyer's 单例
|
||||
static Application &instance();
|
||||
|
||||
// 禁止拷贝
|
||||
Application(const Application &) = delete;
|
||||
Application &operator=(const Application &) = delete;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
bool init(const AppConfig &config);
|
||||
void shutdown();
|
||||
void run();
|
||||
void quit();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态控制
|
||||
// ------------------------------------------------------------------------
|
||||
void pause();
|
||||
void resume();
|
||||
bool isPaused() const { return paused_; }
|
||||
bool isRunning() const { return running_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 子系统访问
|
||||
// ------------------------------------------------------------------------
|
||||
Window &window() { return *window_; }
|
||||
RenderBackend &renderer() { return *renderer_; }
|
||||
Input &input();
|
||||
AudioEngine &audio();
|
||||
SceneManager &scenes();
|
||||
ResourceManager &resources();
|
||||
TimerManager &timers();
|
||||
EventQueue &eventQueue();
|
||||
EventDispatcher &eventDispatcher();
|
||||
Camera &camera();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 便捷方法
|
||||
// ------------------------------------------------------------------------
|
||||
void enterScene(Ptr<class Scene> scene);
|
||||
void enterScene(Ptr<class Scene> scene, Ptr<class TransitionScene> transitionScene);
|
||||
|
||||
float deltaTime() const { return deltaTime_; }
|
||||
float totalTime() const { return totalTime_; }
|
||||
int fps() const { return currentFps_; }
|
||||
|
||||
// 获取配置
|
||||
const AppConfig &getConfig() const { return config_; }
|
||||
|
||||
private:
|
||||
Application() = default;
|
||||
~Application();
|
||||
|
||||
void mainLoop();
|
||||
void update();
|
||||
void render();
|
||||
void prewarmObjectPools();
|
||||
|
||||
// 配置
|
||||
AppConfig config_;
|
||||
|
||||
// 子系统
|
||||
UniquePtr<Window> window_;
|
||||
UniquePtr<RenderBackend> renderer_;
|
||||
UniquePtr<SceneManager> sceneManager_;
|
||||
UniquePtr<ResourceManager> resourceManager_;
|
||||
UniquePtr<TimerManager> timerManager_;
|
||||
UniquePtr<EventQueue> eventQueue_;
|
||||
UniquePtr<EventDispatcher> eventDispatcher_;
|
||||
UniquePtr<Camera> camera_;
|
||||
|
||||
// 状态
|
||||
bool initialized_ = false;
|
||||
bool running_ = false;
|
||||
bool paused_ = false;
|
||||
bool shouldQuit_ = false;
|
||||
|
||||
// 时间
|
||||
float deltaTime_ = 0.0f;
|
||||
float totalTime_ = 0.0f;
|
||||
double lastFrameTime_ = 0.0;
|
||||
int frameCount_ = 0;
|
||||
float fpsTimer_ = 0.0f;
|
||||
int currentFps_ = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Sound;
|
||||
|
||||
class AudioEngine {
|
||||
public:
|
||||
static AudioEngine& getInstance();
|
||||
|
||||
AudioEngine(const AudioEngine&) = delete;
|
||||
AudioEngine& operator=(const AudioEngine&) = delete;
|
||||
AudioEngine(AudioEngine&&) = delete;
|
||||
AudioEngine& operator=(AudioEngine&&) = delete;
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
std::shared_ptr<Sound> loadSound(const std::string& filePath);
|
||||
std::shared_ptr<Sound> loadSound(const std::string& name, const std::string& filePath);
|
||||
|
||||
std::shared_ptr<Sound> getSound(const std::string& name);
|
||||
void unloadSound(const std::string& name);
|
||||
void unloadAllSounds();
|
||||
|
||||
void setMasterVolume(float volume);
|
||||
float getMasterVolume() const;
|
||||
|
||||
void pauseAll();
|
||||
void resumeAll();
|
||||
void stopAll();
|
||||
|
||||
private:
|
||||
AudioEngine() = default;
|
||||
~AudioEngine();
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Sound>> sounds_;
|
||||
float masterVolume_ = 1.0f;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
struct Mix_Chunk;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class AudioEngine;
|
||||
|
||||
class Sound {
|
||||
public:
|
||||
~Sound();
|
||||
|
||||
Sound(const Sound&) = delete;
|
||||
Sound& operator=(const Sound&) = delete;
|
||||
|
||||
bool play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
|
||||
void setVolume(float volume);
|
||||
float getVolume() const { return volume_; }
|
||||
|
||||
void setLooping(bool looping);
|
||||
bool isLooping() const { return looping_; }
|
||||
|
||||
void setPitch(float pitch);
|
||||
float getPitch() const { return pitch_; }
|
||||
|
||||
float getDuration() const;
|
||||
float getCursor() const;
|
||||
void setCursor(float seconds);
|
||||
|
||||
const std::string& getFilePath() const { return filePath_; }
|
||||
const std::string& getName() const { return name_; }
|
||||
|
||||
private:
|
||||
friend class AudioEngine;
|
||||
|
||||
Sound(const std::string& name, const std::string& filePath, Mix_Chunk* chunk);
|
||||
|
||||
std::string name_;
|
||||
std::string filePath_;
|
||||
Mix_Chunk* chunk_ = nullptr;
|
||||
int channel_ = -1; // SDL_mixer 分配的通道,-1 表示未播放
|
||||
float volume_ = 1.0f;
|
||||
float pitch_ = 1.0f;
|
||||
bool looping_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
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;
|
||||
float g = 0.0f;
|
||||
float b = 0.0f;
|
||||
float a = 1.0f;
|
||||
|
||||
constexpr Color() = default;
|
||||
|
||||
constexpr Color(float r, float g, float b, float a = 1.0f)
|
||||
: r(r), g(g), b(b), a(a) {}
|
||||
|
||||
/// 从 0xRRGGBB 整数构造
|
||||
constexpr explicit Color(uint32_t rgb, float a = 1.0f)
|
||||
: r(static_cast<float>((rgb >> 16) & 0xFF) / 255.0f),
|
||||
g(static_cast<float>((rgb >> 8) & 0xFF) / 255.0f),
|
||||
b(static_cast<float>((rgb) & 0xFF) / 255.0f), a(a) {}
|
||||
|
||||
/// 从 0-255 整数构造
|
||||
static constexpr Color fromRGBA(uint8_t r, uint8_t g, uint8_t b,
|
||||
uint8_t a = 255) {
|
||||
return Color(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
|
||||
}
|
||||
|
||||
/// 转换为 glm::vec4
|
||||
glm::vec4 toVec4() const { return {r, g, b, a}; }
|
||||
|
||||
/// 线性插值
|
||||
static Color lerp(const Color &a, const Color &b, float t) {
|
||||
t = std::clamp(t, 0.0f, 1.0f);
|
||||
return Color(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t,
|
||||
a.b + (b.b - a.b) * t, a.a + (b.a - a.a) * t);
|
||||
}
|
||||
|
||||
bool operator==(const Color &other) const {
|
||||
return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||
}
|
||||
|
||||
bool operator!=(const Color &other) const { return !(*this == other); }
|
||||
|
||||
// 算术运算符
|
||||
Color operator+(const Color &other) const {
|
||||
return Color(r + other.r, g + other.g, b + other.b, a + other.a);
|
||||
}
|
||||
|
||||
Color operator-(const Color &other) const {
|
||||
return Color(r - other.r, g - other.g, b - other.b, a - other.a);
|
||||
}
|
||||
|
||||
Color operator*(float scalar) const {
|
||||
return Color(r * scalar, g * scalar, b * scalar, a * scalar);
|
||||
}
|
||||
|
||||
Color operator/(float scalar) const {
|
||||
return Color(r / scalar, g / scalar, b / scalar, a / scalar);
|
||||
}
|
||||
|
||||
Color &operator+=(const Color &other) {
|
||||
r += other.r;
|
||||
g += other.g;
|
||||
b += other.b;
|
||||
a += other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator-=(const Color &other) {
|
||||
r -= other.r;
|
||||
g -= other.g;
|
||||
b -= other.b;
|
||||
a -= other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator*=(float scalar) {
|
||||
r *= scalar;
|
||||
g *= scalar;
|
||||
b *= scalar;
|
||||
a *= scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Color &operator/=(float scalar) {
|
||||
r /= scalar;
|
||||
g /= scalar;
|
||||
b /= scalar;
|
||||
a /= scalar;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// 命名颜色常量
|
||||
namespace Colors {
|
||||
inline constexpr Color White{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
inline constexpr Color Black{0.0f, 0.0f, 0.0f, 1.0f};
|
||||
inline constexpr Color Red{1.0f, 0.0f, 0.0f, 1.0f};
|
||||
inline constexpr Color Green{0.0f, 1.0f, 0.0f, 1.0f};
|
||||
inline constexpr Color Blue{0.0f, 0.0f, 1.0f, 1.0f};
|
||||
inline constexpr Color Yellow{1.0f, 1.0f, 0.0f, 1.0f};
|
||||
inline constexpr Color Cyan{0.0f, 1.0f, 1.0f, 1.0f};
|
||||
inline constexpr Color Magenta{1.0f, 0.0f, 1.0f, 1.0f};
|
||||
inline constexpr Color Orange{1.0f, 0.647f, 0.0f, 1.0f};
|
||||
inline constexpr Color Purple{0.502f, 0.0f, 0.502f, 1.0f};
|
||||
inline constexpr Color Pink{1.0f, 0.753f, 0.796f, 1.0f};
|
||||
inline constexpr Color Gray{0.502f, 0.502f, 0.502f, 1.0f};
|
||||
inline constexpr Color LightGray{0.827f, 0.827f, 0.827f, 1.0f};
|
||||
inline constexpr Color DarkGray{0.412f, 0.412f, 0.412f, 1.0f};
|
||||
inline constexpr Color Brown{0.647f, 0.165f, 0.165f, 1.0f};
|
||||
inline constexpr Color Gold{1.0f, 0.843f, 0.0f, 1.0f};
|
||||
inline constexpr Color Silver{0.753f, 0.753f, 0.753f, 1.0f};
|
||||
inline constexpr Color SkyBlue{0.529f, 0.808f, 0.922f, 1.0f};
|
||||
inline constexpr Color LimeGreen{0.196f, 0.804f, 0.196f, 1.0f};
|
||||
inline constexpr Color Coral{1.0f, 0.498f, 0.314f, 1.0f};
|
||||
inline constexpr Color Transparent{0.0f, 0.0f, 0.0f, 0.0f};
|
||||
} // namespace Colors
|
||||
|
||||
// 为了向后兼容,在 Color 结构体内提供静态引用
|
||||
struct ColorConstants {
|
||||
static const Color &White;
|
||||
static const Color &Black;
|
||||
static const Color &Red;
|
||||
static const Color &Green;
|
||||
static const Color &Blue;
|
||||
static const Color &Yellow;
|
||||
static const Color &Cyan;
|
||||
static const Color &Magenta;
|
||||
static const Color &Transparent;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 常量
|
||||
// ---------------------------------------------------------------------------
|
||||
constexpr float PI_F = 3.14159265358979323846f;
|
||||
constexpr float DEG_TO_RAD = PI_F / 180.0f;
|
||||
constexpr float RAD_TO_DEG = 180.0f / PI_F;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2D 向量
|
||||
// ---------------------------------------------------------------------------
|
||||
struct Vec2 {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
|
||||
constexpr Vec2() = default;
|
||||
constexpr Vec2(float x, float y) : x(x), y(y) {}
|
||||
explicit Vec2(const glm::vec2 &v) : x(v.x), y(v.y) {}
|
||||
|
||||
glm::vec2 toGlm() const { return {x, y}; }
|
||||
static Vec2 fromGlm(const glm::vec2 &v) { return {v.x, v.y}; }
|
||||
|
||||
// 基础运算
|
||||
Vec2 operator+(const Vec2 &v) const { return {x + v.x, y + v.y}; }
|
||||
Vec2 operator-(const Vec2 &v) const { return {x - v.x, y - v.y}; }
|
||||
Vec2 operator*(float s) const { return {x * s, y * s}; }
|
||||
Vec2 operator/(float s) const { return {x / s, y / s}; }
|
||||
Vec2 operator-() const { return {-x, -y}; }
|
||||
|
||||
Vec2 &operator+=(const Vec2 &v) {
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
return *this;
|
||||
}
|
||||
Vec2 &operator-=(const Vec2 &v) {
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
return *this;
|
||||
}
|
||||
Vec2 &operator*=(float s) {
|
||||
x *= s;
|
||||
y *= s;
|
||||
return *this;
|
||||
}
|
||||
Vec2 &operator/=(float s) {
|
||||
x /= s;
|
||||
y /= s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Vec2 &v) const { return x == v.x && y == v.y; }
|
||||
bool operator!=(const Vec2 &v) const { return !(*this == v); }
|
||||
|
||||
// 向量运算
|
||||
float length() const { return std::sqrt(x * x + y * y); }
|
||||
float lengthSquared() const { return x * x + y * y; }
|
||||
|
||||
Vec2 normalized() const {
|
||||
float len = length();
|
||||
if (len > 0.0f)
|
||||
return {x / len, y / len};
|
||||
return {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
float dot(const Vec2 &v) const { return x * v.x + y * v.y; }
|
||||
float cross(const Vec2 &v) const { return x * v.y - y * v.x; }
|
||||
|
||||
float distance(const Vec2 &v) const { return (*this - v).length(); }
|
||||
float angle() const { return std::atan2(y, x) * RAD_TO_DEG; }
|
||||
|
||||
static Vec2 lerp(const Vec2 &a, const Vec2 &b, float t) {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
static constexpr Vec2 Zero() { return {0.0f, 0.0f}; }
|
||||
static constexpr Vec2 One() { return {1.0f, 1.0f}; }
|
||||
static constexpr Vec2 UnitX() { return {1.0f, 0.0f}; }
|
||||
static constexpr Vec2 UnitY() { return {0.0f, 1.0f}; }
|
||||
};
|
||||
|
||||
inline Vec2 operator*(float s, const Vec2 &v) { return v * s; }
|
||||
|
||||
using Point = Vec2;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3D 向量 (用于3D动作)
|
||||
// ---------------------------------------------------------------------------
|
||||
struct Vec3 {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
|
||||
constexpr Vec3() = default;
|
||||
constexpr Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
|
||||
explicit Vec3(const glm::vec3 &v) : x(v.x), y(v.y), z(v.z) {}
|
||||
|
||||
glm::vec3 toGlm() const { return {x, y, z}; }
|
||||
static Vec3 fromGlm(const glm::vec3 &v) { return {v.x, v.y, v.z}; }
|
||||
|
||||
Vec3 operator+(const Vec3 &v) const { return {x + v.x, y + v.y, z + v.z}; }
|
||||
Vec3 operator-(const Vec3 &v) const { return {x - v.x, y - v.y, z - v.z}; }
|
||||
Vec3 operator*(float s) const { return {x * s, y * s, z * s}; }
|
||||
Vec3 operator/(float s) const { return {x / s, y / s, z / s}; }
|
||||
Vec3 operator-() const { return {-x, -y, -z}; }
|
||||
|
||||
Vec3 &operator+=(const Vec3 &v) {
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
z += v.z;
|
||||
return *this;
|
||||
}
|
||||
Vec3 &operator-=(const Vec3 &v) {
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
z -= v.z;
|
||||
return *this;
|
||||
}
|
||||
Vec3 &operator*=(float s) {
|
||||
x *= s;
|
||||
y *= s;
|
||||
z *= s;
|
||||
return *this;
|
||||
}
|
||||
Vec3 &operator/=(float s) {
|
||||
x /= s;
|
||||
y /= s;
|
||||
z /= s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Vec3 &v) const {
|
||||
return x == v.x && y == v.y && z == v.z;
|
||||
}
|
||||
bool operator!=(const Vec3 &v) const { return !(*this == v); }
|
||||
|
||||
float length() const { return std::sqrt(x * x + y * y + z * z); }
|
||||
float lengthSquared() const { return x * x + y * y + z * z; }
|
||||
|
||||
Vec3 normalized() const {
|
||||
float len = length();
|
||||
if (len > 0.0f)
|
||||
return {x / len, y / len, z / len};
|
||||
return {0.0f, 0.0f, 0.0f};
|
||||
}
|
||||
|
||||
float dot(const Vec3 &v) const { return x * v.x + y * v.y + z * v.z; }
|
||||
|
||||
static Vec3 lerp(const Vec3 &a, const Vec3 &b, float t) {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
static constexpr Vec3 Zero() { return {0.0f, 0.0f, 0.0f}; }
|
||||
static constexpr Vec3 One() { return {1.0f, 1.0f, 1.0f}; }
|
||||
};
|
||||
|
||||
inline Vec3 operator*(float s, const Vec3 &v) { return v * s; }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2D 尺寸
|
||||
// ---------------------------------------------------------------------------
|
||||
struct Size {
|
||||
float width = 0.0f;
|
||||
float height = 0.0f;
|
||||
|
||||
constexpr Size() = default;
|
||||
constexpr Size(float w, float h) : width(w), height(h) {}
|
||||
|
||||
bool operator==(const Size &s) const {
|
||||
return width == s.width && height == s.height;
|
||||
}
|
||||
bool operator!=(const Size &s) const { return !(*this == s); }
|
||||
|
||||
float area() const { return width * height; }
|
||||
bool empty() const { return width <= 0.0f || height <= 0.0f; }
|
||||
|
||||
static constexpr Size Zero() { return {0.0f, 0.0f}; }
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2D 矩形
|
||||
// ---------------------------------------------------------------------------
|
||||
struct Rect {
|
||||
Point origin;
|
||||
Size size;
|
||||
|
||||
constexpr Rect() = default;
|
||||
constexpr Rect(float x, float y, float w, float h)
|
||||
: origin(x, y), size(w, h) {}
|
||||
constexpr Rect(const Point &o, const Size &s) : origin(o), size(s) {}
|
||||
|
||||
float left() const { return origin.x; }
|
||||
float top() const { return origin.y; }
|
||||
float right() const { return origin.x + size.width; }
|
||||
float bottom() const { return origin.y + size.height; }
|
||||
float width() const { return size.width; }
|
||||
float height() const { return size.height; }
|
||||
Point center() const {
|
||||
return {origin.x + size.width * 0.5f, origin.y + size.height * 0.5f};
|
||||
}
|
||||
|
||||
bool empty() const { return size.empty(); }
|
||||
|
||||
bool containsPoint(const Point &p) const {
|
||||
return p.x >= left() && p.x <= right() && p.y >= top() && p.y <= bottom();
|
||||
}
|
||||
|
||||
bool contains(const Rect &r) const {
|
||||
return r.left() >= left() && r.right() <= right() && r.top() >= top() &&
|
||||
r.bottom() <= bottom();
|
||||
}
|
||||
|
||||
bool intersects(const Rect &r) const {
|
||||
return !(left() > r.right() || right() < r.left() || top() > r.bottom() ||
|
||||
bottom() < r.top());
|
||||
}
|
||||
|
||||
Rect intersection(const Rect &r) const {
|
||||
float l = std::max(left(), r.left());
|
||||
float t = std::max(top(), r.top());
|
||||
float ri = std::min(right(), r.right());
|
||||
float b = std::min(bottom(), r.bottom());
|
||||
if (l < ri && t < b)
|
||||
return {l, t, ri - l, b - t};
|
||||
return {};
|
||||
}
|
||||
|
||||
Rect unionWith(const Rect &r) const {
|
||||
if (empty())
|
||||
return r;
|
||||
if (r.empty())
|
||||
return *this;
|
||||
float l = std::min(left(), r.left());
|
||||
float t = std::min(top(), r.top());
|
||||
float ri = std::max(right(), r.right());
|
||||
float b = std::max(bottom(), r.bottom());
|
||||
return {l, t, ri - l, b - t};
|
||||
}
|
||||
|
||||
bool operator==(const Rect &r) const {
|
||||
return origin == r.origin && size == r.size;
|
||||
}
|
||||
bool operator!=(const Rect &r) const { return !(*this == r); }
|
||||
|
||||
static constexpr Rect Zero() { return {0, 0, 0, 0}; }
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2D 变换矩阵(基于 glm::mat4,兼容 OpenGL)
|
||||
// ---------------------------------------------------------------------------
|
||||
struct Transform2D {
|
||||
glm::mat4 matrix{1.0f}; // 单位矩阵
|
||||
|
||||
Transform2D() = default;
|
||||
explicit Transform2D(const glm::mat4 &m) : matrix(m) {}
|
||||
|
||||
static Transform2D identity() { return Transform2D{}; }
|
||||
|
||||
static Transform2D translation(float x, float y) {
|
||||
Transform2D t;
|
||||
t.matrix = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, 0.0f));
|
||||
return t;
|
||||
}
|
||||
|
||||
static Transform2D translation(const Vec2 &v) {
|
||||
return translation(v.x, v.y);
|
||||
}
|
||||
|
||||
static Transform2D rotation(float degrees) {
|
||||
Transform2D t;
|
||||
t.matrix = glm::rotate(glm::mat4(1.0f), degrees * DEG_TO_RAD,
|
||||
glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
return t;
|
||||
}
|
||||
|
||||
static Transform2D scaling(float sx, float sy) {
|
||||
Transform2D t;
|
||||
t.matrix = glm::scale(glm::mat4(1.0f), glm::vec3(sx, sy, 1.0f));
|
||||
return t;
|
||||
}
|
||||
|
||||
static Transform2D scaling(float s) { return scaling(s, s); }
|
||||
|
||||
static Transform2D skewing(float skewX, float skewY) {
|
||||
Transform2D t;
|
||||
t.matrix = glm::mat4(1.0f);
|
||||
t.matrix[1][0] = std::tan(skewX * DEG_TO_RAD);
|
||||
t.matrix[0][1] = std::tan(skewY * DEG_TO_RAD);
|
||||
return t;
|
||||
}
|
||||
|
||||
Transform2D operator*(const Transform2D &other) const {
|
||||
return Transform2D(matrix * other.matrix);
|
||||
}
|
||||
|
||||
Transform2D &operator*=(const Transform2D &other) {
|
||||
matrix *= other.matrix;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vec2 transformPoint(const Vec2 &p) const {
|
||||
glm::vec4 result = matrix * glm::vec4(p.x, p.y, 0.0f, 1.0f);
|
||||
return {result.x, result.y};
|
||||
}
|
||||
|
||||
Transform2D inverse() const { return Transform2D(glm::inverse(matrix)); }
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 数学工具函数
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace math {
|
||||
|
||||
inline float clamp(float value, float minVal, float maxVal) {
|
||||
return std::clamp(value, minVal, maxVal);
|
||||
}
|
||||
|
||||
inline float lerp(float a, float b, float t) { return a + (b - a) * t; }
|
||||
|
||||
inline float degrees(float radians) { return radians * RAD_TO_DEG; }
|
||||
|
||||
inline float radians(float degrees) { return degrees * DEG_TO_RAD; }
|
||||
|
||||
} // namespace math
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 字符串编码转换工具函数
|
||||
// 统一使用 std::string (UTF-8) 作为项目标准字符串类型
|
||||
// ============================================================================
|
||||
|
||||
// UTF-8 ↔ UTF-16 转换
|
||||
std::u16string utf8ToUtf16(const std::string& utf8);
|
||||
std::string utf16ToUtf8(const std::u16string& utf16);
|
||||
|
||||
// UTF-8 ↔ UTF-32 转换
|
||||
std::u32string utf8ToUtf32(const std::string& utf8);
|
||||
std::string utf32ToUtf8(const std::u32string& utf32);
|
||||
|
||||
// UTF-8 ↔ Wide String 转换
|
||||
std::wstring utf8ToWide(const std::string& utf8);
|
||||
std::string wideToUtf8(const std::wstring& wide);
|
||||
|
||||
// UTF-8 ↔ GBK/GB2312 转换(Windows 中文系统常用)
|
||||
std::string utf8ToGbk(const std::string& utf8);
|
||||
std::string gbkToUtf8(const std::string& gbk);
|
||||
|
||||
// ============================================================================
|
||||
// 内联实现
|
||||
// ============================================================================
|
||||
|
||||
inline std::u16string utf8ToUtf16(const std::string& utf8) {
|
||||
if (utf8.empty()) return std::u16string();
|
||||
|
||||
// UTF-8 → UTF-32 → UTF-16 (with surrogate pairs)
|
||||
std::u32string u32 = utf8ToUtf32(utf8);
|
||||
std::u16string result;
|
||||
result.reserve(u32.size());
|
||||
|
||||
for (char32_t ch : u32) {
|
||||
if (ch <= 0xFFFF) {
|
||||
result.push_back(static_cast<char16_t>(ch));
|
||||
} else if (ch <= 0x10FFFF) {
|
||||
// Surrogate pair
|
||||
ch -= 0x10000;
|
||||
result.push_back(static_cast<char16_t>(0xD800 | (ch >> 10)));
|
||||
result.push_back(static_cast<char16_t>(0xDC00 | (ch & 0x3FF)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string utf16ToUtf8(const std::u16string& utf16) {
|
||||
if (utf16.empty()) return std::string();
|
||||
|
||||
// UTF-16 → UTF-32 → UTF-8
|
||||
std::u32string u32;
|
||||
u32.reserve(utf16.size());
|
||||
|
||||
for (size_t i = 0; i < utf16.size(); ++i) {
|
||||
char16_t cu = utf16[i];
|
||||
char32_t ch;
|
||||
if (cu >= 0xD800 && cu <= 0xDBFF && i + 1 < utf16.size()) {
|
||||
// High surrogate
|
||||
char16_t cl = utf16[i + 1];
|
||||
if (cl >= 0xDC00 && cl <= 0xDFFF) {
|
||||
ch = 0x10000 + ((static_cast<char32_t>(cu - 0xD800) << 10) |
|
||||
(cl - 0xDC00));
|
||||
++i;
|
||||
} else {
|
||||
ch = cu; // Invalid, pass through
|
||||
}
|
||||
} else {
|
||||
ch = cu;
|
||||
}
|
||||
u32.push_back(ch);
|
||||
}
|
||||
|
||||
return utf32ToUtf8(u32);
|
||||
}
|
||||
|
||||
inline std::u32string utf8ToUtf32(const std::string& utf8) {
|
||||
std::u32string result;
|
||||
result.reserve(utf8.size());
|
||||
|
||||
const char* ptr = utf8.c_str();
|
||||
const char* end = ptr + utf8.size();
|
||||
|
||||
while (ptr < end) {
|
||||
char32_t ch = 0;
|
||||
unsigned char byte = static_cast<unsigned char>(*ptr);
|
||||
|
||||
if ((byte & 0x80) == 0) {
|
||||
// 1-byte sequence
|
||||
ch = byte;
|
||||
ptr += 1;
|
||||
} else if ((byte & 0xE0) == 0xC0) {
|
||||
// 2-byte sequence
|
||||
ch = (byte & 0x1F) << 6;
|
||||
ch |= (static_cast<unsigned char>(ptr[1]) & 0x3F);
|
||||
ptr += 2;
|
||||
} else if ((byte & 0xF0) == 0xE0) {
|
||||
// 3-byte sequence
|
||||
ch = (byte & 0x0F) << 12;
|
||||
ch |= (static_cast<unsigned char>(ptr[1]) & 0x3F) << 6;
|
||||
ch |= (static_cast<unsigned char>(ptr[2]) & 0x3F);
|
||||
ptr += 3;
|
||||
} else if ((byte & 0xF8) == 0xF0) {
|
||||
// 4-byte sequence
|
||||
ch = (byte & 0x07) << 18;
|
||||
ch |= (static_cast<unsigned char>(ptr[1]) & 0x3F) << 12;
|
||||
ch |= (static_cast<unsigned char>(ptr[2]) & 0x3F) << 6;
|
||||
ch |= (static_cast<unsigned char>(ptr[3]) & 0x3F);
|
||||
ptr += 4;
|
||||
} else {
|
||||
// Invalid UTF-8, skip
|
||||
ptr += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(ch);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string utf32ToUtf8(const std::u32string& utf32) {
|
||||
std::string result;
|
||||
|
||||
for (char32_t ch : utf32) {
|
||||
if (ch <= 0x7F) {
|
||||
// 1-byte
|
||||
result.push_back(static_cast<char>(ch));
|
||||
} else if (ch <= 0x7FF) {
|
||||
// 2-byte
|
||||
result.push_back(static_cast<char>(0xC0 | ((ch >> 6) & 0x1F)));
|
||||
result.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
} else if (ch <= 0xFFFF) {
|
||||
// 3-byte
|
||||
result.push_back(static_cast<char>(0xE0 | ((ch >> 12) & 0x0F)));
|
||||
result.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
||||
result.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
} else if (ch <= 0x10FFFF) {
|
||||
// 4-byte
|
||||
result.push_back(static_cast<char>(0xF0 | ((ch >> 18) & 0x07)));
|
||||
result.push_back(static_cast<char>(0x80 | ((ch >> 12) & 0x3F)));
|
||||
result.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
||||
result.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::wstring utf8ToWide(const std::string& utf8) {
|
||||
if (utf8.empty()) return std::wstring();
|
||||
|
||||
if constexpr (sizeof(wchar_t) == 4) {
|
||||
// wchar_t is 32-bit (Linux/Switch): same as UTF-32
|
||||
std::u32string u32 = utf8ToUtf32(utf8);
|
||||
return std::wstring(u32.begin(), u32.end());
|
||||
} else {
|
||||
// wchar_t is 16-bit (Windows): same as UTF-16
|
||||
std::u16string u16 = utf8ToUtf16(utf8);
|
||||
return std::wstring(u16.begin(), u16.end());
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string wideToUtf8(const std::wstring& wide) {
|
||||
if (wide.empty()) return std::string();
|
||||
|
||||
if constexpr (sizeof(wchar_t) == 4) {
|
||||
std::u32string u32(wide.begin(), wide.end());
|
||||
return utf32ToUtf8(u32);
|
||||
} else {
|
||||
std::u16string u16(wide.begin(), wide.end());
|
||||
return utf16ToUtf8(u16);
|
||||
}
|
||||
}
|
||||
|
||||
// GBK/GB2312 转换(Windows 平台实现)
|
||||
// 注意:Windows 实现在 .cpp 文件中,避免头文件包含 windows.h 导致冲突
|
||||
#ifdef _WIN32
|
||||
// 前向声明,实现在 .cpp 文件中
|
||||
std::string utf8ToGbkImpl(const std::string& utf8);
|
||||
std::string gbkToUtf8Impl(const std::string& gbk);
|
||||
|
||||
inline std::string utf8ToGbk(const std::string& utf8) {
|
||||
return utf8ToGbkImpl(utf8);
|
||||
}
|
||||
|
||||
inline std::string gbkToUtf8(const std::string& gbk) {
|
||||
return gbkToUtf8Impl(gbk);
|
||||
}
|
||||
#else
|
||||
// 非 Windows 平台,GBK 转换使用 iconv 或返回原字符串
|
||||
inline std::string utf8ToGbk(const std::string& utf8) {
|
||||
// TODO: 使用 iconv 实现
|
||||
return utf8;
|
||||
}
|
||||
|
||||
inline std::string gbkToUtf8(const std::string& gbk) {
|
||||
// TODO: 使用 iconv 实现
|
||||
return gbk;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 智能指针别名
|
||||
// ---------------------------------------------------------------------------
|
||||
template <typename T> using Ptr = std::shared_ptr<T>;
|
||||
|
||||
template <typename T> using UniquePtr = std::unique_ptr<T>;
|
||||
|
||||
template <typename T> using WeakPtr = std::weak_ptr<T>;
|
||||
|
||||
/// 创建 shared_ptr 的便捷函数
|
||||
template <typename T, typename... Args> inline Ptr<T> makePtr(Args &&...args) {
|
||||
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// 创建 unique_ptr 的便捷函数
|
||||
template <typename T, typename... Args>
|
||||
inline UniquePtr<T> makeUnique(Args &&...args) {
|
||||
return std::make_unique<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 函数别名
|
||||
// ---------------------------------------------------------------------------
|
||||
template <typename Sig> using Function = std::function<Sig>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 基础类型别名
|
||||
// ---------------------------------------------------------------------------
|
||||
using int8 = std::int8_t;
|
||||
using int16 = std::int16_t;
|
||||
using int32 = std::int32_t;
|
||||
using int64 = std::int64_t;
|
||||
using uint8 = std::uint8_t;
|
||||
using uint16 = std::uint16_t;
|
||||
using uint32 = std::uint32_t;
|
||||
using uint64 = std::uint64_t;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,324 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/effects/particle_system.h>
|
||||
#include <extra2d/effects/post_process.h>
|
||||
#include <extra2d/graphics/shader_system.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 自定义特效类型
|
||||
// ============================================================================
|
||||
enum class CustomEffectType {
|
||||
Particle, // 粒子特效
|
||||
PostProcess, // 后处理特效
|
||||
Shader, // Shader特效
|
||||
Combined // 组合特效
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义特效配置
|
||||
// ============================================================================
|
||||
struct CustomEffectConfig {
|
||||
std::string name; // 特效名称
|
||||
CustomEffectType type; // 特效类型
|
||||
std::string description; // 描述
|
||||
|
||||
// 粒子特效配置
|
||||
EmitterConfig emitterConfig;
|
||||
|
||||
// 后处理特效配置
|
||||
std::string shaderVertPath; // 顶点着色器路径
|
||||
std::string shaderFragPath; // 片段着色器路径
|
||||
std::unordered_map<std::string, float> shaderParams; // Shader参数
|
||||
|
||||
// 通用配置
|
||||
float duration; // 持续时间(-1表示无限)
|
||||
bool loop; // 是否循环
|
||||
float delay; // 延迟启动时间
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义特效基类
|
||||
// ============================================================================
|
||||
class CustomEffect {
|
||||
public:
|
||||
explicit CustomEffect(const CustomEffectConfig &config);
|
||||
virtual ~CustomEffect() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool init();
|
||||
virtual void update(float dt);
|
||||
virtual void render(RenderBackend &renderer);
|
||||
virtual void shutdown();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 控制
|
||||
// ------------------------------------------------------------------------
|
||||
void play();
|
||||
void pause();
|
||||
void stop();
|
||||
void reset();
|
||||
|
||||
bool isPlaying() const { return playing_; }
|
||||
bool isFinished() const { return finished_; }
|
||||
float getElapsedTime() const { return elapsedTime_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 配置
|
||||
// ------------------------------------------------------------------------
|
||||
const std::string &getName() const { return config_.name; }
|
||||
const CustomEffectConfig &getConfig() const { return config_; }
|
||||
|
||||
void setPosition(const Vec2 &pos) { position_ = pos; }
|
||||
void setRotation(float rot) { rotation_ = rot; }
|
||||
void setScale(float scale) { scale_ = scale; }
|
||||
|
||||
Vec2 getPosition() const { return position_; }
|
||||
float getRotation() const { return rotation_; }
|
||||
float getScale() const { return scale_; }
|
||||
|
||||
protected:
|
||||
CustomEffectConfig config_;
|
||||
Vec2 position_ = Vec2::Zero();
|
||||
float rotation_ = 0.0f;
|
||||
float scale_ = 1.0f;
|
||||
|
||||
bool playing_ = false;
|
||||
bool paused_ = false;
|
||||
bool finished_ = false;
|
||||
float elapsedTime_ = 0.0f;
|
||||
float delayTimer_ = 0.0f;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义粒子特效
|
||||
// ============================================================================
|
||||
class CustomParticleEffect : public CustomEffect {
|
||||
public:
|
||||
explicit CustomParticleEffect(const CustomEffectConfig &config);
|
||||
|
||||
bool init() override;
|
||||
void update(float dt) override;
|
||||
void render(RenderBackend &renderer) override;
|
||||
void shutdown() override;
|
||||
|
||||
void play();
|
||||
void stop();
|
||||
|
||||
Ptr<ParticleEmitter> getEmitter() { return emitter_; }
|
||||
|
||||
private:
|
||||
Ptr<ParticleSystem> particleSystem_;
|
||||
Ptr<ParticleEmitter> emitter_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义后处理特效
|
||||
// ============================================================================
|
||||
class CustomPostProcessEffect : public CustomEffect, public PostProcessEffect {
|
||||
public:
|
||||
explicit CustomPostProcessEffect(const CustomEffectConfig &config);
|
||||
|
||||
bool init() override;
|
||||
void update(float dt) override;
|
||||
void shutdown() override;
|
||||
|
||||
void onShaderBind(GLShader &shader) override;
|
||||
|
||||
void setParam(const std::string &name, float value);
|
||||
float getParam(const std::string &name) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, float> runtimeParams_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义特效工厂
|
||||
// ============================================================================
|
||||
class CustomEffectFactory {
|
||||
public:
|
||||
using EffectCreator =
|
||||
std::function<Ptr<CustomEffect>(const CustomEffectConfig &)>;
|
||||
|
||||
static CustomEffectFactory &getInstance();
|
||||
|
||||
// 注册自定义特效创建器
|
||||
void registerEffect(const std::string &typeName, EffectCreator creator);
|
||||
|
||||
// 创建特效
|
||||
Ptr<CustomEffect> create(const std::string &typeName,
|
||||
const CustomEffectConfig &config);
|
||||
|
||||
// 检查是否已注册
|
||||
bool isRegistered(const std::string &typeName) const;
|
||||
|
||||
// 获取所有已注册的类型
|
||||
std::vector<std::string> getRegisteredTypes() const;
|
||||
|
||||
private:
|
||||
CustomEffectFactory() = default;
|
||||
~CustomEffectFactory() = default;
|
||||
CustomEffectFactory(const CustomEffectFactory &) = delete;
|
||||
CustomEffectFactory &operator=(const CustomEffectFactory &) = delete;
|
||||
|
||||
std::unordered_map<std::string, EffectCreator> creators_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义特效管理器
|
||||
// ============================================================================
|
||||
class CustomEffectManager {
|
||||
public:
|
||||
static CustomEffectManager &getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
// ------------------------------------------------------------------------
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 特效管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从文件加载特效配置(支持JSON和文本格式)
|
||||
* 自动检测格式:JSON格式优先,失败则回退到文本格式
|
||||
*/
|
||||
bool loadFromFile(const std::string &filepath);
|
||||
|
||||
/**
|
||||
* @brief 从文本文件加载特效配置(简化格式)
|
||||
* 格式:每行一个参数,如 EMISSION 100
|
||||
*/
|
||||
bool loadFromTextFile(const std::string &filepath);
|
||||
|
||||
/**
|
||||
* @brief 保存特效配置到文件
|
||||
* @param useJson true=JSON格式, false=文本格式
|
||||
*/
|
||||
bool saveToFile(const std::string &name, const std::string &filepath,
|
||||
bool useJson = true);
|
||||
|
||||
/**
|
||||
* @brief 保存所有特效配置到一个JSON文件
|
||||
*/
|
||||
bool saveAllToFile(const std::string &filepath);
|
||||
|
||||
/**
|
||||
* @brief 注册特效配置
|
||||
*/
|
||||
void registerConfig(const std::string &name,
|
||||
const CustomEffectConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取特效配置
|
||||
*/
|
||||
CustomEffectConfig *getConfig(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 移除特效配置
|
||||
*/
|
||||
void removeConfig(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 获取所有配置名称
|
||||
*/
|
||||
std::vector<std::string> getConfigNames() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 特效实例管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 创建特效实例
|
||||
*/
|
||||
Ptr<CustomEffect> createEffect(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 从配置直接创建特效
|
||||
*/
|
||||
Ptr<CustomEffect> createEffectFromConfig(const CustomEffectConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 销毁特效实例
|
||||
*/
|
||||
void destroyEffect(Ptr<CustomEffect> effect);
|
||||
|
||||
/**
|
||||
* @brief 更新所有特效
|
||||
*/
|
||||
void update(float dt);
|
||||
|
||||
/**
|
||||
* @brief 渲染所有特效
|
||||
*/
|
||||
void render(RenderBackend &renderer);
|
||||
|
||||
/**
|
||||
* @brief 停止所有特效
|
||||
*/
|
||||
void stopAll();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 便捷方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 播放特效(简写)
|
||||
*/
|
||||
Ptr<CustomEffect> play(const std::string &name, const Vec2 &position);
|
||||
|
||||
/**
|
||||
* @brief 播放特效并自动销毁
|
||||
*/
|
||||
void playOneShot(const std::string &name, const Vec2 &position);
|
||||
|
||||
private:
|
||||
CustomEffectManager() = default;
|
||||
~CustomEffectManager() = default;
|
||||
CustomEffectManager(const CustomEffectManager &) = delete;
|
||||
CustomEffectManager &operator=(const CustomEffectManager &) = delete;
|
||||
|
||||
std::unordered_map<std::string, CustomEffectConfig> configs_;
|
||||
std::vector<Ptr<CustomEffect>> activeEffects_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_CUSTOM_EFFECT_MANAGER() \
|
||||
::extra2d::CustomEffectManager::getInstance()
|
||||
#define E2D_CUSTOM_EFFECT_FACTORY() \
|
||||
::extra2d::CustomEffectFactory::getInstance()
|
||||
|
||||
// ============================================================================
|
||||
// 预设特效快速创建
|
||||
// ============================================================================
|
||||
class EffectBuilder {
|
||||
public:
|
||||
// 粒子特效
|
||||
static CustomEffectConfig Particle(const std::string &name);
|
||||
static CustomEffectConfig Fire(const std::string &name);
|
||||
static CustomEffectConfig Smoke(const std::string &name);
|
||||
static CustomEffectConfig Explosion(const std::string &name);
|
||||
static CustomEffectConfig Magic(const std::string &name);
|
||||
static CustomEffectConfig Sparkle(const std::string &name);
|
||||
|
||||
// 后处理特效
|
||||
static CustomEffectConfig Bloom(const std::string &name);
|
||||
static CustomEffectConfig Blur(const std::string &name);
|
||||
static CustomEffectConfig Vignette(const std::string &name);
|
||||
static CustomEffectConfig ColorGrading(const std::string &name);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 快速随机数生成器 - 使用 xorshift 算法,比 std::mt19937 更快
|
||||
// ============================================================================
|
||||
class FastRNG {
|
||||
public:
|
||||
explicit FastRNG(uint32_t seed = 0) : state_(seed ? seed : 0x853c49e67) {}
|
||||
|
||||
float nextFloat() {
|
||||
return static_cast<float>(next()) / static_cast<float>(UINT32_MAX);
|
||||
}
|
||||
|
||||
float nextFloat(float min, float max) {
|
||||
return min + (max - min) * nextFloat();
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t state_;
|
||||
|
||||
uint32_t next() {
|
||||
uint32_t x = state_;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
state_ = x;
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 粒子数据
|
||||
// ============================================================================
|
||||
struct Particle {
|
||||
Vec2 position;
|
||||
Vec2 velocity;
|
||||
Vec2 acceleration;
|
||||
float rotation;
|
||||
float angularVelocity;
|
||||
float size;
|
||||
float sizeDelta;
|
||||
Color color;
|
||||
Color colorDelta;
|
||||
float life;
|
||||
float maxLife;
|
||||
bool active;
|
||||
|
||||
Particle()
|
||||
: position(Vec2::Zero()), velocity(Vec2::Zero()),
|
||||
acceleration(Vec2::Zero()), rotation(0.0f), angularVelocity(0.0f),
|
||||
size(1.0f), sizeDelta(0.0f), color(Colors::White),
|
||||
colorDelta(Colors::Transparent), life(0.0f), maxLife(1.0f),
|
||||
active(false) {}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 发射器配置
|
||||
// ============================================================================
|
||||
struct EmitterConfig {
|
||||
// 发射速率
|
||||
float emissionRate = 100.0f; // 每秒发射粒子数
|
||||
float emissionDuration = -1.0f; // 发射持续时间(-1表示无限)
|
||||
|
||||
// 粒子生命周期
|
||||
float minLife = 1.0f;
|
||||
float maxLife = 2.0f;
|
||||
|
||||
// 粒子大小
|
||||
float minStartSize = 10.0f;
|
||||
float maxStartSize = 20.0f;
|
||||
float minEndSize = 0.0f;
|
||||
float maxEndSize = 5.0f;
|
||||
|
||||
// 粒子速度
|
||||
Vec2 minVelocity = Vec2(-50.0f, -50.0f);
|
||||
Vec2 maxVelocity = Vec2(50.0f, 50.0f);
|
||||
|
||||
// 粒子加速度
|
||||
Vec2 acceleration = Vec2(0.0f, -100.0f); // 重力
|
||||
|
||||
// 粒子旋转
|
||||
float minRotation = 0.0f;
|
||||
float maxRotation = 360.0f;
|
||||
float minAngularVelocity = -90.0f;
|
||||
float maxAngularVelocity = 90.0f;
|
||||
|
||||
// 颜色
|
||||
Color startColor = Colors::White;
|
||||
Color endColor = Colors::Transparent;
|
||||
|
||||
// 发射形状
|
||||
enum class Shape {
|
||||
Point, // 点发射
|
||||
Circle, // 圆形区域
|
||||
Rectangle, // 矩形区域
|
||||
Cone // 锥形
|
||||
};
|
||||
Shape shape = Shape::Point;
|
||||
float shapeRadius = 50.0f; // 圆形/锥形半径
|
||||
Vec2 shapeSize = Vec2(100.0f, 100.0f); // 矩形大小
|
||||
float coneAngle = 45.0f; // 锥形角度
|
||||
|
||||
// 纹理
|
||||
Ptr<Texture> texture = nullptr;
|
||||
|
||||
// 混合模式
|
||||
BlendMode blendMode = BlendMode::Additive;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 粒子发射器
|
||||
// ============================================================================
|
||||
class ParticleEmitter {
|
||||
public:
|
||||
ParticleEmitter();
|
||||
~ParticleEmitter() = default;
|
||||
|
||||
// 形状生成函数(公有,用于查找表)
|
||||
Vec2 randomPointShape();
|
||||
Vec2 randomCircleShape();
|
||||
Vec2 randomRectangleShape();
|
||||
Vec2 randomConeShape();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
// ------------------------------------------------------------------------
|
||||
bool init(size_t maxParticles);
|
||||
void shutdown();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 配置
|
||||
// ------------------------------------------------------------------------
|
||||
void setConfig(const EmitterConfig &config) { config_ = config; }
|
||||
const EmitterConfig &getConfig() const { return config_; }
|
||||
|
||||
// 链式配置API
|
||||
ParticleEmitter &withEmissionRate(float rate) {
|
||||
config_.emissionRate = rate;
|
||||
return *this;
|
||||
}
|
||||
ParticleEmitter &withLife(float minLife, float maxLife) {
|
||||
config_.minLife = minLife;
|
||||
config_.maxLife = maxLife;
|
||||
return *this;
|
||||
}
|
||||
ParticleEmitter &withSize(float minStart, float maxStart, float minEnd = 0.0f,
|
||||
float maxEnd = 0.0f) {
|
||||
config_.minStartSize = minStart;
|
||||
config_.maxStartSize = maxStart;
|
||||
config_.minEndSize = minEnd;
|
||||
config_.maxEndSize = maxEnd;
|
||||
return *this;
|
||||
}
|
||||
ParticleEmitter &withVelocity(const Vec2 &minVel, const Vec2 &maxVel) {
|
||||
config_.minVelocity = minVel;
|
||||
config_.maxVelocity = maxVel;
|
||||
return *this;
|
||||
}
|
||||
ParticleEmitter &withAcceleration(const Vec2 &accel) {
|
||||
config_.acceleration = accel;
|
||||
return *this;
|
||||
}
|
||||
ParticleEmitter &withColor(const Color &start, const Color &end) {
|
||||
config_.startColor = start;
|
||||
config_.endColor = end;
|
||||
return *this;
|
||||
}
|
||||
ParticleEmitter &withTexture(Ptr<Texture> texture) {
|
||||
config_.texture = texture;
|
||||
return *this;
|
||||
}
|
||||
ParticleEmitter &withBlendMode(BlendMode mode) {
|
||||
config_.blendMode = mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 发射控制
|
||||
// ------------------------------------------------------------------------
|
||||
void start();
|
||||
void stop();
|
||||
void burst(int count); // 爆发发射
|
||||
void reset();
|
||||
bool isEmitting() const { return emitting_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 更新和渲染
|
||||
// ------------------------------------------------------------------------
|
||||
void update(float dt);
|
||||
void render(RenderBackend &renderer);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态查询
|
||||
// ------------------------------------------------------------------------
|
||||
size_t getActiveParticleCount() const { return activeCount_; }
|
||||
size_t getMaxParticles() const { return particles_.size(); }
|
||||
bool isActive() const { return activeCount_ > 0 || emitting_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 变换
|
||||
// ------------------------------------------------------------------------
|
||||
void setPosition(const Vec2 &pos) { position_ = pos; }
|
||||
void setRotation(float rot) { rotation_ = rot; }
|
||||
Vec2 getPosition() const { return position_; }
|
||||
float getRotation() const { return rotation_; }
|
||||
|
||||
private:
|
||||
EmitterConfig config_;
|
||||
std::vector<Particle> particles_;
|
||||
size_t activeCount_ = 0;
|
||||
|
||||
Vec2 position_ = Vec2::Zero();
|
||||
float rotation_ = 0.0f;
|
||||
|
||||
bool emitting_ = false;
|
||||
float emissionTimer_ = 0.0f;
|
||||
float emissionTime_ = 0.0f;
|
||||
|
||||
FastRNG rng_; // 使用快速 RNG 替代 std::mt19937
|
||||
|
||||
void emitParticle();
|
||||
float randomFloat(float min, float max);
|
||||
Vec2 randomPointInShape();
|
||||
Vec2 randomVelocity();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 粒子系统 - 管理多个发射器
|
||||
// ============================================================================
|
||||
class ParticleSystem : public Node {
|
||||
public:
|
||||
ParticleSystem();
|
||||
~ParticleSystem() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<ParticleSystem> create();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 发射器管理
|
||||
// ------------------------------------------------------------------------
|
||||
Ptr<ParticleEmitter> addEmitter(const EmitterConfig &config = {});
|
||||
void removeEmitter(Ptr<ParticleEmitter> emitter);
|
||||
void removeAllEmitters();
|
||||
size_t getEmitterCount() const { return emitters_.size(); }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 全局控制
|
||||
// ------------------------------------------------------------------------
|
||||
void startAll();
|
||||
void stopAll();
|
||||
void resetAll();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 预设
|
||||
// ------------------------------------------------------------------------
|
||||
static EmitterConfig PresetFire();
|
||||
static EmitterConfig PresetSmoke();
|
||||
static EmitterConfig PresetExplosion();
|
||||
static EmitterConfig PresetSparkle();
|
||||
static EmitterConfig PresetRain();
|
||||
static EmitterConfig PresetSnow();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 重写Node方法
|
||||
// ------------------------------------------------------------------------
|
||||
void onUpdate(float dt) override;
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
std::vector<Ptr<ParticleEmitter>> emitters_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 粒子预设(便捷类)
|
||||
// ============================================================================
|
||||
class ParticlePreset {
|
||||
public:
|
||||
static EmitterConfig Fire();
|
||||
static EmitterConfig Smoke();
|
||||
static EmitterConfig Explosion();
|
||||
static EmitterConfig Sparkle();
|
||||
static EmitterConfig Rain();
|
||||
static EmitterConfig Snow();
|
||||
static EmitterConfig Magic();
|
||||
static EmitterConfig Bubbles();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 前向声明
|
||||
// ============================================================================
|
||||
class RenderTarget;
|
||||
class RenderBackend;
|
||||
|
||||
// ============================================================================
|
||||
// 后处理效果基类
|
||||
// ============================================================================
|
||||
class PostProcessEffect {
|
||||
public:
|
||||
PostProcessEffect(const std::string &name);
|
||||
virtual ~PostProcessEffect() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 初始化效果
|
||||
*/
|
||||
virtual bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭效果
|
||||
*/
|
||||
virtual void shutdown();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 渲染
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 应用效果
|
||||
* @param source 输入纹理
|
||||
* @param target 输出渲染目标
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
virtual void apply(const Texture &source, RenderTarget &target,
|
||||
RenderBackend &renderer);
|
||||
|
||||
/**
|
||||
* @brief 设置Shader参数(子类重写)
|
||||
*/
|
||||
virtual void onShaderBind(GLShader &shader) {}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态
|
||||
// ------------------------------------------------------------------------
|
||||
const std::string &getName() const { return name_; }
|
||||
bool isEnabled() const { return enabled_; }
|
||||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool isValid() const { return valid_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式API
|
||||
// ------------------------------------------------------------------------
|
||||
PostProcessEffect &withEnabled(bool enabled) {
|
||||
enabled_ = enabled;
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
bool enabled_ = true;
|
||||
bool valid_ = false;
|
||||
Ptr<GLShader> shader_;
|
||||
|
||||
/**
|
||||
* @brief 加载自定义Shader
|
||||
*/
|
||||
bool loadShader(const std::string &vertSource, const std::string &fragSource);
|
||||
bool loadShaderFromFile(const std::string &vertPath,
|
||||
const std::string &fragPath);
|
||||
|
||||
/**
|
||||
* @brief 渲染全屏四边形
|
||||
*/
|
||||
void renderFullscreenQuad();
|
||||
|
||||
private:
|
||||
static GLuint quadVao_;
|
||||
static GLuint quadVbo_;
|
||||
static bool quadInitialized_;
|
||||
|
||||
void initQuad();
|
||||
void destroyQuad();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 后处理栈 - 管理多个后处理效果
|
||||
// ============================================================================
|
||||
class PostProcessStack {
|
||||
public:
|
||||
PostProcessStack();
|
||||
~PostProcessStack();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
// ------------------------------------------------------------------------
|
||||
bool init(int width, int height);
|
||||
void shutdown();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 效果管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 添加效果到栈
|
||||
*/
|
||||
void addEffect(Ptr<PostProcessEffect> effect);
|
||||
|
||||
/**
|
||||
* @brief 插入效果到指定位置
|
||||
*/
|
||||
void insertEffect(size_t index, Ptr<PostProcessEffect> effect);
|
||||
|
||||
/**
|
||||
* @brief 移除效果
|
||||
*/
|
||||
void removeEffect(const std::string &name);
|
||||
void removeEffect(size_t index);
|
||||
|
||||
/**
|
||||
* @brief 获取效果
|
||||
*/
|
||||
Ptr<PostProcessEffect> getEffect(const std::string &name);
|
||||
Ptr<PostProcessEffect> getEffect(size_t index);
|
||||
|
||||
/**
|
||||
* @brief 清空所有效果
|
||||
*/
|
||||
void clearEffects();
|
||||
|
||||
/**
|
||||
* @brief 获取效果数量
|
||||
*/
|
||||
size_t getEffectCount() const { return effects_.size(); }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 渲染
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 开始捕获场景
|
||||
*/
|
||||
void beginCapture();
|
||||
|
||||
/**
|
||||
* @brief 结束捕获并应用所有效果
|
||||
*/
|
||||
void endCapture(RenderBackend &renderer);
|
||||
|
||||
/**
|
||||
* @brief 直接应用效果到纹理
|
||||
*/
|
||||
void process(const Texture &source, RenderTarget &target,
|
||||
RenderBackend &renderer);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 配置
|
||||
// ------------------------------------------------------------------------
|
||||
void resize(int width, int height);
|
||||
bool isValid() const { return valid_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 便捷方法 - 添加内置效果
|
||||
// ------------------------------------------------------------------------
|
||||
PostProcessStack &addBloom(float intensity = 1.0f, float threshold = 0.8f);
|
||||
PostProcessStack &addBlur(float radius = 2.0f);
|
||||
PostProcessStack &addColorGrading(const Color &tint);
|
||||
PostProcessStack &addVignette(float intensity = 0.5f);
|
||||
PostProcessStack &addChromaticAberration(float amount = 1.0f);
|
||||
|
||||
private:
|
||||
std::vector<Ptr<PostProcessEffect>> effects_;
|
||||
Ptr<RenderTarget> renderTargetA_;
|
||||
Ptr<RenderTarget> renderTargetB_;
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
bool valid_ = false;
|
||||
bool capturing_ = false;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 全局后处理管理
|
||||
// ============================================================================
|
||||
class PostProcessManager {
|
||||
public:
|
||||
static PostProcessManager &getInstance();
|
||||
|
||||
void init(int width, int height);
|
||||
void shutdown();
|
||||
|
||||
PostProcessStack &getMainStack() { return mainStack_; }
|
||||
|
||||
void resize(int width, int height);
|
||||
void beginFrame();
|
||||
void endFrame(RenderBackend &renderer);
|
||||
|
||||
private:
|
||||
PostProcessManager() = default;
|
||||
~PostProcessManager() = default;
|
||||
PostProcessManager(const PostProcessManager &) = delete;
|
||||
PostProcessManager &operator=(const PostProcessManager &) = delete;
|
||||
|
||||
PostProcessStack mainStack_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_POST_PROCESS() ::extra2d::PostProcessManager::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <variant>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件类型枚举
|
||||
// ============================================================================
|
||||
enum class EventType {
|
||||
None = 0,
|
||||
|
||||
// 窗口事件
|
||||
WindowClose,
|
||||
WindowResize,
|
||||
WindowFocus,
|
||||
WindowLostFocus,
|
||||
WindowMoved,
|
||||
|
||||
// 键盘事件
|
||||
KeyPressed,
|
||||
KeyReleased,
|
||||
KeyRepeat,
|
||||
|
||||
// 鼠标事件
|
||||
MouseButtonPressed,
|
||||
MouseButtonReleased,
|
||||
MouseMoved,
|
||||
MouseScrolled,
|
||||
|
||||
// UI 事件
|
||||
UIHoverEnter,
|
||||
UIHoverExit,
|
||||
UIPressed,
|
||||
UIReleased,
|
||||
UIClicked,
|
||||
|
||||
// 游戏手柄事件
|
||||
GamepadConnected,
|
||||
GamepadDisconnected,
|
||||
GamepadButtonPressed,
|
||||
GamepadButtonReleased,
|
||||
GamepadAxisMoved,
|
||||
|
||||
// 触摸事件 (移动端)
|
||||
TouchBegan,
|
||||
TouchMoved,
|
||||
TouchEnded,
|
||||
TouchCancelled,
|
||||
|
||||
// 自定义事件
|
||||
Custom
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 键盘事件数据
|
||||
// ============================================================================
|
||||
struct KeyEvent {
|
||||
int keyCode;
|
||||
int scancode;
|
||||
int mods; // 修饰键 (Shift, Ctrl, Alt, etc.)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标事件数据
|
||||
// ============================================================================
|
||||
struct MouseButtonEvent {
|
||||
int button;
|
||||
int mods;
|
||||
Vec2 position;
|
||||
};
|
||||
|
||||
struct MouseMoveEvent {
|
||||
Vec2 position;
|
||||
Vec2 delta;
|
||||
};
|
||||
|
||||
struct MouseScrollEvent {
|
||||
Vec2 offset;
|
||||
Vec2 position;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 窗口事件数据
|
||||
// ============================================================================
|
||||
struct WindowResizeEvent {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct WindowMoveEvent {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 游戏手柄事件数据
|
||||
// ============================================================================
|
||||
struct GamepadButtonEvent {
|
||||
int gamepadId;
|
||||
int button;
|
||||
};
|
||||
|
||||
struct GamepadAxisEvent {
|
||||
int gamepadId;
|
||||
int axis;
|
||||
float value;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 触摸事件数据
|
||||
// ============================================================================
|
||||
struct TouchEvent {
|
||||
int touchId;
|
||||
Vec2 position;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义事件数据
|
||||
// ============================================================================
|
||||
struct CustomEvent {
|
||||
uint32_t id;
|
||||
void *data;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 事件结构
|
||||
// ============================================================================
|
||||
struct Event {
|
||||
EventType type = EventType::None;
|
||||
double timestamp = 0.0;
|
||||
bool handled = false;
|
||||
|
||||
// 事件数据联合体
|
||||
std::variant<std::monostate, KeyEvent, MouseButtonEvent, MouseMoveEvent,
|
||||
MouseScrollEvent, WindowResizeEvent, WindowMoveEvent,
|
||||
GamepadButtonEvent, GamepadAxisEvent, TouchEvent, CustomEvent>
|
||||
data;
|
||||
|
||||
// 便捷访问方法
|
||||
bool isWindowEvent() const {
|
||||
return type == EventType::WindowClose || type == EventType::WindowResize ||
|
||||
type == EventType::WindowFocus ||
|
||||
type == EventType::WindowLostFocus || type == EventType::WindowMoved;
|
||||
}
|
||||
|
||||
bool isKeyboardEvent() const {
|
||||
return type == EventType::KeyPressed || type == EventType::KeyReleased ||
|
||||
type == EventType::KeyRepeat;
|
||||
}
|
||||
|
||||
bool isMouseEvent() const {
|
||||
return type == EventType::MouseButtonPressed ||
|
||||
type == EventType::MouseButtonReleased ||
|
||||
type == EventType::MouseMoved || type == EventType::MouseScrolled;
|
||||
}
|
||||
|
||||
// 静态工厂方法
|
||||
static Event createWindowResize(int width, int height);
|
||||
static Event createWindowClose();
|
||||
static Event createKeyPress(int keyCode, int scancode, int mods);
|
||||
static Event createKeyRelease(int keyCode, int scancode, int mods);
|
||||
static Event createMouseButtonPress(int button, int mods, const Vec2 &pos);
|
||||
static Event createMouseButtonRelease(int button, int mods, const Vec2 &pos);
|
||||
static Event createMouseMove(const Vec2 &pos, const Vec2 &delta);
|
||||
static Event createMouseScroll(const Vec2 &offset, const Vec2 &pos);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件监听器 ID
|
||||
// ============================================================================
|
||||
using ListenerId = uint64_t;
|
||||
|
||||
// ============================================================================
|
||||
// 事件分发器
|
||||
// ============================================================================
|
||||
class EventDispatcher {
|
||||
public:
|
||||
using EventCallback = std::function<void(Event &)>;
|
||||
|
||||
EventDispatcher();
|
||||
~EventDispatcher() = default;
|
||||
|
||||
// 添加监听器
|
||||
ListenerId addListener(EventType type, EventCallback callback);
|
||||
|
||||
// 移除监听器
|
||||
void removeListener(ListenerId id);
|
||||
void removeAllListeners(EventType type);
|
||||
void removeAllListeners();
|
||||
|
||||
// 分发事件
|
||||
void dispatch(Event &event);
|
||||
void dispatch(const Event &event);
|
||||
|
||||
// 处理事件队列
|
||||
void processQueue(class EventQueue &queue);
|
||||
|
||||
// 统计
|
||||
size_t getListenerCount(EventType type) const;
|
||||
size_t getTotalListenerCount() const;
|
||||
|
||||
private:
|
||||
struct Listener {
|
||||
ListenerId id;
|
||||
EventType type;
|
||||
EventCallback callback;
|
||||
};
|
||||
|
||||
std::unordered_map<EventType, std::vector<Listener>> listeners_;
|
||||
ListenerId nextId_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event.h>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件队列 - 线程安全的事件队列
|
||||
// ============================================================================
|
||||
class EventQueue {
|
||||
public:
|
||||
EventQueue();
|
||||
~EventQueue() = default;
|
||||
|
||||
// 添加事件到队列
|
||||
void push(const Event &event);
|
||||
void push(Event &&event);
|
||||
|
||||
// 从队列取出事件
|
||||
bool poll(Event &event);
|
||||
|
||||
// 查看队列头部事件(不移除)
|
||||
bool peek(Event &event) const;
|
||||
|
||||
// 清空队列
|
||||
void clear();
|
||||
|
||||
// 队列状态
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
|
||||
private:
|
||||
std::queue<Event> queue_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// SDL2 键码定义
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 键盘按键码 (基于 SDL2)
|
||||
// ============================================================================
|
||||
namespace Key {
|
||||
enum : int {
|
||||
Unknown = SDLK_UNKNOWN,
|
||||
Space = SDLK_SPACE,
|
||||
Apostrophe = SDLK_QUOTE,
|
||||
Comma = SDLK_COMMA,
|
||||
Minus = SDLK_MINUS,
|
||||
Period = SDLK_PERIOD,
|
||||
Slash = SDLK_SLASH,
|
||||
Num0 = SDLK_0,
|
||||
Num1 = SDLK_1,
|
||||
Num2 = SDLK_2,
|
||||
Num3 = SDLK_3,
|
||||
Num4 = SDLK_4,
|
||||
Num5 = SDLK_5,
|
||||
Num6 = SDLK_6,
|
||||
Num7 = SDLK_7,
|
||||
Num8 = SDLK_8,
|
||||
Num9 = SDLK_9,
|
||||
Semicolon = SDLK_SEMICOLON,
|
||||
Equal = SDLK_EQUALS,
|
||||
A = SDLK_a,
|
||||
B = SDLK_b,
|
||||
C = SDLK_c,
|
||||
D = SDLK_d,
|
||||
E = SDLK_e,
|
||||
F = SDLK_f,
|
||||
G = SDLK_g,
|
||||
H = SDLK_h,
|
||||
I = SDLK_i,
|
||||
J = SDLK_j,
|
||||
K = SDLK_k,
|
||||
L = SDLK_l,
|
||||
M = SDLK_m,
|
||||
N = SDLK_n,
|
||||
O = SDLK_o,
|
||||
P = SDLK_p,
|
||||
Q = SDLK_q,
|
||||
R = SDLK_r,
|
||||
S = SDLK_s,
|
||||
T = SDLK_t,
|
||||
U = SDLK_u,
|
||||
V = SDLK_v,
|
||||
W = SDLK_w,
|
||||
X = SDLK_x,
|
||||
Y = SDLK_y,
|
||||
Z = SDLK_z,
|
||||
LeftBracket = SDLK_LEFTBRACKET,
|
||||
Backslash = SDLK_BACKSLASH,
|
||||
RightBracket = SDLK_RIGHTBRACKET,
|
||||
GraveAccent = SDLK_BACKQUOTE,
|
||||
Escape = SDLK_ESCAPE,
|
||||
Enter = SDLK_RETURN,
|
||||
Tab = SDLK_TAB,
|
||||
Backspace = SDLK_BACKSPACE,
|
||||
Insert = SDLK_INSERT,
|
||||
Delete = SDLK_DELETE,
|
||||
Right = SDLK_RIGHT,
|
||||
Left = SDLK_LEFT,
|
||||
Down = SDLK_DOWN,
|
||||
Up = SDLK_UP,
|
||||
PageUp = SDLK_PAGEUP,
|
||||
PageDown = SDLK_PAGEDOWN,
|
||||
Home = SDLK_HOME,
|
||||
End = SDLK_END,
|
||||
CapsLock = SDLK_CAPSLOCK,
|
||||
ScrollLock = SDLK_SCROLLLOCK,
|
||||
NumLock = SDLK_NUMLOCKCLEAR,
|
||||
PrintScreen = SDLK_PRINTSCREEN,
|
||||
Pause = SDLK_PAUSE,
|
||||
F1 = SDLK_F1,
|
||||
F2 = SDLK_F2,
|
||||
F3 = SDLK_F3,
|
||||
F4 = SDLK_F4,
|
||||
F5 = SDLK_F5,
|
||||
F6 = SDLK_F6,
|
||||
F7 = SDLK_F7,
|
||||
F8 = SDLK_F8,
|
||||
F9 = SDLK_F9,
|
||||
F10 = SDLK_F10,
|
||||
F11 = SDLK_F11,
|
||||
F12 = SDLK_F12,
|
||||
F13 = SDLK_F13,
|
||||
F14 = SDLK_F14,
|
||||
F15 = SDLK_F15,
|
||||
F16 = SDLK_F16,
|
||||
F17 = SDLK_F17,
|
||||
F18 = SDLK_F18,
|
||||
F19 = SDLK_F19,
|
||||
F20 = SDLK_F20,
|
||||
F21 = SDLK_F21,
|
||||
F22 = SDLK_F22,
|
||||
F23 = SDLK_F23,
|
||||
F24 = SDLK_F24,
|
||||
KP0 = SDLK_KP_0,
|
||||
KP1 = SDLK_KP_1,
|
||||
KP2 = SDLK_KP_2,
|
||||
KP3 = SDLK_KP_3,
|
||||
KP4 = SDLK_KP_4,
|
||||
KP5 = SDLK_KP_5,
|
||||
KP6 = SDLK_KP_6,
|
||||
KP7 = SDLK_KP_7,
|
||||
KP8 = SDLK_KP_8,
|
||||
KP9 = SDLK_KP_9,
|
||||
KPDecimal = SDLK_KP_PERIOD,
|
||||
KPDivide = SDLK_KP_DIVIDE,
|
||||
KPMultiply = SDLK_KP_MULTIPLY,
|
||||
KPSubtract = SDLK_KP_MINUS,
|
||||
KPAdd = SDLK_KP_PLUS,
|
||||
KPEnter = SDLK_KP_ENTER,
|
||||
KPEqual = SDLK_KP_EQUALS,
|
||||
LeftShift = SDLK_LSHIFT,
|
||||
LeftControl = SDLK_LCTRL,
|
||||
LeftAlt = SDLK_LALT,
|
||||
LeftSuper = SDLK_LGUI,
|
||||
RightShift = SDLK_RSHIFT,
|
||||
RightControl = SDLK_RCTRL,
|
||||
RightAlt = SDLK_RALT,
|
||||
RightSuper = SDLK_RGUI,
|
||||
Menu = SDLK_MENU,
|
||||
Last = SDLK_MENU
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 修饰键
|
||||
// ============================================================================
|
||||
namespace Mod {
|
||||
enum : int {
|
||||
Shift = KMOD_SHIFT,
|
||||
Control = KMOD_CTRL,
|
||||
Alt = KMOD_ALT,
|
||||
Super = KMOD_GUI,
|
||||
CapsLock = KMOD_CAPS,
|
||||
NumLock = KMOD_NUM
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标按键码
|
||||
// ============================================================================
|
||||
namespace Mouse {
|
||||
enum : int {
|
||||
Button1 = 0,
|
||||
Button2 = 1,
|
||||
Button3 = 2,
|
||||
Button4 = 3,
|
||||
Button5 = 4,
|
||||
Button6 = 5,
|
||||
Button7 = 6,
|
||||
Button8 = 7,
|
||||
ButtonLast = Button8,
|
||||
ButtonLeft = Button1,
|
||||
ButtonRight = Button2,
|
||||
ButtonMiddle = Button3
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 游戏手柄按键
|
||||
// ============================================================================
|
||||
namespace GamepadButton {
|
||||
enum : int {
|
||||
A = SDL_CONTROLLER_BUTTON_A,
|
||||
B = SDL_CONTROLLER_BUTTON_B,
|
||||
X = SDL_CONTROLLER_BUTTON_X,
|
||||
Y = SDL_CONTROLLER_BUTTON_Y,
|
||||
LeftBumper = SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
|
||||
RightBumper = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
||||
Back = SDL_CONTROLLER_BUTTON_BACK,
|
||||
Start = SDL_CONTROLLER_BUTTON_START,
|
||||
Guide = SDL_CONTROLLER_BUTTON_GUIDE,
|
||||
LeftThumb = SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
||||
RightThumb = SDL_CONTROLLER_BUTTON_RIGHTSTICK,
|
||||
DPadUp = SDL_CONTROLLER_BUTTON_DPAD_UP,
|
||||
DPadRight = SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
|
||||
DPadDown = SDL_CONTROLLER_BUTTON_DPAD_DOWN,
|
||||
DPadLeft = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
Last = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||
Cross = A,
|
||||
Circle = B,
|
||||
Square = X,
|
||||
Triangle = Y
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 游戏手柄轴
|
||||
// ============================================================================
|
||||
namespace GamepadAxis {
|
||||
enum : int {
|
||||
LeftX = SDL_CONTROLLER_AXIS_LEFTX,
|
||||
LeftY = SDL_CONTROLLER_AXIS_LEFTY,
|
||||
RightX = SDL_CONTROLLER_AXIS_RIGHTX,
|
||||
RightY = SDL_CONTROLLER_AXIS_RIGHTY,
|
||||
LeftTrigger = SDL_CONTROLLER_AXIS_TRIGGERLEFT,
|
||||
RightTrigger = SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
||||
Last = SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// Easy2D v3.0 - 统一入口头文件
|
||||
// 包含所有公共 API
|
||||
|
||||
// Core
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/string.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
|
||||
// Platform
|
||||
#include <extra2d/platform/window.h>
|
||||
#include <extra2d/platform/input.h>
|
||||
|
||||
// Graphics
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/graphics/shader_system.h>
|
||||
|
||||
#include <extra2d/graphics/render_target.h>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
|
||||
// Scene
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/scene/sprite.h>
|
||||
#include <extra2d/scene/shape_node.h>
|
||||
#include <extra2d/scene/scene_manager.h>
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <extra2d/scene/transition_fade_scene.h>
|
||||
#include <extra2d/scene/transition_slide_scene.h>
|
||||
#include <extra2d/scene/transition_scale_scene.h>
|
||||
#include <extra2d/scene/transition_flip_scene.h>
|
||||
#include <extra2d/scene/transition_box_scene.h>
|
||||
|
||||
// Animation
|
||||
#include <extra2d/animation/sprite_frame.h>
|
||||
#include <extra2d/animation/sprite_frame_cache.h>
|
||||
#include <extra2d/animation/frame_property.h>
|
||||
#include <extra2d/animation/animation_frame.h>
|
||||
#include <extra2d/animation/animation_clip.h>
|
||||
#include <extra2d/animation/animation_controller.h>
|
||||
#include <extra2d/animation/animation_cache.h>
|
||||
#include <extra2d/animation/interpolation_engine.h>
|
||||
#include <extra2d/animation/animated_sprite.h>
|
||||
#include <extra2d/animation/frame_renderer.h>
|
||||
#include <extra2d/animation/animation_event.h>
|
||||
#include <extra2d/animation/animation_node.h>
|
||||
#include <extra2d/animation/composite_animation.h>
|
||||
#include <extra2d/animation/ani_parser.h>
|
||||
#include <extra2d/animation/ani_binary_parser.h>
|
||||
#include <extra2d/animation/als_parser.h>
|
||||
|
||||
// UI
|
||||
#include <extra2d/ui/widget.h>
|
||||
#include <extra2d/ui/button.h>
|
||||
#include <extra2d/ui/text.h>
|
||||
#include <extra2d/ui/label.h>
|
||||
#include <extra2d/ui/progress_bar.h>
|
||||
#include <extra2d/ui/check_box.h>
|
||||
#include <extra2d/ui/radio_button.h>
|
||||
#include <extra2d/ui/slider.h>
|
||||
|
||||
// Action
|
||||
#include <extra2d/action/action.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
|
||||
#include <extra2d/event/event.h>
|
||||
#include <extra2d/event/event_queue.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/event/input_codes.h>
|
||||
|
||||
// Audio
|
||||
#include <extra2d/audio/audio_engine.h>
|
||||
#include <extra2d/audio/sound.h>
|
||||
|
||||
// Resource
|
||||
#include <extra2d/resource/resource_manager.h>
|
||||
|
||||
// Utils
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
#include <extra2d/utils/data.h>
|
||||
#include <extra2d/utils/random.h>
|
||||
|
||||
// Spatial
|
||||
#include <extra2d/spatial/spatial_index.h>
|
||||
#include <extra2d/spatial/quadtree.h>
|
||||
#include <extra2d/spatial/spatial_hash.h>
|
||||
#include <extra2d/spatial/spatial_manager.h>
|
||||
|
||||
// Effects
|
||||
#include <extra2d/effects/post_process.h>
|
||||
#include <extra2d/effects/particle_system.h>
|
||||
#include <extra2d/effects/custom_effect_manager.h>
|
||||
|
||||
// Application
|
||||
#include <extra2d/app/application.h>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#endif
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Alpha 遮罩 - 存储图片的非透明区域信息
|
||||
// ============================================================================
|
||||
class AlphaMask {
|
||||
public:
|
||||
AlphaMask() = default;
|
||||
AlphaMask(int width, int height);
|
||||
|
||||
/// 从像素数据创建遮罩
|
||||
static AlphaMask createFromPixels(const uint8_t *pixels, int width,
|
||||
int height, int channels);
|
||||
|
||||
/// 获取指定位置的透明度(0-255)
|
||||
uint8_t getAlpha(int x, int y) const;
|
||||
|
||||
/// 检查指定位置是否不透明
|
||||
bool isOpaque(int x, int y, uint8_t threshold = 128) const;
|
||||
|
||||
/// 检查指定位置是否在遮罩范围内
|
||||
bool isValid(int x, int y) const;
|
||||
|
||||
/// 获取遮罩尺寸
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
Size getSize() const {
|
||||
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||
}
|
||||
|
||||
/// 获取原始数据
|
||||
const std::vector<uint8_t> &getData() const { return data_; }
|
||||
|
||||
/// 检查遮罩是否有效
|
||||
bool isValid() const { return !data_.empty() && width_ > 0 && height_ > 0; }
|
||||
|
||||
private:
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
std::vector<uint8_t> data_; // Alpha值数组
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 2D 正交相机
|
||||
// ============================================================================
|
||||
class Camera {
|
||||
public:
|
||||
Camera();
|
||||
Camera(float left, float right, float bottom, float top);
|
||||
Camera(const Size &viewport);
|
||||
~Camera() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 位置和变换
|
||||
// ------------------------------------------------------------------------
|
||||
void setPosition(const Vec2 &position);
|
||||
void setPosition(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
void setRotation(float degrees);
|
||||
float getRotation() const { return rotation_; }
|
||||
|
||||
void setZoom(float zoom);
|
||||
float getZoom() const { return zoom_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setViewport(float left, float right, float bottom, float top);
|
||||
void setViewport(const Rect &rect);
|
||||
Rect getViewport() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 矩阵获取
|
||||
// ------------------------------------------------------------------------
|
||||
glm::mat4 getViewMatrix() const;
|
||||
glm::mat4 getProjectionMatrix() const;
|
||||
glm::mat4 getViewProjectionMatrix() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标转换
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 screenToWorld(const Vec2 &screenPos) const;
|
||||
Vec2 worldToScreen(const Vec2 &worldPos) const;
|
||||
Vec2 screenToWorld(float x, float y) const;
|
||||
Vec2 worldToScreen(float x, float y) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 移动相机
|
||||
// ------------------------------------------------------------------------
|
||||
void move(const Vec2 &offset);
|
||||
void move(float x, float y);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边界限制
|
||||
// ------------------------------------------------------------------------
|
||||
void setBounds(const Rect &bounds);
|
||||
void clearBounds();
|
||||
void clampToBounds();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 快捷方法:看向某点
|
||||
// ------------------------------------------------------------------------
|
||||
void lookAt(const Vec2 &target);
|
||||
|
||||
private:
|
||||
Vec2 position_ = Vec2::Zero();
|
||||
float rotation_ = 0.0f;
|
||||
float zoom_ = 1.0f;
|
||||
|
||||
float left_ = -1.0f;
|
||||
float right_ = 1.0f;
|
||||
float bottom_ = -1.0f;
|
||||
float top_ = 1.0f;
|
||||
|
||||
Rect bounds_;
|
||||
bool hasBounds_ = false;
|
||||
|
||||
mutable glm::mat4 viewMatrix_;
|
||||
mutable glm::mat4 projMatrix_;
|
||||
mutable glm::mat4 vpMatrix_;
|
||||
mutable bool viewDirty_ = true;
|
||||
mutable bool projDirty_ = true;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 字形信息
|
||||
// ============================================================================
|
||||
struct Glyph {
|
||||
float u0, v0; // 纹理坐标左下角
|
||||
float u1, v1; // 纹理坐标右上角
|
||||
float width; // 字形宽度(像素)
|
||||
float height; // 字形高度(像素)
|
||||
float bearingX; // 水平偏移
|
||||
float bearingY; // 垂直偏移
|
||||
float advance; // 前进距离
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 字体图集接口
|
||||
// ============================================================================
|
||||
class FontAtlas {
|
||||
public:
|
||||
virtual ~FontAtlas() = default;
|
||||
|
||||
// 获取字形信息
|
||||
virtual const Glyph *getGlyph(char32_t codepoint) const = 0;
|
||||
|
||||
// 获取纹理
|
||||
virtual class Texture *getTexture() const = 0;
|
||||
|
||||
// 获取字体大小
|
||||
virtual int getFontSize() const = 0;
|
||||
|
||||
virtual float getAscent() const = 0;
|
||||
virtual float getDescent() const = 0;
|
||||
virtual float getLineGap() const = 0;
|
||||
virtual float getLineHeight() const = 0;
|
||||
|
||||
// 计算文字尺寸
|
||||
virtual Vec2 measureText(const std::string &text) = 0;
|
||||
|
||||
// 是否支持 SDF 渲染
|
||||
virtual bool isSDF() const = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// GPU 上下文状态管理器
|
||||
// 用于跟踪 OpenGL/Vulkan 等 GPU 上下文的生命周期状态
|
||||
// 确保在 GPU 资源析构时能安全地检查上下文是否有效
|
||||
// ============================================================================
|
||||
|
||||
class GPUContext {
|
||||
public:
|
||||
/// 获取单例实例
|
||||
static GPUContext& getInstance();
|
||||
|
||||
/// 标记 GPU 上下文为有效(在初始化完成后调用)
|
||||
void markValid();
|
||||
|
||||
/// 标记 GPU 上下文为无效(在销毁前调用)
|
||||
void markInvalid();
|
||||
|
||||
/// 检查 GPU 上下文是否有效
|
||||
bool isValid() const;
|
||||
|
||||
private:
|
||||
GPUContext() = default;
|
||||
~GPUContext() = default;
|
||||
GPUContext(const GPUContext&) = delete;
|
||||
GPUContext& operator=(const GPUContext&) = delete;
|
||||
|
||||
std::atomic<bool> valid_{false};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <memory>
|
||||
#include <stb/stb_rect_pack.h>
|
||||
#include <stb/stb_truetype.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 字体图集实现 - 使用 stb_rect_pack 进行矩形打包
|
||||
// ============================================================================
|
||||
class GLFontAtlas : public FontAtlas {
|
||||
public:
|
||||
GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF = false);
|
||||
~GLFontAtlas();
|
||||
|
||||
// FontAtlas 接口实现
|
||||
const Glyph *getGlyph(char32_t codepoint) const override;
|
||||
Texture *getTexture() const override { return texture_.get(); }
|
||||
int getFontSize() const override { return fontSize_; }
|
||||
float getAscent() const override { return ascent_; }
|
||||
float getDescent() const override { return descent_; }
|
||||
float getLineGap() const override { return lineGap_; }
|
||||
float getLineHeight() const override { return ascent_ - descent_ + lineGap_; }
|
||||
Vec2 measureText(const std::string &text) override;
|
||||
bool isSDF() const override { return useSDF_; }
|
||||
|
||||
private:
|
||||
// 图集配置 - 增大尺寸以支持更多字符
|
||||
static constexpr int ATLAS_WIDTH = 1024;
|
||||
static constexpr int ATLAS_HEIGHT = 1024;
|
||||
static constexpr int PADDING = 2; // 字形之间的间距
|
||||
|
||||
int fontSize_;
|
||||
bool useSDF_;
|
||||
mutable std::unique_ptr<GLTexture> texture_;
|
||||
mutable std::unordered_map<char32_t, Glyph> glyphs_;
|
||||
|
||||
// stb_rect_pack 上下文
|
||||
mutable stbrp_context packContext_;
|
||||
mutable std::vector<stbrp_node> packNodes_;
|
||||
mutable int currentY_;
|
||||
|
||||
std::vector<unsigned char> fontData_;
|
||||
stbtt_fontinfo fontInfo_;
|
||||
float scale_;
|
||||
float ascent_;
|
||||
float descent_;
|
||||
float lineGap_;
|
||||
|
||||
// 预分配字形位图缓冲区,避免每次动态分配
|
||||
mutable std::vector<uint8_t> glyphBitmapCache_;
|
||||
mutable std::vector<uint8_t> glyphRgbaCache_;
|
||||
|
||||
void createAtlas();
|
||||
void cacheGlyph(char32_t codepoint) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/opengl/gl_sprite_batch.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
|
||||
#include <array>
|
||||
#include <glad/glad.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Window;
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 渲染器实现
|
||||
// ============================================================================
|
||||
class GLRenderer : public RenderBackend {
|
||||
public:
|
||||
GLRenderer();
|
||||
~GLRenderer() override;
|
||||
|
||||
// RenderBackend 接口实现
|
||||
bool init(Window *window) override;
|
||||
void shutdown() override;
|
||||
|
||||
void beginFrame(const Color &clearColor) override;
|
||||
void endFrame() override;
|
||||
void setViewport(int x, int y, int width, int height) override;
|
||||
void setVSync(bool enabled) override;
|
||||
|
||||
void setBlendMode(BlendMode mode) override;
|
||||
void setViewProjection(const glm::mat4 &matrix) override;
|
||||
|
||||
// 变换矩阵栈
|
||||
void pushTransform(const glm::mat4 &transform) override;
|
||||
void popTransform() override;
|
||||
glm::mat4 getCurrentTransform() const override;
|
||||
|
||||
Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels,
|
||||
int channels) override;
|
||||
Ptr<Texture> loadTexture(const std::string &filepath) override;
|
||||
|
||||
void beginSpriteBatch() override;
|
||||
void drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint, float rotation,
|
||||
const Vec2 &anchor) override;
|
||||
void drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) override;
|
||||
void endSpriteBatch() override;
|
||||
|
||||
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width) override;
|
||||
void drawRect(const Rect &rect, const Color &color, float width) override;
|
||||
void fillRect(const Rect &rect, const Color &color) override;
|
||||
void drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments, float width) override;
|
||||
void fillCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments) override;
|
||||
void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width) override;
|
||||
void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) override;
|
||||
void drawPolygon(const std::vector<Vec2> &points, const Color &color,
|
||||
float width) override;
|
||||
void fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) override;
|
||||
|
||||
Ptr<FontAtlas> createFontAtlas(const std::string &filepath, int fontSize,
|
||||
bool useSDF = false) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) override;
|
||||
void drawText(const FontAtlas &font, const std::string &text, float x,
|
||||
float y, const Color &color) override;
|
||||
|
||||
Stats getStats() const override { return stats_; }
|
||||
void resetStats() override;
|
||||
|
||||
private:
|
||||
// 形状批处理常量
|
||||
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
|
||||
static constexpr size_t MAX_SHAPE_VERTICES = 8192; // 最大形状顶点数
|
||||
static constexpr size_t MAX_LINE_VERTICES = 16384; // 最大线条顶点数
|
||||
|
||||
// 形状顶点结构(包含颜色)
|
||||
struct ShapeVertex {
|
||||
float x, y;
|
||||
float r, g, b, a;
|
||||
};
|
||||
|
||||
Window *window_;
|
||||
GLSpriteBatch spriteBatch_;
|
||||
GLShader shapeShader_;
|
||||
|
||||
GLuint shapeVao_;
|
||||
GLuint shapeVbo_;
|
||||
GLuint lineVao_; // 线条专用 VAO
|
||||
GLuint lineVbo_; // 线条专用 VBO
|
||||
|
||||
glm::mat4 viewProjection_;
|
||||
std::vector<glm::mat4> transformStack_;
|
||||
Stats stats_;
|
||||
bool vsync_;
|
||||
|
||||
// 形状批处理缓冲区(预分配,避免每帧内存分配)
|
||||
std::array<ShapeVertex, MAX_SHAPE_VERTICES> shapeVertexCache_;
|
||||
size_t shapeVertexCount_ = 0;
|
||||
GLenum currentShapeMode_ = GL_TRIANGLES;
|
||||
|
||||
// 线条批处理缓冲区
|
||||
std::array<ShapeVertex, MAX_LINE_VERTICES> lineVertexCache_;
|
||||
size_t lineVertexCount_ = 0;
|
||||
float currentLineWidth_ = 1.0f;
|
||||
|
||||
// OpenGL 状态缓存
|
||||
BlendMode cachedBlendMode_ = BlendMode::None;
|
||||
bool blendEnabled_ = false;
|
||||
int cachedViewportX_ = 0;
|
||||
int cachedViewportY_ = 0;
|
||||
int cachedViewportWidth_ = 0;
|
||||
int cachedViewportHeight_ = 0;
|
||||
|
||||
void initShapeRendering();
|
||||
void flushShapeBatch();
|
||||
void flushLineBatch();
|
||||
void addShapeVertex(float x, float y, const Color &color);
|
||||
void addLineVertex(float x, float y, const Color &color);
|
||||
void submitShapeBatch(GLenum mode);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL Shader 程序
|
||||
// ============================================================================
|
||||
class GLShader {
|
||||
public:
|
||||
GLShader();
|
||||
~GLShader();
|
||||
|
||||
// 从源码编译
|
||||
bool compileFromSource(const char* vertexSource, const char* fragmentSource);
|
||||
|
||||
// 从文件加载并编译
|
||||
bool compileFromFile(const std::string& vertexPath, const std::string& fragmentPath);
|
||||
|
||||
// 使用/激活
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
|
||||
// Uniform 设置
|
||||
void setBool(const std::string& name, bool value);
|
||||
void setInt(const std::string& name, int value);
|
||||
void setFloat(const std::string& name, float value);
|
||||
void setVec2(const std::string& name, const glm::vec2& value);
|
||||
void setVec3(const std::string& name, const glm::vec3& value);
|
||||
void setVec4(const std::string& name, const glm::vec4& value);
|
||||
void setMat4(const std::string& name, const glm::mat4& value);
|
||||
|
||||
// 获取程序 ID
|
||||
GLuint getProgramID() const { return programID_; }
|
||||
|
||||
// 检查是否有效
|
||||
bool isValid() const { return programID_ != 0; }
|
||||
|
||||
private:
|
||||
GLuint programID_;
|
||||
std::unordered_map<std::string, GLint> uniformCache_;
|
||||
|
||||
GLuint compileShader(GLenum type, const char* source);
|
||||
GLint getUniformLocation(const std::string& name);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 精灵批渲染器 - 优化版本
|
||||
// ============================================================================
|
||||
class GLSpriteBatch {
|
||||
public:
|
||||
static constexpr size_t MAX_SPRITES = 10000;
|
||||
static constexpr size_t VERTICES_PER_SPRITE = 4;
|
||||
static constexpr size_t INDICES_PER_SPRITE = 6;
|
||||
static constexpr size_t MAX_VERTICES = MAX_SPRITES * VERTICES_PER_SPRITE;
|
||||
static constexpr size_t MAX_INDICES = MAX_SPRITES * INDICES_PER_SPRITE;
|
||||
|
||||
struct Vertex {
|
||||
glm::vec2 position;
|
||||
glm::vec2 texCoord;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
struct SpriteData {
|
||||
glm::vec2 position;
|
||||
glm::vec2 size;
|
||||
glm::vec2 texCoordMin;
|
||||
glm::vec2 texCoordMax;
|
||||
glm::vec4 color;
|
||||
float rotation;
|
||||
glm::vec2 anchor;
|
||||
bool isSDF = false;
|
||||
};
|
||||
|
||||
GLSpriteBatch();
|
||||
~GLSpriteBatch();
|
||||
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
void begin(const glm::mat4 &viewProjection);
|
||||
void draw(const Texture &texture, const SpriteData &data);
|
||||
void end();
|
||||
|
||||
// 批量绘制接口 - 用于自动批处理
|
||||
void drawBatch(const Texture& texture, const std::vector<SpriteData>& sprites);
|
||||
|
||||
// 立即绘制(不缓存)
|
||||
void drawImmediate(const Texture& texture, const SpriteData& data);
|
||||
|
||||
// 统计
|
||||
uint32_t getDrawCallCount() const { return drawCallCount_; }
|
||||
uint32_t getSpriteCount() const { return spriteCount_; }
|
||||
uint32_t getBatchCount() const { return batchCount_; }
|
||||
|
||||
// 检查是否需要刷新
|
||||
bool needsFlush(const Texture& texture, bool isSDF) const;
|
||||
|
||||
private:
|
||||
GLuint vao_;
|
||||
GLuint vbo_;
|
||||
GLuint ibo_;
|
||||
GLShader shader_;
|
||||
|
||||
// 使用固定大小数组减少内存分配
|
||||
std::array<Vertex, MAX_VERTICES> vertexBuffer_;
|
||||
size_t vertexCount_;
|
||||
|
||||
const Texture *currentTexture_;
|
||||
bool currentIsSDF_;
|
||||
glm::mat4 viewProjection_;
|
||||
|
||||
// 缓存上一帧的 viewProjection,避免重复设置
|
||||
glm::mat4 cachedViewProjection_;
|
||||
bool viewProjectionDirty_ = true;
|
||||
|
||||
uint32_t drawCallCount_;
|
||||
uint32_t spriteCount_;
|
||||
uint32_t batchCount_;
|
||||
|
||||
void flush();
|
||||
void setupShader();
|
||||
|
||||
// 添加顶点到缓冲区
|
||||
void addVertices(const SpriteData& data);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/alpha_mask.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// OpenGL 纹理实现
|
||||
// ============================================================================
|
||||
class GLTexture : public Texture {
|
||||
public:
|
||||
GLTexture(int width, int height, const uint8_t* pixels, int channels);
|
||||
GLTexture(const std::string& filepath);
|
||||
~GLTexture();
|
||||
|
||||
// Texture 接口实现
|
||||
int getWidth() const override { return width_; }
|
||||
int getHeight() const override { return height_; }
|
||||
Size getSize() const override { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
||||
int getChannels() const override { return channels_; }
|
||||
PixelFormat getFormat() const override;
|
||||
void* getNativeHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(textureID_)); }
|
||||
bool isValid() const override { return textureID_ != 0; }
|
||||
void setFilter(bool linear) override;
|
||||
void setWrap(bool repeat) override;
|
||||
|
||||
// 从参数创建纹理的工厂方法
|
||||
static Ptr<Texture> create(int width, int height, PixelFormat format);
|
||||
|
||||
// 加载压缩纹理(KTX/DDS 格式)
|
||||
bool loadCompressed(const std::string& filepath);
|
||||
|
||||
// OpenGL 特定
|
||||
GLuint getTextureID() const { return textureID_; }
|
||||
void bind(unsigned int slot = 0) const;
|
||||
void unbind() const;
|
||||
|
||||
// 获取纹理数据大小(字节),用于 VRAM 跟踪
|
||||
size_t getDataSize() const { return dataSize_; }
|
||||
|
||||
// Alpha 遮罩
|
||||
bool hasAlphaMask() const { return alphaMask_ != nullptr && alphaMask_->isValid(); }
|
||||
const AlphaMask* getAlphaMask() const { return alphaMask_.get(); }
|
||||
void generateAlphaMask(); // 从当前纹理数据生成遮罩
|
||||
|
||||
private:
|
||||
GLuint textureID_;
|
||||
int width_;
|
||||
int height_;
|
||||
int channels_;
|
||||
PixelFormat format_;
|
||||
size_t dataSize_;
|
||||
|
||||
// 原始像素数据(用于生成遮罩)
|
||||
std::vector<uint8_t> pixelData_;
|
||||
std::unique_ptr<AlphaMask> alphaMask_;
|
||||
|
||||
void createTexture(const uint8_t* pixels);
|
||||
|
||||
// KTX 文件加载
|
||||
bool loadKTX(const std::string& filepath);
|
||||
// DDS 文件加载
|
||||
bool loadDDS(const std::string& filepath);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Window;
|
||||
class Texture;
|
||||
class FontAtlas;
|
||||
class Shader;
|
||||
|
||||
// ============================================================================
|
||||
// 渲染后端类型
|
||||
// ============================================================================
|
||||
enum class BackendType {
|
||||
OpenGL,
|
||||
// Vulkan,
|
||||
// Metal,
|
||||
// D3D11,
|
||||
// D3D12
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 混合模式
|
||||
// ============================================================================
|
||||
enum class BlendMode {
|
||||
None, // 不混合
|
||||
Alpha, // 标准 Alpha 混合
|
||||
Additive, // 加法混合
|
||||
Multiply // 乘法混合
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染后端抽象接口
|
||||
// ============================================================================
|
||||
class RenderBackend {
|
||||
public:
|
||||
virtual ~RenderBackend() = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool init(Window *window) = 0;
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 帧管理
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void beginFrame(const Color &clearColor) = 0;
|
||||
virtual void endFrame() = 0;
|
||||
virtual void setViewport(int x, int y, int width, int height) = 0;
|
||||
virtual void setVSync(bool enabled) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态设置
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void setBlendMode(BlendMode mode) = 0;
|
||||
virtual void setViewProjection(const glm::mat4 &matrix) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 变换矩阵栈
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void pushTransform(const glm::mat4 &transform) = 0;
|
||||
virtual void popTransform() = 0;
|
||||
virtual glm::mat4 getCurrentTransform() const = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纹理
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Ptr<Texture> createTexture(int width, int height,
|
||||
const uint8_t *pixels, int channels) = 0;
|
||||
virtual Ptr<Texture> loadTexture(const std::string &filepath) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 精灵批渲染
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void beginSpriteBatch() = 0;
|
||||
virtual void drawSprite(const Texture &texture, const Rect &destRect,
|
||||
const Rect &srcRect, const Color &tint,
|
||||
float rotation, const Vec2 &anchor) = 0;
|
||||
virtual void drawSprite(const Texture &texture, const Vec2 &position,
|
||||
const Color &tint) = 0;
|
||||
virtual void endSpriteBatch() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 形状渲染
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
|
||||
float width = 1.0f) = 0;
|
||||
virtual void drawRect(const Rect &rect, const Color &color,
|
||||
float width = 1.0f) = 0;
|
||||
virtual void fillRect(const Rect &rect, const Color &color) = 0;
|
||||
virtual void drawCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments = 32, float width = 1.0f) = 0;
|
||||
virtual void fillCircle(const Vec2 ¢er, float radius, const Color &color,
|
||||
int segments = 32) = 0;
|
||||
virtual void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color, float width = 1.0f) = 0;
|
||||
virtual void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
|
||||
const Color &color) = 0;
|
||||
virtual void drawPolygon(const std::vector<Vec2> &points, const Color &color,
|
||||
float width = 1.0f) = 0;
|
||||
virtual void fillPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字渲染
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Ptr<FontAtlas> createFontAtlas(const std::string &filepath,
|
||||
int fontSize, bool useSDF = false) = 0;
|
||||
virtual void drawText(const FontAtlas &font, const std::string &text,
|
||||
const Vec2 &position, const Color &color) = 0;
|
||||
virtual void drawText(const FontAtlas &font, const std::string &text, float x,
|
||||
float y, const Color &color) = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 统计信息
|
||||
// ------------------------------------------------------------------------
|
||||
struct Stats {
|
||||
uint32_t drawCalls = 0;
|
||||
uint32_t triangleCount = 0;
|
||||
uint32_t textureBinds = 0;
|
||||
uint32_t shaderBinds = 0;
|
||||
};
|
||||
virtual Stats getStats() const = 0;
|
||||
virtual void resetStats() = 0;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工厂方法
|
||||
// ------------------------------------------------------------------------
|
||||
static UniquePtr<RenderBackend> create(BackendType type);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Texture;
|
||||
class FontAtlas;
|
||||
|
||||
/**
|
||||
* @brief 渲染命令类型枚举
|
||||
*/
|
||||
enum class RenderCommandType : uint8_t {
|
||||
None = 0,
|
||||
Sprite, // 精灵绘制
|
||||
Line, // 线条绘制
|
||||
Rect, // 矩形绘制
|
||||
FilledRect, // 填充矩形
|
||||
Circle, // 圆形绘制
|
||||
FilledCircle, // 填充圆形
|
||||
Triangle, // 三角形绘制
|
||||
FilledTriangle, // 填充三角形
|
||||
Polygon, // 多边形绘制
|
||||
FilledPolygon, // 填充多边形
|
||||
Text, // 文本绘制
|
||||
Custom // 自定义绘制
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 精灵渲染命令数据
|
||||
*/
|
||||
struct SpriteCommandData {
|
||||
const Texture* texture;
|
||||
Rect destRect;
|
||||
Rect srcRect;
|
||||
Color tint;
|
||||
float rotation;
|
||||
Vec2 anchor;
|
||||
uint32_t sortKey; // 用于自动排序的键值
|
||||
|
||||
SpriteCommandData()
|
||||
: texture(nullptr), destRect(), srcRect(), tint(Colors::White),
|
||||
rotation(0.0f), anchor(0.0f, 0.0f), sortKey(0) {}
|
||||
SpriteCommandData(const Texture* tex, const Rect& dest, const Rect& src,
|
||||
const Color& t, float rot, const Vec2& anc, uint32_t key)
|
||||
: texture(tex), destRect(dest), srcRect(src), tint(t),
|
||||
rotation(rot), anchor(anc), sortKey(key) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 线条渲染命令数据
|
||||
*/
|
||||
struct LineCommandData {
|
||||
Vec2 start;
|
||||
Vec2 end;
|
||||
Color color;
|
||||
float width;
|
||||
|
||||
LineCommandData() : start(), end(), color(Colors::White), width(1.0f) {}
|
||||
LineCommandData(const Vec2& s, const Vec2& e, const Color& c, float w)
|
||||
: start(s), end(e), color(c), width(w) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 矩形渲染命令数据
|
||||
*/
|
||||
struct RectCommandData {
|
||||
Rect rect;
|
||||
Color color;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
RectCommandData() : rect(), color(Colors::White), width(1.0f), filled(false) {}
|
||||
RectCommandData(const Rect& r, const Color& c, float w, bool f)
|
||||
: rect(r), color(c), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 圆形渲染命令数据
|
||||
*/
|
||||
struct CircleCommandData {
|
||||
Vec2 center;
|
||||
float radius;
|
||||
Color color;
|
||||
int segments;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
CircleCommandData() : center(), radius(0.0f), color(Colors::White),
|
||||
segments(32), width(1.0f), filled(false) {}
|
||||
CircleCommandData(const Vec2& c, float r, const Color& col, int seg, float w, bool f)
|
||||
: center(c), radius(r), color(col), segments(seg), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 三角形渲染命令数据
|
||||
*/
|
||||
struct TriangleCommandData {
|
||||
Vec2 p1, p2, p3;
|
||||
Color color;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
TriangleCommandData() : p1(), p2(), p3(), color(Colors::White),
|
||||
width(1.0f), filled(false) {}
|
||||
TriangleCommandData(const Vec2& a, const Vec2& b, const Vec2& c, const Color& col, float w, bool f)
|
||||
: p1(a), p2(b), p3(c), color(col), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 多边形渲染命令数据
|
||||
*/
|
||||
struct PolygonCommandData {
|
||||
std::vector<Vec2> points;
|
||||
Color color;
|
||||
float width;
|
||||
bool filled;
|
||||
|
||||
PolygonCommandData() : color(Colors::White), width(1.0f), filled(false) {}
|
||||
PolygonCommandData(std::vector<Vec2> pts, const Color& col, float w, bool f)
|
||||
: points(std::move(pts)), color(col), width(w), filled(f) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 文本渲染命令数据
|
||||
*/
|
||||
struct TextCommandData {
|
||||
const FontAtlas* font;
|
||||
std::string text;
|
||||
Vec2 position;
|
||||
Color color;
|
||||
|
||||
TextCommandData() : font(nullptr), text(), position(), color(Colors::White) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 统一渲染命令结构
|
||||
* 使用 variant 存储不同类型的命令数据,减少内存分配
|
||||
*/
|
||||
struct RenderCommand {
|
||||
RenderCommandType type;
|
||||
uint32_t layer; // 渲染层级,用于排序
|
||||
uint32_t order; // 提交顺序,保证同层级内稳定排序
|
||||
glm::mat4 transform; // 变换矩阵
|
||||
|
||||
// 使用 variant 存储具体数据
|
||||
std::variant<
|
||||
SpriteCommandData,
|
||||
LineCommandData,
|
||||
RectCommandData,
|
||||
CircleCommandData,
|
||||
TriangleCommandData,
|
||||
PolygonCommandData,
|
||||
TextCommandData
|
||||
> data;
|
||||
|
||||
RenderCommand() : type(RenderCommandType::None), layer(0), order(0),
|
||||
transform(1.0f) {}
|
||||
|
||||
// 便捷构造函数
|
||||
static RenderCommand makeSprite(const Texture* tex, const Rect& dest,
|
||||
const Rect& src, const Color& tint,
|
||||
float rot = 0.0f, const Vec2& anc = Vec2(0, 0),
|
||||
uint32_t lyr = 0);
|
||||
static RenderCommand makeLine(const Vec2& s, const Vec2& e, const Color& c,
|
||||
float w = 1.0f, uint32_t lyr = 0);
|
||||
static RenderCommand makeRect(const Rect& r, const Color& c,
|
||||
float w = 1.0f, bool fill = false, uint32_t lyr = 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染命令缓冲区
|
||||
* 用于收集和批量处理渲染命令
|
||||
*/
|
||||
class RenderCommandBuffer {
|
||||
public:
|
||||
static constexpr size_t INITIAL_CAPACITY = 1024;
|
||||
static constexpr size_t MAX_CAPACITY = 65536;
|
||||
|
||||
RenderCommandBuffer();
|
||||
~RenderCommandBuffer();
|
||||
|
||||
// 添加渲染命令
|
||||
void addCommand(const RenderCommand& cmd);
|
||||
void addCommand(RenderCommand&& cmd);
|
||||
|
||||
// 批量添加(预留空间后使用)
|
||||
RenderCommand& emplaceCommand();
|
||||
|
||||
// 排序命令(按纹理、层级等)
|
||||
void sortCommands();
|
||||
|
||||
// 清空缓冲区
|
||||
void clear();
|
||||
|
||||
// 获取命令列表
|
||||
const std::vector<RenderCommand>& getCommands() const { return commands_; }
|
||||
std::vector<RenderCommand>& getCommands() { return commands_; }
|
||||
|
||||
// 统计
|
||||
size_t size() const { return commands_.size(); }
|
||||
bool empty() const { return commands_.empty(); }
|
||||
size_t capacity() const { return commands_.capacity(); }
|
||||
|
||||
// 预分配空间
|
||||
void reserve(size_t capacity);
|
||||
|
||||
private:
|
||||
std::vector<RenderCommand> commands_;
|
||||
uint32_t nextOrder_;
|
||||
|
||||
// 排序比较函数
|
||||
static bool compareCommands(const RenderCommand& a, const RenderCommand& b);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标配置
|
||||
// ============================================================================
|
||||
struct RenderTargetConfig {
|
||||
int width = 800; // 宽度
|
||||
int height = 600; // 高度
|
||||
PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式
|
||||
bool hasDepth = true; // 是否包含深度缓冲
|
||||
bool hasDepthBuffer = true; // 兼容旧API的别名 (同hasDepth)
|
||||
bool hasStencil = false; // 是否包含模板缓冲
|
||||
int samples = 1; // 多重采样数 (1 = 无MSAA)
|
||||
bool autoResize = true; // 是否自动调整大小
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标 - 基于FBO的离屏渲染
|
||||
// ============================================================================
|
||||
class RenderTarget {
|
||||
public:
|
||||
RenderTarget();
|
||||
~RenderTarget();
|
||||
|
||||
// 禁止拷贝
|
||||
RenderTarget(const RenderTarget &) = delete;
|
||||
RenderTarget &operator=(const RenderTarget &) = delete;
|
||||
|
||||
// 允许移动
|
||||
RenderTarget(RenderTarget &&other) noexcept;
|
||||
RenderTarget &operator=(RenderTarget &&other) noexcept;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 创建和销毁
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 创建渲染目标
|
||||
*/
|
||||
bool create(const RenderTargetConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染目标(create的别名,兼容旧API)
|
||||
*/
|
||||
bool init(const RenderTargetConfig &config) { return create(config); }
|
||||
|
||||
/**
|
||||
* @brief 从现有纹理创建渲染目标
|
||||
*/
|
||||
bool createFromTexture(Ptr<Texture> texture, bool hasDepth = false);
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染目标
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 关闭渲染目标(destroy的别名,兼容旧API)
|
||||
*/
|
||||
void shutdown() { destroy(); }
|
||||
|
||||
/**
|
||||
* @brief 检查是否有效
|
||||
*/
|
||||
bool isValid() const { return fbo_ != 0; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 尺寸和格式
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
Vec2 getSize() const {
|
||||
return Vec2(static_cast<float>(width_), static_cast<float>(height_));
|
||||
}
|
||||
PixelFormat getColorFormat() const { return colorFormat_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 绑定和解绑
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 绑定为当前渲染目标
|
||||
*/
|
||||
void bind();
|
||||
|
||||
/**
|
||||
* @brief 解绑(恢复默认渲染目标)
|
||||
*/
|
||||
void unbind();
|
||||
|
||||
/**
|
||||
* @brief 清除渲染目标
|
||||
*/
|
||||
void clear(const Color &color = Colors::Transparent);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纹理访问
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取颜色纹理
|
||||
*/
|
||||
Ptr<Texture> getColorTexture() const { return colorTexture_; }
|
||||
|
||||
/**
|
||||
* @brief 获取深度纹理(如果有)
|
||||
*/
|
||||
Ptr<Texture> getDepthTexture() const { return depthTexture_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口和裁剪
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置视口(相对于渲染目标)
|
||||
*/
|
||||
void setViewport(int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 获取完整视口
|
||||
*/
|
||||
void getFullViewport(int &x, int &y, int &width, int &height) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 调整大小(会销毁并重新创建)
|
||||
*/
|
||||
bool resize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 复制到另一个渲染目标
|
||||
*/
|
||||
void copyTo(RenderTarget &target);
|
||||
|
||||
/**
|
||||
* @brief 复制到另一个渲染目标(blitTo的别名,兼容旧API)
|
||||
* @param target 目标渲染目标
|
||||
* @param color 是否复制颜色缓冲
|
||||
* @param depth 是否复制深度缓冲
|
||||
*/
|
||||
void blitTo(RenderTarget &target, bool color = true, bool depth = false);
|
||||
|
||||
/**
|
||||
* @brief 复制到屏幕
|
||||
*/
|
||||
void copyToScreen(int screenWidth, int screenHeight);
|
||||
|
||||
/**
|
||||
* @brief 保存为图像文件
|
||||
*/
|
||||
bool saveToFile(const std::string &filepath);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 创建渲染目标的静态工厂方法
|
||||
*/
|
||||
static Ptr<RenderTarget> createFromConfig(const RenderTargetConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取当前绑定的渲染目标ID
|
||||
*/
|
||||
static GLuint getCurrentFBO();
|
||||
|
||||
/**
|
||||
* @brief 绑定默认渲染目标(屏幕)
|
||||
*/
|
||||
static void bindDefault();
|
||||
|
||||
/**
|
||||
* @brief 获取FBO ID(供内部使用)
|
||||
*/
|
||||
GLuint getFBO() const { return fbo_; }
|
||||
|
||||
protected:
|
||||
GLuint fbo_ = 0; // 帧缓冲对象
|
||||
GLuint rbo_ = 0; // 渲染缓冲对象(深度/模板)
|
||||
|
||||
Ptr<Texture> colorTexture_; // 颜色纹理
|
||||
Ptr<Texture> depthTexture_; // 深度纹理(可选)
|
||||
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
PixelFormat colorFormat_ = PixelFormat::RGBA8;
|
||||
bool hasDepth_ = false;
|
||||
bool hasStencil_ = false;
|
||||
int samples_ = 1;
|
||||
|
||||
bool createFBO();
|
||||
void deleteFBO();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 多重采样渲染目标(用于MSAA)
|
||||
// ============================================================================
|
||||
class MultisampleRenderTarget : public RenderTarget {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建多重采样渲染目标
|
||||
*/
|
||||
bool create(int width, int height, int samples = 4);
|
||||
|
||||
/**
|
||||
* @brief 解析到普通渲染目标(用于显示)
|
||||
*/
|
||||
void resolveTo(RenderTarget &target);
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染目标
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
GLuint colorRBO_ = 0; // 多重采样颜色渲染缓冲
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标栈(用于嵌套渲染)
|
||||
// ============================================================================
|
||||
class RenderTargetStack {
|
||||
public:
|
||||
static RenderTargetStack &getInstance();
|
||||
|
||||
/**
|
||||
* @brief 压入渲染目标
|
||||
*/
|
||||
void push(RenderTarget *target);
|
||||
|
||||
/**
|
||||
* @brief 弹出渲染目标
|
||||
*/
|
||||
void pop();
|
||||
|
||||
/**
|
||||
* @brief 获取当前渲染目标
|
||||
*/
|
||||
RenderTarget *getCurrent() const;
|
||||
|
||||
/**
|
||||
* @brief 获取栈大小
|
||||
*/
|
||||
size_t size() const;
|
||||
|
||||
/**
|
||||
* @brief 清空栈
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
RenderTargetStack() = default;
|
||||
~RenderTargetStack() = default;
|
||||
|
||||
std::vector<RenderTarget *> stack_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 渲染目标管理器 - 全局渲染目标管理
|
||||
// ============================================================================
|
||||
class RenderTargetManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static RenderTargetManager &getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染目标管理器
|
||||
* @param width 默认宽度
|
||||
* @param height 默认高度
|
||||
*/
|
||||
bool init(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 关闭渲染目标管理器
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 创建新的渲染目标
|
||||
*/
|
||||
Ptr<RenderTarget> createRenderTarget(const RenderTargetConfig &config);
|
||||
|
||||
/**
|
||||
* @brief 获取默认渲染目标
|
||||
*/
|
||||
RenderTarget *getDefaultRenderTarget() const {
|
||||
return defaultRenderTarget_.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整所有受管渲染目标的大小
|
||||
*/
|
||||
void resize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
*/
|
||||
bool isInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
RenderTargetManager() = default;
|
||||
~RenderTargetManager() = default;
|
||||
RenderTargetManager(const RenderTargetManager &) = delete;
|
||||
RenderTargetManager &operator=(const RenderTargetManager &) = delete;
|
||||
|
||||
Ptr<RenderTarget> defaultRenderTarget_;
|
||||
std::vector<Ptr<RenderTarget>> renderTargets_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::getInstance()
|
||||
#define E2D_RENDER_TARGET_MANAGER() \
|
||||
::extra2d::RenderTargetManager::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
struct WaterParams {
|
||||
float waveSpeed = 1.0f;
|
||||
float waveAmplitude = 0.02f;
|
||||
float waveFrequency = 4.0f;
|
||||
};
|
||||
|
||||
struct OutlineParams {
|
||||
Color color = Colors::Black;
|
||||
float thickness = 2.0f;
|
||||
};
|
||||
|
||||
struct DistortionParams {
|
||||
float distortionAmount = 0.02f;
|
||||
float timeScale = 1.0f;
|
||||
};
|
||||
|
||||
struct PixelateParams {
|
||||
float pixelSize = 8.0f;
|
||||
};
|
||||
|
||||
struct InvertParams {
|
||||
float strength = 1.0f;
|
||||
};
|
||||
|
||||
struct GrayscaleParams {
|
||||
float intensity = 1.0f;
|
||||
};
|
||||
|
||||
struct BlurParams {
|
||||
float radius = 5.0f;
|
||||
};
|
||||
|
||||
namespace ShaderSource {
|
||||
|
||||
static const char* StandardVert = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
layout(location = 0) in vec2 a_position;
|
||||
layout(location = 1) in vec2 a_texCoord;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
|
||||
uniform mat4 u_viewProjection;
|
||||
uniform mat4 u_model;
|
||||
|
||||
out vec2 v_texCoord;
|
||||
out vec4 v_color;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0);
|
||||
v_texCoord = a_texCoord;
|
||||
v_color = a_color;
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* StandardFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
fragColor = texColor * v_color;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* WaterFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_waveSpeed;
|
||||
uniform float u_waveAmplitude;
|
||||
uniform float u_waveFrequency;
|
||||
uniform float u_time;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 uv = v_texCoord;
|
||||
|
||||
// 水波纹效果
|
||||
float wave = sin(uv.y * u_waveFrequency + u_time * u_waveSpeed) * u_waveAmplitude;
|
||||
uv.x += wave;
|
||||
|
||||
vec4 texColor = texture(u_texture, uv);
|
||||
fragColor = texColor * v_color;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* OutlineFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_outlineColor;
|
||||
uniform float u_thickness;
|
||||
uniform vec2 u_textureSize;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(u_texture, v_texCoord);
|
||||
|
||||
// 简单的描边检测
|
||||
float alpha = 0.0;
|
||||
vec2 offset = u_thickness / u_textureSize;
|
||||
|
||||
alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a;
|
||||
alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a;
|
||||
alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a;
|
||||
alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a;
|
||||
|
||||
if (color.a < 0.1 && alpha > 0.0) {
|
||||
fragColor = u_outlineColor;
|
||||
} else {
|
||||
fragColor = color;
|
||||
}
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* DistortionFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_distortionAmount;
|
||||
uniform float u_time;
|
||||
uniform float u_timeScale;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 uv = v_texCoord;
|
||||
|
||||
// 扭曲效果
|
||||
float t = u_time * u_timeScale;
|
||||
float dx = sin(uv.y * 10.0 + t) * u_distortionAmount;
|
||||
float dy = cos(uv.x * 10.0 + t) * u_distortionAmount;
|
||||
uv += vec2(dx, dy);
|
||||
|
||||
vec4 texColor = texture(u_texture, uv);
|
||||
fragColor = texColor * v_color;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* PixelateFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_pixelSize;
|
||||
uniform vec2 u_textureSize;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 pixel = u_pixelSize / u_textureSize;
|
||||
vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5;
|
||||
|
||||
vec4 texColor = texture(u_texture, uv);
|
||||
fragColor = texColor * v_color;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* InvertFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_strength;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord) * v_color;
|
||||
vec3 inverted = vec3(1.0) - texColor.rgb;
|
||||
texColor.rgb = mix(texColor.rgb, inverted, u_strength);
|
||||
|
||||
fragColor = texColor;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* GrayscaleFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_intensity;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord) * v_color;
|
||||
|
||||
float gray = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||
texColor.rgb = mix(texColor.rgb, vec3(gray), u_intensity);
|
||||
|
||||
fragColor = texColor;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
static const char* BlurFrag = R"(
|
||||
#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_radius;
|
||||
uniform vec2 u_textureSize;
|
||||
uniform float u_opacity;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec2 texel = u_radius / u_textureSize;
|
||||
|
||||
vec4 sum = vec4(0.0);
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, -1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, -1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, -1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 0.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 0.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 0.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 1.0));
|
||||
sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 1.0));
|
||||
|
||||
vec4 texColor = sum / 9.0;
|
||||
fragColor = texColor * v_color;
|
||||
fragColor.a *= u_opacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
} // namespace ShaderSource
|
||||
|
||||
class ShaderPreset {
|
||||
public:
|
||||
static Ptr<GLShader> Water(const WaterParams& params);
|
||||
static Ptr<GLShader> Outline(const OutlineParams& params);
|
||||
static Ptr<GLShader> Distortion(const DistortionParams& params);
|
||||
static Ptr<GLShader> Pixelate(const PixelateParams& params);
|
||||
static Ptr<GLShader> Invert(const InvertParams& params);
|
||||
static Ptr<GLShader> Grayscale(const GrayscaleParams& params);
|
||||
static Ptr<GLShader> Blur(const BlurParams& params);
|
||||
|
||||
static Ptr<GLShader> GrayscaleOutline(const GrayscaleParams& grayParams,
|
||||
const OutlineParams& outlineParams);
|
||||
static Ptr<GLShader> PixelateInvert(const PixelateParams& pixParams,
|
||||
const InvertParams& invParams);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/opengl/gl_shader.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Shader参数绑定回调
|
||||
// ============================================================================
|
||||
using ShaderBindCallback = std::function<void(GLShader &)>;
|
||||
|
||||
// ============================================================================
|
||||
// Shader系统 - 管理所有Shader的加载、缓存和热重载
|
||||
// ============================================================================
|
||||
class ShaderSystem {
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// 单例访问
|
||||
// ------------------------------------------------------------------------
|
||||
static ShaderSystem &getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 初始化和关闭
|
||||
// ------------------------------------------------------------------------
|
||||
bool init();
|
||||
void shutdown();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Shader加载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从文件加载Shader
|
||||
* @param name Shader名称(用于缓存)
|
||||
* @param vertPath 顶点着色器文件路径
|
||||
* @param fragPath 片段着色器文件路径
|
||||
* @return 加载的Shader,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> loadFromFile(const std::string &name,
|
||||
const std::string &vertPath,
|
||||
const std::string &fragPath);
|
||||
|
||||
/**
|
||||
* @brief 从源码字符串加载Shader
|
||||
* @param name Shader名称(用于缓存)
|
||||
* @param vertSource 顶点着色器源码
|
||||
* @param fragSource 片段着色器源码
|
||||
* @return 加载的Shader,失败返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> loadFromSource(const std::string &name,
|
||||
const std::string &vertSource,
|
||||
const std::string &fragSource);
|
||||
|
||||
/**
|
||||
* @brief 从已编译的程序获取Shader
|
||||
* @param name Shader名称
|
||||
* @return 缓存的Shader,不存在返回nullptr
|
||||
*/
|
||||
Ptr<GLShader> get(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 检查Shader是否存在
|
||||
*/
|
||||
bool has(const std::string &name) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Shader移除
|
||||
// ------------------------------------------------------------------------
|
||||
void remove(const std::string &name);
|
||||
void clear();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 热重载支持
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用文件监视
|
||||
*/
|
||||
void setFileWatching(bool enable);
|
||||
bool isFileWatching() const { return fileWatching_; }
|
||||
|
||||
/**
|
||||
* @brief 更新文件监视(在主循环中调用)
|
||||
*/
|
||||
void updateFileWatching();
|
||||
|
||||
/**
|
||||
* @brief 重载指定Shader
|
||||
*/
|
||||
bool reload(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief 重载所有Shader
|
||||
*/
|
||||
void reloadAll();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内置Shader获取
|
||||
// ------------------------------------------------------------------------
|
||||
Ptr<GLShader> getBuiltinSpriteShader();
|
||||
Ptr<GLShader> getBuiltinParticleShader();
|
||||
Ptr<GLShader> getBuiltinPostProcessShader();
|
||||
Ptr<GLShader> getBuiltinShapeShader();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 从文件读取文本内容
|
||||
*/
|
||||
static std::string readFile(const std::string &filepath);
|
||||
|
||||
/**
|
||||
* @brief 获取Shader文件的最后修改时间
|
||||
*/
|
||||
static uint64_t getFileModifiedTime(const std::string &filepath);
|
||||
|
||||
private:
|
||||
ShaderSystem() = default;
|
||||
~ShaderSystem() = default;
|
||||
ShaderSystem(const ShaderSystem &) = delete;
|
||||
ShaderSystem &operator=(const ShaderSystem &) = delete;
|
||||
|
||||
struct ShaderInfo {
|
||||
Ptr<GLShader> shader;
|
||||
std::string vertPath;
|
||||
std::string fragPath;
|
||||
uint64_t vertModifiedTime;
|
||||
uint64_t fragModifiedTime;
|
||||
bool isBuiltin;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, ShaderInfo> shaders_;
|
||||
bool fileWatching_ = false;
|
||||
float watchTimer_ = 0.0f;
|
||||
static constexpr float WATCH_INTERVAL = 1.0f; // 检查间隔(秒)
|
||||
|
||||
// 内置Shader缓存
|
||||
Ptr<GLShader> builtinSpriteShader_;
|
||||
Ptr<GLShader> builtinParticleShader_;
|
||||
Ptr<GLShader> builtinPostProcessShader_;
|
||||
Ptr<GLShader> builtinShapeShader_;
|
||||
|
||||
bool loadBuiltinShaders();
|
||||
void checkAndReload();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Shader参数包装器 - 简化Uniform设置
|
||||
// ============================================================================
|
||||
class ShaderParams {
|
||||
public:
|
||||
explicit ShaderParams(GLShader &shader);
|
||||
|
||||
ShaderParams &setBool(const std::string &name, bool value);
|
||||
ShaderParams &setInt(const std::string &name, int value);
|
||||
ShaderParams &setFloat(const std::string &name, float value);
|
||||
ShaderParams &setVec2(const std::string &name, const glm::vec2 &value);
|
||||
ShaderParams &setVec3(const std::string &name, const glm::vec3 &value);
|
||||
ShaderParams &setVec4(const std::string &name, const glm::vec4 &value);
|
||||
ShaderParams &setMat4(const std::string &name, const glm::mat4 &value);
|
||||
ShaderParams &setColor(const std::string &name, const Color &color);
|
||||
|
||||
private:
|
||||
GLShader &shader_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷宏
|
||||
// ============================================================================
|
||||
#define E2D_SHADER_SYSTEM() ::extra2d::ShaderSystem::getInstance()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 像素格式枚举
|
||||
// ============================================================================
|
||||
enum class PixelFormat {
|
||||
R8, // 单通道灰度
|
||||
RG8, // 双通道
|
||||
RGB8, // RGB 24位
|
||||
RGBA8, // RGBA 32位(默认)
|
||||
RGB16F, // RGB 半精度浮点
|
||||
RGBA16F, // RGBA 半精度浮点
|
||||
RGB32F, // RGB 全精度浮点
|
||||
RGBA32F, // RGBA 全精度浮点
|
||||
Depth16, // 16位深度
|
||||
Depth24, // 24位深度
|
||||
Depth32F, // 32位浮点深度
|
||||
Depth24Stencil8, // 24位深度 + 8位模板
|
||||
|
||||
// 压缩纹理格式
|
||||
ETC2_RGB8, // ETC2 RGB 压缩
|
||||
ETC2_RGBA8, // ETC2 RGBA 压缩
|
||||
ASTC_4x4, // ASTC 4x4 压缩
|
||||
ASTC_6x6, // ASTC 6x6 压缩
|
||||
ASTC_8x8 // ASTC 8x8 压缩
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理接口
|
||||
// ============================================================================
|
||||
class Texture {
|
||||
public:
|
||||
virtual ~Texture() = default;
|
||||
|
||||
// 获取尺寸
|
||||
virtual int getWidth() const = 0;
|
||||
virtual int getHeight() const = 0;
|
||||
virtual Size getSize() const = 0;
|
||||
|
||||
// 获取通道数
|
||||
virtual int getChannels() const = 0;
|
||||
|
||||
// 获取像素格式
|
||||
virtual PixelFormat getFormat() const = 0;
|
||||
|
||||
// 获取原始句柄(用于底层渲染)
|
||||
virtual void* getNativeHandle() const = 0;
|
||||
|
||||
// 是否有效
|
||||
virtual bool isValid() const = 0;
|
||||
|
||||
// 设置过滤模式
|
||||
virtual void setFilter(bool linear) = 0;
|
||||
|
||||
// 设置环绕模式
|
||||
virtual void setWrap(bool repeat) = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/graphics/opengl/gl_texture.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 纹理图集 - 自动将小纹理合并到大图集以减少 DrawCall
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 图集中的单个纹理条目
|
||||
*/
|
||||
struct AtlasEntry {
|
||||
std::string name; // 原始纹理名称/路径
|
||||
Rect uvRect; // 在图集中的 UV 坐标范围
|
||||
Vec2 originalSize; // 原始纹理尺寸
|
||||
uint32_t padding; // 边距(用于避免纹理 bleeding)
|
||||
|
||||
AtlasEntry() : uvRect(), originalSize(), padding(2) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理图集页面
|
||||
* 当单个图集放不下时,创建多个页面
|
||||
*/
|
||||
class TextureAtlasPage {
|
||||
public:
|
||||
static constexpr int DEFAULT_SIZE = 2048;
|
||||
static constexpr int MAX_SIZE = 4096;
|
||||
static constexpr int MIN_TEXTURE_SIZE = 32; // 小于此大小的纹理才考虑合并
|
||||
static constexpr int PADDING = 2; // 纹理间边距
|
||||
|
||||
TextureAtlasPage(int width = DEFAULT_SIZE, int height = DEFAULT_SIZE);
|
||||
~TextureAtlasPage();
|
||||
|
||||
// 尝试添加纹理到图集
|
||||
// 返回是否成功,如果成功则输出 uvRect
|
||||
bool tryAddTexture(const std::string& name, int texWidth, int texHeight,
|
||||
const uint8_t* pixels, Rect& outUvRect);
|
||||
|
||||
// 获取图集纹理
|
||||
Ptr<Texture> getTexture() const { return texture_; }
|
||||
|
||||
// 获取条目
|
||||
const AtlasEntry* getEntry(const std::string& name) const;
|
||||
|
||||
// 获取使用率
|
||||
float getUsageRatio() const;
|
||||
|
||||
// 获取尺寸
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
|
||||
// 是否已满
|
||||
bool isFull() const { return isFull_; }
|
||||
|
||||
private:
|
||||
int width_, height_;
|
||||
Ptr<Texture> texture_;
|
||||
std::unordered_map<std::string, AtlasEntry> entries_;
|
||||
|
||||
// 矩形打包数据
|
||||
struct PackNode {
|
||||
int x, y, width, height;
|
||||
bool used;
|
||||
std::unique_ptr<PackNode> left;
|
||||
std::unique_ptr<PackNode> right;
|
||||
|
||||
PackNode(int x_, int y_, int w, int h)
|
||||
: x(x_), y(y_), width(w), height(h), used(false) {}
|
||||
};
|
||||
|
||||
std::unique_ptr<PackNode> root_;
|
||||
bool isFull_;
|
||||
int usedArea_;
|
||||
|
||||
// 递归插入
|
||||
PackNode* insert(PackNode* node, int width, int height);
|
||||
void writePixels(int x, int y, int w, int h, const uint8_t* pixels);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理图集管理器
|
||||
* 自动管理多个图集页面,提供统一的纹理查询接口
|
||||
*/
|
||||
class TextureAtlas {
|
||||
public:
|
||||
TextureAtlas();
|
||||
~TextureAtlas();
|
||||
|
||||
// 初始化
|
||||
void init(int pageSize = TextureAtlasPage::DEFAULT_SIZE);
|
||||
|
||||
// 添加纹理到图集
|
||||
// 如果纹理太大,返回 false,应该作为独立纹理加载
|
||||
bool addTexture(const std::string& name, int width, int height,
|
||||
const uint8_t* pixels);
|
||||
|
||||
// 查询纹理是否在图集中
|
||||
bool contains(const std::string& name) const;
|
||||
|
||||
// 获取纹理在图集中的信息
|
||||
// 返回图集纹理和 UV 坐标
|
||||
const Texture* getAtlasTexture(const std::string& name) const;
|
||||
Rect getUVRect(const std::string& name) const;
|
||||
|
||||
// 获取原始纹理尺寸
|
||||
Vec2 getOriginalSize(const std::string& name) const;
|
||||
|
||||
// 获取所有图集页面
|
||||
const std::vector<std::unique_ptr<TextureAtlasPage>>& getPages() const { return pages_; }
|
||||
|
||||
// 获取总使用率
|
||||
float getTotalUsageRatio() const;
|
||||
|
||||
// 清空所有图集
|
||||
void clear();
|
||||
|
||||
// 设置是否启用自动图集
|
||||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
// 设置纹理大小阈值(小于此大小的纹理才进入图集)
|
||||
void setSizeThreshold(int threshold) { sizeThreshold_ = threshold; }
|
||||
int getSizeThreshold() const { return sizeThreshold_; }
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<TextureAtlasPage>> pages_;
|
||||
std::unordered_map<std::string, TextureAtlasPage*> entryToPage_;
|
||||
|
||||
int pageSize_;
|
||||
int sizeThreshold_;
|
||||
bool enabled_;
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 全局图集管理器(单例)
|
||||
*/
|
||||
class TextureAtlasManager {
|
||||
public:
|
||||
static TextureAtlasManager& getInstance();
|
||||
|
||||
// 获取主图集
|
||||
TextureAtlas& getAtlas() { return atlas_; }
|
||||
|
||||
// 快捷方法
|
||||
bool addTexture(const std::string& name, int width, int height,
|
||||
const uint8_t* pixels) {
|
||||
return atlas_.addTexture(name, width, height, pixels);
|
||||
}
|
||||
|
||||
bool contains(const std::string& name) const {
|
||||
return atlas_.contains(name);
|
||||
}
|
||||
|
||||
const Texture* getAtlasTexture(const std::string& name) const {
|
||||
return atlas_.getAtlasTexture(name);
|
||||
}
|
||||
|
||||
Rect getUVRect(const std::string& name) const {
|
||||
return atlas_.getUVRect(name);
|
||||
}
|
||||
|
||||
private:
|
||||
TextureAtlasManager() = default;
|
||||
~TextureAtlasManager() = default;
|
||||
|
||||
TextureAtlasManager(const TextureAtlasManager&) = delete;
|
||||
TextureAtlasManager& operator=(const TextureAtlasManager&) = delete;
|
||||
|
||||
TextureAtlas atlas_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// VRAM 管理器 - 跟踪显存使用情况
|
||||
// ============================================================================
|
||||
class VRAMManager {
|
||||
public:
|
||||
static VRAMManager& getInstance();
|
||||
|
||||
// 纹理显存跟踪
|
||||
void allocTexture(size_t size);
|
||||
void freeTexture(size_t size);
|
||||
|
||||
// VBO/FBO 显存跟踪
|
||||
void allocBuffer(size_t size);
|
||||
void freeBuffer(size_t size);
|
||||
|
||||
// 查询显存使用情况
|
||||
size_t getUsedVRAM() const;
|
||||
size_t getTextureVRAM() const;
|
||||
size_t getBufferVRAM() const;
|
||||
size_t getAvailableVRAM() const;
|
||||
|
||||
// 显存预算管理
|
||||
void setVRAMBudget(size_t budget);
|
||||
size_t getVRAMBudget() const;
|
||||
bool isOverBudget() const;
|
||||
|
||||
// 统计信息
|
||||
void printStats() const;
|
||||
|
||||
// 重置计数器
|
||||
void reset();
|
||||
|
||||
private:
|
||||
VRAMManager();
|
||||
~VRAMManager() = default;
|
||||
VRAMManager(const VRAMManager&) = delete;
|
||||
VRAMManager& operator=(const VRAMManager&) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
size_t textureVRAM_;
|
||||
size_t bufferVRAM_;
|
||||
size_t vramBudget_;
|
||||
|
||||
// 统计
|
||||
uint32_t textureAllocCount_;
|
||||
uint32_t textureFreeCount_;
|
||||
uint32_t bufferAllocCount_;
|
||||
uint32_t bufferFreeCount_;
|
||||
size_t peakTextureVRAM_;
|
||||
size_t peakBufferVRAM_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/input_codes.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标按钮枚举
|
||||
// ============================================================================
|
||||
enum class MouseButton {
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Middle = 2,
|
||||
Button4 = 3,
|
||||
Button5 = 4,
|
||||
Button6 = 5,
|
||||
Button7 = 6,
|
||||
Button8 = 7,
|
||||
Count = 8
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Input 类 - 跨平台输入管理
|
||||
// 支持: 键盘、鼠标、手柄、触摸屏
|
||||
// ============================================================================
|
||||
class Input {
|
||||
public:
|
||||
Input();
|
||||
~Input();
|
||||
|
||||
// 初始化
|
||||
void init();
|
||||
void shutdown();
|
||||
|
||||
// 每帧更新
|
||||
void update();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 键盘输入
|
||||
// ------------------------------------------------------------------------
|
||||
bool isKeyDown(int keyCode) const;
|
||||
bool isKeyPressed(int keyCode) const;
|
||||
bool isKeyReleased(int keyCode) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 手柄按钮
|
||||
// ------------------------------------------------------------------------
|
||||
bool isButtonDown(int button) const;
|
||||
bool isButtonPressed(int button) const;
|
||||
bool isButtonReleased(int button) const;
|
||||
|
||||
// 摇杆
|
||||
Vec2 getLeftStick() const;
|
||||
Vec2 getRightStick() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 鼠标输入
|
||||
// ------------------------------------------------------------------------
|
||||
bool isMouseDown(MouseButton button) const;
|
||||
bool isMousePressed(MouseButton button) const;
|
||||
bool isMouseReleased(MouseButton button) const;
|
||||
|
||||
Vec2 getMousePosition() const;
|
||||
Vec2 getMouseDelta() const;
|
||||
float getMouseScroll() const { return mouseScroll_; }
|
||||
float getMouseScrollDelta() const { return mouseScroll_ - prevMouseScroll_; }
|
||||
|
||||
void setMousePosition(const Vec2 &position);
|
||||
void setMouseVisible(bool visible);
|
||||
void setMouseLocked(bool locked);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 触摸屏 (Switch 原生支持,PC 端模拟或禁用)
|
||||
// ------------------------------------------------------------------------
|
||||
bool isTouching() const { return touching_; }
|
||||
Vec2 getTouchPosition() const { return touchPosition_; }
|
||||
int getTouchCount() const { return touchCount_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 便捷方法
|
||||
// ------------------------------------------------------------------------
|
||||
bool isAnyKeyDown() const;
|
||||
bool isAnyMouseDown() const;
|
||||
|
||||
private:
|
||||
static constexpr int MAX_BUTTONS = SDL_CONTROLLER_BUTTON_MAX;
|
||||
static constexpr int MAX_KEYS = SDL_NUM_SCANCODES;
|
||||
|
||||
SDL_GameController *controller_;
|
||||
|
||||
// 键盘状态 (PC 端使用)
|
||||
std::array<bool, MAX_KEYS> keysDown_;
|
||||
std::array<bool, MAX_KEYS> prevKeysDown_;
|
||||
|
||||
// 手柄按钮状态
|
||||
std::array<bool, MAX_BUTTONS> buttonsDown_;
|
||||
std::array<bool, MAX_BUTTONS> prevButtonsDown_;
|
||||
|
||||
// 摇杆状态
|
||||
float leftStickX_;
|
||||
float leftStickY_;
|
||||
float rightStickX_;
|
||||
float rightStickY_;
|
||||
|
||||
// 鼠标状态 (PC 端使用)
|
||||
Vec2 mousePosition_;
|
||||
Vec2 prevMousePosition_;
|
||||
float mouseScroll_;
|
||||
float prevMouseScroll_;
|
||||
std::array<bool, 8> mouseButtonsDown_;
|
||||
std::array<bool, 8> prevMouseButtonsDown_;
|
||||
|
||||
// 触摸屏状态 (Switch 原生)
|
||||
bool touching_;
|
||||
bool prevTouching_;
|
||||
Vec2 touchPosition_;
|
||||
Vec2 prevTouchPosition_;
|
||||
int touchCount_;
|
||||
|
||||
// 映射键盘 keyCode 到 SDL GameController 按钮 (Switch 兼容模式)
|
||||
SDL_GameControllerButton mapKeyToButton(int keyCode) const;
|
||||
|
||||
// 更新键盘状态
|
||||
void updateKeyboard();
|
||||
|
||||
// 更新鼠标状态
|
||||
void updateMouse();
|
||||
|
||||
// 更新手柄状态
|
||||
void updateGamepad();
|
||||
|
||||
// 更新触摸屏状态
|
||||
void updateTouch();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/string.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <functional>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class EventQueue;
|
||||
class Input;
|
||||
|
||||
// ============================================================================
|
||||
// 窗口配置
|
||||
// ============================================================================
|
||||
struct WindowConfig {
|
||||
std::string title = "Extra2D Application";
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
bool fullscreen = true;
|
||||
bool resizable = false;
|
||||
bool vsync = true;
|
||||
int msaaSamples = 0;
|
||||
bool centerWindow = true;
|
||||
bool enableCursors = true;
|
||||
bool enableDpiScale = true;
|
||||
bool fullscreenDesktop = true; // true: SDL_WINDOW_FULLSCREEN_DESKTOP, false: SDL_WINDOW_FULLSCREEN
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标光标形状枚举
|
||||
// ============================================================================
|
||||
enum class CursorShape {
|
||||
Arrow,
|
||||
IBeam,
|
||||
Crosshair,
|
||||
Hand,
|
||||
HResize,
|
||||
VResize,
|
||||
ResizeAll,
|
||||
ResizeNWSE,
|
||||
ResizeNESW
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Window 类 - SDL2 Window + GLES 3.2 封装
|
||||
// 支持平台: Nintendo Switch, Windows, Linux, macOS
|
||||
// ============================================================================
|
||||
class Window {
|
||||
public:
|
||||
Window();
|
||||
~Window();
|
||||
|
||||
// 创建窗口
|
||||
bool create(const WindowConfig& config);
|
||||
void destroy();
|
||||
|
||||
// 窗口操作
|
||||
void pollEvents();
|
||||
void swapBuffers();
|
||||
bool shouldClose() const;
|
||||
void setShouldClose(bool close);
|
||||
|
||||
// 窗口属性
|
||||
void setTitle(const std::string& title);
|
||||
void setSize(int width, int height);
|
||||
void setPosition(int x, int y);
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setVSync(bool enabled);
|
||||
void setResizable(bool resizable);
|
||||
|
||||
// 获取窗口属性
|
||||
int getWidth() const { return width_; }
|
||||
int getHeight() const { return height_; }
|
||||
Size getSize() const { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
||||
Vec2 getPosition() const;
|
||||
bool isFullscreen() const { return fullscreen_; }
|
||||
bool isVSync() const { return vsync_; }
|
||||
|
||||
// DPI 缩放 (PC 端自动检测,Switch 固定 1.0)
|
||||
float getContentScaleX() const;
|
||||
float getContentScaleY() const;
|
||||
Vec2 getContentScale() const;
|
||||
|
||||
// 窗口状态
|
||||
bool isFocused() const { return focused_; }
|
||||
bool isMinimized() const;
|
||||
bool isMaximized() const;
|
||||
|
||||
// 获取 SDL2 窗口和 GL 上下文
|
||||
SDL_Window* getSDLWindow() const { return sdlWindow_; }
|
||||
SDL_GLContext getGLContext() const { return glContext_; }
|
||||
|
||||
// 设置/获取用户数据
|
||||
void setUserData(void* data) { userData_ = data; }
|
||||
void* getUserData() const { return userData_; }
|
||||
|
||||
// 事件队列
|
||||
void setEventQueue(EventQueue* queue) { eventQueue_ = queue; }
|
||||
EventQueue* getEventQueue() const { return eventQueue_; }
|
||||
|
||||
// 获取输入管理器
|
||||
Input* getInput() const { return input_.get(); }
|
||||
|
||||
// 光标操作 (PC 端有效,Switch 上为空操作)
|
||||
void setCursor(CursorShape shape);
|
||||
void resetCursor();
|
||||
void setMouseVisible(bool visible);
|
||||
|
||||
// 窗口回调
|
||||
using ResizeCallback = std::function<void(int width, int height)>;
|
||||
using FocusCallback = std::function<void(bool focused)>;
|
||||
using CloseCallback = std::function<void()>;
|
||||
|
||||
void setResizeCallback(ResizeCallback callback) { resizeCallback_ = callback; }
|
||||
void setFocusCallback(FocusCallback callback) { focusCallback_ = callback; }
|
||||
void setCloseCallback(CloseCallback callback) { closeCallback_ = callback; }
|
||||
|
||||
private:
|
||||
// SDL2 状态
|
||||
SDL_Window* sdlWindow_;
|
||||
SDL_GLContext glContext_;
|
||||
SDL_Cursor* sdlCursors_[9]; // 光标缓存
|
||||
SDL_Cursor* currentCursor_;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
bool vsync_;
|
||||
bool shouldClose_;
|
||||
bool fullscreen_;
|
||||
bool focused_;
|
||||
float contentScaleX_;
|
||||
float contentScaleY_;
|
||||
bool enableDpiScale_;
|
||||
void* userData_;
|
||||
EventQueue* eventQueue_;
|
||||
UniquePtr<Input> input_;
|
||||
|
||||
ResizeCallback resizeCallback_;
|
||||
FocusCallback focusCallback_;
|
||||
CloseCallback closeCallback_;
|
||||
|
||||
bool initSDL(const WindowConfig& config);
|
||||
void deinitSDL();
|
||||
void initCursors();
|
||||
void deinitCursors();
|
||||
void updateContentScale();
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,356 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/audio/sound.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/alpha_mask.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 资源管理器 - 统一管理纹理、字体、音效等资源
|
||||
// 支持异步加载和纹理压缩
|
||||
// ============================================================================
|
||||
|
||||
// 纹理格式枚举
|
||||
enum class TextureFormat {
|
||||
Auto = 0, // 自动选择最佳格式
|
||||
RGBA8, // 32位 RGBA
|
||||
RGB8, // 24位 RGB
|
||||
DXT1, // BC1/DXT1 压缩(1 bit alpha)
|
||||
DXT5, // BC3/DXT5 压缩(完整 alpha)
|
||||
ETC2, // ETC2 压缩(移动平台)
|
||||
ASTC4x4, // ASTC 4x4 压缩(高质量)
|
||||
ASTC8x8, // ASTC 8x8 压缩(高压缩率)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 纹理LRU缓存项
|
||||
// ============================================================================
|
||||
struct TextureCacheEntry {
|
||||
Ptr<Texture> texture;
|
||||
size_t size = 0; // 纹理大小(字节)
|
||||
float lastAccessTime = 0.0f; // 最后访问时间
|
||||
uint32_t accessCount = 0; // 访问次数
|
||||
};
|
||||
|
||||
// 异步加载回调类型
|
||||
using TextureLoadCallback = std::function<void(Ptr<Texture>)>;
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// 单例访问
|
||||
// ------------------------------------------------------------------------
|
||||
static ResourceManager &getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纹理资源 - 同步加载
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载纹理(带缓存)
|
||||
Ptr<Texture> loadTexture(const std::string &filepath);
|
||||
|
||||
/// 加载纹理(指定是否异步)
|
||||
Ptr<Texture> loadTexture(const std::string &filepath, bool async);
|
||||
|
||||
/// 加载纹理(完整参数:异步 + 压缩格式)
|
||||
Ptr<Texture> loadTexture(const std::string &filepath, bool async, TextureFormat format);
|
||||
|
||||
/// 异步加载纹理(带回调)
|
||||
void loadTextureAsync(const std::string &filepath, TextureLoadCallback callback);
|
||||
|
||||
/// 异步加载纹理(指定格式 + 回调)
|
||||
void loadTextureAsync(const std::string &filepath, TextureFormat format, TextureLoadCallback callback);
|
||||
|
||||
/// 加载纹理并生成Alpha遮罩(用于不规则形状图片)
|
||||
Ptr<Texture> loadTextureWithAlphaMask(const std::string &filepath);
|
||||
|
||||
/// 通过key获取已缓存的纹理
|
||||
Ptr<Texture> getTexture(const std::string &key) const;
|
||||
|
||||
/// 检查纹理是否已缓存
|
||||
bool hasTexture(const std::string &key) const;
|
||||
|
||||
/// 卸载指定纹理
|
||||
void unloadTexture(const std::string &key);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Alpha遮罩资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 获取纹理的Alpha遮罩(如果已生成)
|
||||
const AlphaMask *getAlphaMask(const std::string &textureKey) const;
|
||||
|
||||
/// 为已加载的纹理生成Alpha遮罩
|
||||
bool generateAlphaMask(const std::string &textureKey);
|
||||
|
||||
/// 检查纹理是否有Alpha遮罩
|
||||
bool hasAlphaMask(const std::string &textureKey) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体图集资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载字体图集(带缓存)
|
||||
Ptr<FontAtlas> loadFont(const std::string &filepath, int fontSize,
|
||||
bool useSDF = false);
|
||||
|
||||
/// 通过key获取已缓存的字体图集
|
||||
Ptr<FontAtlas> getFont(const std::string &key) const;
|
||||
|
||||
/// 检查字体是否已缓存
|
||||
bool hasFont(const std::string &key) const;
|
||||
|
||||
/// 卸载指定字体
|
||||
void unloadFont(const std::string &key);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 音效资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载音效(带缓存)
|
||||
Ptr<Sound> loadSound(const std::string &filepath);
|
||||
Ptr<Sound> loadSound(const std::string &name, const std::string &filepath);
|
||||
|
||||
/// 通过key获取已缓存的音效
|
||||
Ptr<Sound> getSound(const std::string &key) const;
|
||||
|
||||
/// 检查音效是否已缓存
|
||||
bool hasSound(const std::string &key) const;
|
||||
|
||||
/// 卸载指定音效
|
||||
void unloadSound(const std::string &key);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本文件资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载文本文件(带缓存)
|
||||
/// @param filepath 文件路径,支持 romfs:/ 前缀
|
||||
/// @return 文件内容字符串,加载失败返回空字符串
|
||||
std::string loadTextFile(const std::string &filepath);
|
||||
|
||||
/// 加载文本文件(指定编码)
|
||||
/// @param filepath 文件路径
|
||||
/// @param encoding 文件编码(默认 UTF-8)
|
||||
/// @return 文件内容字符串
|
||||
std::string loadTextFile(const std::string &filepath, const std::string &encoding);
|
||||
|
||||
/// 通过key获取已缓存的文本内容
|
||||
std::string getTextFile(const std::string &key) const;
|
||||
|
||||
/// 检查文本文件是否已缓存
|
||||
bool hasTextFile(const std::string &key) const;
|
||||
|
||||
/// 卸载指定文本文件
|
||||
void unloadTextFile(const std::string &key);
|
||||
|
||||
/// 清理所有文本文件缓存
|
||||
void clearTextFileCache();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// JSON 文件资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载并解析 JSON 文件
|
||||
/// @param filepath 文件路径,支持 romfs:/ 前缀
|
||||
/// @return JSON 字符串内容,加载或解析失败返回空字符串
|
||||
/// @note 返回的是原始 JSON 字符串,需要自行解析
|
||||
std::string loadJsonFile(const std::string &filepath);
|
||||
|
||||
/// 通过key获取已缓存的 JSON 内容
|
||||
std::string getJsonFile(const std::string &key) const;
|
||||
|
||||
/// 检查 JSON 文件是否已缓存
|
||||
bool hasJsonFile(const std::string &key) const;
|
||||
|
||||
/// 卸载指定 JSON 文件
|
||||
void unloadJsonFile(const std::string &key);
|
||||
|
||||
/// 清理所有 JSON 文件缓存
|
||||
void clearJsonFileCache();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 缓存清理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 清理所有失效的弱引用(自动清理已释放的资源)
|
||||
void purgeUnused();
|
||||
|
||||
/// 清理指定类型的所有缓存
|
||||
void clearTextureCache();
|
||||
void clearFontCache();
|
||||
void clearSoundCache();
|
||||
|
||||
/// 清理所有资源缓存
|
||||
void clearAllCaches();
|
||||
|
||||
/// 获取各类资源的缓存数量
|
||||
size_t getTextureCacheSize() const;
|
||||
size_t getFontCacheSize() const;
|
||||
size_t getSoundCacheSize() const;
|
||||
size_t getTextFileCacheSize() const;
|
||||
size_t getJsonFileCacheSize() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// LRU 缓存管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 设置纹理缓存参数
|
||||
void setTextureCache(size_t maxCacheSize, size_t maxTextureCount,
|
||||
float unloadInterval);
|
||||
|
||||
/// 获取当前缓存的总大小(字节)
|
||||
size_t getTextureCacheMemoryUsage() const;
|
||||
|
||||
/// 获取缓存命中率
|
||||
float getTextureCacheHitRate() const;
|
||||
|
||||
/// 打印缓存统计信息
|
||||
void printTextureCacheStats() const;
|
||||
|
||||
/// 更新缓存(在主循环中调用,用于自动清理)
|
||||
void update(float dt);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 异步加载控制
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 初始化异步加载系统(可选,自动在首次异步加载时初始化)
|
||||
void initAsyncLoader();
|
||||
|
||||
/// 关闭异步加载系统
|
||||
void shutdownAsyncLoader();
|
||||
|
||||
/// 等待所有异步加载完成
|
||||
void waitForAsyncLoads();
|
||||
|
||||
/// 检查是否有正在进行的异步加载
|
||||
bool hasPendingAsyncLoads() const;
|
||||
|
||||
ResourceManager();
|
||||
~ResourceManager();
|
||||
ResourceManager(const ResourceManager &) = delete;
|
||||
ResourceManager &operator=(const ResourceManager &) = delete;
|
||||
|
||||
private:
|
||||
// 生成字体缓存key
|
||||
std::string makeFontKey(const std::string &filepath, int fontSize,
|
||||
bool useSDF) const;
|
||||
|
||||
// 内部加载实现
|
||||
Ptr<Texture> loadTextureInternal(const std::string &filepath, TextureFormat format);
|
||||
|
||||
// 选择最佳纹理格式
|
||||
TextureFormat selectBestFormat(TextureFormat requested) const;
|
||||
|
||||
// 压缩纹理数据
|
||||
std::vector<uint8_t> compressTexture(const uint8_t* data, int width, int height,
|
||||
int channels, TextureFormat format);
|
||||
|
||||
// 互斥锁保护缓存
|
||||
mutable std::mutex textureMutex_;
|
||||
mutable std::mutex fontMutex_;
|
||||
mutable std::mutex soundMutex_;
|
||||
mutable std::mutex textFileMutex_;
|
||||
mutable std::mutex jsonFileMutex_;
|
||||
|
||||
// 资源缓存 - 使用弱指针实现自动清理
|
||||
std::unordered_map<std::string, WeakPtr<FontAtlas>> fontCache_;
|
||||
std::unordered_map<std::string, WeakPtr<Sound>> soundCache_;
|
||||
|
||||
// 文本文件缓存 - 使用强引用(字符串值类型)
|
||||
std::unordered_map<std::string, std::string> textFileCache_;
|
||||
std::unordered_map<std::string, std::string> jsonFileCache_;
|
||||
|
||||
// ============================================================================
|
||||
// 纹理LRU缓存
|
||||
// ============================================================================
|
||||
|
||||
// LRU链表节点
|
||||
struct LRUNode {
|
||||
std::string key;
|
||||
uint32_t prev = 0; // 数组索引,0表示无效
|
||||
uint32_t next = 0; // 数组索引,0表示无效
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
// 纹理缓存配置
|
||||
size_t maxCacheSize_ = 64 * 1024 * 1024; // 最大缓存大小 (64MB)
|
||||
size_t maxTextureCount_ = 256; // 最大纹理数量
|
||||
float unloadInterval_ = 30.0f; // 自动清理间隔 (秒)
|
||||
|
||||
// 纹理缓存 - 使用强指针保持引用
|
||||
std::unordered_map<std::string, TextureCacheEntry> textureCache_;
|
||||
|
||||
// 侵入式LRU链表 - 使用数组索引代替指针,提高缓存局部性
|
||||
std::vector<LRUNode> lruNodes_;
|
||||
uint32_t lruHead_ = 0; // 最近使用
|
||||
uint32_t lruTail_ = 0; // 最久未使用
|
||||
uint32_t freeList_ = 0; // 空闲节点链表
|
||||
|
||||
// 统计
|
||||
size_t totalTextureSize_ = 0;
|
||||
uint64_t textureHitCount_ = 0;
|
||||
uint64_t textureMissCount_ = 0;
|
||||
float autoUnloadTimer_ = 0.0f;
|
||||
|
||||
// 异步加载相关
|
||||
struct AsyncLoadTask {
|
||||
std::string filepath;
|
||||
TextureFormat format;
|
||||
TextureLoadCallback callback;
|
||||
std::promise<Ptr<Texture>> promise;
|
||||
};
|
||||
|
||||
std::queue<AsyncLoadTask> asyncTaskQueue_;
|
||||
std::mutex asyncQueueMutex_;
|
||||
std::condition_variable asyncCondition_;
|
||||
std::unique_ptr<std::thread> asyncThread_;
|
||||
std::atomic<bool> asyncRunning_{false};
|
||||
std::atomic<int> pendingAsyncLoads_{0};
|
||||
|
||||
void asyncLoadLoop();
|
||||
|
||||
// ============================================================================
|
||||
// LRU 缓存内部方法
|
||||
// ============================================================================
|
||||
|
||||
/// 分配LRU节点
|
||||
uint32_t allocateLRUNode(const std::string &key);
|
||||
|
||||
/// 释放LRU节点
|
||||
void freeLRUNode(uint32_t index);
|
||||
|
||||
/// 将节点移到链表头部(最近使用)
|
||||
void moveToFront(uint32_t index);
|
||||
|
||||
/// 从链表中移除节点
|
||||
void removeFromList(uint32_t index);
|
||||
|
||||
/// 驱逐最久未使用的纹理
|
||||
std::string evictLRU();
|
||||
|
||||
/// 访问纹理(更新LRU位置)
|
||||
void touchTexture(const std::string &key);
|
||||
|
||||
/// 驱逐纹理直到满足大小限制
|
||||
void evictTexturesIfNeeded();
|
||||
|
||||
/// 计算纹理大小
|
||||
size_t calculateTextureSize(int width, int height, PixelFormat format) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/graphics/render_backend.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Scene;
|
||||
class Action;
|
||||
class RenderBackend;
|
||||
struct RenderCommand;
|
||||
|
||||
// ============================================================================
|
||||
// 节点基类 - 场景图的基础
|
||||
// ============================================================================
|
||||
class Node : public std::enable_shared_from_this<Node> {
|
||||
public:
|
||||
Node();
|
||||
virtual ~Node();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 层级管理
|
||||
// ------------------------------------------------------------------------
|
||||
void addChild(Ptr<Node> child);
|
||||
|
||||
/**
|
||||
* @brief 批量添加子节点
|
||||
* @param children 子节点列表
|
||||
*/
|
||||
void addChildren(std::vector<Ptr<Node>> &&children);
|
||||
|
||||
void removeChild(Ptr<Node> child);
|
||||
void removeChildByName(const std::string &name);
|
||||
void removeFromParent();
|
||||
void removeAllChildren();
|
||||
|
||||
Ptr<Node> getParent() const { return parent_.lock(); }
|
||||
const std::vector<Ptr<Node>> &getChildren() const { return children_; }
|
||||
Ptr<Node> getChildByName(const std::string &name) const;
|
||||
Ptr<Node> getChildByTag(int tag) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 变换属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setPosition(const Vec2 &pos);
|
||||
void setPosition(float x, float y);
|
||||
Vec2 getPosition() const { return position_; }
|
||||
|
||||
void setRotation(float degrees);
|
||||
float getRotation() const { return rotation_; }
|
||||
|
||||
void setScale(const Vec2 &scale);
|
||||
void setScale(float scale);
|
||||
void setScale(float x, float y);
|
||||
Vec2 getScale() const { return scale_; }
|
||||
|
||||
void setAnchor(const Vec2 &anchor);
|
||||
void setAnchor(float x, float y);
|
||||
Vec2 getAnchor() const { return anchor_; }
|
||||
|
||||
void setSkew(const Vec2 &skew);
|
||||
void setSkew(float x, float y);
|
||||
Vec2 getSkew() const { return skew_; }
|
||||
|
||||
void setOpacity(float opacity);
|
||||
float getOpacity() const { return opacity_; }
|
||||
|
||||
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_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 世界变换
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 convertToWorldSpace(const Vec2 &localPos) const;
|
||||
Vec2 convertToNodeSpace(const Vec2 &worldPos) const;
|
||||
|
||||
glm::mat4 getLocalTransform() const;
|
||||
glm::mat4 getWorldTransform() const;
|
||||
|
||||
/**
|
||||
* @brief 标记变换矩阵为脏状态,并传播到所有子节点
|
||||
*/
|
||||
void markTransformDirty();
|
||||
|
||||
/**
|
||||
* @brief 批量更新变换矩阵
|
||||
* 在渲染前统一计算所有脏节点的变换矩阵,避免逐节点计算时的重复递归
|
||||
*/
|
||||
void batchUpdateTransforms();
|
||||
|
||||
/**
|
||||
* @brief 获取变换脏标记状态
|
||||
*/
|
||||
bool isTransformDirty() const { return transformDirty_; }
|
||||
bool isWorldTransformDirty() const { return worldTransformDirty_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 名称和标签
|
||||
// ------------------------------------------------------------------------
|
||||
void setName(const std::string &name) { name_ = name; }
|
||||
const std::string &getName() const { return name_; }
|
||||
|
||||
void setTag(int tag) { tag_ = tag; }
|
||||
int getTag() const { return tag_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期回调
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void onEnter();
|
||||
virtual void onExit();
|
||||
virtual void onUpdate(float dt);
|
||||
virtual void onRender(RenderBackend &renderer);
|
||||
virtual void onAttachToScene(Scene *scene);
|
||||
virtual void onDetachFromScene();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边界框(用于空间索引)
|
||||
// ------------------------------------------------------------------------
|
||||
virtual Rect getBoundingBox() const;
|
||||
|
||||
// 是否需要参与空间索引(默认 true)
|
||||
void setSpatialIndexed(bool indexed) { spatialIndexed_ = indexed; }
|
||||
bool isSpatialIndexed() const { return spatialIndexed_; }
|
||||
|
||||
// 更新空间索引(手动调用,通常在边界框变化后)
|
||||
void updateSpatialIndex();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 动作系统
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 运行动作
|
||||
* @param action 动作指针(所有权转移)
|
||||
* @return 动作指针
|
||||
*/
|
||||
Action* runAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 停止所有动作
|
||||
*/
|
||||
void stopAllActions();
|
||||
|
||||
/**
|
||||
* @brief 停止指定动作
|
||||
* @param action 动作指针
|
||||
*/
|
||||
void stopAction(Action* action);
|
||||
|
||||
/**
|
||||
* @brief 根据标签停止动作
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void stopActionByTag(int tag);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 事件系统
|
||||
// ------------------------------------------------------------------------
|
||||
EventDispatcher &getEventDispatcher() { return eventDispatcher_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内部方法
|
||||
// ------------------------------------------------------------------------
|
||||
void update(float dt);
|
||||
void render(RenderBackend &renderer);
|
||||
void sortChildren();
|
||||
|
||||
bool isRunning() const { return running_; }
|
||||
Scene *getScene() const { return scene_; }
|
||||
|
||||
// 多线程渲染命令收集
|
||||
virtual void collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||
int parentZOrder = 0);
|
||||
|
||||
protected:
|
||||
// 子类重写
|
||||
virtual void onDraw(RenderBackend &renderer) {}
|
||||
virtual void onUpdateNode(float dt) {}
|
||||
virtual void generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||
int zOrder) {};
|
||||
|
||||
// 供子类访问的内部状态
|
||||
Vec2 &getPositionRef() { return position_; }
|
||||
Vec2 &getScaleRef() { return scale_; }
|
||||
Vec2 &getAnchorRef() { return anchor_; }
|
||||
float getRotationRef() { return rotation_; }
|
||||
float getOpacityRef() { return opacity_; }
|
||||
|
||||
private:
|
||||
// ==========================================================================
|
||||
// 成员变量按类型大小降序排列,减少内存对齐填充
|
||||
// 64位系统对齐:std::string(32) > glm::mat4(64) > std::vector(24) >
|
||||
// double(8) > float(4) > int(4) > bool(1)
|
||||
// ==========================================================================
|
||||
|
||||
// 1. 大块内存(64字节)
|
||||
mutable glm::mat4 localTransform_; // 64 bytes
|
||||
mutable glm::mat4 worldTransform_; // 64 bytes
|
||||
|
||||
// 2. 字符串和容器(24-32字节)
|
||||
std::string name_; // 32 bytes
|
||||
std::vector<Ptr<Node>> children_; // 24 bytes
|
||||
|
||||
// 3. 子节点索引(加速查找)
|
||||
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
|
||||
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
|
||||
|
||||
// 4. 事件分发器
|
||||
EventDispatcher eventDispatcher_; // 大小取决于实现
|
||||
|
||||
// 5. 父节点引用
|
||||
WeakPtr<Node> parent_; // 16 bytes
|
||||
|
||||
// 7. 变换属性(按访问频率分组)
|
||||
Vec2 position_ = Vec2::Zero(); // 8 bytes
|
||||
Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes
|
||||
Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes
|
||||
Vec2 skew_ = Vec2::Zero(); // 8 bytes
|
||||
|
||||
// 8. 边界框(用于空间索引)
|
||||
Rect lastSpatialBounds_; // 16 bytes
|
||||
|
||||
// 9. 浮点属性
|
||||
float rotation_ = 0.0f; // 4 bytes
|
||||
float opacity_ = 1.0f; // 4 bytes
|
||||
|
||||
// 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
|
||||
|
||||
// 12. 布尔标志(打包在一起)
|
||||
mutable bool transformDirty_ = true; // 1 byte
|
||||
mutable bool worldTransformDirty_ = true; // 1 byte
|
||||
bool childrenOrderDirty_ = false; // 1 byte
|
||||
bool visible_ = true; // 1 byte
|
||||
bool running_ = false; // 1 byte
|
||||
bool spatialIndexed_ = true; // 1 byte
|
||||
// 填充 2 bytes 到 8 字节对齐
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <extra2d/spatial/spatial_manager.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
struct RenderCommand;
|
||||
|
||||
// ============================================================================
|
||||
// 场景类 - 节点容器,管理整个场景图
|
||||
// ============================================================================
|
||||
class Scene : public Node {
|
||||
public:
|
||||
Scene();
|
||||
~Scene() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 场景属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundColor(const Color &color) { backgroundColor_ = color; }
|
||||
Color getBackgroundColor() const { return backgroundColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 摄像机
|
||||
// ------------------------------------------------------------------------
|
||||
void setCamera(Ptr<Camera> camera);
|
||||
Ptr<Camera> getCamera() const { return camera_; }
|
||||
|
||||
Camera *getActiveCamera() const {
|
||||
return camera_ ? camera_.get() : defaultCamera_.get();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 视口和尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setViewportSize(float width, float height);
|
||||
void setViewportSize(const Size &size);
|
||||
Size getViewportSize() const { return viewportSize_; }
|
||||
|
||||
float getWidth() const { return viewportSize_.width; }
|
||||
float getHeight() const { return viewportSize_.height; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 场景状态
|
||||
// ------------------------------------------------------------------------
|
||||
bool isPaused() const { return paused_; }
|
||||
void pause() { paused_ = true; }
|
||||
void resume() { paused_ = false; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 渲染和更新
|
||||
// ------------------------------------------------------------------------
|
||||
void renderScene(RenderBackend &renderer);
|
||||
virtual void renderContent(RenderBackend &renderer);
|
||||
void updateScene(float dt);
|
||||
void collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||
int parentZOrder = 0) override;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 空间索引系统
|
||||
// ------------------------------------------------------------------------
|
||||
SpatialManager &getSpatialManager() { return spatialManager_; }
|
||||
const SpatialManager &getSpatialManager() const { return spatialManager_; }
|
||||
|
||||
// 启用/禁用空间索引
|
||||
void setSpatialIndexingEnabled(bool enabled) {
|
||||
spatialIndexingEnabled_ = enabled;
|
||||
}
|
||||
bool isSpatialIndexingEnabled() const { return spatialIndexingEnabled_; }
|
||||
|
||||
// 节点空间索引管理(内部使用)
|
||||
void updateNodeInSpatialIndex(Node *node, const Rect &oldBounds,
|
||||
const Rect &newBounds);
|
||||
void removeNodeFromSpatialIndex(Node *node);
|
||||
|
||||
// 碰撞检测查询
|
||||
std::vector<Node *> queryNodesInArea(const Rect &area) const;
|
||||
std::vector<Node *> queryNodesAtPoint(const Vec2 &point) const;
|
||||
std::vector<std::pair<Node *, Node *>> queryCollisions() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Scene> create();
|
||||
|
||||
protected:
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
|
||||
// 过渡场景生命周期回调(供 TransitionScene 使用)
|
||||
virtual void onExitTransitionDidStart() {}
|
||||
virtual void onEnterTransitionDidFinish() {}
|
||||
|
||||
friend class SceneManager;
|
||||
friend class TransitionScene;
|
||||
|
||||
private:
|
||||
Color backgroundColor_ = Colors::Black;
|
||||
Size viewportSize_ = Size::Zero();
|
||||
|
||||
Ptr<Camera> camera_;
|
||||
Ptr<Camera> defaultCamera_;
|
||||
|
||||
bool paused_ = false;
|
||||
|
||||
// 空间索引系统
|
||||
SpatialManager spatialManager_;
|
||||
bool spatialIndexingEnabled_ = true;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
struct RenderCommand;
|
||||
class TransitionScene;
|
||||
|
||||
// ============================================================================
|
||||
// 场景管理器 - 管理场景的生命周期和切换
|
||||
// ============================================================================
|
||||
class SceneManager {
|
||||
public:
|
||||
using TransitionCallback = std::function<void()>;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 单例访问
|
||||
// ------------------------------------------------------------------------
|
||||
static SceneManager &getInstance();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 场景栈操作
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// 运行第一个场景
|
||||
void runWithScene(Ptr<Scene> scene);
|
||||
|
||||
// 替换当前场景
|
||||
void replaceScene(Ptr<Scene> scene);
|
||||
void replaceScene(Ptr<Scene> scene, TransitionType transition,
|
||||
float duration = 0.5f);
|
||||
|
||||
// 压入新场景(当前场景暂停)
|
||||
void pushScene(Ptr<Scene> scene);
|
||||
void pushScene(Ptr<Scene> scene, TransitionType transition,
|
||||
float duration = 0.5f);
|
||||
|
||||
// 弹出当前场景(恢复上一个场景)
|
||||
void popScene();
|
||||
void popScene(TransitionType transition, float duration = 0.5f);
|
||||
|
||||
// 弹出到根场景
|
||||
void popToRootScene();
|
||||
void popToRootScene(TransitionType transition, float duration = 0.5f);
|
||||
|
||||
// 弹出到指定场景
|
||||
void popToScene(const std::string &name);
|
||||
void popToScene(const std::string &name, TransitionType transition,
|
||||
float duration = 0.5f);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 获取场景
|
||||
// ------------------------------------------------------------------------
|
||||
Ptr<Scene> getCurrentScene() const;
|
||||
Ptr<Scene> getPreviousScene() const;
|
||||
Ptr<Scene> getRootScene() const;
|
||||
|
||||
// 通过名称获取场景
|
||||
Ptr<Scene> getSceneByName(const std::string &name) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 查询
|
||||
// ------------------------------------------------------------------------
|
||||
size_t getSceneCount() const { return sceneStack_.size(); }
|
||||
bool isEmpty() const { return sceneStack_.empty(); }
|
||||
bool hasScene(const std::string &name) const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 更新和渲染
|
||||
// ------------------------------------------------------------------------
|
||||
void update(float dt);
|
||||
void render(RenderBackend &renderer);
|
||||
void collectRenderCommands(std::vector<RenderCommand> &commands);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 过渡控制
|
||||
// ------------------------------------------------------------------------
|
||||
bool isTransitioning() const { return isTransitioning_; }
|
||||
void setTransitionCallback(TransitionCallback callback) {
|
||||
transitionCallback_ = callback;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 清理
|
||||
// ------------------------------------------------------------------------
|
||||
void end();
|
||||
void purgeCachedScenes();
|
||||
|
||||
public:
|
||||
SceneManager() = default;
|
||||
~SceneManager() = default;
|
||||
SceneManager(const SceneManager &) = delete;
|
||||
SceneManager &operator=(const SceneManager &) = delete;
|
||||
|
||||
// 场景切换(供 Application 使用)
|
||||
void enterScene(Ptr<Scene> scene);
|
||||
void enterScene(Ptr<Scene> scene, Ptr<TransitionScene> transitionScene);
|
||||
|
||||
private:
|
||||
void doSceneSwitch();
|
||||
void startTransition(Ptr<Scene> from, Ptr<Scene> to, TransitionType type,
|
||||
float duration, Function<void()> stackAction);
|
||||
void finishTransition();
|
||||
void dispatchPointerEvents(Scene &scene);
|
||||
|
||||
// 创建过渡场景
|
||||
Ptr<TransitionScene> createTransitionScene(TransitionType type,
|
||||
float duration,
|
||||
Ptr<Scene> inScene);
|
||||
|
||||
std::stack<Ptr<Scene>> sceneStack_;
|
||||
std::unordered_map<std::string, Ptr<Scene>> namedScenes_;
|
||||
|
||||
// Transition state
|
||||
bool isTransitioning_ = false;
|
||||
TransitionType currentTransition_ = TransitionType::None;
|
||||
Ptr<TransitionScene> activeTransitionScene_;
|
||||
Function<void()> transitionStackAction_;
|
||||
TransitionCallback transitionCallback_;
|
||||
|
||||
// Next scene to switch to (queued during transition)
|
||||
Ptr<Scene> nextScene_;
|
||||
bool sendCleanupToScene_ = false;
|
||||
|
||||
Node *hoverTarget_ = nullptr;
|
||||
Node *captureTarget_ = nullptr;
|
||||
Vec2 lastPointerWorld_ = Vec2::Zero();
|
||||
bool hasLastPointerWorld_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 形状类型
|
||||
// ============================================================================
|
||||
enum class ShapeType { Point, Line, Rect, Circle, Triangle, Polygon };
|
||||
|
||||
// ============================================================================
|
||||
// 形状节点 - 用于绘制几何形状
|
||||
// ============================================================================
|
||||
class ShapeNode : public Node {
|
||||
public:
|
||||
ShapeNode();
|
||||
~ShapeNode() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<ShapeNode> create();
|
||||
|
||||
// 点
|
||||
static Ptr<ShapeNode> createPoint(const Vec2 &pos, const Color &color);
|
||||
|
||||
// 线
|
||||
static Ptr<ShapeNode> createLine(const Vec2 &start, const Vec2 &end,
|
||||
const Color &color, float width = 1.0f);
|
||||
|
||||
// 矩形
|
||||
static Ptr<ShapeNode> createRect(const Rect &rect, const Color &color,
|
||||
float width = 1.0f);
|
||||
static Ptr<ShapeNode> createFilledRect(const Rect &rect, const Color &color);
|
||||
|
||||
// 圆形
|
||||
static Ptr<ShapeNode> createCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color, int segments = 32,
|
||||
float width = 1.0f);
|
||||
static Ptr<ShapeNode> createFilledCircle(const Vec2 ¢er, float radius,
|
||||
const Color &color,
|
||||
int segments = 32);
|
||||
|
||||
// 三角形
|
||||
static Ptr<ShapeNode> createTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3, const Color &color,
|
||||
float width = 1.0f);
|
||||
static Ptr<ShapeNode> createFilledTriangle(const Vec2 &p1, const Vec2 &p2,
|
||||
const Vec2 &p3,
|
||||
const Color &color);
|
||||
|
||||
// 多边形
|
||||
static Ptr<ShapeNode> createPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color, float width = 1.0f);
|
||||
static Ptr<ShapeNode> createFilledPolygon(const std::vector<Vec2> &points,
|
||||
const Color &color);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 属性设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setShapeType(ShapeType type) { shapeType_ = type; }
|
||||
ShapeType getShapeType() const { return shapeType_; }
|
||||
|
||||
void setColor(const Color &color) { color_ = color; }
|
||||
Color getColor() const { return color_; }
|
||||
|
||||
void setFilled(bool filled) { filled_ = filled; }
|
||||
bool isFilled() const { return filled_; }
|
||||
|
||||
void setLineWidth(float width) { lineWidth_ = width; }
|
||||
float getLineWidth() const { return lineWidth_; }
|
||||
|
||||
void setSegments(int segments) { segments_ = segments; }
|
||||
int getSegments() const { return segments_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 点设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setPoints(const std::vector<Vec2> &points);
|
||||
const std::vector<Vec2> &getPoints() const { return points_; }
|
||||
void addPoint(const Vec2 &point);
|
||||
void clearPoints();
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||
int zOrder) override;
|
||||
|
||||
private:
|
||||
ShapeType shapeType_ = ShapeType::Rect;
|
||||
Color color_ = Colors::White;
|
||||
bool filled_ = false;
|
||||
float lineWidth_ = 1.0f;
|
||||
int segments_ = 32;
|
||||
std::vector<Vec2> points_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 精灵节点
|
||||
// ============================================================================
|
||||
class Sprite : public Node {
|
||||
public:
|
||||
Sprite();
|
||||
explicit Sprite(Ptr<Texture> texture);
|
||||
~Sprite() override = default;
|
||||
|
||||
// 纹理
|
||||
void setTexture(Ptr<Texture> texture);
|
||||
Ptr<Texture> getTexture() const { return texture_; }
|
||||
|
||||
// 纹理矩形 (用于图集)
|
||||
void setTextureRect(const Rect &rect);
|
||||
Rect getTextureRect() const { return textureRect_; }
|
||||
|
||||
// 颜色混合
|
||||
void setColor(const Color &color);
|
||||
Color getColor() const { return color_; }
|
||||
|
||||
// 翻转
|
||||
void setFlipX(bool flip);
|
||||
void setFlipY(bool flip);
|
||||
bool isFlipX() const { return flipX_; }
|
||||
bool isFlipY() const { return flipY_; }
|
||||
|
||||
// 静态创建方法
|
||||
static Ptr<Sprite> create();
|
||||
static Ptr<Sprite> create(Ptr<Texture> texture);
|
||||
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||
int zOrder) override;
|
||||
|
||||
private:
|
||||
Ptr<Texture> texture_;
|
||||
Rect textureRect_;
|
||||
Color color_ = Colors::White;
|
||||
bool flipX_ = false;
|
||||
bool flipY_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 方块/马赛克过渡场景
|
||||
// 实现原理:
|
||||
// 1. 将屏幕分成多个方块
|
||||
// 2. 方块逐个消失,显示新场景
|
||||
// ============================================================================
|
||||
class TransitionBoxScene : public TransitionScene {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建方块过渡场景
|
||||
* @param duration 过渡持续时间(秒)
|
||||
* @param inScene 要进入的目标场景
|
||||
* @param divisions 方块分割数(默认为 8,表示 8x8 网格)
|
||||
*/
|
||||
TransitionBoxScene(float duration, Ptr<Scene> inScene, int divisions = 8);
|
||||
|
||||
static Ptr<TransitionBoxScene> create(float duration, Ptr<Scene> inScene,
|
||||
int divisions = 8);
|
||||
|
||||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
int divisions_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
#include <extra2d/scene/sprite.h>
|
||||
#include <extra2d/core/color.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 淡入淡出过渡场景
|
||||
// 实现原理:
|
||||
// 1. 创建一个纯色精灵作为遮罩层
|
||||
// 2. 第一阶段:遮罩从透明淡入到不透明(黑屏),同时显示旧场景
|
||||
// 3. 切换显示新场景
|
||||
// 4. 第二阶段:遮罩从不透明淡出到透明,显示新场景
|
||||
// ============================================================================
|
||||
class TransitionFadeScene : public TransitionScene {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建淡入淡出过渡场景
|
||||
* @param duration 过渡持续时间(秒)
|
||||
* @param inScene 要进入的目标场景
|
||||
* @param color 遮罩颜色(默认为黑色)
|
||||
*/
|
||||
TransitionFadeScene(float duration, Ptr<Scene> inScene,
|
||||
const Color &color = Colors::Black);
|
||||
|
||||
static Ptr<TransitionFadeScene> create(float duration, Ptr<Scene> inScene,
|
||||
const Color &color = Colors::Black);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 启动过渡动画
|
||||
* 创建遮罩层并运行动作序列
|
||||
*/
|
||||
void onTransitionStart() override;
|
||||
|
||||
/**
|
||||
* @brief 渲染内容
|
||||
* 根据进度控制新旧场景的显示
|
||||
*/
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 隐藏退出场景,显示进入场景
|
||||
*/
|
||||
void hideOutShowIn();
|
||||
|
||||
Color maskColor_; // 遮罩颜色
|
||||
bool hasSwitched_ = false; // 是否已经切换场景
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 翻页过渡场景
|
||||
// 实现原理:
|
||||
// 1. 前半段:旧场景翻转消失
|
||||
// 2. 后半段:新场景翻转出现
|
||||
// ============================================================================
|
||||
class TransitionFlipScene : public TransitionScene {
|
||||
public:
|
||||
enum class Axis { Horizontal, Vertical };
|
||||
|
||||
/**
|
||||
* @brief 创建翻页过渡场景
|
||||
* @param duration 过渡持续时间(秒)
|
||||
* @param inScene 要进入的目标场景
|
||||
* @param axis 翻转轴(水平或垂直)
|
||||
*/
|
||||
TransitionFlipScene(float duration, Ptr<Scene> inScene,
|
||||
Axis axis = Axis::Horizontal);
|
||||
|
||||
static Ptr<TransitionFlipScene> create(float duration, Ptr<Scene> inScene,
|
||||
Axis axis = Axis::Horizontal);
|
||||
|
||||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
Axis axis_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 缩放过渡场景
|
||||
// 实现原理:
|
||||
// 1. 旧场景缩小消失
|
||||
// 2. 新场景放大出现
|
||||
// ============================================================================
|
||||
class TransitionScaleScene : public TransitionScene {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建缩放过渡场景
|
||||
* @param duration 过渡持续时间(秒)
|
||||
* @param inScene 要进入的目标场景
|
||||
*/
|
||||
TransitionScaleScene(float duration, Ptr<Scene> inScene);
|
||||
|
||||
static Ptr<TransitionScaleScene> create(float duration, Ptr<Scene> inScene);
|
||||
|
||||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 过渡方向
|
||||
// ============================================================================
|
||||
enum class TransitionDirection { Left, Right, Up, Down };
|
||||
|
||||
// ============================================================================
|
||||
// 过渡效果类型
|
||||
// ============================================================================
|
||||
enum class TransitionType {
|
||||
None,
|
||||
Fade,
|
||||
SlideLeft,
|
||||
SlideRight,
|
||||
SlideUp,
|
||||
SlideDown,
|
||||
Scale,
|
||||
Flip,
|
||||
Box
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 过渡场景基类 - 继承自 Scene,作为中介场景管理过渡效果
|
||||
// 设计参考 Cocos2d-x 的 TransitionScene
|
||||
// ============================================================================
|
||||
class TransitionScene : public Scene {
|
||||
public:
|
||||
using FinishCallback = std::function<void()>;
|
||||
|
||||
/**
|
||||
* @brief 创建过渡场景
|
||||
* @param duration 过渡持续时间(秒)
|
||||
* @param inScene 要进入的目标场景
|
||||
*/
|
||||
TransitionScene(float duration, Ptr<Scene> inScene);
|
||||
~TransitionScene() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 场景管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取要进入的场景
|
||||
*/
|
||||
Ptr<Scene> getInScene() const { return inScene_; }
|
||||
|
||||
/**
|
||||
* @brief 获取要退出的场景
|
||||
*/
|
||||
Ptr<Scene> getOutScene() const { return outScene_; }
|
||||
|
||||
/**
|
||||
* @brief 设置要退出的场景(由 SceneManager 调用)
|
||||
*/
|
||||
void setOutScene(Ptr<Scene> outScene) { outScene_ = outScene; }
|
||||
|
||||
/**
|
||||
* @brief 设置过渡完成回调
|
||||
*/
|
||||
void setFinishCallback(FinishCallback callback) { finishCallback_ = callback; }
|
||||
|
||||
/**
|
||||
* @brief 获取过渡持续时间
|
||||
*/
|
||||
float getDuration() const { return duration_; }
|
||||
|
||||
/**
|
||||
* @brief 获取当前进度 [0, 1]
|
||||
*/
|
||||
float getProgress() const { return progress_; }
|
||||
|
||||
/**
|
||||
* @brief 是否已完成
|
||||
*/
|
||||
bool isFinished() const { return isFinished_; }
|
||||
|
||||
/**
|
||||
* @brief 完成过渡,通知 SceneManager 切换到目标场景
|
||||
*/
|
||||
void finish();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 渲染 - 在 TransitionScene 上渲染新旧两个子场景
|
||||
// ------------------------------------------------------------------------
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 生命周期
|
||||
// ------------------------------------------------------------------------
|
||||
void onEnter() override;
|
||||
void onExit() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 子类实现具体的过渡逻辑
|
||||
* 在 onEnter 中设置动画,动画完成后调用 finish()
|
||||
*/
|
||||
virtual void onTransitionStart() = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制源场景(旧场景)
|
||||
*/
|
||||
virtual void drawOutScene(RenderBackend &renderer);
|
||||
|
||||
/**
|
||||
* @brief 绘制目标场景(新场景)
|
||||
*/
|
||||
virtual void drawInScene(RenderBackend &renderer);
|
||||
|
||||
float duration_;
|
||||
float elapsed_ = 0.0f;
|
||||
float progress_ = 0.0f;
|
||||
bool isFinished_ = false;
|
||||
|
||||
Ptr<Scene> inScene_; // 要进入的场景
|
||||
Ptr<Scene> outScene_; // 要退出的场景
|
||||
|
||||
FinishCallback finishCallback_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/scene/transition_scene.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 滑动过渡场景
|
||||
// 实现原理:
|
||||
// 1. 旧场景向指定方向滑出
|
||||
// 2. 新场景从相反方向滑入
|
||||
// ============================================================================
|
||||
class TransitionSlideScene : public TransitionScene {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建滑动过渡场景
|
||||
* @param duration 过渡持续时间(秒)
|
||||
* @param inScene 要进入的目标场景
|
||||
* @param direction 滑动方向
|
||||
*/
|
||||
TransitionSlideScene(float duration, Ptr<Scene> inScene,
|
||||
TransitionDirection direction);
|
||||
|
||||
static Ptr<TransitionSlideScene> create(float duration, Ptr<Scene> inScene,
|
||||
TransitionDirection direction);
|
||||
|
||||
protected:
|
||||
void onTransitionStart() override;
|
||||
void renderContent(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
TransitionDirection direction_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <extra2d/spatial/spatial_index.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class QuadTree : public ISpatialIndex {
|
||||
public:
|
||||
static constexpr int MAX_OBJECTS = 10;
|
||||
static constexpr int MAX_LEVELS = 5;
|
||||
|
||||
struct QuadTreeNode {
|
||||
Rect bounds;
|
||||
int level;
|
||||
std::vector<std::pair<Node *, Rect>> objects;
|
||||
std::array<std::unique_ptr<QuadTreeNode>, 4> children;
|
||||
|
||||
QuadTreeNode(const Rect &bounds, int level);
|
||||
bool contains(const Rect &rect) const;
|
||||
bool intersects(const Rect &rect) const;
|
||||
};
|
||||
|
||||
explicit QuadTree(const Rect &worldBounds);
|
||||
~QuadTree() override = default;
|
||||
|
||||
void insert(Node *node, const Rect &bounds) override;
|
||||
void remove(Node *node) override;
|
||||
void update(Node *node, const Rect &newBounds) override;
|
||||
|
||||
std::vector<Node *> query(const Rect &area) const override;
|
||||
std::vector<Node *> query(const Vec2 &point) const override;
|
||||
std::vector<std::pair<Node *, Node *>> queryCollisions() const override;
|
||||
|
||||
void clear() override;
|
||||
size_t size() const override;
|
||||
bool empty() const override;
|
||||
|
||||
void rebuild() override;
|
||||
|
||||
private:
|
||||
void split(QuadTreeNode *node);
|
||||
void insertIntoNode(QuadTreeNode *node, Node *object, const Rect &bounds);
|
||||
void queryNode(const QuadTreeNode *node, const Rect &area,
|
||||
std::vector<Node *> &results) const;
|
||||
void queryNode(const QuadTreeNode *node, const Vec2 &point,
|
||||
std::vector<Node *> &results) const;
|
||||
void
|
||||
collectCollisions(const QuadTreeNode *node,
|
||||
std::vector<std::pair<Node *, Node *>> &collisions) const;
|
||||
bool removeFromNode(QuadTreeNode *node, Node *object);
|
||||
|
||||
/**
|
||||
* @brief 使用扫描线算法检测节点内对象的碰撞
|
||||
* @param objects 对象列表
|
||||
* @param collisions 输出碰撞对
|
||||
*/
|
||||
void detectCollisionsInNode(
|
||||
const std::vector<std::pair<Node *, Rect>> &objects,
|
||||
std::vector<std::pair<Node *, Node *>> &collisions) const;
|
||||
|
||||
std::unique_ptr<QuadTreeNode> root_;
|
||||
Rect worldBounds_;
|
||||
size_t objectCount_ = 0;
|
||||
|
||||
// 碰撞检测用的临时缓冲区,避免重复分配
|
||||
mutable std::vector<std::pair<Node *, Rect>> collisionBuffer_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/spatial/spatial_index.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 空间哈希实现 - 优化内存布局版本
|
||||
* 使用连续内存存储单元格内容,减少内存碎片
|
||||
*/
|
||||
class SpatialHash : public ISpatialIndex {
|
||||
public:
|
||||
using CellKey = std::pair<int64_t, int64_t>;
|
||||
|
||||
struct CellKeyHash {
|
||||
size_t operator()(const CellKey &key) const {
|
||||
return std::hash<int64_t>()(key.first) ^
|
||||
(std::hash<int64_t>()(key.second) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
explicit SpatialHash(float cellSize = 64.0f);
|
||||
~SpatialHash() override = default;
|
||||
|
||||
void insert(Node *node, const Rect &bounds) override;
|
||||
void remove(Node *node) override;
|
||||
void update(Node *node, const Rect &newBounds) override;
|
||||
|
||||
std::vector<Node *> query(const Rect &area) const override;
|
||||
std::vector<Node *> query(const Vec2 &point) const override;
|
||||
std::vector<std::pair<Node *, Node *>> queryCollisions() const override;
|
||||
|
||||
void clear() override;
|
||||
size_t size() const override;
|
||||
bool empty() const override;
|
||||
|
||||
void rebuild() override;
|
||||
|
||||
void setCellSize(float cellSize);
|
||||
float getCellSize() const { return cellSize_; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 单元格数据 - 使用vector代替unordered_set减少内存开销
|
||||
*/
|
||||
struct Cell {
|
||||
std::vector<Node *> objects;
|
||||
|
||||
void insert(Node *node);
|
||||
void remove(Node *node);
|
||||
bool contains(Node *node) const;
|
||||
void clear() { objects.clear(); }
|
||||
size_t size() const { return objects.size(); }
|
||||
bool empty() const { return objects.empty(); }
|
||||
};
|
||||
|
||||
CellKey getCellKey(float x, float y) const;
|
||||
void getCellsForRect(const Rect &rect, std::vector<CellKey> &cells) const;
|
||||
void insertIntoCells(Node *node, const Rect &bounds);
|
||||
void removeFromCells(Node *node, const Rect &bounds);
|
||||
|
||||
float cellSize_;
|
||||
// 使用vector存储对象列表代替unordered_set,内存更紧凑
|
||||
std::unordered_map<CellKey, Cell, CellKeyHash> grid_;
|
||||
std::unordered_map<Node *, Rect> objectBounds_;
|
||||
size_t objectCount_ = 0;
|
||||
|
||||
// 查询用的临时缓冲区,避免重复分配
|
||||
mutable std::vector<Node *> queryBuffer_;
|
||||
mutable std::vector<std::pair<Node *, Node *>> collisionBuffer_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
|
||||
enum class SpatialStrategy { Auto, QuadTree, SpatialHash };
|
||||
|
||||
struct SpatialQueryResult {
|
||||
Node *node;
|
||||
Rect bounds;
|
||||
};
|
||||
|
||||
class ISpatialIndex {
|
||||
public:
|
||||
virtual ~ISpatialIndex() = default;
|
||||
|
||||
virtual void insert(Node *node, const Rect &bounds) = 0;
|
||||
virtual void remove(Node *node) = 0;
|
||||
virtual void update(Node *node, const Rect &newBounds) = 0;
|
||||
|
||||
virtual std::vector<Node *> query(const Rect &area) const = 0;
|
||||
virtual std::vector<Node *> query(const Vec2 &point) const = 0;
|
||||
virtual std::vector<std::pair<Node *, Node *>> queryCollisions() const = 0;
|
||||
|
||||
virtual void clear() = 0;
|
||||
virtual size_t size() const = 0;
|
||||
virtual bool empty() const = 0;
|
||||
|
||||
virtual void rebuild() = 0;
|
||||
};
|
||||
|
||||
using SpatialIndexPtr = std::unique_ptr<ISpatialIndex>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/spatial/spatial_index.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class SpatialManager {
|
||||
public:
|
||||
using QueryCallback = std::function<bool(Node *)>;
|
||||
|
||||
SpatialManager();
|
||||
explicit SpatialManager(const Rect &worldBounds);
|
||||
~SpatialManager() = default;
|
||||
|
||||
void setStrategy(SpatialStrategy strategy);
|
||||
void setAutoThresholds(size_t quadTreeThreshold, size_t hashThreshold);
|
||||
|
||||
void setWorldBounds(const Rect &bounds);
|
||||
Rect getWorldBounds() const { return worldBounds_; }
|
||||
|
||||
void insert(Node *node, const Rect &bounds);
|
||||
void remove(Node *node);
|
||||
void update(Node *node, const Rect &newBounds);
|
||||
|
||||
std::vector<Node *> query(const Rect &area) const;
|
||||
std::vector<Node *> query(const Vec2 &point) const;
|
||||
std::vector<std::pair<Node *, Node *>> queryCollisions() const;
|
||||
|
||||
void query(const Rect &area, const QueryCallback &callback) const;
|
||||
void query(const Vec2 &point, const QueryCallback &callback) const;
|
||||
|
||||
void clear();
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
|
||||
void rebuild();
|
||||
void optimize();
|
||||
|
||||
SpatialStrategy getCurrentStrategy() const;
|
||||
const char *getStrategyName() const;
|
||||
|
||||
static std::unique_ptr<ISpatialIndex> createIndex(SpatialStrategy strategy,
|
||||
const Rect &bounds);
|
||||
|
||||
private:
|
||||
void selectOptimalStrategy();
|
||||
|
||||
SpatialStrategy currentStrategy_ = SpatialStrategy::Auto;
|
||||
SpatialStrategy activeStrategy_ = SpatialStrategy::QuadTree;
|
||||
std::unique_ptr<ISpatialIndex> index_;
|
||||
Rect worldBounds_;
|
||||
|
||||
size_t quadTreeThreshold_ = 1000;
|
||||
size_t hashThreshold_ = 5000;
|
||||
|
||||
mutable size_t queryCount_ = 0;
|
||||
mutable size_t totalQueryTime_ = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,261 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/graphics/texture.h>
|
||||
#include <extra2d/platform/window.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 图片缩放模式
|
||||
enum class ImageScaleMode {
|
||||
Original, // 使用原图大小
|
||||
Stretch, // 拉伸填充
|
||||
ScaleFit, // 等比缩放,保持完整显示
|
||||
ScaleFill // 等比缩放,填充整个区域(可能裁剪)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 基础按钮类
|
||||
// ============================================================================
|
||||
class Button : public Widget {
|
||||
public:
|
||||
Button();
|
||||
explicit Button(const std::string &text);
|
||||
~Button() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Button> create();
|
||||
static Ptr<Button> create(const std::string &text);
|
||||
static Ptr<Button> create(const std::string &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字内容
|
||||
// ------------------------------------------------------------------------
|
||||
void setText(const std::string &text);
|
||||
const std::string &getText() const { return text_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内边距
|
||||
// ------------------------------------------------------------------------
|
||||
void setPadding(const Vec2 &padding);
|
||||
void setPadding(float x, float y);
|
||||
Vec2 getPadding() const { return padding_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纯色背景设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundColor(const Color &normal, const Color &hover,
|
||||
const Color &pressed);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边框设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBorder(const Color &color, float width);
|
||||
float getBorderWidth() const { return borderWidth_; }
|
||||
Color getBorderColor() const { return borderColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 图片背景设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundImage(Ptr<Texture> normal, Ptr<Texture> hover = nullptr,
|
||||
Ptr<Texture> pressed = nullptr);
|
||||
void setBackgroundImage(Ptr<Texture> texture, const Rect &rect);
|
||||
|
||||
/**
|
||||
* @brief 为切换按钮的两种状态设置图片背景
|
||||
* @param offNormal 关闭状态的普通图片
|
||||
* @param onNormal 开启状态的普通图片
|
||||
* @param offHover 关闭状态的悬停图片(可选)
|
||||
* @param onHover 开启状态的悬停图片(可选)
|
||||
* @param offPressed 关闭状态的按下图片(可选)
|
||||
* @param onPressed 开启状态的按下图片(可选)
|
||||
*/
|
||||
void setStateBackgroundImage(Ptr<Texture> offNormal, Ptr<Texture> onNormal,
|
||||
Ptr<Texture> offHover = nullptr,
|
||||
Ptr<Texture> onHover = nullptr,
|
||||
Ptr<Texture> offPressed = nullptr,
|
||||
Ptr<Texture> onPressed = nullptr);
|
||||
|
||||
void setBackgroundImageScaleMode(ImageScaleMode mode);
|
||||
void setCustomSize(const Vec2 &size);
|
||||
void setCustomSize(float width, float height);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 圆角矩形设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCornerRadius(float radius);
|
||||
float getCornerRadius() const { return cornerRadius_; }
|
||||
void setRoundedCornersEnabled(bool enabled);
|
||||
bool isRoundedCornersEnabled() const { return roundedCornersEnabled_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 鼠标光标设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setHoverCursor(CursorShape cursor);
|
||||
CursorShape getHoverCursor() const { return hoverCursor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Alpha遮罩点击检测
|
||||
// ------------------------------------------------------------------------
|
||||
void setUseAlphaMaskForHitTest(bool enabled);
|
||||
bool isUseAlphaMaskForHitTest() const { return useAlphaMaskForHitTest_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 点击回调
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnClick(Function<void()> callback);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 切换模式支持(Toggle Button)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置是否为切换模式
|
||||
* @param enabled true 表示启用切换模式,点击时自动切换 on/off 状态
|
||||
*/
|
||||
void setToggleMode(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 获取当前是否为切换模式
|
||||
* @return true 表示处于切换模式
|
||||
*/
|
||||
bool isToggleMode() const { return toggleMode_; }
|
||||
|
||||
/**
|
||||
* @brief 设置当前状态(仅切换模式有效)
|
||||
* @param on true 表示开启状态,false 表示关闭状态
|
||||
*/
|
||||
void setOn(bool on);
|
||||
|
||||
/**
|
||||
* @brief 获取当前状态
|
||||
* @return true 表示开启状态,false 表示关闭状态
|
||||
*/
|
||||
bool isOn() const { return isOn_; }
|
||||
|
||||
/**
|
||||
* @brief 切换当前状态
|
||||
*/
|
||||
void toggle();
|
||||
|
||||
/**
|
||||
* @brief 设置状态改变回调
|
||||
* @param callback 状态改变时调用的回调函数,参数为新状态
|
||||
*/
|
||||
void setOnStateChange(Function<void(bool)> callback);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态文字设置(用于切换按钮)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 为两种状态设置不同的文字
|
||||
* @param textOff 关闭状态显示的文字
|
||||
* @param textOn 开启状态显示的文字
|
||||
*/
|
||||
void setStateText(const std::string &textOff, const std::string &textOn);
|
||||
|
||||
/**
|
||||
* @brief 为两种状态设置不同的文字颜色
|
||||
* @param colorOff 关闭状态的文字颜色
|
||||
* @param colorOn 开启状态的文字颜色
|
||||
*/
|
||||
void setStateTextColor(const Color &colorOff, const Color &colorOn);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
void drawBackgroundImage(RenderBackend &renderer, const Rect &rect);
|
||||
void drawRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||
const Color &color, float radius);
|
||||
void fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||
const Color &color, float radius);
|
||||
Vec2 calculateImageSize(const Vec2 &buttonSize, const Vec2 &imageSize);
|
||||
|
||||
// 状态访问(供子类使用)
|
||||
bool isHovered() const { return hovered_; }
|
||||
bool isPressed() const { return pressed_; }
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Vec2 padding_ = Vec2(10.0f, 6.0f);
|
||||
|
||||
// 文字颜色
|
||||
Color textColor_ = Colors::White;
|
||||
|
||||
// 纯色背景
|
||||
Color bgNormal_ = Color(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
Color bgHover_ = Color(0.28f, 0.28f, 0.28f, 1.0f);
|
||||
Color bgPressed_ = Color(0.15f, 0.15f, 0.15f, 1.0f);
|
||||
|
||||
// 图片背景
|
||||
Ptr<Texture> imgNormal_;
|
||||
Ptr<Texture> imgHover_;
|
||||
Ptr<Texture> imgPressed_;
|
||||
Rect imgNormalRect_;
|
||||
Rect imgHoverRect_;
|
||||
Rect imgPressedRect_;
|
||||
ImageScaleMode scaleMode_ = ImageScaleMode::Original;
|
||||
bool useImageBackground_ = false;
|
||||
bool useTextureRect_ = false;
|
||||
|
||||
// 切换按钮状态图片
|
||||
Ptr<Texture> imgOffNormal_, imgOnNormal_;
|
||||
Ptr<Texture> imgOffHover_, imgOnHover_;
|
||||
Ptr<Texture> imgOffPressed_, imgOnPressed_;
|
||||
bool useStateImages_ = false;
|
||||
|
||||
// 边框
|
||||
Color borderColor_ = Color(0.6f, 0.6f, 0.6f, 1.0f);
|
||||
float borderWidth_ = 1.0f;
|
||||
|
||||
// 圆角矩形
|
||||
float cornerRadius_ = 8.0f;
|
||||
bool roundedCornersEnabled_ = false;
|
||||
|
||||
// 鼠标光标
|
||||
CursorShape hoverCursor_ = CursorShape::Hand;
|
||||
bool cursorChanged_ = false;
|
||||
|
||||
// Alpha遮罩点击检测
|
||||
bool useAlphaMaskForHitTest_ = false;
|
||||
|
||||
bool hovered_ = false;
|
||||
bool pressed_ = false;
|
||||
|
||||
// 切换模式相关
|
||||
bool toggleMode_ = false;
|
||||
bool isOn_ = false;
|
||||
Function<void(bool)> onStateChange_;
|
||||
|
||||
// 状态文字
|
||||
std::string textOff_, textOn_;
|
||||
bool useStateText_ = false;
|
||||
|
||||
// 状态文字颜色
|
||||
Color textColorOff_ = Colors::White;
|
||||
Color textColorOn_ = Colors::White;
|
||||
bool useStateTextColor_ = false;
|
||||
|
||||
Function<void()> onClick_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 复选框组件
|
||||
// ============================================================================
|
||||
class CheckBox : public Widget {
|
||||
public:
|
||||
CheckBox();
|
||||
~CheckBox() override = default;
|
||||
|
||||
static Ptr<CheckBox> create();
|
||||
static Ptr<CheckBox> create(const std::string &label);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 选中状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setChecked(bool checked);
|
||||
bool isChecked() const { return checked_; }
|
||||
void toggle();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 标签设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setLabel(const std::string &label);
|
||||
const std::string &getLabel() const { return label_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 复选框尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setBoxSize(float size);
|
||||
float getBoxSize() const { return boxSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 间距
|
||||
// ------------------------------------------------------------------------
|
||||
void setSpacing(float spacing);
|
||||
float getSpacing() const { return spacing_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCheckedColor(const Color &color);
|
||||
Color getCheckedColor() const { return checkedColor_; }
|
||||
|
||||
void setUncheckedColor(const Color &color);
|
||||
Color getUncheckedColor() const { return uncheckedColor_; }
|
||||
|
||||
void setCheckMarkColor(const Color &color);
|
||||
Color getCheckMarkColor() const { return checkMarkColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 回调设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnStateChange(Function<void(bool)> callback);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
|
||||
private:
|
||||
bool checked_ = false;
|
||||
std::string label_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
|
||||
float boxSize_ = 20.0f;
|
||||
float spacing_ = 8.0f;
|
||||
|
||||
Color checkedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||
Color uncheckedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
Color checkMarkColor_ = Colors::White;
|
||||
|
||||
bool pressed_ = false;
|
||||
Function<void(bool)> onStateChange_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 文本标签组件 - 用于显示静态文本
|
||||
// 支持多行、对齐、阴影、描边等游戏常用效果
|
||||
// ============================================================================
|
||||
class Label : public Widget {
|
||||
public:
|
||||
Label();
|
||||
explicit Label(const std::string &text);
|
||||
~Label() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Label> create();
|
||||
static Ptr<Label> create(const std::string &text);
|
||||
static Ptr<Label> create(const std::string &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本内容
|
||||
// ------------------------------------------------------------------------
|
||||
void setText(const std::string &text);
|
||||
const std::string &getText() const { return text_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体大小
|
||||
// ------------------------------------------------------------------------
|
||||
void setFontSize(int size);
|
||||
int getFontSize() const { return fontSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 水平对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
enum class HorizontalAlign { Left, Center, Right };
|
||||
|
||||
void setHorizontalAlign(HorizontalAlign align);
|
||||
HorizontalAlign getHorizontalAlign() const { return hAlign_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 垂直对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
enum class VerticalAlign { Top, Middle, Bottom };
|
||||
|
||||
void setVerticalAlign(VerticalAlign align);
|
||||
VerticalAlign getVerticalAlign() const { return vAlign_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 阴影效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setShadowEnabled(bool enabled);
|
||||
bool isShadowEnabled() const { return shadowEnabled_; }
|
||||
|
||||
void setShadowColor(const Color &color);
|
||||
Color getShadowColor() const { return shadowColor_; }
|
||||
|
||||
void setShadowOffset(const Vec2 &offset);
|
||||
Vec2 getShadowOffset() const { return shadowOffset_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 描边效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setOutlineEnabled(bool enabled);
|
||||
bool isOutlineEnabled() const { return outlineEnabled_; }
|
||||
|
||||
void setOutlineColor(const Color &color);
|
||||
Color getOutlineColor() const { return outlineColor_; }
|
||||
|
||||
void setOutlineWidth(float width);
|
||||
float getOutlineWidth() const { return outlineWidth_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 多行文本
|
||||
// ------------------------------------------------------------------------
|
||||
void setMultiLine(bool multiLine);
|
||||
bool isMultiLine() const { return multiLine_; }
|
||||
|
||||
void setLineSpacing(float spacing);
|
||||
float getLineSpacing() const { return lineSpacing_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 最大宽度(用于自动换行)
|
||||
// ------------------------------------------------------------------------
|
||||
void setMaxWidth(float maxWidth);
|
||||
float getMaxWidth() const { return maxWidth_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 尺寸计算
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 getTextSize() const;
|
||||
float getLineHeight() const;
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
int fontSize_ = 16;
|
||||
|
||||
HorizontalAlign hAlign_ = HorizontalAlign::Left;
|
||||
VerticalAlign vAlign_ = VerticalAlign::Top;
|
||||
|
||||
bool shadowEnabled_ = false;
|
||||
Color shadowColor_ = Color(0.0f, 0.0f, 0.0f, 0.5f);
|
||||
Vec2 shadowOffset_ = Vec2(2.0f, 2.0f);
|
||||
|
||||
bool outlineEnabled_ = false;
|
||||
Color outlineColor_ = Colors::Black;
|
||||
float outlineWidth_ = 1.0f;
|
||||
|
||||
bool multiLine_ = false;
|
||||
float lineSpacing_ = 1.0f;
|
||||
float maxWidth_ = 0.0f;
|
||||
|
||||
mutable Vec2 cachedSize_ = Vec2::Zero();
|
||||
mutable bool sizeDirty_ = true;
|
||||
|
||||
void updateCache() const;
|
||||
void drawText(RenderBackend &renderer, const Vec2 &position, const Color &color);
|
||||
Vec2 calculateDrawPosition() const;
|
||||
std::vector<std::string> splitLines() const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 进度条组件 - 用于显示进度/百分比
|
||||
// 适用于血条、能量条、加载进度、经验条等游戏场景
|
||||
// ============================================================================
|
||||
class ProgressBar : public Widget {
|
||||
public:
|
||||
ProgressBar();
|
||||
~ProgressBar() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<ProgressBar> create();
|
||||
static Ptr<ProgressBar> create(float min, float max, float value);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 数值范围
|
||||
// ------------------------------------------------------------------------
|
||||
void setRange(float min, float max);
|
||||
float getMin() const { return min_; }
|
||||
float getMax() const { return max_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 当前值
|
||||
// ------------------------------------------------------------------------
|
||||
void setValue(float value);
|
||||
float getValue() const { return value_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 获取百分比 (0.0 - 1.0)
|
||||
// ------------------------------------------------------------------------
|
||||
float getPercent() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 进度条方向
|
||||
// ------------------------------------------------------------------------
|
||||
enum class Direction { LeftToRight, RightToLeft, BottomToTop, TopToBottom };
|
||||
|
||||
void setDirection(Direction dir);
|
||||
Direction getDirection() const { return direction_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundColor(const Color &color);
|
||||
Color getBackgroundColor() const { return bgColor_; }
|
||||
|
||||
void setFillColor(const Color &color);
|
||||
Color getFillColor() const { return fillColor_; }
|
||||
|
||||
// 渐变填充色(从 fillColor_ 到 fillColorEnd_)
|
||||
void setGradientFillEnabled(bool enabled);
|
||||
bool isGradientFillEnabled() const { return gradientEnabled_; }
|
||||
|
||||
void setFillColorEnd(const Color &color);
|
||||
Color getFillColorEnd() const { return fillColorEnd_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 分段颜色(根据百分比自动切换颜色)
|
||||
// ------------------------------------------------------------------------
|
||||
void setSegmentedColorsEnabled(bool enabled);
|
||||
bool isSegmentedColorsEnabled() const { return segmentedColorsEnabled_; }
|
||||
|
||||
// 设置分段阈值和颜色,例如:>70%绿色, >30%黄色, 其他红色
|
||||
void addColorSegment(float percentThreshold, const Color &color);
|
||||
void clearColorSegments();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 圆角设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCornerRadius(float radius);
|
||||
float getCornerRadius() const { return cornerRadius_; }
|
||||
|
||||
void setRoundedCornersEnabled(bool enabled);
|
||||
bool isRoundedCornersEnabled() const { return roundedCornersEnabled_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边框设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBorderEnabled(bool enabled);
|
||||
bool isBorderEnabled() const { return borderEnabled_; }
|
||||
|
||||
void setBorderColor(const Color &color);
|
||||
Color getBorderColor() const { return borderColor_; }
|
||||
|
||||
void setBorderWidth(float width);
|
||||
float getBorderWidth() const { return borderWidth_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内边距(填充与边框的距离)
|
||||
// ------------------------------------------------------------------------
|
||||
void setPadding(float padding);
|
||||
float getPadding() const { return padding_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本显示
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextEnabled(bool enabled);
|
||||
bool isTextEnabled() const { return textEnabled_; }
|
||||
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// 文本格式:"{value}/{max}", "{percent}%", "{value:.1f}" 等
|
||||
void setTextFormat(const std::string &format);
|
||||
const std::string &getTextFormat() const { return textFormat_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 动画效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setAnimatedChangeEnabled(bool enabled);
|
||||
bool isAnimatedChangeEnabled() const { return animatedChangeEnabled_; }
|
||||
|
||||
void setAnimationSpeed(float speed); // 每秒变化量
|
||||
float getAnimationSpeed() const { return animationSpeed_; }
|
||||
|
||||
// 延迟显示效果(如LOL血条)
|
||||
void setDelayedDisplayEnabled(bool enabled);
|
||||
bool isDelayedDisplayEnabled() const { return delayedDisplayEnabled_; }
|
||||
|
||||
void setDelayTime(float seconds);
|
||||
float getDelayTime() const { return delayTime_; }
|
||||
|
||||
void setDelayedFillColor(const Color &color);
|
||||
Color getDelayedFillColor() const { return delayedFillColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 条纹效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setStripedEnabled(bool enabled);
|
||||
bool isStripedEnabled() const { return stripedEnabled_; }
|
||||
|
||||
void setStripeColor(const Color &color);
|
||||
Color getStripeColor() const { return stripeColor_; }
|
||||
|
||||
void setStripeSpeed(float speed); // 条纹移动速度
|
||||
float getStripeSpeed() const { return stripeSpeed_; }
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onUpdate(float deltaTime) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
// 数值
|
||||
float min_ = 0.0f;
|
||||
float max_ = 100.0f;
|
||||
float value_ = 50.0f;
|
||||
|
||||
// 方向
|
||||
Direction direction_ = Direction::LeftToRight;
|
||||
|
||||
// 颜色
|
||||
Color bgColor_ = Color(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
Color fillColor_ = Color(0.0f, 0.8f, 0.2f, 1.0f);
|
||||
Color fillColorEnd_ = Color(0.0f, 0.6f, 0.1f, 1.0f);
|
||||
bool gradientEnabled_ = false;
|
||||
|
||||
// 分段颜色
|
||||
bool segmentedColorsEnabled_ = false;
|
||||
std::vector<std::pair<float, Color>> colorSegments_;
|
||||
|
||||
// 圆角
|
||||
float cornerRadius_ = 4.0f;
|
||||
bool roundedCornersEnabled_ = true;
|
||||
|
||||
// 边框
|
||||
bool borderEnabled_ = false;
|
||||
Color borderColor_ = Colors::White;
|
||||
float borderWidth_ = 1.0f;
|
||||
|
||||
// 内边距
|
||||
float padding_ = 2.0f;
|
||||
|
||||
// 文本
|
||||
bool textEnabled_ = false;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
std::string textFormat_ = "{percent:.0f}%";
|
||||
|
||||
// 动画
|
||||
bool animatedChangeEnabled_ = false;
|
||||
float animationSpeed_ = 100.0f;
|
||||
float displayValue_ = 50.0f; // 用于动画的显示值
|
||||
|
||||
// 延迟显示
|
||||
bool delayedDisplayEnabled_ = false;
|
||||
float delayTime_ = 0.3f;
|
||||
float delayTimer_ = 0.0f;
|
||||
float delayedValue_ = 50.0f;
|
||||
Color delayedFillColor_ = Color(1.0f, 0.0f, 0.0f, 0.5f);
|
||||
|
||||
// 条纹
|
||||
bool stripedEnabled_ = false;
|
||||
Color stripeColor_ = Color(1.0f, 1.0f, 1.0f, 0.2f);
|
||||
float stripeSpeed_ = 50.0f;
|
||||
float stripeOffset_ = 0.0f;
|
||||
|
||||
Color getCurrentFillColor() const;
|
||||
std::string formatText() const;
|
||||
void drawRoundedRect(RenderBackend &renderer, const Rect &rect, const Color &color, float radius);
|
||||
void fillRoundedRect(RenderBackend &renderer, const Rect &rect, const Color &color, float radius);
|
||||
void drawStripes(RenderBackend &renderer, const Rect &rect);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 单选按钮组件
|
||||
// ============================================================================
|
||||
class RadioButton : public Widget {
|
||||
public:
|
||||
RadioButton();
|
||||
~RadioButton() override = default;
|
||||
|
||||
static Ptr<RadioButton> create();
|
||||
static Ptr<RadioButton> create(const std::string &label);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 选择状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setSelected(bool selected);
|
||||
bool isSelected() const { return selected_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 标签设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setLabel(const std::string &label);
|
||||
const std::string &getLabel() const { return label_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 圆形尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setCircleSize(float size);
|
||||
float getCircleSize() const { return circleSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 间距
|
||||
// ------------------------------------------------------------------------
|
||||
void setSpacing(float spacing);
|
||||
float getSpacing() const { return spacing_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setSelectedColor(const Color &color);
|
||||
Color getSelectedColor() const { return selectedColor_; }
|
||||
|
||||
void setUnselectedColor(const Color &color);
|
||||
Color getUnselectedColor() const { return unselectedColor_; }
|
||||
|
||||
void setDotColor(const Color &color);
|
||||
Color getDotColor() const { return dotColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 分组
|
||||
// ------------------------------------------------------------------------
|
||||
void setGroupId(int groupId);
|
||||
int getGroupId() const { return groupId_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 回调设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnStateChange(Function<void(bool)> callback);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
|
||||
private:
|
||||
bool selected_ = false;
|
||||
std::string label_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
|
||||
float circleSize_ = 20.0f;
|
||||
float spacing_ = 8.0f;
|
||||
|
||||
Color selectedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||
Color unselectedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
Color dotColor_ = Colors::White;
|
||||
|
||||
int groupId_ = 0;
|
||||
|
||||
bool pressed_ = false;
|
||||
Function<void(bool)> onStateChange_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 单选按钮组管理器
|
||||
// ============================================================================
|
||||
class RadioButtonGroup {
|
||||
public:
|
||||
void addButton(RadioButton *button);
|
||||
void removeButton(RadioButton *button);
|
||||
void selectButton(RadioButton *button);
|
||||
RadioButton *getSelectedButton() const { return selectedButton_; }
|
||||
|
||||
void setOnSelectionChange(Function<void(RadioButton*)> callback);
|
||||
|
||||
private:
|
||||
std::vector<RadioButton*> buttons_;
|
||||
RadioButton *selectedButton_ = nullptr;
|
||||
Function<void(RadioButton*)> onSelectionChange_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 滑动条组件
|
||||
// ============================================================================
|
||||
class Slider : public Widget {
|
||||
public:
|
||||
Slider();
|
||||
~Slider() override = default;
|
||||
|
||||
static Ptr<Slider> create();
|
||||
static Ptr<Slider> create(float min, float max, float value);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 数值范围
|
||||
// ------------------------------------------------------------------------
|
||||
void setRange(float min, float max);
|
||||
float getMin() const { return min_; }
|
||||
float getMax() const { return max_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 当前值
|
||||
// ------------------------------------------------------------------------
|
||||
void setValue(float value);
|
||||
float getValue() const { return value_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 步进值
|
||||
// ------------------------------------------------------------------------
|
||||
void setStep(float step);
|
||||
float getStep() const { return step_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 方向
|
||||
// ------------------------------------------------------------------------
|
||||
void setVertical(bool vertical);
|
||||
bool isVertical() const { return vertical_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 轨道尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setTrackSize(float size);
|
||||
float getTrackSize() const { return trackSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 滑块尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setThumbSize(float size);
|
||||
float getThumbSize() const { return thumbSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setTrackColor(const Color &color);
|
||||
Color getTrackColor() const { return trackColor_; }
|
||||
|
||||
void setFillColor(const Color &color);
|
||||
Color getFillColor() const { return fillColor_; }
|
||||
|
||||
void setThumbColor(const Color &color);
|
||||
Color getThumbColor() const { return thumbColor_; }
|
||||
|
||||
void setThumbHoverColor(const Color &color);
|
||||
Color getThumbHoverColor() const { return thumbHoverColor_; }
|
||||
|
||||
void setThumbPressedColor(const Color &color);
|
||||
Color getThumbPressedColor() const { return thumbPressedColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 显示设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setShowThumb(bool show);
|
||||
bool isShowThumb() const { return showThumb_; }
|
||||
|
||||
void setShowFill(bool show);
|
||||
bool isShowFill() const { return showFill_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本显示
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextEnabled(bool enabled);
|
||||
bool isTextEnabled() const { return textEnabled_; }
|
||||
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
void setTextFormat(const std::string &format);
|
||||
const std::string &getTextFormat() const { return textFormat_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 回调设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnValueChange(Function<void(float)> callback);
|
||||
void setOnDragStart(Function<void()> callback);
|
||||
void setOnDragEnd(Function<void()> callback);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
bool onMouseMove(const MouseEvent &event) override;
|
||||
void onMouseEnter() override;
|
||||
void onMouseLeave() override;
|
||||
|
||||
private:
|
||||
float min_ = 0.0f;
|
||||
float max_ = 100.0f;
|
||||
float value_ = 50.0f;
|
||||
float step_ = 0.0f;
|
||||
|
||||
bool vertical_ = false;
|
||||
float trackSize_ = 6.0f;
|
||||
float thumbSize_ = 16.0f;
|
||||
|
||||
Color trackColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
Color fillColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||
Color thumbColor_ = Color(0.8f, 0.8f, 0.8f, 1.0f);
|
||||
Color thumbHoverColor_ = Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
Color thumbPressedColor_ = Color(0.6f, 0.6f, 0.6f, 1.0f);
|
||||
|
||||
bool showThumb_ = true;
|
||||
bool showFill_ = true;
|
||||
|
||||
bool textEnabled_ = false;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
std::string textFormat_ = "{value:.0f}";
|
||||
|
||||
bool dragging_ = false;
|
||||
bool hovered_ = false;
|
||||
|
||||
Function<void(float)> onValueChange_;
|
||||
Function<void()> onDragStart_;
|
||||
Function<void()> onDragEnd_;
|
||||
|
||||
float valueToPosition(float value) const;
|
||||
float positionToValue(float pos) const;
|
||||
Rect getThumbRect() const;
|
||||
Rect getTrackRect() const;
|
||||
std::string formatText() const;
|
||||
float snapToStep(float value) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/graphics/font.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 文本组件 - 继承自 Widget 的 UI 组件
|
||||
// ============================================================================
|
||||
class Text : public Widget {
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// 对齐方式枚举
|
||||
// ------------------------------------------------------------------------
|
||||
enum class Alignment { Left, Center, Right };
|
||||
enum class VerticalAlignment { Top, Middle, Bottom };
|
||||
|
||||
Text();
|
||||
explicit Text(const std::string &text);
|
||||
~Text() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Text> create();
|
||||
static Ptr<Text> create(const std::string &text);
|
||||
static Ptr<Text> create(const std::string &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 格式化创建方法(类似 printf)
|
||||
// 使用示例:
|
||||
// auto text = Text::createFormat("FPS: %d", 60);
|
||||
// auto text = Text::createFormat(font, "得分: %d", score);
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Text> createFormat(const char *fmt, ...);
|
||||
static Ptr<Text> createFormat(Ptr<FontAtlas> font, const char *fmt, ...);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字内容
|
||||
// ------------------------------------------------------------------------
|
||||
void setText(const std::string &text);
|
||||
const std::string &getText() const { return text_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 格式化文本设置(类似 printf)
|
||||
// 使用示例:
|
||||
// text->setFormat("FPS: %d", 60);
|
||||
// text->setFormat("位置: (%.1f, %.1f)", x, y);
|
||||
// text->setFormat("生命值: %d/100", hp);
|
||||
// ------------------------------------------------------------------------
|
||||
void setFormat(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char buffer[256];
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
text_ = buffer;
|
||||
sizeDirty_ = true;
|
||||
updateSpatialIndex();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return color_; }
|
||||
|
||||
void setFontSize(int size);
|
||||
int getFontSize() const { return fontSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
void setAlignment(Alignment align);
|
||||
Alignment getAlignment() const { return alignment_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 垂直对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
void setVerticalAlignment(VerticalAlignment align);
|
||||
VerticalAlignment getVerticalAlignment() const { return verticalAlignment_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 尺寸计算
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 getTextSize() const;
|
||||
float getLineHeight() const;
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color color_ = Colors::White;
|
||||
int fontSize_ = 16;
|
||||
Alignment alignment_ = Alignment::Left;
|
||||
VerticalAlignment verticalAlignment_ = VerticalAlignment::Top;
|
||||
|
||||
mutable Vec2 cachedSize_ = Vec2::Zero();
|
||||
mutable bool sizeDirty_ = true;
|
||||
|
||||
void updateCache() const;
|
||||
Vec2 calculateDrawPosition() const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/platform/input.h>
|
||||
#include <extra2d/scene/node.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标事件结构
|
||||
// ============================================================================
|
||||
struct MouseEvent {
|
||||
MouseButton button;
|
||||
float x;
|
||||
float y;
|
||||
int mods; // 修饰键状态
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 坐标空间枚举 - 定义 UI 组件的渲染坐标空间
|
||||
// ============================================================================
|
||||
enum class CoordinateSpace {
|
||||
Screen, // 屏幕空间 - 固定位置,不随相机移动
|
||||
World, // 世界空间 - 随相机移动(默认行为)
|
||||
Camera, // 相机空间 - 相对于相机位置的偏移
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Widget 基类 - UI 组件的基础
|
||||
// ============================================================================
|
||||
class Widget : public Node {
|
||||
public:
|
||||
Widget();
|
||||
~Widget() override = default;
|
||||
|
||||
void setSize(const Size &size);
|
||||
void setSize(float width, float height);
|
||||
Size getSize() const { return size_; }
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCoordinateSpace(CoordinateSpace space);
|
||||
CoordinateSpace getCoordinateSpace() const { return coordinateSpace_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 屏幕空间位置设置(仅在 Screen 空间下有效)
|
||||
// ------------------------------------------------------------------------
|
||||
void setScreenPosition(const Vec2 &pos);
|
||||
void setScreenPosition(float x, float y);
|
||||
Vec2 getScreenPosition() const { return screenPosition_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 相机空间偏移设置(仅在 Camera 空间下有效)
|
||||
// ------------------------------------------------------------------------
|
||||
void setCameraOffset(const Vec2 &offset);
|
||||
void setCameraOffset(float x, float y);
|
||||
Vec2 getCameraOffset() const { return cameraOffset_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 鼠标事件处理(子类可重写)
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool onMousePress(const MouseEvent &event) { return false; }
|
||||
virtual bool onMouseRelease(const MouseEvent &event) { return false; }
|
||||
virtual bool onMouseMove(const MouseEvent &event) { return false; }
|
||||
virtual void onMouseEnter() {}
|
||||
virtual void onMouseLeave() {}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 启用/禁用状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 焦点状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setFocused(bool focused) { focused_ = focused; }
|
||||
bool isFocused() const { return focused_; }
|
||||
|
||||
protected:
|
||||
// 供子类使用的辅助方法
|
||||
bool isPointInside(float x, float y) const {
|
||||
return getBoundingBox().containsPoint(Point(x, y));
|
||||
}
|
||||
|
||||
// 获取实际渲染位置(根据坐标空间计算)
|
||||
Vec2 getRenderPosition() const;
|
||||
|
||||
// 子类重写此方法以支持自定义渲染
|
||||
virtual void onDrawWidget(RenderBackend &renderer) {}
|
||||
|
||||
// 重写 Node 的 onDraw 以处理坐标空间
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
Size size_ = Size::Zero();
|
||||
bool enabled_ = true;
|
||||
bool focused_ = false;
|
||||
bool hovered_ = false;
|
||||
|
||||
// 坐标空间相关
|
||||
CoordinateSpace coordinateSpace_ = CoordinateSpace::World;
|
||||
Vec2 screenPosition_ = Vec2::Zero(); // 屏幕空间位置
|
||||
Vec2 cameraOffset_ = Vec2::Zero(); // 相机空间偏移
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 存档类型枚举
|
||||
// ============================================================================
|
||||
enum class SaveDataType {
|
||||
Account, // 用户存档(与特定用户关联)
|
||||
Common, // 公共存档(所有用户共享)
|
||||
Cache, // 缓存数据(可删除)
|
||||
Device, // 设备存档
|
||||
Temporary, // 临时数据
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 用户ID结构(封装 Switch AccountUid)
|
||||
// ============================================================================
|
||||
struct UserId {
|
||||
uint64_t uid[2] = {0, 0};
|
||||
|
||||
bool isValid() const { return uid[0] != 0 || uid[1] != 0; }
|
||||
bool operator==(const UserId &other) const {
|
||||
return uid[0] == other.uid[0] && uid[1] == other.uid[1];
|
||||
}
|
||||
bool operator!=(const UserId &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// DataStore 类 - 数据持久化(支持 Switch 存档系统)
|
||||
// ============================================================================
|
||||
class DataStore {
|
||||
public:
|
||||
DataStore();
|
||||
~DataStore();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文件操作
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载 INI 文件
|
||||
bool load(const std::string &filename);
|
||||
|
||||
/// 保存到 INI 文件
|
||||
bool save(const std::string &filename);
|
||||
|
||||
/// 获取当前文件名
|
||||
const std::string &getFilename() const { return filename_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Switch 存档系统支持
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 挂载 Switch 存档数据
|
||||
* @param type 存档类型
|
||||
* @param userId 用户ID(Account 类型需要)
|
||||
* @param mountName 挂载点名称(默认 "save")
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool mountSaveData(SaveDataType type = SaveDataType::Account,
|
||||
const UserId &userId = UserId(),
|
||||
const std::string &mountName = "save");
|
||||
|
||||
/**
|
||||
* @brief 卸载存档挂载
|
||||
* @param mountName 挂载点名称
|
||||
*/
|
||||
void unmountSaveData(const std::string &mountName = "save");
|
||||
|
||||
/**
|
||||
* @brief 提交存档更改(重要:修改后必须调用)
|
||||
* @param mountName 挂载点名称
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool commitSaveData(const std::string &mountName = "save");
|
||||
|
||||
/**
|
||||
* @brief 检查存档是否已挂载
|
||||
*/
|
||||
bool isSaveDataMounted() const { return saveDataMounted_; }
|
||||
|
||||
/**
|
||||
* @brief 获取挂载点路径
|
||||
*/
|
||||
std::string getSaveDataPath(const std::string &path = "") const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 用户账户管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取当前预选用户ID
|
||||
* @return 用户ID(无效时返回空ID)
|
||||
*/
|
||||
static UserId getCurrentUserId();
|
||||
|
||||
/**
|
||||
* @brief 设置默认用户ID
|
||||
*/
|
||||
void setDefaultUserId(const UserId &userId) { defaultUserId_ = userId; }
|
||||
|
||||
/**
|
||||
* @brief 获取默认用户ID
|
||||
*/
|
||||
UserId getDefaultUserId() const { return defaultUserId_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 数据读写
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 获取字符串值
|
||||
std::string getString(const std::string §ion, const std::string &key,
|
||||
const std::string &defaultValue = "");
|
||||
|
||||
/// 获取整数值
|
||||
int getInt(const std::string §ion, const std::string &key,
|
||||
int defaultValue = 0);
|
||||
|
||||
/// 获取浮点数值
|
||||
float getFloat(const std::string §ion, const std::string &key,
|
||||
float defaultValue = 0.0f);
|
||||
|
||||
/// 获取布尔值
|
||||
bool getBool(const std::string §ion, const std::string &key,
|
||||
bool defaultValue = false);
|
||||
|
||||
/// 设置字符串值
|
||||
void setString(const std::string §ion, const std::string &key,
|
||||
const std::string &value);
|
||||
|
||||
/// 设置整数值
|
||||
void setInt(const std::string §ion, const std::string &key, int value);
|
||||
|
||||
/// 设置浮点数值
|
||||
void setFloat(const std::string §ion, const std::string &key,
|
||||
float value);
|
||||
|
||||
/// 设置布尔值
|
||||
void setBool(const std::string §ion, const std::string &key, bool value);
|
||||
|
||||
/// 删除键
|
||||
void removeKey(const std::string §ion, const std::string &key);
|
||||
|
||||
/// 删除整个 section
|
||||
void removeSection(const std::string §ion);
|
||||
|
||||
/// 检查键是否存在
|
||||
bool hasKey(const std::string §ion, const std::string &key);
|
||||
|
||||
/// 检查 section 是否存在
|
||||
bool hasSection(const std::string §ion);
|
||||
|
||||
/// 清除所有数据
|
||||
void clear();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 事务支持
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 开始事务(批量操作,延迟写入)
|
||||
*/
|
||||
void beginTransaction();
|
||||
|
||||
/**
|
||||
* @brief 提交事务(写入文件)
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool commit();
|
||||
|
||||
/**
|
||||
* @brief 回滚事务(放弃更改)
|
||||
*/
|
||||
void rollback();
|
||||
|
||||
/**
|
||||
* @brief 检查是否在事务中
|
||||
*/
|
||||
bool isInTransaction() const { return inTransaction_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 获取所有 section 名称
|
||||
std::vector<std::string> getAllSections() const;
|
||||
|
||||
/// 获取指定 section 的所有 key
|
||||
std::vector<std::string> getAllKeys(const std::string §ion) const;
|
||||
|
||||
/// 从存档加载(自动处理挂载路径)
|
||||
bool loadFromSave(const std::string &path);
|
||||
|
||||
/// 保存到存档(自动处理挂载路径和提交)
|
||||
bool saveToSave(const std::string &path);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
UniquePtr<Impl> impl_;
|
||||
std::string filename_;
|
||||
std::string mountName_;
|
||||
UserId defaultUserId_;
|
||||
bool saveDataMounted_ = false;
|
||||
bool inTransaction_ = false;
|
||||
bool dirty_ = false;
|
||||
|
||||
// 内部辅助方法
|
||||
bool internalSave(const std::string &filename);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,492 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 对象池 - 自动管理的高性能内存池
|
||||
// 特性:
|
||||
// - 自动内存对齐
|
||||
// - 侵入式空闲链表(零额外内存开销)
|
||||
// - 线程本地缓存(减少锁竞争)
|
||||
// - 自动容量管理(自动扩展/收缩)
|
||||
// - 自动预热
|
||||
// - 异常安全
|
||||
// ============================================================================
|
||||
|
||||
// 线程本地缓存配置
|
||||
struct PoolConfig {
|
||||
static constexpr size_t DEFAULT_BLOCK_SIZE = 64;
|
||||
static constexpr size_t THREAD_CACHE_SIZE = 16;
|
||||
static constexpr size_t SHRINK_THRESHOLD_MS = 30000;
|
||||
static constexpr double SHRINK_RATIO = 0.5;
|
||||
};
|
||||
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
|
||||
class ObjectPool {
|
||||
public:
|
||||
static_assert(std::is_default_constructible_v<T>, "T must be default constructible");
|
||||
static_assert(std::is_destructible_v<T>, "T must be destructible");
|
||||
static_assert(BlockSize > 0, "BlockSize must be greater than 0");
|
||||
static_assert(alignof(T) <= alignof(std::max_align_t),
|
||||
"Alignment requirement too high");
|
||||
|
||||
ObjectPool()
|
||||
: freeListHead_(nullptr)
|
||||
, blocks_()
|
||||
, allocatedCount_(0)
|
||||
, totalCapacity_(0)
|
||||
, isDestroyed_(false)
|
||||
, lastShrinkCheck_(0)
|
||||
, prewarmed_(false) {
|
||||
}
|
||||
|
||||
~ObjectPool() {
|
||||
clear();
|
||||
}
|
||||
|
||||
ObjectPool(const ObjectPool&) = delete;
|
||||
ObjectPool& operator=(const ObjectPool&) = delete;
|
||||
ObjectPool(ObjectPool&&) noexcept = delete;
|
||||
ObjectPool& operator=(ObjectPool&&) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief 分配一个对象(自动预热、自动扩展)
|
||||
* @return 指向分配的对象的指针,失败返回 nullptr
|
||||
*/
|
||||
T* allocate() {
|
||||
auto& cache = getThreadCache();
|
||||
|
||||
if (T* obj = cache.pop()) {
|
||||
new (obj) T();
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (isDestroyed_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!prewarmed_) {
|
||||
prewarmInternal();
|
||||
}
|
||||
|
||||
if (!freeListHead_) {
|
||||
growInternal();
|
||||
}
|
||||
|
||||
T* obj = popFreeList();
|
||||
if (obj) {
|
||||
new (obj) T();
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分配并构造一个对象(带参数)
|
||||
*/
|
||||
template <typename... Args>
|
||||
T* allocate(Args&&... args) {
|
||||
auto& cache = getThreadCache();
|
||||
|
||||
if (T* obj = cache.pop()) {
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (isDestroyed_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!prewarmed_) {
|
||||
prewarmInternal();
|
||||
}
|
||||
|
||||
if (!freeListHead_) {
|
||||
growInternal();
|
||||
}
|
||||
|
||||
T* obj = popFreeList();
|
||||
if (obj) {
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 回收一个对象(自动异常处理)
|
||||
* @param obj 要回收的对象指针
|
||||
* @return true 如果对象成功回收
|
||||
*/
|
||||
bool deallocate(T* obj) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
obj->~T();
|
||||
} catch (const std::exception& e) {
|
||||
Logger::log(LogLevel::Error, "ObjectPool: Exception in destructor: {}", e.what());
|
||||
} catch (...) {
|
||||
Logger::log(LogLevel::Error, "ObjectPool: Unknown exception in destructor");
|
||||
}
|
||||
|
||||
auto& cache = getThreadCache();
|
||||
if (cache.push(obj)) {
|
||||
allocatedCount_.fetch_sub(1, std::memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!isDestroyed_) {
|
||||
pushFreeList(obj);
|
||||
allocatedCount_.fetch_sub(1, std::memory_order_relaxed);
|
||||
tryAutoShrink();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前已分配的对象数量
|
||||
*/
|
||||
size_t allocatedCount() const {
|
||||
return allocatedCount_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取池中总的对象容量
|
||||
*/
|
||||
size_t capacity() const {
|
||||
return totalCapacity_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内存使用量(字节)
|
||||
*/
|
||||
size_t memoryUsage() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return blocks_.size() * BlockSize * sizeof(T);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空所有内存块
|
||||
*/
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
isDestroyed_ = true;
|
||||
|
||||
for (auto& block : blocks_) {
|
||||
alignedFree(block);
|
||||
}
|
||||
blocks_.clear();
|
||||
freeListHead_ = nullptr;
|
||||
totalCapacity_.store(0, std::memory_order_relaxed);
|
||||
allocatedCount_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
struct FreeNode {
|
||||
FreeNode* next;
|
||||
};
|
||||
|
||||
struct ThreadCache {
|
||||
T* objects[PoolConfig::THREAD_CACHE_SIZE];
|
||||
size_t count = 0;
|
||||
|
||||
T* pop() {
|
||||
if (count > 0) {
|
||||
return objects[--count];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool push(T* obj) {
|
||||
if (count < PoolConfig::THREAD_CACHE_SIZE) {
|
||||
objects[count++] = obj;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
static ThreadCache& getThreadCache() {
|
||||
thread_local ThreadCache cache;
|
||||
return cache;
|
||||
}
|
||||
|
||||
static constexpr size_t Alignment = alignof(T);
|
||||
static constexpr size_t AlignedSize = ((sizeof(T) + Alignment - 1) / Alignment) * Alignment;
|
||||
|
||||
static void* alignedAlloc(size_t size) {
|
||||
#ifdef _WIN32
|
||||
return _aligned_malloc(size, Alignment);
|
||||
#else
|
||||
return std::aligned_alloc(Alignment, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void alignedFree(void* ptr) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
std::free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void prewarmInternal() {
|
||||
if (!freeListHead_) {
|
||||
growInternal();
|
||||
}
|
||||
prewarmed_ = true;
|
||||
}
|
||||
|
||||
void growInternal() {
|
||||
size_t blockSize = AlignedSize > sizeof(FreeNode) ? AlignedSize : sizeof(FreeNode);
|
||||
size_t totalSize = blockSize * BlockSize;
|
||||
|
||||
void* block = alignedAlloc(totalSize);
|
||||
if (!block) {
|
||||
Logger::log(LogLevel::Error, "ObjectPool: Failed to allocate memory block");
|
||||
return;
|
||||
}
|
||||
|
||||
blocks_.push_back(block);
|
||||
totalCapacity_.fetch_add(BlockSize, std::memory_order_relaxed);
|
||||
|
||||
char* ptr = static_cast<char*>(block);
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
pushFreeList(reinterpret_cast<T*>(ptr + i * blockSize));
|
||||
}
|
||||
}
|
||||
|
||||
void pushFreeList(T* obj) {
|
||||
FreeNode* node = reinterpret_cast<FreeNode*>(obj);
|
||||
node->next = freeListHead_;
|
||||
freeListHead_ = node;
|
||||
}
|
||||
|
||||
T* popFreeList() {
|
||||
if (!freeListHead_) {
|
||||
return nullptr;
|
||||
}
|
||||
FreeNode* node = freeListHead_;
|
||||
freeListHead_ = freeListHead_->next;
|
||||
return reinterpret_cast<T*>(node);
|
||||
}
|
||||
|
||||
void tryAutoShrink() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch()).count();
|
||||
|
||||
if (elapsed - lastShrinkCheck_ < PoolConfig::SHRINK_THRESHOLD_MS) {
|
||||
return;
|
||||
}
|
||||
lastShrinkCheck_ = elapsed;
|
||||
|
||||
size_t allocated = allocatedCount_.load(std::memory_order_relaxed);
|
||||
size_t capacity = totalCapacity_.load(std::memory_order_relaxed);
|
||||
|
||||
if (capacity > BlockSize &&
|
||||
static_cast<double>(allocated) / capacity < PoolConfig::SHRINK_RATIO) {
|
||||
shrinkInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void shrinkInternal() {
|
||||
size_t toFree = 0;
|
||||
size_t freeCount = 0;
|
||||
|
||||
FreeNode* node = freeListHead_;
|
||||
while (node) {
|
||||
++freeCount;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (freeCount < BlockSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t blocksToKeep = blocks_.size();
|
||||
if (allocatedCount_.load(std::memory_order_relaxed) > 0) {
|
||||
blocksToKeep = (allocatedCount_.load() + BlockSize - 1) / BlockSize;
|
||||
blocksToKeep = std::max(blocksToKeep, size_t(1));
|
||||
}
|
||||
|
||||
if (blocksToKeep >= blocks_.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t blocksToRemove = blocks_.size() - blocksToKeep;
|
||||
for (size_t i = 0; i < blocksToRemove; ++i) {
|
||||
if (blocks_.empty()) break;
|
||||
|
||||
void* block = blocks_.back();
|
||||
blocks_.pop_back();
|
||||
alignedFree(block);
|
||||
totalCapacity_.fetch_sub(BlockSize, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
rebuildFreeList();
|
||||
}
|
||||
|
||||
void rebuildFreeList() {
|
||||
freeListHead_ = nullptr;
|
||||
size_t blockSize = AlignedSize > sizeof(FreeNode) ? AlignedSize : sizeof(FreeNode);
|
||||
|
||||
for (void* block : blocks_) {
|
||||
char* ptr = static_cast<char*>(block);
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
pushFreeList(reinterpret_cast<T*>(ptr + i * blockSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
FreeNode* freeListHead_;
|
||||
std::vector<void*> blocks_;
|
||||
std::atomic<size_t> allocatedCount_;
|
||||
std::atomic<size_t> totalCapacity_;
|
||||
bool isDestroyed_;
|
||||
uint64_t lastShrinkCheck_;
|
||||
bool prewarmed_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 智能指针支持的内存池分配器
|
||||
// ============================================================================
|
||||
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
|
||||
class PooledAllocator {
|
||||
public:
|
||||
using PoolType = ObjectPool<T, BlockSize>;
|
||||
|
||||
PooledAllocator() : pool_(std::make_shared<PoolType>()) {}
|
||||
explicit PooledAllocator(std::shared_ptr<PoolType> pool) : pool_(pool) {}
|
||||
|
||||
/**
|
||||
* @brief 创建一个使用内存池的对象(自动管理)
|
||||
*/
|
||||
template <typename... Args>
|
||||
Ptr<T> makeShared(Args&&... args) {
|
||||
std::weak_ptr<PoolType> weakPool = pool_;
|
||||
T* obj = pool_->allocate(std::forward<Args>(args)...);
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
return Ptr<T>(obj, Deleter{weakPool});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取底层内存池
|
||||
*/
|
||||
std::shared_ptr<PoolType> getPool() const {
|
||||
return pool_;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Deleter {
|
||||
std::weak_ptr<PoolType> pool;
|
||||
|
||||
void operator()(T* obj) const {
|
||||
if (auto sharedPool = pool.lock()) {
|
||||
if (!sharedPool->deallocate(obj)) {
|
||||
Logger::log(LogLevel::Warn, "PooledAllocator: Pool destroyed, memory leaked");
|
||||
}
|
||||
} else {
|
||||
Logger::log(LogLevel::Warn, "PooledAllocator: Pool expired during deallocation");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<PoolType> pool_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 全局内存池管理器 - 自动管理所有池的生命周期
|
||||
// ============================================================================
|
||||
|
||||
class ObjectPoolManager {
|
||||
public:
|
||||
static ObjectPoolManager& getInstance() {
|
||||
static ObjectPoolManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定类型的内存池(自动管理)
|
||||
*/
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
|
||||
std::shared_ptr<ObjectPool<T, BlockSize>> getPool() {
|
||||
static auto pool = std::make_shared<ObjectPool<T, BlockSize>>();
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建使用内存池的对象(自动管理)
|
||||
*/
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE, typename... Args>
|
||||
Ptr<T> makePooled(Args&&... args) {
|
||||
auto pool = getPool<T, BlockSize>();
|
||||
std::weak_ptr<ObjectPool<T, BlockSize>> weakPool = pool;
|
||||
T* obj = pool->allocate(std::forward<Args>(args)...);
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
return Ptr<T>(obj, [weakPool](T* p) {
|
||||
if (auto sharedPool = weakPool.lock()) {
|
||||
if (!sharedPool->deallocate(p)) {
|
||||
Logger::log(LogLevel::Warn, "ObjectPoolManager: Pool destroyed during deallocation");
|
||||
}
|
||||
} else {
|
||||
Logger::log(LogLevel::Warn, "ObjectPoolManager: Pool expired");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectPoolManager() = default;
|
||||
~ObjectPoolManager() = default;
|
||||
ObjectPoolManager(const ObjectPoolManager&) = delete;
|
||||
ObjectPoolManager& operator=(const ObjectPoolManager&) = delete;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 内存池宏定义(便于使用)
|
||||
// ============================================================================
|
||||
|
||||
#define E2D_DECLARE_POOL(T, BlockSize) \
|
||||
static extra2d::ObjectPool<T, BlockSize>& getPool() { \
|
||||
static extra2d::ObjectPool<T, BlockSize> pool; \
|
||||
return pool; \
|
||||
}
|
||||
|
||||
#define E2D_MAKE_POOLED(T, ...) \
|
||||
extra2d::ObjectPoolManager::getInstance().makePooled<T>(__VA_ARGS__)
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <random>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Random 类 - 随机数生成器
|
||||
// ============================================================================
|
||||
class Random {
|
||||
public:
|
||||
/// 获取单例实例
|
||||
static Random &getInstance();
|
||||
|
||||
/// 设置随机种子
|
||||
void setSeed(uint32 seed);
|
||||
|
||||
/// 使用当前时间作为种子
|
||||
void randomize();
|
||||
|
||||
/// 获取 [0, 1) 范围内的随机浮点数
|
||||
float getFloat();
|
||||
|
||||
/// 获取 [min, max] 范围内的随机浮点数
|
||||
float getFloat(float min, float max);
|
||||
|
||||
/// 获取 [0, max] 范围内的随机整数
|
||||
int getInt(int max);
|
||||
|
||||
/// 获取 [min, max] 范围内的随机整数
|
||||
int getInt(int min, int max);
|
||||
|
||||
/// 获取随机布尔值
|
||||
bool getBool();
|
||||
|
||||
/// 获取随机布尔值(带概率)
|
||||
bool getBool(float probability);
|
||||
|
||||
/// 获取指定范围内的随机角度(弧度)
|
||||
float getAngle();
|
||||
|
||||
/// 获取 [-1, 1] 范围内的随机数(用于方向)
|
||||
float getSigned();
|
||||
|
||||
private:
|
||||
Random();
|
||||
~Random() = default;
|
||||
|
||||
Random(const Random &) = delete;
|
||||
Random &operator=(const Random &) = delete;
|
||||
|
||||
std::mt19937 generator_;
|
||||
std::uniform_real_distribution<float> floatDist_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷函数
|
||||
// ============================================================================
|
||||
|
||||
/// 获取 [0, 1) 范围内的随机浮点数
|
||||
inline float randomFloat() { return Random::getInstance().getFloat(); }
|
||||
|
||||
/// 获取 [min, max] 范围内的随机浮点数
|
||||
inline float randomFloat(float min, float max) {
|
||||
return Random::getInstance().getFloat(min, max);
|
||||
}
|
||||
|
||||
/// 获取 [0, max] 范围内的随机整数
|
||||
inline int randomInt(int max) { return Random::getInstance().getInt(max); }
|
||||
|
||||
/// 获取 [min, max] 范围内的随机整数
|
||||
inline int randomInt(int min, int max) {
|
||||
return Random::getInstance().getInt(min, max);
|
||||
}
|
||||
|
||||
/// 获取随机布尔值
|
||||
inline bool randomBool() { return Random::getInstance().getBool(); }
|
||||
|
||||
/// 获取随机布尔值(带概率)
|
||||
inline bool randomBool(float probability) {
|
||||
return Random::getInstance().getBool(probability);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Timer 类 - 单次/重复计时器
|
||||
// ============================================================================
|
||||
class Timer {
|
||||
public:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Duration = Clock::duration;
|
||||
using Callback = Function<void()>;
|
||||
|
||||
Timer(float interval, bool repeat, Callback callback);
|
||||
|
||||
/// 更新计时器,返回 true 如果触发了回调
|
||||
bool update(float deltaTime);
|
||||
|
||||
/// 重置计时器
|
||||
void reset();
|
||||
|
||||
/// 暂停计时器
|
||||
void pause();
|
||||
|
||||
/// 恢复计时器
|
||||
void resume();
|
||||
|
||||
/// 取消计时器(标记为无效)
|
||||
void cancel();
|
||||
|
||||
/// 是否有效
|
||||
bool isValid() const { return valid_; }
|
||||
|
||||
/// 是否暂停
|
||||
bool isPaused() const { return paused_; }
|
||||
|
||||
/// 获取剩余时间(秒)
|
||||
float getRemaining() const;
|
||||
|
||||
/// 获取唯一ID
|
||||
uint32 getId() const { return id_; }
|
||||
|
||||
private:
|
||||
uint32 id_;
|
||||
float interval_;
|
||||
float elapsed_;
|
||||
bool repeat_;
|
||||
bool paused_;
|
||||
bool valid_;
|
||||
Callback callback_;
|
||||
|
||||
static uint32 nextId_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// TimerManager 类 - 管理所有计时器
|
||||
// ============================================================================
|
||||
class TimerManager {
|
||||
public:
|
||||
TimerManager() = default;
|
||||
~TimerManager() = default;
|
||||
|
||||
/// 创建单次计时器,返回计时器ID
|
||||
uint32 addTimer(float delay, Timer::Callback callback);
|
||||
|
||||
/// 创建重复计时器,返回计时器ID
|
||||
uint32 addRepeatingTimer(float interval, Timer::Callback callback);
|
||||
|
||||
/// 取消指定ID的计时器
|
||||
void cancelTimer(uint32 timerId);
|
||||
|
||||
/// 暂停指定ID的计时器
|
||||
void pauseTimer(uint32 timerId);
|
||||
|
||||
/// 恢复指定ID的计时器
|
||||
void resumeTimer(uint32 timerId);
|
||||
|
||||
/// 更新所有计时器(每帧调用)
|
||||
void update(float deltaTime);
|
||||
|
||||
/// 清除所有计时器
|
||||
void clear();
|
||||
|
||||
/// 获取计时器数量
|
||||
size_t getTimerCount() const { return timers_.size(); }
|
||||
|
||||
private:
|
||||
std::map<uint32, std::unique_ptr<Timer>> timers_;
|
||||
std::vector<uint32> timersToRemove_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,428 +0,0 @@
|
|||
// stb_perlin.h - v0.5 - perlin noise
|
||||
// public domain single-file C implementation by Sean Barrett
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file.
|
||||
//
|
||||
//
|
||||
// to create the implementation,
|
||||
// #define STB_PERLIN_IMPLEMENTATION
|
||||
// in *one* C/CPP file that includes this file.
|
||||
//
|
||||
//
|
||||
// Documentation:
|
||||
//
|
||||
// float stb_perlin_noise3( float x,
|
||||
// float y,
|
||||
// float z,
|
||||
// int x_wrap=0,
|
||||
// int y_wrap=0,
|
||||
// int z_wrap=0)
|
||||
//
|
||||
// This function computes a random value at the coordinate (x,y,z).
|
||||
// Adjacent random values are continuous but the noise fluctuates
|
||||
// its randomness with period 1, i.e. takes on wholly unrelated values
|
||||
// at integer points. Specifically, this implements Ken Perlin's
|
||||
// revised noise function from 2002.
|
||||
//
|
||||
// The "wrap" parameters can be used to create wraparound noise that
|
||||
// wraps at powers of two. The numbers MUST be powers of two. Specify
|
||||
// 0 to mean "don't care". (The noise always wraps every 256 due
|
||||
// details of the implementation, even if you ask for larger or no
|
||||
// wrapping.)
|
||||
//
|
||||
// float stb_perlin_noise3_seed( float x,
|
||||
// float y,
|
||||
// float z,
|
||||
// int x_wrap=0,
|
||||
// int y_wrap=0,
|
||||
// int z_wrap=0,
|
||||
// int seed)
|
||||
//
|
||||
// As above, but 'seed' selects from multiple different variations of the
|
||||
// noise function. The current implementation only uses the bottom 8 bits
|
||||
// of 'seed', but possibly in the future more bits will be used.
|
||||
//
|
||||
//
|
||||
// Fractal Noise:
|
||||
//
|
||||
// Three common fractal noise functions are included, which produce
|
||||
// a wide variety of nice effects depending on the parameters
|
||||
// provided. Note that each function will call stb_perlin_noise3
|
||||
// 'octaves' times, so this parameter will affect runtime.
|
||||
//
|
||||
// float stb_perlin_ridge_noise3(float x, float y, float z,
|
||||
// float lacunarity, float gain, float offset, int octaves)
|
||||
//
|
||||
// float stb_perlin_fbm_noise3(float x, float y, float z,
|
||||
// float lacunarity, float gain, int octaves)
|
||||
//
|
||||
// float stb_perlin_turbulence_noise3(float x, float y, float z,
|
||||
// float lacunarity, float gain, int octaves)
|
||||
//
|
||||
// Typical values to start playing with:
|
||||
// octaves = 6 -- number of "octaves" of noise3() to sum
|
||||
// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
|
||||
// gain = 0.5 -- relative weighting applied to each successive octave
|
||||
// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure
|
||||
//
|
||||
//
|
||||
// Contributors:
|
||||
// Jack Mott - additional noise functions
|
||||
// Jordan Peck - seeded noise
|
||||
//
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
|
||||
extern float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed);
|
||||
extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves);
|
||||
extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
|
||||
extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
|
||||
extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef STB_PERLIN_IMPLEMENTATION
|
||||
|
||||
#include <math.h> // fabs()
|
||||
|
||||
// not same permutation table as Perlin's reference to avoid copyright issues;
|
||||
// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
|
||||
static unsigned char stb__perlin_randtab[512] =
|
||||
{
|
||||
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||
|
||||
// and a second copy so we don't need an extra mask or static initializer
|
||||
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||
};
|
||||
|
||||
|
||||
// perlin's gradient has 12 cases so some get used 1/16th of the time
|
||||
// and some 2/16ths. We reduce bias by changing those fractions
|
||||
// to 5/64ths and 6/64ths
|
||||
|
||||
// this array is designed to match the previous implementation
|
||||
// of gradient hash: indices[stb__perlin_randtab[i]&63]
|
||||
static unsigned char stb__perlin_randtab_grad_idx[512] =
|
||||
{
|
||||
7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7,
|
||||
8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8,
|
||||
7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8,
|
||||
8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5,
|
||||
5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1,
|
||||
2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4,
|
||||
9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11,
|
||||
1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6,
|
||||
10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0,
|
||||
6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2,
|
||||
4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3,
|
||||
11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11,
|
||||
10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1,
|
||||
3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10,
|
||||
11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7,
|
||||
9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5,
|
||||
|
||||
// and a second copy so we don't need an extra mask or static initializer
|
||||
7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7,
|
||||
8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8,
|
||||
7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8,
|
||||
8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5,
|
||||
5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1,
|
||||
2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4,
|
||||
9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11,
|
||||
1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6,
|
||||
10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0,
|
||||
6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2,
|
||||
4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3,
|
||||
11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11,
|
||||
10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1,
|
||||
3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10,
|
||||
11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7,
|
||||
9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5,
|
||||
};
|
||||
|
||||
static float stb__perlin_lerp(float a, float b, float t)
|
||||
{
|
||||
return a + (b-a) * t;
|
||||
}
|
||||
|
||||
static int stb__perlin_fastfloor(float a)
|
||||
{
|
||||
int ai = (int) a;
|
||||
return (a < ai) ? ai-1 : ai;
|
||||
}
|
||||
|
||||
// different grad function from Perlin's, but easy to modify to match reference
|
||||
static float stb__perlin_grad(int grad_idx, float x, float y, float z)
|
||||
{
|
||||
static float basis[12][4] =
|
||||
{
|
||||
{ 1, 1, 0 },
|
||||
{ -1, 1, 0 },
|
||||
{ 1,-1, 0 },
|
||||
{ -1,-1, 0 },
|
||||
{ 1, 0, 1 },
|
||||
{ -1, 0, 1 },
|
||||
{ 1, 0,-1 },
|
||||
{ -1, 0,-1 },
|
||||
{ 0, 1, 1 },
|
||||
{ 0,-1, 1 },
|
||||
{ 0, 1,-1 },
|
||||
{ 0,-1,-1 },
|
||||
};
|
||||
|
||||
float *grad = basis[grad_idx];
|
||||
return grad[0]*x + grad[1]*y + grad[2]*z;
|
||||
}
|
||||
|
||||
float stb_perlin_noise3_internal(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed)
|
||||
{
|
||||
float u,v,w;
|
||||
float n000,n001,n010,n011,n100,n101,n110,n111;
|
||||
float n00,n01,n10,n11;
|
||||
float n0,n1;
|
||||
|
||||
unsigned int x_mask = (x_wrap-1) & 255;
|
||||
unsigned int y_mask = (y_wrap-1) & 255;
|
||||
unsigned int z_mask = (z_wrap-1) & 255;
|
||||
int px = stb__perlin_fastfloor(x);
|
||||
int py = stb__perlin_fastfloor(y);
|
||||
int pz = stb__perlin_fastfloor(z);
|
||||
int x0 = px & x_mask, x1 = (px+1) & x_mask;
|
||||
int y0 = py & y_mask, y1 = (py+1) & y_mask;
|
||||
int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
|
||||
int r0,r1, r00,r01,r10,r11;
|
||||
|
||||
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
|
||||
|
||||
x -= px; u = stb__perlin_ease(x);
|
||||
y -= py; v = stb__perlin_ease(y);
|
||||
z -= pz; w = stb__perlin_ease(z);
|
||||
|
||||
r0 = stb__perlin_randtab[x0+seed];
|
||||
r1 = stb__perlin_randtab[x1+seed];
|
||||
|
||||
r00 = stb__perlin_randtab[r0+y0];
|
||||
r01 = stb__perlin_randtab[r0+y1];
|
||||
r10 = stb__perlin_randtab[r1+y0];
|
||||
r11 = stb__perlin_randtab[r1+y1];
|
||||
|
||||
n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z );
|
||||
n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 );
|
||||
n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z );
|
||||
n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 );
|
||||
n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z );
|
||||
n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 );
|
||||
n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z );
|
||||
n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 );
|
||||
|
||||
n00 = stb__perlin_lerp(n000,n001,w);
|
||||
n01 = stb__perlin_lerp(n010,n011,w);
|
||||
n10 = stb__perlin_lerp(n100,n101,w);
|
||||
n11 = stb__perlin_lerp(n110,n111,w);
|
||||
|
||||
n0 = stb__perlin_lerp(n00,n01,v);
|
||||
n1 = stb__perlin_lerp(n10,n11,v);
|
||||
|
||||
return stb__perlin_lerp(n0,n1,u);
|
||||
}
|
||||
|
||||
float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
|
||||
{
|
||||
return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap,0);
|
||||
}
|
||||
|
||||
float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed)
|
||||
{
|
||||
return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap, (unsigned char) seed);
|
||||
}
|
||||
|
||||
float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float prev = 1.0f;
|
||||
float amplitude = 0.5f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i);
|
||||
r = offset - (float) fabs(r);
|
||||
r = r*r;
|
||||
sum += r*amplitude*prev;
|
||||
prev = r;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float amplitude = 1.0f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
sum += stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude;
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves)
|
||||
{
|
||||
int i;
|
||||
float frequency = 1.0f;
|
||||
float amplitude = 1.0f;
|
||||
float sum = 0.0f;
|
||||
|
||||
for (i = 0; i < octaves; i++) {
|
||||
float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude;
|
||||
sum += (float) fabs(r);
|
||||
frequency *= lacunarity;
|
||||
amplitude *= gain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed)
|
||||
{
|
||||
float u,v,w;
|
||||
float n000,n001,n010,n011,n100,n101,n110,n111;
|
||||
float n00,n01,n10,n11;
|
||||
float n0,n1;
|
||||
|
||||
int px = stb__perlin_fastfloor(x);
|
||||
int py = stb__perlin_fastfloor(y);
|
||||
int pz = stb__perlin_fastfloor(z);
|
||||
int x_wrap2 = (x_wrap ? x_wrap : 256);
|
||||
int y_wrap2 = (y_wrap ? y_wrap : 256);
|
||||
int z_wrap2 = (z_wrap ? z_wrap : 256);
|
||||
int x0 = px % x_wrap2, x1;
|
||||
int y0 = py % y_wrap2, y1;
|
||||
int z0 = pz % z_wrap2, z1;
|
||||
int r0,r1, r00,r01,r10,r11;
|
||||
|
||||
if (x0 < 0) x0 += x_wrap2;
|
||||
if (y0 < 0) y0 += y_wrap2;
|
||||
if (z0 < 0) z0 += z_wrap2;
|
||||
x1 = (x0+1) % x_wrap2;
|
||||
y1 = (y0+1) % y_wrap2;
|
||||
z1 = (z0+1) % z_wrap2;
|
||||
|
||||
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
|
||||
|
||||
x -= px; u = stb__perlin_ease(x);
|
||||
y -= py; v = stb__perlin_ease(y);
|
||||
z -= pz; w = stb__perlin_ease(z);
|
||||
|
||||
r0 = stb__perlin_randtab[x0];
|
||||
r0 = stb__perlin_randtab[r0+seed];
|
||||
r1 = stb__perlin_randtab[x1];
|
||||
r1 = stb__perlin_randtab[r1+seed];
|
||||
|
||||
r00 = stb__perlin_randtab[r0+y0];
|
||||
r01 = stb__perlin_randtab[r0+y1];
|
||||
r10 = stb__perlin_randtab[r1+y0];
|
||||
r11 = stb__perlin_randtab[r1+y1];
|
||||
|
||||
n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z );
|
||||
n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 );
|
||||
n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z );
|
||||
n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 );
|
||||
n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z );
|
||||
n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 );
|
||||
n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z );
|
||||
n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 );
|
||||
|
||||
n00 = stb__perlin_lerp(n000,n001,w);
|
||||
n01 = stb__perlin_lerp(n010,n011,w);
|
||||
n10 = stb__perlin_lerp(n100,n101,w);
|
||||
n11 = stb__perlin_lerp(n110,n111,w);
|
||||
|
||||
n0 = stb__perlin_lerp(n00,n01,v);
|
||||
n1 = stb__perlin_lerp(n10,n11,v);
|
||||
|
||||
return stb__perlin_lerp(n0,n1,u);
|
||||
}
|
||||
#endif // STB_PERLIN_IMPLEMENTATION
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#include "extra2d/action/action.h"
|
||||
#include "extra2d/scene/node.h"
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Action::Action() : tag_(-1), flags_(0) {}
|
||||
|
||||
bool Action::isDone() const {
|
||||
return state_ == ActionState::Completed;
|
||||
}
|
||||
|
||||
void Action::startWithTarget(Node* target) {
|
||||
target_ = target;
|
||||
originalTarget_ = target;
|
||||
state_ = ActionState::Running;
|
||||
onStart();
|
||||
}
|
||||
|
||||
void Action::stop() {
|
||||
target_ = nullptr;
|
||||
state_ = ActionState::Completed;
|
||||
}
|
||||
|
||||
void Action::step(float dt) {
|
||||
(void)dt;
|
||||
}
|
||||
|
||||
void Action::update(float time) {
|
||||
(void)time;
|
||||
}
|
||||
|
||||
void Action::pause() {
|
||||
if (state_ == ActionState::Running) {
|
||||
state_ = ActionState::Paused;
|
||||
}
|
||||
}
|
||||
|
||||
void Action::resume() {
|
||||
if (state_ == ActionState::Paused) {
|
||||
state_ = ActionState::Running;
|
||||
}
|
||||
}
|
||||
|
||||
void Action::restart() {
|
||||
state_ = ActionState::Running;
|
||||
onStart();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,683 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue