diff --git a/Extra2D/include/extra2d/action/action.h b/Extra2D/include/extra2d/action/action.h deleted file mode 100644 index 8fbbae5..0000000 --- a/Extra2D/include/extra2d/action/action.h +++ /dev/null @@ -1,172 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -class Node; - -/** - * @brief 动作状态枚举 - */ -enum class ActionState { - Idle, - Running, - Paused, - Completed -}; - -/** - * @brief 动作基类 - * - * 所有动作的基类,定义了动作的核心接口。 - * 动作用于修改 Node 的属性,实现动画效果。 - */ -class Action { -public: - using CompletionCallback = std::function; - - Action(); - virtual ~Action() = default; - - Action(const Action&) = delete; - Action& operator=(const Action&) = delete; - Action(Action&&) = default; - Action& operator=(Action&&) = default; - - /** - * @brief 检查动作是否完成 - * @return true 如果动作已完成 - */ - virtual bool isDone() const; - - /** - * @brief 使用目标节点启动动作 - * @param target 目标节点 - */ - virtual void startWithTarget(Node* target); - - /** - * @brief 停止动作 - */ - virtual void stop(); - - /** - * @brief 每帧调用的步进函数 - * @param dt 帧时间间隔 - */ - virtual void step(float dt); - - /** - * @brief 更新动作状态 - * @param time 归一化时间 [0, 1] - */ - virtual void update(float time); - - /** - * @brief 克隆动作 - * @return 动作的深拷贝 - */ - virtual Action* clone() const = 0; - - /** - * @brief 创建反向动作 - * @return 反向动作 - */ - virtual Action* reverse() const = 0; - - /** - * @brief 暂停动作 - */ - void pause(); - - /** - * @brief 恢复动作 - */ - void resume(); - - /** - * @brief 重启动作 - */ - void restart(); - - /** - * @brief 设置完成回调 - * @param callback 回调函数 - */ - void setCompletionCallback(const CompletionCallback& callback) { - completionCallback_ = callback; - } - - /** - * @brief 获取目标节点 - * @return 目标节点指针 - */ - Node* getTarget() const { return target_; } - - /** - * @brief 获取原始目标节点 - * @return 原始目标节点指针 - */ - Node* getOriginalTarget() const { return originalTarget_; } - - /** - * @brief 获取动作状态 - * @return 当前状态 - */ - ActionState getState() const { return state_; } - - /** - * @brief 获取标签 - * @return 标签值 - */ - int getTag() const { return tag_; } - - /** - * @brief 设置标签 - * @param tag 标签值 - */ - void setTag(int tag) { tag_ = tag; } - - /** - * @brief 获取标志位 - * @return 标志位 - */ - unsigned int getFlags() const { return flags_; } - - /** - * @brief 设置标志位 - * @param flags 标志位 - */ - void setFlags(unsigned int flags) { flags_ = flags; } - -protected: - /** - * @brief 动作开始时调用 - */ - virtual void onStart() {} - - /** - * @brief 动作完成时调用 - */ - virtual void onComplete() { - if (completionCallback_) { - completionCallback_(); - } - } - - /** - * @brief 设置动作完成状态 - */ - void setDone() { state_ = ActionState::Completed; } - - Node* target_ = nullptr; - Node* originalTarget_ = nullptr; - ActionState state_ = ActionState::Idle; - int tag_ = -1; - unsigned int flags_ = 0; - CompletionCallback completionCallback_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_ease.h b/Extra2D/include/extra2d/action/action_ease.h deleted file mode 100644 index f6bb6dd..0000000 --- a/Extra2D/include/extra2d/action/action_ease.h +++ /dev/null @@ -1,344 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -/** - * @brief 缓动动作基类 - * - * 使用装饰器模式包装其他动作,实现缓动效果。 - */ -class ActionEase : public ActionInterval { -public: - virtual ~ActionEase(); - - /** - * @brief 获取内部动作 - * @return 内部动作指针 - */ - ActionInterval* getInnerAction() const { return innerAction_; } - - void startWithTarget(Node* target) override; - void stop() override; - void update(float time) override; - ActionInterval* clone() const override = 0; - ActionInterval* reverse() const override = 0; - -protected: - ActionEase() = default; - bool initWithAction(ActionInterval* action); - void onUpdate(float progress) override {} - - ActionInterval* innerAction_ = nullptr; -}; - -// ============================================================================ -// 指数缓动 -// ============================================================================ - -class EaseExponentialIn : public ActionEase { -public: - static EaseExponentialIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseExponentialOut : public ActionEase { -public: - static EaseExponentialOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseExponentialInOut : public ActionEase { -public: - static EaseExponentialInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 正弦缓动 -// ============================================================================ - -class EaseSineIn : public ActionEase { -public: - static EaseSineIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseSineOut : public ActionEase { -public: - static EaseSineOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseSineInOut : public ActionEase { -public: - static EaseSineInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 弹性缓动 -// ============================================================================ - -class EaseElasticIn : public ActionEase { -public: - static EaseElasticIn* create(ActionInterval* action, float period = 0.3f); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; - -protected: - float period_ = 0.3f; -}; - -class EaseElasticOut : public ActionEase { -public: - static EaseElasticOut* create(ActionInterval* action, float period = 0.3f); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; - -protected: - float period_ = 0.3f; -}; - -class EaseElasticInOut : public ActionEase { -public: - static EaseElasticInOut* create(ActionInterval* action, float period = 0.3f); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; - -protected: - float period_ = 0.3f; -}; - -// ============================================================================ -// 弹跳缓动 -// ============================================================================ - -class EaseBounceIn : public ActionEase { -public: - static EaseBounceIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseBounceOut : public ActionEase { -public: - static EaseBounceOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseBounceInOut : public ActionEase { -public: - static EaseBounceInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 回震缓动 -// ============================================================================ - -class EaseBackIn : public ActionEase { -public: - static EaseBackIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseBackOut : public ActionEase { -public: - static EaseBackOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseBackInOut : public ActionEase { -public: - static EaseBackInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 二次缓动 -// ============================================================================ - -class EaseQuadIn : public ActionEase { -public: - static EaseQuadIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseQuadOut : public ActionEase { -public: - static EaseQuadOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseQuadInOut : public ActionEase { -public: - static EaseQuadInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 三次缓动 -// ============================================================================ - -class EaseCubicIn : public ActionEase { -public: - static EaseCubicIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseCubicOut : public ActionEase { -public: - static EaseCubicOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseCubicInOut : public ActionEase { -public: - static EaseCubicInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 四次缓动 -// ============================================================================ - -class EaseQuartIn : public ActionEase { -public: - static EaseQuartIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseQuartOut : public ActionEase { -public: - static EaseQuartOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseQuartInOut : public ActionEase { -public: - static EaseQuartInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 五次缓动 -// ============================================================================ - -class EaseQuintIn : public ActionEase { -public: - static EaseQuintIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseQuintOut : public ActionEase { -public: - static EaseQuintOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseQuintInOut : public ActionEase { -public: - static EaseQuintInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 圆形缓动 -// ============================================================================ - -class EaseCircleIn : public ActionEase { -public: - static EaseCircleIn* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseCircleOut : public ActionEase { -public: - static EaseCircleOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -class EaseCircleInOut : public ActionEase { -public: - static EaseCircleInOut* create(ActionInterval* action); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; -}; - -// ============================================================================ -// 自定义缓动 -// ============================================================================ - -/** - * @brief 自定义缓动动作 - */ -class EaseCustom : public ActionEase { -public: - static EaseCustom* create(ActionInterval* action, EaseFunction easeFunc); - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - void update(float time) override; - -protected: - EaseFunction easeFunc_ = nullptr; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_instant.h b/Extra2D/include/extra2d/action/action_instant.h deleted file mode 100644 index af2e45d..0000000 --- a/Extra2D/include/extra2d/action/action_instant.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include - -namespace extra2d { - -/** - * @brief 瞬时动作基类 - * - * 瞬间完成的动作,中间没有任何动画效果。 - * 继承自 FiniteTimeAction,持续时间为 0。 - */ -class ActionInstant : public FiniteTimeAction { -public: - ActionInstant(); - virtual ~ActionInstant() = default; - - /** - * @brief 检查动作是否完成 - * @return 总是返回 true - */ - bool isDone() const override; - - /** - * @brief 使用目标节点启动动作 - * @param target 目标节点 - */ - void startWithTarget(Node* target) override; - - /** - * @brief 每帧调用的步进函数 - * @param dt 帧时间间隔 - */ - void step(float dt) override; - - /** - * @brief 克隆动作 - * @return 动作的深拷贝 - */ - ActionInstant* clone() const override = 0; - - /** - * @brief 创建反向动作 - * @return 反向动作 - */ - ActionInstant* reverse() const override = 0; - -protected: - /** - * @brief 执行瞬时动作 - */ - virtual void execute() = 0; - - bool done_ = false; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_instant_actions.h b/Extra2D/include/extra2d/action/action_instant_actions.h deleted file mode 100644 index c0a7563..0000000 --- a/Extra2D/include/extra2d/action/action_instant_actions.h +++ /dev/null @@ -1,169 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 回调动作 -// ============================================================================ - -/** - * @brief 无参数回调动作 - */ -class CallFunc : public ActionInstant { -public: - using Callback = std::function; - - static CallFunc* create(const Callback& callback); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; - - Callback callback_; -}; - -/** - * @brief 带节点参数的回调动作 - */ -class CallFuncN : public ActionInstant { -public: - using Callback = std::function; - - static CallFuncN* create(const Callback& callback); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; - - Callback callback_; -}; - -// ============================================================================ -// 位置动作 -// ============================================================================ - -/** - * @brief 瞬间放置到指定位置 - */ -class Place : public ActionInstant { -public: - static Place* create(const Vec2& position); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; - - Vec2 position_; -}; - -// ============================================================================ -// 翻转动作 -// ============================================================================ - -/** - * @brief X轴翻转动作 - */ -class FlipX : public ActionInstant { -public: - static FlipX* create(bool flipX); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; - - bool flipX_ = false; -}; - -/** - * @brief Y轴翻转动作 - */ -class FlipY : public ActionInstant { -public: - static FlipY* create(bool flipY); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; - - bool flipY_ = false; -}; - -// ============================================================================ -// 可见性动作 -// ============================================================================ - -/** - * @brief 显示动作 - */ -class Show : public ActionInstant { -public: - static Show* create(); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; -}; - -/** - * @brief 隐藏动作 - */ -class Hide : public ActionInstant { -public: - static Hide* create(); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; -}; - -/** - * @brief 切换可见性动作 - */ -class ToggleVisibility : public ActionInstant { -public: - static ToggleVisibility* create(); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; -}; - -// ============================================================================ -// 节点管理动作 -// ============================================================================ - -/** - * @brief 移除自身动作 - */ -class RemoveSelf : public ActionInstant { -public: - static RemoveSelf* create(); - - ActionInstant* clone() const override; - ActionInstant* reverse() const override; - -protected: - void execute() override; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_interval.h b/Extra2D/include/extra2d/action/action_interval.h deleted file mode 100644 index 194e751..0000000 --- a/Extra2D/include/extra2d/action/action_interval.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -/** - * @brief 时间间隔动作基类 - * - * 在指定时间内完成的动作,中间会有动画效果。 - * 继承自 FiniteTimeAction。 - */ -class ActionInterval : public FiniteTimeAction { -public: - ActionInterval() = default; - explicit ActionInterval(float duration); - virtual ~ActionInterval() = default; - - /** - * @brief 获取已流逝时间 - * @return 已流逝时间(秒) - */ - float getElapsed() const { return elapsed_; } - - /** - * @brief 检查动作是否完成 - * @return true 如果动作已完成 - */ - bool isDone() const override; - - /** - * @brief 使用目标节点启动动作 - * @param target 目标节点 - */ - void startWithTarget(Node* target) override; - - /** - * @brief 停止动作 - */ - void stop() override; - - /** - * @brief 每帧调用的步进函数 - * @param dt 帧时间间隔 - */ - void step(float dt) override; - - /** - * @brief 设置振幅比率(用于缓动) - * @param amp 振幅比率 - */ - void setAmplitudeRate(float amp) { amplitudeRate_ = amp; } - - /** - * @brief 获取振幅比率 - * @return 振幅比率 - */ - float getAmplitudeRate() const { return amplitudeRate_; } - - /** - * @brief 设置内置缓动函数 - * @param easeFunc 缓动函数 - */ - void setEaseFunction(EaseFunction easeFunc) { easeFunc_ = easeFunc; } - - /** - * @brief 获取内置缓动函数 - * @return 缓动函数 - */ - EaseFunction getEaseFunction() const { return easeFunc_; } - - /** - * @brief 克隆动作 - * @return 动作的深拷贝 - */ - ActionInterval* clone() const override = 0; - - /** - * @brief 创建反向动作 - * @return 反向动作 - */ - ActionInterval* reverse() const override = 0; - -protected: - /** - * @brief 动作开始时调用 - */ - virtual void onStart() {} - - /** - * @brief 动作更新时调用 - * @param progress 归一化进度 [0, 1] - */ - virtual void onUpdate(float progress) = 0; - - float elapsed_ = 0.0f; - bool firstTick_ = true; - float amplitudeRate_ = 1.0f; - EaseFunction easeFunc_ = nullptr; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_interval_actions.h b/Extra2D/include/extra2d/action/action_interval_actions.h deleted file mode 100644 index df369c5..0000000 --- a/Extra2D/include/extra2d/action/action_interval_actions.h +++ /dev/null @@ -1,469 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 移动动作 -// ============================================================================ - -/** - * @brief 相对移动动作 - */ -class MoveBy : public ActionInterval { -public: - static MoveBy* create(float duration, const Vec2& delta); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - Vec2 delta_; - Vec2 startPosition_; -}; - -/** - * @brief 绝对移动动作 - */ -class MoveTo : public ActionInterval { -public: - static MoveTo* create(float duration, const Vec2& position); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - Vec2 endPosition_; - Vec2 startPosition_; - Vec2 delta_; -}; - -// ============================================================================ -// 跳跃动作 -// ============================================================================ - -/** - * @brief 相对跳跃动作 - */ -class JumpBy : public ActionInterval { -public: - static JumpBy* create(float duration, const Vec2& position, float height, int jumps); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - Vec2 startPosition_; - Vec2 delta_; - float height_ = 0.0f; - int jumps_ = 1; -}; - -/** - * @brief 绝对跳跃动作 - */ -class JumpTo : public JumpBy { -public: - static JumpTo* create(float duration, const Vec2& position, float height, int jumps); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - - Vec2 endPosition_; -}; - -// ============================================================================ -// 贝塞尔曲线动作 -// ============================================================================ - -/** - * @brief 贝塞尔曲线配置 - */ -struct BezierConfig { - Vec2 controlPoint1; - Vec2 controlPoint2; - Vec2 endPosition; -}; - -/** - * @brief 相对贝塞尔曲线动作 - */ -class BezierBy : public ActionInterval { -public: - static BezierBy* create(float duration, const BezierConfig& config); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - static float bezierat(float a, float b, float c, float d, float t); - - BezierConfig config_; - Vec2 startPosition_; -}; - -/** - * @brief 绝对贝塞尔曲线动作 - */ -class BezierTo : public BezierBy { -public: - static BezierTo* create(float duration, const BezierConfig& config); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - - BezierConfig originalConfig_; -}; - -// ============================================================================ -// 缩放动作 -// ============================================================================ - -/** - * @brief 相对缩放动作 - */ -class ScaleBy : public ActionInterval { -public: - static ScaleBy* create(float duration, float scale); - static ScaleBy* create(float duration, float scaleX, float scaleY); - static ScaleBy* create(float duration, const Vec2& scale); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - Vec2 deltaScale_; - Vec2 startScale_; -}; - -/** - * @brief 绝对缩放动作 - */ -class ScaleTo : public ActionInterval { -public: - static ScaleTo* create(float duration, float scale); - static ScaleTo* create(float duration, float scaleX, float scaleY); - static ScaleTo* create(float duration, const Vec2& scale); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - Vec2 endScale_; - Vec2 startScale_; - Vec2 delta_; -}; - -// ============================================================================ -// 旋转动作 -// ============================================================================ - -/** - * @brief 相对旋转动作 - */ -class RotateBy : public ActionInterval { -public: - static RotateBy* create(float duration, float deltaAngle); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - float deltaAngle_ = 0.0f; - float startAngle_ = 0.0f; -}; - -/** - * @brief 绝对旋转动作 - */ -class RotateTo : public ActionInterval { -public: - static RotateTo* create(float duration, float angle); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - float endAngle_ = 0.0f; - float startAngle_ = 0.0f; - float deltaAngle_ = 0.0f; -}; - -// ============================================================================ -// 淡入淡出动作 -// ============================================================================ - -/** - * @brief 淡入动作 - */ -class FadeIn : public ActionInterval { -public: - static FadeIn* create(float duration); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - float startOpacity_ = 0.0f; -}; - -/** - * @brief 淡出动作 - */ -class FadeOut : public ActionInterval { -public: - static FadeOut* create(float duration); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - float startOpacity_ = 0.0f; -}; - -/** - * @brief 淡入到指定透明度动作 - */ -class FadeTo : public ActionInterval { -public: - static FadeTo* create(float duration, float opacity); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - float endOpacity_ = 0.0f; - float startOpacity_ = 0.0f; - float deltaOpacity_ = 0.0f; -}; - -// ============================================================================ -// 闪烁动作 -// ============================================================================ - -/** - * @brief 闪烁动作 - */ -class Blink : public ActionInterval { -public: - static Blink* create(float duration, int times); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - int times_ = 1; - int currentTimes_ = 0; - bool originalVisible_ = true; -}; - -// ============================================================================ -// 色调动作 -// ============================================================================ - -/** - * @brief 色调变化动作 - */ -class TintTo : public ActionInterval { -public: - static TintTo* create(float duration, uint8_t red, uint8_t green, uint8_t blue); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - Color3B startColor_; - Color3B endColor_; - Color3B deltaColor_; -}; - -/** - * @brief 相对色调变化动作 - */ -class TintBy : public ActionInterval { -public: - static TintBy* create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - Color3B startColor_; - int16_t deltaR_ = 0; - int16_t deltaG_ = 0; - int16_t deltaB_ = 0; -}; - -// ============================================================================ -// 组合动作 -// ============================================================================ - -/** - * @brief 序列动作 - */ -class Sequence : public ActionInterval { -public: - static Sequence* create(ActionInterval* action1, ...); - static Sequence* create(const std::vector& actions); - - ~Sequence(); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - std::vector actions_; - size_t currentIndex_ = 0; - float split_ = 0.0f; - float last_ = -1.0f; -}; - -/** - * @brief 并行动作 - */ -class Spawn : public ActionInterval { -public: - static Spawn* create(ActionInterval* action1, ...); - static Spawn* create(const std::vector& actions); - - ~Spawn(); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - std::vector actions_; -}; - -/** - * @brief 重复动作 - */ -class Repeat : public ActionInterval { -public: - static Repeat* create(ActionInterval* action, int times); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - - bool isDone() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - ActionInterval* innerAction_ = nullptr; - int times_ = 1; - int currentTimes_ = 0; -}; - -/** - * @brief 永久重复动作 - */ -class RepeatForever : public ActionInterval { -public: - static RepeatForever* create(ActionInterval* action); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - - bool isDone() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - ActionInterval* innerAction_ = nullptr; -}; - -/** - * @brief 延时动作 - */ -class DelayTime : public ActionInterval { -public: - static DelayTime* create(float duration); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onUpdate(float progress) override {} -}; - -/** - * @brief 反向时间动作 - */ -class ReverseTime : public ActionInterval { -public: - static ReverseTime* create(ActionInterval* action); - - ~ReverseTime(); - - ActionInterval* clone() const override; - ActionInterval* reverse() const override; - -protected: - void onStart() override; - void onUpdate(float progress) override; - - ActionInterval* innerAction_ = nullptr; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_manager.h b/Extra2D/include/extra2d/action/action_manager.h deleted file mode 100644 index 9acd14d..0000000 --- a/Extra2D/include/extra2d/action/action_manager.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -/** - * @brief 动作管理器 - * - * 单例模式,集中管理所有动作的调度和生命周期。 - * 负责动作的添加、移除、暂停、恢复和更新。 - */ -class ActionManager { -public: - /** - * @brief 获取单例实例 - * @return ActionManager 实例指针 - */ - static ActionManager* getInstance(); - - /** - * @brief 销毁单例实例 - */ - static void destroyInstance(); - - /** - * @brief 添加动作到管理器 - * @param action 动作指针 - * @param target 目标节点 - * @param paused 是否暂停 - */ - void addAction(Action* action, Node* target, bool paused = false); - - /** - * @brief 移除指定动作 - * @param action 动作指针 - */ - void removeAction(Action* action); - - /** - * @brief 根据标签移除动作 - * @param tag 标签值 - * @param target 目标节点 - */ - void removeActionByTag(int tag, Node* target); - - /** - * @brief 根据标志位移除动作 - * @param flags 标志位 - * @param target 目标节点 - */ - void removeActionsByFlags(unsigned int flags, Node* target); - - /** - * @brief 移除目标节点的所有动作 - * @param target 目标节点 - */ - void removeAllActionsFromTarget(Node* target); - - /** - * @brief 移除所有动作 - */ - void removeAllActions(); - - /** - * @brief 根据标签获取动作 - * @param tag 标签值 - * @param target 目标节点 - * @return 动作指针,未找到返回 nullptr - */ - Action* getActionByTag(int tag, Node* target); - - /** - * @brief 获取目标节点的动作数量 - * @param target 目标节点 - * @return 动作数量 - */ - size_t getActionCount(Node* target) const; - - /** - * @brief 暂停目标节点的所有动作 - * @param target 目标节点 - */ - void pauseTarget(Node* target); - - /** - * @brief 恢复目标节点的所有动作 - * @param target 目标节点 - */ - void resumeTarget(Node* target); - - /** - * @brief 检查目标节点是否暂停 - * @param target 目标节点 - * @return true 如果暂停 - */ - bool isPaused(Node* target) const; - - /** - * @brief 更新所有动作(每帧调用) - * @param dt 帧时间间隔 - */ - void update(float dt); - -private: - ActionManager(); - ~ActionManager(); - - struct ActionElement { - std::vector actions; - Node* target = nullptr; - bool paused = false; - int actionIndex = 0; - Action* currentAction = nullptr; - bool currentActionSalvaged = false; - }; - - using ActionMap = std::unordered_map; - - void removeActionAt(size_t index, ActionElement& element); - void deleteAction(Action* action); - - ActionMap targets_; - static ActionManager* instance_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/action_special.h b/Extra2D/include/extra2d/action/action_special.h deleted file mode 100644 index b40527e..0000000 --- a/Extra2D/include/extra2d/action/action_special.h +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -/** - * @brief 速度控制动作 - * - * 包装其他动作,实现动态速度控制。 - * 可以在运行时调整动作的播放速度。 - */ -class Speed : public Action { -public: - /** - * @brief 创建速度控制动作 - * @param action 内部动作 - * @param speed 速度倍率(1.0 为正常速度) - * @return 动作指针 - */ - static Speed* create(ActionInterval* action, float speed); - - ~Speed(); - - /** - * @brief 获取速度倍率 - * @return 速度倍率 - */ - float getSpeed() const { return speed_; } - - /** - * @brief 设置速度倍率 - * @param speed 速度倍率 - */ - void setSpeed(float speed) { speed_ = speed; } - - /** - * @brief 获取内部动作 - * @return 内部动作指针 - */ - ActionInterval* getInnerAction() const { return innerAction_; } - - void startWithTarget(Node* target) override; - void stop() override; - void step(float dt) override; - bool isDone() const override; - Action* clone() const override; - Action* reverse() const override; - -protected: - Speed() = default; - - ActionInterval* innerAction_ = nullptr; - float speed_ = 1.0f; -}; - -/** - * @brief 跟随动作 - * - * 使节点跟随另一个节点移动。 - * 常用于相机跟随玩家。 - */ -class Follow : public Action { -public: - /** - * @brief 创建跟随动作 - * @param followedNode 被跟随的节点 - * @return 动作指针 - */ - static Follow* create(Node* followedNode); - - /** - * @brief 创建带边界的跟随动作 - * @param followedNode 被跟随的节点 - * @param boundary 边界矩形 - * @return 动作指针 - */ - static Follow* create(Node* followedNode, const Rect& boundary); - - ~Follow(); - - /** - * @brief 获取被跟随的节点 - * @return 被跟随的节点指针 - */ - Node* getFollowedNode() const { return followedNode_; } - - /** - * @brief 检查是否设置了边界 - * @return true 如果设置了边界 - */ - bool isBoundarySet() const { return boundarySet_; } - - void startWithTarget(Node* target) override; - void stop() override; - void step(float dt) override; - bool isDone() const override; - Action* clone() const override; - Action* reverse() const override; - -protected: - Follow() = default; - - Node* followedNode_ = nullptr; - Rect boundary_; - bool boundarySet_ = false; - Vec2 halfScreenSize_; - Vec2 fullScreenSize_; - Vec2 leftBoundary_; - Vec2 rightBoundary_; - Vec2 topBoundary_; - Vec2 bottomBoundary_; -}; - -/** - * @brief 目标动作 - * - * 允许在一个节点上运行动作,但目标为另一个节点。 - */ -class TargetedAction : public Action { -public: - /** - * @brief 创建目标动作 - * @param target 目标节点 - * @param action 要运行的动作 - * @return 动作指针 - */ - static TargetedAction* create(Node* target, FiniteTimeAction* action); - - ~TargetedAction(); - - /** - * @brief 获取目标节点 - * @return 目标节点指针 - */ - Node* getTargetNode() const { return targetNode_; } - - /** - * @brief 设置目标节点 - * @param target 目标节点 - */ - void setTargetNode(Node* target) { targetNode_ = target; } - - /** - * @brief 获取内部动作 - * @return 内部动作指针 - */ - FiniteTimeAction* getAction() const { return innerAction_; } - - void startWithTarget(Node* target) override; - void stop() override; - void step(float dt) override; - bool isDone() const override; - Action* clone() const override; - Action* reverse() const override; - -protected: - TargetedAction() = default; - - Node* targetNode_ = nullptr; - FiniteTimeAction* innerAction_ = nullptr; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/ease.h b/Extra2D/include/extra2d/action/ease.h deleted file mode 100644 index 842fe2f..0000000 --- a/Extra2D/include/extra2d/action/ease.h +++ /dev/null @@ -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 diff --git a/Extra2D/include/extra2d/action/finite_time_action.h b/Extra2D/include/extra2d/action/finite_time_action.h deleted file mode 100644 index 7601e5f..0000000 --- a/Extra2D/include/extra2d/action/finite_time_action.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -namespace extra2d { - -/** - * @brief 有限时间动作基类 - * - * 所有具有持续时间的动作的基类。 - * 继承自 Action,添加了持续时间属性。 - */ -class FiniteTimeAction : public Action { -public: - FiniteTimeAction() = default; - explicit FiniteTimeAction(float duration); - virtual ~FiniteTimeAction() = default; - - /** - * @brief 获取动作持续时间 - * @return 持续时间(秒) - */ - float getDuration() const { return duration_; } - - /** - * @brief 设置动作持续时间 - * @param duration 持续时间(秒) - */ - void setDuration(float duration) { duration_ = duration; } - - /** - * @brief 克隆动作 - * @return 动作的深拷贝 - */ - FiniteTimeAction* clone() const override = 0; - - /** - * @brief 创建反向动作 - * @return 反向动作 - */ - FiniteTimeAction* reverse() const override = 0; - -protected: - float duration_ = 0.0f; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/als_parser.h b/Extra2D/include/extra2d/animation/als_parser.h deleted file mode 100644 index 5215091..0000000 --- a/Extra2D/include/extra2d/animation/als_parser.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -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 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 diff --git a/Extra2D/include/extra2d/animation/ani_binary_parser.h b/Extra2D/include/extra2d/animation/ani_binary_parser.h deleted file mode 100644 index 08627a8..0000000 --- a/Extra2D/include/extra2d/animation/ani_binary_parser.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -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 diff --git a/Extra2D/include/extra2d/animation/ani_parser.h b/Extra2D/include/extra2d/animation/ani_parser.h deleted file mode 100644 index d704faa..0000000 --- a/Extra2D/include/extra2d/animation/ani_parser.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// ANI 文件解析结果 -// ============================================================================ -struct AniParseResult { - bool success = false; - std::string errorMessage; - Ptr 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 diff --git a/Extra2D/include/extra2d/animation/animated_sprite.h b/Extra2D/include/extra2d/animation/animated_sprite.h deleted file mode 100644 index 66fc72f..0000000 --- a/Extra2D/include/extra2d/animation/animated_sprite.h +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// AnimatedSprite - 动画精灵节点 -// 将 AnimationController 与 Sprite 渲染桥接,接入场景图 -// ============================================================================ -class AnimatedSprite : public Sprite { -public: - AnimatedSprite(); - ~AnimatedSprite() override = default; - - // ------ 静态工厂 ------ - static Ptr create(); - static Ptr create(Ptr clip); - static Ptr create(const std::string &aniFilePath); - - // ------ 动画绑定 ------ - void setAnimationClip(Ptr clip); - void loadAnimation(const std::string &aniFilePath); - Ptr getAnimationClip() const; - - // ------ 动画字典 ------ - void addAnimation(const std::string &name, Ptr clip); - void play(const std::string &name, bool loop = true); - bool hasAnimation(const std::string &name) const; - Ptr 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 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> &getCurrentDamageBoxes() const; - const std::vector> &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> animations_; - std::string currentAnimationName_; - static const std::string emptyString_; - - // 帧范围限制(用于精灵图动画) - int frameRangeStart_ = 0; // 起始帧索引 - int frameRangeEnd_ = -1; // 结束帧索引,-1表示不限制 - - // 空碰撞盒列表(用于无帧时返回引用) - static const std::vector> emptyBoxes_; - - void applyFrame(const AnimationFrame &frame); - void onFrameChanged(size_t oldIdx, size_t newIdx, - const AnimationFrame &frame); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_cache.h b/Extra2D/include/extra2d/animation/animation_cache.h deleted file mode 100644 index e894fd0..0000000 --- a/Extra2D/include/extra2d/animation/animation_cache.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -// 路径替换回调(对应原始 AdditionalOptions) -using PathResolveCallback = std::function; - -// ============================================================================ -// AnimationCache - 动画片段全局缓存(借鉴 Cocos AnimationCache) -// 同一 ANI 文件只解析一次,后续直接复用数据 -// ============================================================================ -class AnimationCache { -public: - static AnimationCache &getInstance() { - static AnimationCache instance; - return instance; - } - - // ------ 加载与获取 ------ - - /// 从文件加载(自动缓存),已缓存则直接返回 - /// 注意:实际的 ANI 解析逻辑在 AniParser 中实现 - /// 此方法在 animation_cache.cpp 中实现,依赖 AniParser - Ptr loadClip(const std::string &aniFilePath); - - /// 从缓存获取(不触发加载) - Ptr getClip(const std::string &name) const { - std::lock_guard lock(mutex_); - auto it = clips_.find(name); - if (it != clips_.end()) - return it->second; - return nullptr; - } - - /// 手动添加到缓存 - void addClip(Ptr clip, const std::string &name) { - std::lock_guard lock(mutex_); - clips_[name] = std::move(clip); - } - - // ------ 缓存管理 ------ - - bool has(const std::string &name) const { - std::lock_guard lock(mutex_); - return clips_.find(name) != clips_.end(); - } - - void removeClip(const std::string &name) { - std::lock_guard lock(mutex_); - clips_.erase(name); - } - - /// 移除未被外部引用的动画片段 - void removeUnusedClips() { - std::lock_guard 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 lock(mutex_); - clips_.clear(); - } - - size_t count() const { - std::lock_guard 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> clips_; - PathResolveCallback pathResolver_; -}; - -// 便捷宏 -#define E2D_ANIMATION_CACHE() ::extra2d::AnimationCache::getInstance() - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_clip.h b/Extra2D/include/extra2d/animation/animation_clip.h deleted file mode 100644 index 16bd5c2..0000000 --- a/Extra2D/include/extra2d/animation/animation_clip.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -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(index), frame); - } - - void removeFrame(size_t index) { - assert(index < frames_.size()); - frames_.erase(frames_.begin() + static_cast(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(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 create(const std::string &name = "") { - return makePtr(name); - } - - /// 从精灵图网格创建(所有帧按顺序) - static Ptr createFromGrid(Ptr 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(); - 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(margin + col * (frameWidth + spacing)), - static_cast(margin + flippedRow * (frameHeight + spacing)), - static_cast(frameWidth), static_cast(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 - createFromGridIndices(Ptr texture, int frameWidth, int frameHeight, - const std::vector &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(); - for (int idx : frameIndices) { - int col = idx % cols; - int row = idx / cols; - - // 翻转行顺序:精灵图第0行在顶部,但OpenGL纹理V坐标从底部开始 - int flippedRow = (rows - 1) - row; - - Rect rect( - static_cast(margin + col * (frameWidth + spacing)), - static_cast(margin + flippedRow * (frameHeight + spacing)), - static_cast(frameWidth), static_cast(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 frames_; - FramePropertySet globalProperties_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_controller.h b/Extra2D/include/extra2d/animation/animation_controller.h deleted file mode 100644 index 0bf18f4..0000000 --- a/Extra2D/include/extra2d/animation/animation_controller.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 动画播放状态 -// ============================================================================ -enum class AnimPlayState : uint8 { Stopped, Playing, Paused }; - -// ============================================================================ -// AnimationController - 动画播放控制器 -// 借鉴 Cocos Creator 的 AnimationState:纯播放逻辑,不持有渲染资源 -// ============================================================================ -class AnimationController { -public: - // 回调类型定义 - using FrameChangeCallback = std::function; - using KeyframeCallback = std::function; - using SoundTriggerCallback = std::function; - using CompletionCallback = std::function; - - AnimationController() = default; - - // ------ 绑定动画数据 ------ - void setClip(Ptr clip); - Ptr 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 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 diff --git a/Extra2D/include/extra2d/animation/animation_event.h b/Extra2D/include/extra2d/animation/animation_event.h deleted file mode 100644 index f94af58..0000000 --- a/Extra2D/include/extra2d/animation/animation_event.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include - -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; -using KeyframeHitCallback = std::function; -using AnimationCompleteCallback = std::function; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_frame.h b/Extra2D/include/extra2d/animation/animation_frame.h deleted file mode 100644 index 71536a2..0000000 --- a/Extra2D/include/extra2d/animation/animation_frame.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// AnimationFrame - 单帧数据 -// 引用 SpriteFrame 而非直接持有纹理(借鉴 Cocos 模式) -// 通过 FramePropertySet 支持不固定数据(ANI Flag 系统增强版) -// ============================================================================ -struct AnimationFrame { - // ------ 核心数据(固定部分)------ - Ptr spriteFrame; // 精灵帧引用(Cocos 模式) - std::string texturePath; // 原始图片路径(用于解析时定位资源) - int textureIndex = 0; // 精灵图集索引 - Vec2 offset; // 位置偏移 - float delay = 100.0f; // 帧延迟(毫秒) - - // ------ 碰撞盒数据(DNF ANI 格式)------ - std::vector> damageBoxes; // 伤害碰撞盒 - std::vector> attackBoxes; // 攻击碰撞盒 - - // ------ 不固定数据(属性集合)------ - FramePropertySet properties; // 类型安全的 Flag 系统 - - // ------ 便捷方法 ------ - bool hasTexture() const { - return spriteFrame != nullptr && spriteFrame->isValid(); - } - - bool hasInterpolation() const { - return properties.getOr(FramePropertyKey::Interpolation, false); - } - - bool hasKeyframeCallback() const { - return properties.has(FramePropertyKey::SetFlag); - } - - int getKeyframeIndex() const { - return properties.getOr(FramePropertyKey::SetFlag, -1); - } - - Vec2 getEffectiveScale() const { - return properties.getOr(FramePropertyKey::ImageRate, Vec2::One()); - } - - float getEffectiveRotation() const { - return properties.getOr(FramePropertyKey::ImageRotate, 0.0f); - } - - Color getEffectiveColor() const { - return properties.getOr(FramePropertyKey::ColorTint, Colors::White); - } -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_node.h b/Extra2D/include/extra2d/animation/animation_node.h deleted file mode 100644 index 84d5f10..0000000 --- a/Extra2D/include/extra2d/animation/animation_node.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// AnimationNode - 动画节点(继承 Node) -// 使用 FrameRenderer 单渲染器策略,不依赖 Sprite 基类 -// 适用于需要独立渲染控制的动画(如特效、复合动画图层) -// ============================================================================ -class AnimationNode : public Node { -public: - AnimationNode(); - ~AnimationNode() override = default; - - // ------ 静态工厂(Cocos 风格)------ - static Ptr create(); - static Ptr create(Ptr clip); - static Ptr create(const std::string &aniFilePath); - - // ------ 动画数据 ------ - void setClip(Ptr clip); - Ptr 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> &getCurrentDamageBoxes() const; - const std::vector> &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 eventListeners_; - - static const std::vector> emptyBoxes_; - - void setupControllerCallbacks(); - void dispatchEvent(const AnimationEvent &event); -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/composite_animation.h b/Extra2D/include/extra2d/animation/composite_animation.h deleted file mode 100644 index 2439749..0000000 --- a/Extra2D/include/extra2d/animation/composite_animation.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// CompositeAnimation - ALS 多层复合动画节点 -// 管理多个 AnimationNode 图层,统一控制播放 -// 对应 DNF 的 ALS 格式(多层动画叠加) -// ============================================================================ -class CompositeAnimation : public Node { -public: - CompositeAnimation() = default; - ~CompositeAnimation() override = default; - - // ------ 静态工厂 ------ - static Ptr create(); - static Ptr create(const std::string &alsFilePath); - - // ------ 加载 ------ - bool loadFromFile(const std::string &alsFilePath); - - // ------ 图层管理 ------ - void addLayer(Ptr node, int zOrder = 0); - void removeLayer(size_t index); - Ptr getLayer(size_t index) const; - Ptr 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 node; - int zOrder = 0; - }; - std::vector layers_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/frame_property.h b/Extra2D/include/extra2d/animation/frame_property.h deleted file mode 100644 index 418e7bf..0000000 --- a/Extra2D/include/extra2d/animation/frame_property.h +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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: 裁剪区域 [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 使用索引引用 -}; - -// 紧凑的属性值结构(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{}(static_cast(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& value); - - void setCustom(const std::string &key, std::any value); - - // ------ 类型安全获取 ------ - template std::optional get(FramePropertyKey key) const; - - template - T getOr(FramePropertyKey key, const T &defaultValue) const { - auto result = get(key); - return result.value_or(defaultValue); - } - - std::optional 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; - 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 customProperties_; - - // 字符串池和vector池,用于存储大对象 - mutable std::vector stringPool_; - mutable std::vector> vectorPool_; - mutable uint32_t nextStringIndex_ = 0; - mutable uint32_t nextVectorIndex_ = 0; - - uint32_t allocateString(const std::string& str); - uint32_t allocateVector(const std::vector& vec); - const std::string* getString(uint32_t index) const; - const std::vector* getVector(uint32_t index) const; -}; - -// 模板特化声明 -template <> std::optional FramePropertySet::get(FramePropertyKey key) const; -template <> std::optional FramePropertySet::get(FramePropertyKey key) const; -template <> std::optional FramePropertySet::get(FramePropertyKey key) const; -template <> std::optional FramePropertySet::get(FramePropertyKey key) const; -template <> std::optional FramePropertySet::get(FramePropertyKey key) const; -template <> std::optional FramePropertySet::get(FramePropertyKey key) const; -template <> std::optional> FramePropertySet::get>(FramePropertyKey key) const; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/frame_renderer.h b/Extra2D/include/extra2d/animation/frame_renderer.h deleted file mode 100644 index e9be061..0000000 --- a/Extra2D/include/extra2d/animation/frame_renderer.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// FrameRenderer - 帧渲染器 -// 单渲染器 + SpriteFrame 引用策略,替代 N帧=N个Sprite 的旧设计 -// 负责预加载帧的 SpriteFrame、渲染当前帧、处理混合模式 -// ============================================================================ -class FrameRenderer { -public: - FrameRenderer() = default; - - // ------ 预加载 ------ - // 解析所有帧的 SpriteFrame(通过 SpriteFrameCache) - bool preloadFrames(const std::vector &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 getSpriteFrame(size_t frameIndex) const; - Size getMaxFrameSize() const { return maxFrameSize_; } - bool isLoaded() const { return !spriteFrames_.empty(); } - -private: - std::vector> spriteFrames_; - Size maxFrameSize_; - - void drawSpriteFrame(RenderBackend &renderer, Ptr 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 diff --git a/Extra2D/include/extra2d/animation/interpolation_engine.h b/Extra2D/include/extra2d/animation/interpolation_engine.h deleted file mode 100644 index 8978ab5..0000000 --- a/Extra2D/include/extra2d/animation/interpolation_engine.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -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 diff --git a/Extra2D/include/extra2d/animation/sprite_frame.h b/Extra2D/include/extra2d/animation/sprite_frame.h deleted file mode 100644 index 45a843d..0000000 --- a/Extra2D/include/extra2d/animation/sprite_frame.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// SpriteFrame - 精灵帧(纹理 + 区域 + 偏移的中间抽象) -// 借鉴 Cocos2d-x SpriteFrame:解耦纹理物理存储与逻辑帧 -// 一个纹理图集可包含多个 SpriteFrame,减少纹理切换提升渲染性能 -// ============================================================================ -class SpriteFrame { -public: - SpriteFrame() = default; - - SpriteFrame(Ptr texture, const Rect &rect) - : texture_(std::move(texture)), rect_(rect), originalSize_(rect.size) {} - - SpriteFrame(Ptr texture, const Rect &rect, const Vec2 &offset, - const Size &originalSize) - : texture_(std::move(texture)), rect_(rect), offset_(offset), - originalSize_(originalSize) {} - - // ------ 静态创建 ------ - static Ptr create(Ptr texture, const Rect &rect) { - return makePtr(std::move(texture), rect); - } - - static Ptr create(Ptr texture, const Rect &rect, - const Vec2 &offset, const Size &originalSize) { - return makePtr(std::move(texture), rect, offset, originalSize); - } - - // ------ 纹理信息 ------ - void setTexture(Ptr texture) { texture_ = std::move(texture); } - Ptr 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_; - Rect rect_; - Vec2 offset_; - Size originalSize_; - bool rotated_ = false; - std::string name_; -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/sprite_frame_cache.h b/Extra2D/include/extra2d/animation/sprite_frame_cache.h deleted file mode 100644 index de47f4f..0000000 --- a/Extra2D/include/extra2d/animation/sprite_frame_cache.h +++ /dev/null @@ -1,201 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// SpriteFrameCache - 精灵帧全局缓存(借鉴 Cocos SpriteFrameCache) -// 全局单例管理所有精灵帧,避免重复创建,支持图集自动切割 -// ============================================================================ -class SpriteFrameCache { -public: - static SpriteFrameCache &getInstance() { - static SpriteFrameCache instance; - return instance; - } - - // ------ 添加帧 ------ - - /// 添加单个精灵帧 - void addSpriteFrame(Ptr frame, const std::string &name) { - std::lock_guard lock(mutex_); - frames_[name] = std::move(frame); - } - - /// 从纹理和矩形区域创建并添加帧 - void addSpriteFrameFromTexture(Ptr 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, - 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 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(margin + col * (frameWidth + spacing)), - static_cast(margin + row * (frameHeight + spacing)), - static_cast(frameWidth), - static_cast(frameHeight)); - - std::string name = keyPrefix + "#" + std::to_string(i); - auto frame = SpriteFrame::create(texture, rect); - frame->setName(name); - frames_[name] = std::move(frame); - } - } - - // ------ 获取帧 ------ - - /// 按名称获取 - Ptr getSpriteFrame(const std::string &name) const { - std::lock_guard lock(mutex_); - auto it = frames_.find(name); - if (it != frames_.end()) - return it->second; - return nullptr; - } - - /// 通过路径+索引获取或创建(ANI 格式的定位方式) - Ptr getOrCreateFromFile(const std::string &texturePath, - int index = 0) { - std::string key = texturePath + "#" + std::to_string(index); - - { - std::lock_guard 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(texture->getWidth()), - static_cast(texture->getHeight())); - - auto frame = SpriteFrame::create(texture, rect); - frame->setName(key); - - std::lock_guard lock(mutex_); - frames_[key] = frame; - return frame; - } - - // ------ 缓存管理 ------ - - bool has(const std::string &name) const { - std::lock_guard lock(mutex_); - return frames_.find(name) != frames_.end(); - } - - void removeSpriteFrame(const std::string &name) { - std::lock_guard lock(mutex_); - frames_.erase(name); - } - - /// 移除未被外部引用的精灵帧(use_count == 1 表示仅缓存自身持有) - void removeUnusedSpriteFrames() { - std::lock_guard 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 lock(mutex_); - frames_.clear(); - } - - size_t count() const { - std::lock_guard 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 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> frames_; -}; - -// 便捷宏 -#define E2D_SPRITE_FRAME_CACHE() ::extra2d::SpriteFrameCache::getInstance() - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/effects/custom_effect_manager.h b/Extra2D/include/extra2d/effects/custom_effect_manager.h deleted file mode 100644 index 9cc7000..0000000 --- a/Extra2D/include/extra2d/effects/custom_effect_manager.h +++ /dev/null @@ -1,324 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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 getEmitter() { return emitter_; } - -private: - Ptr particleSystem_; - Ptr 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 runtimeParams_; -}; - -// ============================================================================ -// 自定义特效工厂 -// ============================================================================ -class CustomEffectFactory { -public: - using EffectCreator = - std::function(const CustomEffectConfig &)>; - - static CustomEffectFactory &getInstance(); - - // 注册自定义特效创建器 - void registerEffect(const std::string &typeName, EffectCreator creator); - - // 创建特效 - Ptr create(const std::string &typeName, - const CustomEffectConfig &config); - - // 检查是否已注册 - bool isRegistered(const std::string &typeName) const; - - // 获取所有已注册的类型 - std::vector getRegisteredTypes() const; - -private: - CustomEffectFactory() = default; - ~CustomEffectFactory() = default; - CustomEffectFactory(const CustomEffectFactory &) = delete; - CustomEffectFactory &operator=(const CustomEffectFactory &) = delete; - - std::unordered_map 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 getConfigNames() const; - - // ------------------------------------------------------------------------ - // 特效实例管理 - // ------------------------------------------------------------------------ - - /** - * @brief 创建特效实例 - */ - Ptr createEffect(const std::string &name); - - /** - * @brief 从配置直接创建特效 - */ - Ptr createEffectFromConfig(const CustomEffectConfig &config); - - /** - * @brief 销毁特效实例 - */ - void destroyEffect(Ptr effect); - - /** - * @brief 更新所有特效 - */ - void update(float dt); - - /** - * @brief 渲染所有特效 - */ - void render(RenderBackend &renderer); - - /** - * @brief 停止所有特效 - */ - void stopAll(); - - // ------------------------------------------------------------------------ - // 便捷方法 - // ------------------------------------------------------------------------ - - /** - * @brief 播放特效(简写) - */ - Ptr 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 configs_; - std::vector> 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 diff --git a/Extra2D/include/extra2d/effects/particle_system.h b/Extra2D/include/extra2d/effects/particle_system.h deleted file mode 100644 index 05bad4a..0000000 --- a/Extra2D/include/extra2d/effects/particle_system.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 快速随机数生成器 - 使用 xorshift 算法,比 std::mt19937 更快 -// ============================================================================ -class FastRNG { -public: - explicit FastRNG(uint32_t seed = 0) : state_(seed ? seed : 0x853c49e67) {} - - float nextFloat() { - return static_cast(next()) / static_cast(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 = 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) { - 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 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 create(); - - // ------------------------------------------------------------------------ - // 发射器管理 - // ------------------------------------------------------------------------ - Ptr addEmitter(const EmitterConfig &config = {}); - void removeEmitter(Ptr 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> 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 diff --git a/Extra2D/include/extra2d/effects/post_process.h b/Extra2D/include/extra2d/effects/post_process.h deleted file mode 100644 index 638bc68..0000000 --- a/Extra2D/include/extra2d/effects/post_process.h +++ /dev/null @@ -1,228 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -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 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 effect); - - /** - * @brief 插入效果到指定位置 - */ - void insertEffect(size_t index, Ptr effect); - - /** - * @brief 移除效果 - */ - void removeEffect(const std::string &name); - void removeEffect(size_t index); - - /** - * @brief 获取效果 - */ - Ptr getEffect(const std::string &name); - Ptr 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> effects_; - Ptr renderTargetA_; - Ptr 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 diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index 476a636..d9284f5 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -36,24 +36,6 @@ #include #include -// Animation -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - // UI #include #include @@ -64,18 +46,6 @@ #include #include -// Action -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - // Event #include #include @@ -101,14 +71,9 @@ #include #include -// Effects -#include -#include -#include - // Application #include #ifdef __SWITCH__ #include -#endif \ No newline at end of file +#endif diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h index 8924e12..6d448bd 100644 --- a/Extra2D/include/extra2d/scene/node.h +++ b/Extra2D/include/extra2d/scene/node.h @@ -14,7 +14,6 @@ namespace extra2d { // 前向声明 class Scene; -class Action; class RenderBackend; struct RenderCommand; @@ -155,58 +154,6 @@ public: // 更新空间索引(手动调用,通常在边界框变化后) 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; - // ------------------------------------------------------------------------ // 事件系统 // ------------------------------------------------------------------------ diff --git a/Extra2D/src/action/action.cpp b/Extra2D/src/action/action.cpp deleted file mode 100644 index e770896..0000000 --- a/Extra2D/src/action/action.cpp +++ /dev/null @@ -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 diff --git a/Extra2D/src/action/action_ease.cpp b/Extra2D/src/action/action_ease.cpp deleted file mode 100644 index e2eec8b..0000000 --- a/Extra2D/src/action/action_ease.cpp +++ /dev/null @@ -1,683 +0,0 @@ -#include "extra2d/action/action_ease.h" -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846f -#endif - -namespace extra2d { - -// ============================================================================ -// ActionEase 基类 -// ============================================================================ - -ActionEase::~ActionEase() { - delete innerAction_; -} - -bool ActionEase::initWithAction(ActionInterval* action) { - if (!action) { - return false; - } - innerAction_ = action; - duration_ = action->getDuration(); - return true; -} - -void ActionEase::startWithTarget(Node* target) { - ActionInterval::startWithTarget(target); - innerAction_->startWithTarget(target); -} - -void ActionEase::stop() { - innerAction_->stop(); - ActionInterval::stop(); -} - -void ActionEase::update(float time) { - innerAction_->update(time); -} - -// ============================================================================ -// 指数缓动 -// ============================================================================ - -EaseExponentialIn* EaseExponentialIn::create(ActionInterval* action) { - auto* ease = new EaseExponentialIn(); - ease->initWithAction(action); - return ease; -} - -void EaseExponentialIn::update(float time) { - innerAction_->update(easeInExpo(time)); -} - -ActionInterval* EaseExponentialIn::clone() const { - return EaseExponentialIn::create(innerAction_->clone()); -} - -ActionInterval* EaseExponentialIn::reverse() const { - return EaseExponentialOut::create(innerAction_->reverse()); -} - -EaseExponentialOut* EaseExponentialOut::create(ActionInterval* action) { - auto* ease = new EaseExponentialOut(); - ease->initWithAction(action); - return ease; -} - -void EaseExponentialOut::update(float time) { - innerAction_->update(easeOutExpo(time)); -} - -ActionInterval* EaseExponentialOut::clone() const { - return EaseExponentialOut::create(innerAction_->clone()); -} - -ActionInterval* EaseExponentialOut::reverse() const { - return EaseExponentialIn::create(innerAction_->reverse()); -} - -EaseExponentialInOut* EaseExponentialInOut::create(ActionInterval* action) { - auto* ease = new EaseExponentialInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseExponentialInOut::update(float time) { - innerAction_->update(easeInOutExpo(time)); -} - -ActionInterval* EaseExponentialInOut::clone() const { - return EaseExponentialInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseExponentialInOut::reverse() const { - return EaseExponentialInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 正弦缓动 -// ============================================================================ - -EaseSineIn* EaseSineIn::create(ActionInterval* action) { - auto* ease = new EaseSineIn(); - ease->initWithAction(action); - return ease; -} - -void EaseSineIn::update(float time) { - innerAction_->update(easeInSine(time)); -} - -ActionInterval* EaseSineIn::clone() const { - return EaseSineIn::create(innerAction_->clone()); -} - -ActionInterval* EaseSineIn::reverse() const { - return EaseSineOut::create(innerAction_->reverse()); -} - -EaseSineOut* EaseSineOut::create(ActionInterval* action) { - auto* ease = new EaseSineOut(); - ease->initWithAction(action); - return ease; -} - -void EaseSineOut::update(float time) { - innerAction_->update(easeOutSine(time)); -} - -ActionInterval* EaseSineOut::clone() const { - return EaseSineOut::create(innerAction_->clone()); -} - -ActionInterval* EaseSineOut::reverse() const { - return EaseSineIn::create(innerAction_->reverse()); -} - -EaseSineInOut* EaseSineInOut::create(ActionInterval* action) { - auto* ease = new EaseSineInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseSineInOut::update(float time) { - innerAction_->update(easeInOutSine(time)); -} - -ActionInterval* EaseSineInOut::clone() const { - return EaseSineInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseSineInOut::reverse() const { - return EaseSineInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 弹性缓动 -// ============================================================================ - -EaseElasticIn* EaseElasticIn::create(ActionInterval* action, float period) { - auto* ease = new EaseElasticIn(); - ease->initWithAction(action); - ease->period_ = period; - return ease; -} - -void EaseElasticIn::update(float time) { - float newT = 0.0f; - if (time == 0.0f || time == 1.0f) { - newT = time; - } else { - float s = period_ / 4.0f; - time = time - 1.0f; - newT = -std::pow(2.0f, 10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_); - } - innerAction_->update(newT); -} - -ActionInterval* EaseElasticIn::clone() const { - return EaseElasticIn::create(innerAction_->clone(), period_); -} - -ActionInterval* EaseElasticIn::reverse() const { - return EaseElasticOut::create(innerAction_->reverse(), period_); -} - -EaseElasticOut* EaseElasticOut::create(ActionInterval* action, float period) { - auto* ease = new EaseElasticOut(); - ease->initWithAction(action); - ease->period_ = period; - return ease; -} - -void EaseElasticOut::update(float time) { - float newT = 0.0f; - if (time == 0.0f || time == 1.0f) { - newT = time; - } else { - float s = period_ / 4.0f; - newT = std::pow(2.0f, -10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_) + 1.0f; - } - innerAction_->update(newT); -} - -ActionInterval* EaseElasticOut::clone() const { - return EaseElasticOut::create(innerAction_->clone(), period_); -} - -ActionInterval* EaseElasticOut::reverse() const { - return EaseElasticIn::create(innerAction_->reverse(), period_); -} - -EaseElasticInOut* EaseElasticInOut::create(ActionInterval* action, float period) { - auto* ease = new EaseElasticInOut(); - ease->initWithAction(action); - ease->period_ = period; - return ease; -} - -void EaseElasticInOut::update(float time) { - float newT = 0.0f; - if (time == 0.0f || time == 1.0f) { - newT = time; - } else { - time = time * 2.0f; - if (period_ == 0.0f) { - period_ = 0.3f * 1.5f; - } - float s = period_ / 4.0f; - if (time < 1.0f) { - time -= 1.0f; - newT = -0.5f * std::pow(2.0f, 10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_); - } else { - time -= 1.0f; - newT = std::pow(2.0f, -10.0f * time) * std::sin((time - s) * M_PI * 2.0f / period_) * 0.5f + 1.0f; - } - } - innerAction_->update(newT); -} - -ActionInterval* EaseElasticInOut::clone() const { - return EaseElasticInOut::create(innerAction_->clone(), period_); -} - -ActionInterval* EaseElasticInOut::reverse() const { - return EaseElasticInOut::create(innerAction_->reverse(), period_); -} - -// ============================================================================ -// 弹跳缓动 -// ============================================================================ - -EaseBounceIn* EaseBounceIn::create(ActionInterval* action) { - auto* ease = new EaseBounceIn(); - ease->initWithAction(action); - return ease; -} - -void EaseBounceIn::update(float time) { - innerAction_->update(easeInBounce(time)); -} - -ActionInterval* EaseBounceIn::clone() const { - return EaseBounceIn::create(innerAction_->clone()); -} - -ActionInterval* EaseBounceIn::reverse() const { - return EaseBounceOut::create(innerAction_->reverse()); -} - -EaseBounceOut* EaseBounceOut::create(ActionInterval* action) { - auto* ease = new EaseBounceOut(); - ease->initWithAction(action); - return ease; -} - -void EaseBounceOut::update(float time) { - innerAction_->update(easeOutBounce(time)); -} - -ActionInterval* EaseBounceOut::clone() const { - return EaseBounceOut::create(innerAction_->clone()); -} - -ActionInterval* EaseBounceOut::reverse() const { - return EaseBounceIn::create(innerAction_->reverse()); -} - -EaseBounceInOut* EaseBounceInOut::create(ActionInterval* action) { - auto* ease = new EaseBounceInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseBounceInOut::update(float time) { - innerAction_->update(easeInOutBounce(time)); -} - -ActionInterval* EaseBounceInOut::clone() const { - return EaseBounceInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseBounceInOut::reverse() const { - return EaseBounceInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 回震缓动 -// ============================================================================ - -EaseBackIn* EaseBackIn::create(ActionInterval* action) { - auto* ease = new EaseBackIn(); - ease->initWithAction(action); - return ease; -} - -void EaseBackIn::update(float time) { - innerAction_->update(easeInBack(time)); -} - -ActionInterval* EaseBackIn::clone() const { - return EaseBackIn::create(innerAction_->clone()); -} - -ActionInterval* EaseBackIn::reverse() const { - return EaseBackOut::create(innerAction_->reverse()); -} - -EaseBackOut* EaseBackOut::create(ActionInterval* action) { - auto* ease = new EaseBackOut(); - ease->initWithAction(action); - return ease; -} - -void EaseBackOut::update(float time) { - innerAction_->update(easeOutBack(time)); -} - -ActionInterval* EaseBackOut::clone() const { - return EaseBackOut::create(innerAction_->clone()); -} - -ActionInterval* EaseBackOut::reverse() const { - return EaseBackIn::create(innerAction_->reverse()); -} - -EaseBackInOut* EaseBackInOut::create(ActionInterval* action) { - auto* ease = new EaseBackInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseBackInOut::update(float time) { - innerAction_->update(easeInOutBack(time)); -} - -ActionInterval* EaseBackInOut::clone() const { - return EaseBackInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseBackInOut::reverse() const { - return EaseBackInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 二次缓动 -// ============================================================================ - -EaseQuadIn* EaseQuadIn::create(ActionInterval* action) { - auto* ease = new EaseQuadIn(); - ease->initWithAction(action); - return ease; -} - -void EaseQuadIn::update(float time) { - innerAction_->update(easeInQuad(time)); -} - -ActionInterval* EaseQuadIn::clone() const { - return EaseQuadIn::create(innerAction_->clone()); -} - -ActionInterval* EaseQuadIn::reverse() const { - return EaseQuadOut::create(innerAction_->reverse()); -} - -EaseQuadOut* EaseQuadOut::create(ActionInterval* action) { - auto* ease = new EaseQuadOut(); - ease->initWithAction(action); - return ease; -} - -void EaseQuadOut::update(float time) { - innerAction_->update(easeOutQuad(time)); -} - -ActionInterval* EaseQuadOut::clone() const { - return EaseQuadOut::create(innerAction_->clone()); -} - -ActionInterval* EaseQuadOut::reverse() const { - return EaseQuadIn::create(innerAction_->reverse()); -} - -EaseQuadInOut* EaseQuadInOut::create(ActionInterval* action) { - auto* ease = new EaseQuadInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseQuadInOut::update(float time) { - innerAction_->update(easeInOutQuad(time)); -} - -ActionInterval* EaseQuadInOut::clone() const { - return EaseQuadInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseQuadInOut::reverse() const { - return EaseQuadInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 三次缓动 -// ============================================================================ - -EaseCubicIn* EaseCubicIn::create(ActionInterval* action) { - auto* ease = new EaseCubicIn(); - ease->initWithAction(action); - return ease; -} - -void EaseCubicIn::update(float time) { - innerAction_->update(easeInCubic(time)); -} - -ActionInterval* EaseCubicIn::clone() const { - return EaseCubicIn::create(innerAction_->clone()); -} - -ActionInterval* EaseCubicIn::reverse() const { - return EaseCubicOut::create(innerAction_->reverse()); -} - -EaseCubicOut* EaseCubicOut::create(ActionInterval* action) { - auto* ease = new EaseCubicOut(); - ease->initWithAction(action); - return ease; -} - -void EaseCubicOut::update(float time) { - innerAction_->update(easeOutCubic(time)); -} - -ActionInterval* EaseCubicOut::clone() const { - return EaseCubicOut::create(innerAction_->clone()); -} - -ActionInterval* EaseCubicOut::reverse() const { - return EaseCubicIn::create(innerAction_->reverse()); -} - -EaseCubicInOut* EaseCubicInOut::create(ActionInterval* action) { - auto* ease = new EaseCubicInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseCubicInOut::update(float time) { - innerAction_->update(easeInOutCubic(time)); -} - -ActionInterval* EaseCubicInOut::clone() const { - return EaseCubicInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseCubicInOut::reverse() const { - return EaseCubicInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 四次缓动 -// ============================================================================ - -EaseQuartIn* EaseQuartIn::create(ActionInterval* action) { - auto* ease = new EaseQuartIn(); - ease->initWithAction(action); - return ease; -} - -void EaseQuartIn::update(float time) { - innerAction_->update(easeInQuart(time)); -} - -ActionInterval* EaseQuartIn::clone() const { - return EaseQuartIn::create(innerAction_->clone()); -} - -ActionInterval* EaseQuartIn::reverse() const { - return EaseQuartOut::create(innerAction_->reverse()); -} - -EaseQuartOut* EaseQuartOut::create(ActionInterval* action) { - auto* ease = new EaseQuartOut(); - ease->initWithAction(action); - return ease; -} - -void EaseQuartOut::update(float time) { - innerAction_->update(easeOutQuart(time)); -} - -ActionInterval* EaseQuartOut::clone() const { - return EaseQuartOut::create(innerAction_->clone()); -} - -ActionInterval* EaseQuartOut::reverse() const { - return EaseQuartIn::create(innerAction_->reverse()); -} - -EaseQuartInOut* EaseQuartInOut::create(ActionInterval* action) { - auto* ease = new EaseQuartInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseQuartInOut::update(float time) { - innerAction_->update(easeInOutQuart(time)); -} - -ActionInterval* EaseQuartInOut::clone() const { - return EaseQuartInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseQuartInOut::reverse() const { - return EaseQuartInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 五次缓动 -// ============================================================================ - -EaseQuintIn* EaseQuintIn::create(ActionInterval* action) { - auto* ease = new EaseQuintIn(); - ease->initWithAction(action); - return ease; -} - -void EaseQuintIn::update(float time) { - innerAction_->update(easeInQuint(time)); -} - -ActionInterval* EaseQuintIn::clone() const { - return EaseQuintIn::create(innerAction_->clone()); -} - -ActionInterval* EaseQuintIn::reverse() const { - return EaseQuintOut::create(innerAction_->reverse()); -} - -EaseQuintOut* EaseQuintOut::create(ActionInterval* action) { - auto* ease = new EaseQuintOut(); - ease->initWithAction(action); - return ease; -} - -void EaseQuintOut::update(float time) { - innerAction_->update(easeOutQuint(time)); -} - -ActionInterval* EaseQuintOut::clone() const { - return EaseQuintOut::create(innerAction_->clone()); -} - -ActionInterval* EaseQuintOut::reverse() const { - return EaseQuintIn::create(innerAction_->reverse()); -} - -EaseQuintInOut* EaseQuintInOut::create(ActionInterval* action) { - auto* ease = new EaseQuintInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseQuintInOut::update(float time) { - innerAction_->update(easeInOutQuint(time)); -} - -ActionInterval* EaseQuintInOut::clone() const { - return EaseQuintInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseQuintInOut::reverse() const { - return EaseQuintInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 圆形缓动 -// ============================================================================ - -EaseCircleIn* EaseCircleIn::create(ActionInterval* action) { - auto* ease = new EaseCircleIn(); - ease->initWithAction(action); - return ease; -} - -void EaseCircleIn::update(float time) { - innerAction_->update(easeInCirc(time)); -} - -ActionInterval* EaseCircleIn::clone() const { - return EaseCircleIn::create(innerAction_->clone()); -} - -ActionInterval* EaseCircleIn::reverse() const { - return EaseCircleOut::create(innerAction_->reverse()); -} - -EaseCircleOut* EaseCircleOut::create(ActionInterval* action) { - auto* ease = new EaseCircleOut(); - ease->initWithAction(action); - return ease; -} - -void EaseCircleOut::update(float time) { - innerAction_->update(easeOutCirc(time)); -} - -ActionInterval* EaseCircleOut::clone() const { - return EaseCircleOut::create(innerAction_->clone()); -} - -ActionInterval* EaseCircleOut::reverse() const { - return EaseCircleIn::create(innerAction_->reverse()); -} - -EaseCircleInOut* EaseCircleInOut::create(ActionInterval* action) { - auto* ease = new EaseCircleInOut(); - ease->initWithAction(action); - return ease; -} - -void EaseCircleInOut::update(float time) { - innerAction_->update(easeInOutCirc(time)); -} - -ActionInterval* EaseCircleInOut::clone() const { - return EaseCircleInOut::create(innerAction_->clone()); -} - -ActionInterval* EaseCircleInOut::reverse() const { - return EaseCircleInOut::create(innerAction_->reverse()); -} - -// ============================================================================ -// 自定义缓动 -// ============================================================================ - -EaseCustom* EaseCustom::create(ActionInterval* action, EaseFunction easeFunc) { - auto* ease = new EaseCustom(); - ease->initWithAction(action); - ease->easeFunc_ = easeFunc; - return ease; -} - -void EaseCustom::update(float time) { - if (easeFunc_) { - innerAction_->update(easeFunc_(time)); - } else { - innerAction_->update(time); - } -} - -ActionInterval* EaseCustom::clone() const { - return EaseCustom::create(innerAction_->clone(), easeFunc_); -} - -ActionInterval* EaseCustom::reverse() const { - return EaseCustom::create(innerAction_->reverse(), easeFunc_); -} - -} // namespace extra2d diff --git a/Extra2D/src/action/action_instant.cpp b/Extra2D/src/action/action_instant.cpp deleted file mode 100644 index 7cd8ad1..0000000 --- a/Extra2D/src/action/action_instant.cpp +++ /dev/null @@ -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 diff --git a/Extra2D/src/action/action_instant_actions.cpp b/Extra2D/src/action/action_instant_actions.cpp deleted file mode 100644 index 7afcae3..0000000 --- a/Extra2D/src/action/action_instant_actions.cpp +++ /dev/null @@ -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 diff --git a/Extra2D/src/action/action_interval.cpp b/Extra2D/src/action/action_interval.cpp deleted file mode 100644 index 5643020..0000000 --- a/Extra2D/src/action/action_interval.cpp +++ /dev/null @@ -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 diff --git a/Extra2D/src/action/action_interval_actions.cpp b/Extra2D/src/action/action_interval_actions.cpp deleted file mode 100644 index f986c99..0000000 --- a/Extra2D/src/action/action_interval_actions.cpp +++ /dev/null @@ -1,787 +0,0 @@ -#include "extra2d/action/action_interval_actions.h" -#include "extra2d/scene/node.h" -#include -#include - -namespace extra2d { - -// ============================================================================ -// 移动动作 -// ============================================================================ - -MoveBy* MoveBy::create(float duration, const Vec2& delta) { - auto* action = new MoveBy(); - action->duration_ = duration; - action->delta_ = delta; - return action; -} - -void MoveBy::onStart() { - startPosition_ = target_->getPosition(); -} - -void MoveBy::onUpdate(float progress) { - Vec2 newPos = startPosition_ + delta_ * progress; - target_->setPosition(newPos); -} - -ActionInterval* MoveBy::clone() const { - return MoveBy::create(duration_, delta_); -} - -ActionInterval* MoveBy::reverse() const { - return MoveBy::create(duration_, -delta_); -} - -// MoveTo -MoveTo* MoveTo::create(float duration, const Vec2& position) { - auto* action = new MoveTo(); - action->duration_ = duration; - action->endPosition_ = position; - return action; -} - -void MoveTo::onStart() { - startPosition_ = target_->getPosition(); - delta_ = endPosition_ - startPosition_; -} - -void MoveTo::onUpdate(float progress) { - Vec2 newPos = startPosition_ + delta_ * progress; - target_->setPosition(newPos); -} - -ActionInterval* MoveTo::clone() const { - return MoveTo::create(duration_, endPosition_); -} - -ActionInterval* MoveTo::reverse() const { - return MoveTo::create(duration_, startPosition_); -} - -// ============================================================================ -// 跳跃动作 -// ============================================================================ - -JumpBy* JumpBy::create(float duration, const Vec2& position, float height, int jumps) { - auto* action = new JumpBy(); - action->duration_ = duration; - action->delta_ = position; - action->height_ = height; - action->jumps_ = jumps; - return action; -} - -void JumpBy::onStart() { - startPosition_ = target_->getPosition(); -} - -void JumpBy::onUpdate(float progress) { - float frac = (progress * jumps_) - static_cast(progress * jumps_); - float y = height_ * 4.0f * frac * (1.0f - frac); - y += delta_.y * progress; - float x = delta_.x * progress; - target_->setPosition(startPosition_ + Vec2(x, y)); -} - -ActionInterval* JumpBy::clone() const { - return JumpBy::create(duration_, delta_, height_, jumps_); -} - -ActionInterval* JumpBy::reverse() const { - return JumpBy::create(duration_, -delta_, height_, jumps_); -} - -// JumpTo -JumpTo* JumpTo::create(float duration, const Vec2& position, float height, int jumps) { - auto* action = new JumpTo(); - action->duration_ = duration; - action->endPosition_ = position; - action->height_ = height; - action->jumps_ = jumps; - return action; -} - -void JumpTo::onStart() { - JumpBy::onStart(); - delta_ = endPosition_ - startPosition_; -} - -ActionInterval* JumpTo::clone() const { - return JumpTo::create(duration_, endPosition_, height_, jumps_); -} - -ActionInterval* JumpTo::reverse() const { - return JumpTo::create(duration_, startPosition_, height_, jumps_); -} - -// ============================================================================ -// 贝塞尔曲线动作 -// ============================================================================ - -BezierBy* BezierBy::create(float duration, const BezierConfig& config) { - auto* action = new BezierBy(); - action->duration_ = duration; - action->config_ = config; - return action; -} - -void BezierBy::onStart() { - startPosition_ = target_->getPosition(); -} - -void BezierBy::onUpdate(float progress) { - float xa = startPosition_.x; - float xb = config_.controlPoint1.x; - float xc = config_.controlPoint2.x; - float xd = config_.endPosition.x; - - float ya = startPosition_.y; - float yb = config_.controlPoint1.y; - float yc = config_.controlPoint2.y; - float yd = config_.endPosition.y; - - float x = bezierat(xa, xb, xc, xd, progress); - float y = bezierat(ya, yb, yc, yd, progress); - - target_->setPosition(Vec2(x, y)); -} - -ActionInterval* BezierBy::clone() const { - return BezierBy::create(duration_, config_); -} - -ActionInterval* BezierBy::reverse() const { - BezierConfig rev; - rev.controlPoint1 = config_.controlPoint2 + config_.endPosition; - rev.controlPoint2 = config_.controlPoint1 + config_.endPosition; - rev.endPosition = config_.endPosition; - return BezierBy::create(duration_, rev); -} - -float BezierBy::bezierat(float a, float b, float c, float d, float t) { - return (powf(1 - t, 3) * a + - 3.0f * t * powf(1 - t, 2) * b + - 3.0f * t * t * (1 - t) * c + - t * t * t * d); -} - -// BezierTo -BezierTo* BezierTo::create(float duration, const BezierConfig& config) { - auto* action = new BezierTo(); - action->duration_ = duration; - action->originalConfig_ = config; - return action; -} - -void BezierTo::onStart() { - BezierBy::onStart(); - config_.controlPoint1 = originalConfig_.controlPoint1 - startPosition_; - config_.controlPoint2 = originalConfig_.controlPoint2 - startPosition_; - config_.endPosition = originalConfig_.endPosition - startPosition_; -} - -ActionInterval* BezierTo::clone() const { - return BezierTo::create(duration_, originalConfig_); -} - -ActionInterval* BezierTo::reverse() const { - BezierConfig rev; - rev.controlPoint1 = originalConfig_.controlPoint2; - rev.controlPoint2 = originalConfig_.controlPoint1; - rev.endPosition = startPosition_; - return BezierTo::create(duration_, rev); -} - -// ============================================================================ -// 缩放动作 -// ============================================================================ - -ScaleBy* ScaleBy::create(float duration, float scale) { - return create(duration, scale, scale); -} - -ScaleBy* ScaleBy::create(float duration, float scaleX, float scaleY) { - auto* action = new ScaleBy(); - action->duration_ = duration; - action->deltaScale_ = Vec2(scaleX - 1.0f, scaleY - 1.0f); - return action; -} - -ScaleBy* ScaleBy::create(float duration, const Vec2& scale) { - return create(duration, scale.x, scale.y); -} - -void ScaleBy::onStart() { - startScale_ = target_->getScale(); -} - -void ScaleBy::onUpdate(float progress) { - Vec2 newScale = startScale_ + deltaScale_ * progress; - target_->setScale(newScale); -} - -ActionInterval* ScaleBy::clone() const { - return ScaleBy::create(duration_, Vec2(startScale_.x + deltaScale_.x, startScale_.y + deltaScale_.y)); -} - -ActionInterval* ScaleBy::reverse() const { - return ScaleBy::create(duration_, Vec2(startScale_.x - deltaScale_.x, startScale_.y - deltaScale_.y)); -} - -// ScaleTo -ScaleTo* ScaleTo::create(float duration, float scale) { - return create(duration, scale, scale); -} - -ScaleTo* ScaleTo::create(float duration, float scaleX, float scaleY) { - auto* action = new ScaleTo(); - action->duration_ = duration; - action->endScale_ = Vec2(scaleX, scaleY); - return action; -} - -ScaleTo* ScaleTo::create(float duration, const Vec2& scale) { - return create(duration, scale.x, scale.y); -} - -void ScaleTo::onStart() { - startScale_ = target_->getScale(); - delta_ = endScale_ - startScale_; -} - -void ScaleTo::onUpdate(float progress) { - Vec2 newScale = startScale_ + delta_ * progress; - target_->setScale(newScale); -} - -ActionInterval* ScaleTo::clone() const { - return ScaleTo::create(duration_, endScale_); -} - -ActionInterval* ScaleTo::reverse() const { - return ScaleTo::create(duration_, startScale_); -} - -// ============================================================================ -// 旋转动作 -// ============================================================================ - -RotateBy* RotateBy::create(float duration, float deltaAngle) { - auto* action = new RotateBy(); - action->duration_ = duration; - action->deltaAngle_ = deltaAngle; - return action; -} - -void RotateBy::onStart() { - startAngle_ = target_->getRotation(); -} - -void RotateBy::onUpdate(float progress) { - float newAngle = startAngle_ + deltaAngle_ * progress; - target_->setRotation(newAngle); -} - -ActionInterval* RotateBy::clone() const { - return RotateBy::create(duration_, deltaAngle_); -} - -ActionInterval* RotateBy::reverse() const { - return RotateBy::create(duration_, -deltaAngle_); -} - -// RotateTo -RotateTo* RotateTo::create(float duration, float angle) { - auto* action = new RotateTo(); - action->duration_ = duration; - action->endAngle_ = angle; - return action; -} - -void RotateTo::onStart() { - startAngle_ = target_->getRotation(); - deltaAngle_ = endAngle_ - startAngle_; - - if (deltaAngle_ > 180.0f) deltaAngle_ -= 360.0f; - if (deltaAngle_ < -180.0f) deltaAngle_ += 360.0f; -} - -void RotateTo::onUpdate(float progress) { - float newAngle = startAngle_ + deltaAngle_ * progress; - target_->setRotation(newAngle); -} - -ActionInterval* RotateTo::clone() const { - return RotateTo::create(duration_, endAngle_); -} - -ActionInterval* RotateTo::reverse() const { - return RotateTo::create(duration_, startAngle_); -} - -// ============================================================================ -// 淡入淡出动作 -// ============================================================================ - -FadeIn* FadeIn::create(float duration) { - auto* action = new FadeIn(); - action->duration_ = duration; - return action; -} - -void FadeIn::onStart() { - startOpacity_ = target_->getOpacity(); - target_->setOpacity(0.0f); -} - -void FadeIn::onUpdate(float progress) { - target_->setOpacity(progress); -} - -ActionInterval* FadeIn::clone() const { - return FadeIn::create(duration_); -} - -ActionInterval* FadeIn::reverse() const { - return FadeOut::create(duration_); -} - -// FadeOut -FadeOut* FadeOut::create(float duration) { - auto* action = new FadeOut(); - action->duration_ = duration; - return action; -} - -void FadeOut::onStart() { - startOpacity_ = target_->getOpacity(); - target_->setOpacity(1.0f); -} - -void FadeOut::onUpdate(float progress) { - target_->setOpacity(1.0f - progress); -} - -ActionInterval* FadeOut::clone() const { - return FadeOut::create(duration_); -} - -ActionInterval* FadeOut::reverse() const { - return FadeIn::create(duration_); -} - -// FadeTo -FadeTo* FadeTo::create(float duration, float opacity) { - auto* action = new FadeTo(); - action->duration_ = duration; - action->endOpacity_ = opacity; - return action; -} - -void FadeTo::onStart() { - startOpacity_ = target_->getOpacity(); - deltaOpacity_ = endOpacity_ - startOpacity_; -} - -void FadeTo::onUpdate(float progress) { - target_->setOpacity(startOpacity_ + deltaOpacity_ * progress); -} - -ActionInterval* FadeTo::clone() const { - return FadeTo::create(duration_, endOpacity_); -} - -ActionInterval* FadeTo::reverse() const { - return FadeTo::create(duration_, startOpacity_); -} - -// ============================================================================ -// 闪烁动作 -// ============================================================================ - -Blink* Blink::create(float duration, int times) { - auto* action = new Blink(); - action->duration_ = duration; - action->times_ = times; - return action; -} - -void Blink::onStart() { - originalVisible_ = target_->isVisible(); - currentTimes_ = 0; -} - -void Blink::onUpdate(float progress) { - float slice = 1.0f / times_; - float m = fmodf(progress, slice); - target_->setVisible(m > slice / 2.0f); -} - -ActionInterval* Blink::clone() const { - return Blink::create(duration_, times_); -} - -ActionInterval* Blink::reverse() const { - return Blink::create(duration_, times_); -} - -// ============================================================================ -// 色调动作 -// ============================================================================ - -TintTo* TintTo::create(float duration, uint8_t red, uint8_t green, uint8_t blue) { - auto* action = new TintTo(); - action->duration_ = duration; - action->endColor_ = Color3B(red, green, blue); - return action; -} - -void TintTo::onStart() { - startColor_ = target_->getColor(); - deltaColor_ = Color3B( - static_cast(endColor_.r) - static_cast(startColor_.r), - static_cast(endColor_.g) - static_cast(startColor_.g), - static_cast(endColor_.b) - static_cast(startColor_.b) - ); -} - -void TintTo::onUpdate(float progress) { - Color3B newColor( - static_cast(startColor_.r + deltaColor_.r * progress), - static_cast(startColor_.g + deltaColor_.g * progress), - static_cast(startColor_.b + deltaColor_.b * progress) - ); - target_->setColor(newColor); -} - -ActionInterval* TintTo::clone() const { - return TintTo::create(duration_, endColor_.r, endColor_.g, endColor_.b); -} - -ActionInterval* TintTo::reverse() const { - return TintTo::create(duration_, startColor_.r, startColor_.g, startColor_.b); -} - -// TintBy -TintBy* TintBy::create(float duration, int16_t deltaRed, int16_t deltaGreen, int16_t deltaBlue) { - auto* action = new TintBy(); - action->duration_ = duration; - action->deltaR_ = deltaRed; - action->deltaG_ = deltaGreen; - action->deltaB_ = deltaBlue; - return action; -} - -void TintBy::onStart() { - startColor_ = target_->getColor(); -} - -void TintBy::onUpdate(float progress) { - Color3B newColor( - static_cast(startColor_.r + deltaR_ * progress), - static_cast(startColor_.g + deltaG_ * progress), - static_cast(startColor_.b + deltaB_ * progress) - ); - target_->setColor(newColor); -} - -ActionInterval* TintBy::clone() const { - return TintBy::create(duration_, deltaR_, deltaG_, deltaB_); -} - -ActionInterval* TintBy::reverse() const { - return TintBy::create(duration_, -deltaR_, -deltaG_, -deltaB_); -} - -// ============================================================================ -// 组合动作 -// ============================================================================ - -Sequence* Sequence::create(ActionInterval* action1, ...) { - std::vector actions; - actions.push_back(action1); - - va_list args; - va_start(args, action1); - ActionInterval* action = nullptr; - while ((action = va_arg(args, ActionInterval*)) != nullptr) { - actions.push_back(action); - } - va_end(args); - - return create(actions); -} - -Sequence* Sequence::create(const std::vector& actions) { - auto* seq = new Sequence(); - seq->duration_ = 0.0f; - - for (auto* action : actions) { - if (action) { - seq->actions_.push_back(action); - seq->duration_ += action->getDuration(); - } - } - return seq; -} - -Sequence::~Sequence() { - for (auto* action : actions_) { - delete action; - } -} - -void Sequence::onStart() { - currentIndex_ = 0; - split_ = 0.0f; - last_ = -1.0f; - - if (!actions_.empty()) { - actions_[0]->startWithTarget(target_); - } -} - -void Sequence::onUpdate(float progress) { - float newTime = progress * duration_; - - if (newTime < last_) { - for (auto* action : actions_) { - action->stop(); - } - } - last_ = newTime; - - float foundSplit = 0.0f; - size_t found = 0; - - for (size_t i = 0; i < actions_.size(); ++i) { - foundSplit += actions_[i]->getDuration(); - if (foundSplit > newTime) { - found = i; - break; - } else if (foundSplit == newTime) { - found = i + 1; - break; - } - } - - if (found != currentIndex_) { - if (currentIndex_ < actions_.size()) { - actions_[currentIndex_]->update(1.0f); - } - if (found < actions_.size()) { - actions_[found]->startWithTarget(target_); - } - currentIndex_ = found; - } - - if (currentIndex_ < actions_.size()) { - float localTime = newTime - (foundSplit - actions_[currentIndex_]->getDuration()); - float localProgress = actions_[currentIndex_]->getDuration() > 0.0f - ? localTime / actions_[currentIndex_]->getDuration() : 1.0f; - actions_[currentIndex_]->update(localProgress); - } -} - -ActionInterval* Sequence::clone() const { - std::vector cloned; - for (auto* action : actions_) { - cloned.push_back(action->clone()); - } - return Sequence::create(cloned); -} - -ActionInterval* Sequence::reverse() const { - std::vector rev; - for (auto it = actions_.rbegin(); it != actions_.rend(); ++it) { - rev.push_back((*it)->reverse()); - } - return Sequence::create(rev); -} - -// Spawn -Spawn* Spawn::create(ActionInterval* action1, ...) { - std::vector actions; - actions.push_back(action1); - - va_list args; - va_start(args, action1); - ActionInterval* action = nullptr; - while ((action = va_arg(args, ActionInterval*)) != nullptr) { - actions.push_back(action); - } - va_end(args); - - return create(actions); -} - -Spawn* Spawn::create(const std::vector& actions) { - auto* spawn = new Spawn(); - spawn->duration_ = 0.0f; - - for (auto* action : actions) { - if (action) { - spawn->actions_.push_back(action); - spawn->duration_ = std::max(spawn->duration_, action->getDuration()); - } - } - return spawn; -} - -Spawn::~Spawn() { - for (auto* action : actions_) { - delete action; - } -} - -void Spawn::onStart() { - for (auto* action : actions_) { - action->startWithTarget(target_); - } -} - -void Spawn::onUpdate(float progress) { - for (auto* action : actions_) { - float localProgress = action->getDuration() > 0.0f - ? std::min(1.0f, (progress * duration_) / action->getDuration()) - : 1.0f; - action->update(localProgress); - } -} - -ActionInterval* Spawn::clone() const { - std::vector cloned; - for (auto* action : actions_) { - cloned.push_back(action->clone()); - } - return Spawn::create(cloned); -} - -ActionInterval* Spawn::reverse() const { - std::vector rev; - for (auto* action : actions_) { - rev.push_back(action->reverse()); - } - return Spawn::create(rev); -} - -// Repeat -Repeat* Repeat::create(ActionInterval* action, int times) { - auto* repeat = new Repeat(); - repeat->innerAction_ = action; - repeat->times_ = times; - repeat->duration_ = action->getDuration() * times; - return repeat; -} - -ActionInterval* Repeat::clone() const { - return Repeat::create(innerAction_->clone(), times_); -} - -ActionInterval* Repeat::reverse() const { - return Repeat::create(innerAction_->reverse(), times_); -} - -bool Repeat::isDone() const { - return currentTimes_ >= times_; -} - -void Repeat::onStart() { - currentTimes_ = 0; - innerAction_->startWithTarget(target_); -} - -void Repeat::onUpdate(float progress) { - float t = progress * times_; - int current = static_cast(t); - - if (current > currentTimes_) { - innerAction_->update(1.0f); - currentTimes_++; - if (currentTimes_ < times_) { - innerAction_->startWithTarget(target_); - } - } - - if (currentTimes_ < times_) { - innerAction_->update(t - current); - } -} - -// RepeatForever -RepeatForever* RepeatForever::create(ActionInterval* action) { - auto* repeat = new RepeatForever(); - repeat->innerAction_ = action; - repeat->duration_ = action->getDuration(); - return repeat; -} - -ActionInterval* RepeatForever::clone() const { - return RepeatForever::create(innerAction_->clone()); -} - -ActionInterval* RepeatForever::reverse() const { - return RepeatForever::create(innerAction_->reverse()); -} - -bool RepeatForever::isDone() const { - return false; -} - -void RepeatForever::onStart() { - innerAction_->startWithTarget(target_); -} - -void RepeatForever::onUpdate(float progress) { - innerAction_->update(progress); - if (innerAction_->isDone()) { - innerAction_->startWithTarget(target_); - elapsed_ = 0.0f; - } -} - -// DelayTime -DelayTime* DelayTime::create(float duration) { - auto* delay = new DelayTime(); - delay->duration_ = duration; - return delay; -} - -ActionInterval* DelayTime::clone() const { - return DelayTime::create(duration_); -} - -ActionInterval* DelayTime::reverse() const { - return DelayTime::create(duration_); -} - -// ReverseTime -ReverseTime* ReverseTime::create(ActionInterval* action) { - auto* rev = new ReverseTime(); - rev->innerAction_ = action; - rev->duration_ = action->getDuration(); - return rev; -} - -ReverseTime::~ReverseTime() { - delete innerAction_; -} - -ActionInterval* ReverseTime::clone() const { - return ReverseTime::create(innerAction_->clone()); -} - -ActionInterval* ReverseTime::reverse() const { - return innerAction_->clone(); -} - -void ReverseTime::onStart() { - innerAction_->startWithTarget(target_); -} - -void ReverseTime::onUpdate(float progress) { - innerAction_->update(1.0f - progress); -} - -} // namespace extra2d diff --git a/Extra2D/src/action/action_manager.cpp b/Extra2D/src/action/action_manager.cpp deleted file mode 100644 index cbecbf8..0000000 --- a/Extra2D/src/action/action_manager.cpp +++ /dev/null @@ -1,244 +0,0 @@ -#include "extra2d/action/action_manager.h" -#include "extra2d/scene/node.h" -#include "extra2d/utils/logger.h" - -namespace extra2d { - -ActionManager* ActionManager::instance_ = nullptr; - -ActionManager::ActionManager() {} - -ActionManager::~ActionManager() { - removeAllActions(); -} - -ActionManager* ActionManager::getInstance() { - if (!instance_) { - instance_ = new ActionManager(); - } - return instance_; -} - -void ActionManager::destroyInstance() { - if (instance_) { - delete instance_; - instance_ = nullptr; - } -} - -void ActionManager::addAction(Action* action, Node* target, bool paused) { - if (!action || !target) { - return; - } - - auto it = targets_.find(target); - if (it == targets_.end()) { - ActionElement element; - element.target = target; - element.paused = paused; - targets_[target] = element; - it = targets_.find(target); - } - - auto& element = it->second; - element.actions.push_back(action); - action->startWithTarget(target); -} - -void ActionManager::removeAction(Action* action) { - if (!action) { - return; - } - - Node* target = action->getOriginalTarget(); - if (!target) { - return; - } - - auto it = targets_.find(target); - if (it == targets_.end()) { - return; - } - - auto& element = it->second; - for (size_t i = 0; i < element.actions.size(); ++i) { - if (element.actions[i] == action) { - removeActionAt(i, element); - break; - } - } -} - -void ActionManager::removeActionByTag(int tag, Node* target) { - if (!target) { - return; - } - - auto it = targets_.find(target); - if (it == targets_.end()) { - return; - } - - auto& element = it->second; - for (size_t i = 0; i < element.actions.size(); ++i) { - if (element.actions[i]->getTag() == tag) { - removeActionAt(i, element); - break; - } - } -} - -void ActionManager::removeActionsByFlags(unsigned int flags, Node* target) { - if (!target) { - return; - } - - auto it = targets_.find(target); - if (it == targets_.end()) { - return; - } - - auto& element = it->second; - for (int i = static_cast(element.actions.size()) - 1; i >= 0; --i) { - if (element.actions[i]->getFlags() & flags) { - removeActionAt(i, element); - } - } -} - -void ActionManager::removeAllActionsFromTarget(Node* target) { - if (!target) { - return; - } - - auto it = targets_.find(target); - if (it == targets_.end()) { - return; - } - - auto& element = it->second; - for (auto* action : element.actions) { - action->stop(); - deleteAction(action); - } - element.actions.clear(); - targets_.erase(it); -} - -void ActionManager::removeAllActions() { - for (auto& pair : targets_) { - auto& element = pair.second; - for (auto* action : element.actions) { - action->stop(); - deleteAction(action); - } - } - targets_.clear(); -} - -Action* ActionManager::getActionByTag(int tag, Node* target) { - if (!target) { - return nullptr; - } - - auto it = targets_.find(target); - if (it == targets_.end()) { - return nullptr; - } - - auto& element = it->second; - for (auto* action : element.actions) { - if (action->getTag() == tag) { - return action; - } - } - return nullptr; -} - -size_t ActionManager::getActionCount(Node* target) const { - auto it = targets_.find(target); - if (it == targets_.end()) { - return 0; - } - return it->second.actions.size(); -} - -void ActionManager::pauseTarget(Node* target) { - auto it = targets_.find(target); - if (it != targets_.end()) { - it->second.paused = true; - } -} - -void ActionManager::resumeTarget(Node* target) { - auto it = targets_.find(target); - if (it != targets_.end()) { - it->second.paused = false; - } -} - -bool ActionManager::isPaused(Node* target) const { - auto it = targets_.find(target); - if (it == targets_.end()) { - return false; - } - return it->second.paused; -} - -void ActionManager::update(float dt) { - for (auto it = targets_.begin(); it != targets_.end(); ) { - auto& element = it->second; - Node* target = element.target; - - if (!element.paused && target) { - element.actionIndex = 0; - while (static_cast(element.actionIndex) < element.actions.size()) { - element.currentAction = element.actions[element.actionIndex]; - element.currentActionSalvaged = false; - - if (element.currentAction->getState() != ActionState::Paused) { - element.currentAction->step(dt); - } - - if (element.currentActionSalvaged) { - deleteAction(element.currentAction); - } else if (element.currentAction->isDone()) { - element.currentAction->stop(); - deleteAction(element.currentAction); - element.actions.erase(element.actions.begin() + element.actionIndex); - continue; - } - - element.actionIndex++; - } - } - - if (element.actions.empty()) { - it = targets_.erase(it); - } else { - ++it; - } - } -} - -void ActionManager::removeActionAt(size_t index, ActionElement& element) { - if (index >= element.actions.size()) { - return; - } - - Action* action = element.actions[index]; - if (action == element.currentAction) { - element.currentActionSalvaged = true; - } - action->stop(); - deleteAction(action); - element.actions.erase(element.actions.begin() + index); -} - -void ActionManager::deleteAction(Action* action) { - if (action) { - delete action; - } -} - -} // namespace extra2d diff --git a/Extra2D/src/action/action_special.cpp b/Extra2D/src/action/action_special.cpp deleted file mode 100644 index a3df981..0000000 --- a/Extra2D/src/action/action_special.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "extra2d/action/action_special.h" -#include "extra2d/scene/node.h" - -namespace extra2d { - -// ============================================================================ -// Speed -// ============================================================================ - -Speed* Speed::create(ActionInterval* action, float speed) { - auto* speedAction = new Speed(); - speedAction->innerAction_ = action; - speedAction->speed_ = speed; - return speedAction; -} - -Speed::~Speed() { - delete innerAction_; -} - -void Speed::startWithTarget(Node* target) { - Action::startWithTarget(target); - innerAction_->startWithTarget(target); -} - -void Speed::stop() { - innerAction_->stop(); - Action::stop(); -} - -void Speed::step(float dt) { - if (state_ != ActionState::Running) { - return; - } - innerAction_->step(dt * speed_); - if (innerAction_->isDone()) { - state_ = ActionState::Completed; - onComplete(); - } -} - -bool Speed::isDone() const { - return innerAction_->isDone(); -} - -Action* Speed::clone() const { - return Speed::create(innerAction_->clone(), speed_); -} - -Action* Speed::reverse() const { - return Speed::create(innerAction_->reverse(), speed_); -} - -// ============================================================================ -// Follow -// ============================================================================ - -Follow* Follow::create(Node* followedNode) { - return create(followedNode, Rect::Zero()); -} - -Follow* Follow::create(Node* followedNode, const Rect& boundary) { - auto* follow = new Follow(); - follow->followedNode_ = followedNode; - follow->boundary_ = boundary; - follow->boundarySet_ = (boundary != Rect::Zero()); - return follow; -} - -Follow::~Follow() { - followedNode_ = nullptr; -} - -void Follow::startWithTarget(Node* target) { - Action::startWithTarget(target); - if (target && followedNode_) { - halfScreenSize_ = Vec2(0, 0); - fullScreenSize_ = Vec2(0, 0); - - if (boundarySet_) { - leftBoundary_ = Vec2(boundary_.origin.x, 0); - rightBoundary_ = Vec2(boundary_.origin.x + boundary_.size.width, 0); - topBoundary_ = Vec2(0, boundary_.origin.y); - bottomBoundary_ = Vec2(0, boundary_.origin.y + boundary_.size.height); - } - } -} - -void Follow::stop() { - followedNode_ = nullptr; - Action::stop(); -} - -void Follow::step(float dt) { - (void)dt; - if (state_ != ActionState::Running || !followedNode_ || !target_) { - return; - } - - Vec2 pos = followedNode_->getPosition(); - - if (boundarySet_) { - pos.x = std::clamp(pos.x, leftBoundary_.x, rightBoundary_.x); - pos.y = std::clamp(pos.y, bottomBoundary_.y, topBoundary_.y); - } - - target_->setPosition(pos); -} - -bool Follow::isDone() const { - return followedNode_ == nullptr || !followedNode_->isRunning(); -} - -Action* Follow::clone() const { - return Follow::create(followedNode_, boundary_); -} - -Action* Follow::reverse() const { - return Follow::create(followedNode_, boundary_); -} - -// ============================================================================ -// TargetedAction -// ============================================================================ - -TargetedAction* TargetedAction::create(Node* target, FiniteTimeAction* action) { - auto* targeted = new TargetedAction(); - targeted->targetNode_ = target; - targeted->innerAction_ = action; - return targeted; -} - -TargetedAction::~TargetedAction() { - delete innerAction_; -} - -void TargetedAction::startWithTarget(Node* target) { - Action::startWithTarget(target); - if (targetNode_) { - innerAction_->startWithTarget(targetNode_); - } -} - -void TargetedAction::stop() { - innerAction_->stop(); - Action::stop(); -} - -void TargetedAction::step(float dt) { - if (state_ != ActionState::Running) { - return; - } - innerAction_->step(dt); - if (innerAction_->isDone()) { - state_ = ActionState::Completed; - onComplete(); - } -} - -bool TargetedAction::isDone() const { - return innerAction_->isDone(); -} - -Action* TargetedAction::clone() const { - return TargetedAction::create(targetNode_, innerAction_->clone()); -} - -Action* TargetedAction::reverse() const { - return TargetedAction::create(targetNode_, innerAction_->reverse()); -} - -} // namespace extra2d diff --git a/Extra2D/src/action/ease.cpp b/Extra2D/src/action/ease.cpp deleted file mode 100644 index aa382b9..0000000 --- a/Extra2D/src/action/ease.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "extra2d/action/ease.h" -#include - -namespace extra2d { - -#ifndef M_PI -#define M_PI 3.14159265358979323846f -#endif - -// ============================================================================ -// 线性缓动 -// ============================================================================ - -float easeLinear(float t) { - return t; -} - -// ============================================================================ -// 二次缓动 (Quad) -// ============================================================================ - -float easeInQuad(float t) { - return t * t; -} - -float easeOutQuad(float t) { - return 1.0f - (1.0f - t) * (1.0f - t); -} - -float easeInOutQuad(float t) { - return t < 0.5f ? 2.0f * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 2.0f) / 2.0f; -} - -// ============================================================================ -// 三次缓动 (Cubic) -// ============================================================================ - -float easeInCubic(float t) { - return t * t * t; -} - -float easeOutCubic(float t) { - return 1.0f - std::pow(1.0f - t, 3.0f); -} - -float easeInOutCubic(float t) { - return t < 0.5f ? 4.0f * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 3.0f) / 2.0f; -} - -// ============================================================================ -// 四次缓动 (Quart) -// ============================================================================ - -float easeInQuart(float t) { - return t * t * t * t; -} - -float easeOutQuart(float t) { - return 1.0f - std::pow(1.0f - t, 4.0f); -} - -float easeInOutQuart(float t) { - return t < 0.5f ? 8.0f * t * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 4.0f) / 2.0f; -} - -// ============================================================================ -// 五次缓动 (Quint) -// ============================================================================ - -float easeInQuint(float t) { - return t * t * t * t * t; -} - -float easeOutQuint(float t) { - return 1.0f - std::pow(1.0f - t, 5.0f); -} - -float easeInOutQuint(float t) { - return t < 0.5f ? 16.0f * t * t * t * t * t : 1.0f - std::pow(-2.0f * t + 2.0f, 5.0f) / 2.0f; -} - -// ============================================================================ -// 正弦缓动 (Sine) -// ============================================================================ - -float easeInSine(float t) { - return 1.0f - std::cos((t * M_PI) / 2.0f); -} - -float easeOutSine(float t) { - return std::sin((t * M_PI) / 2.0f); -} - -float easeInOutSine(float t) { - return -(std::cos(M_PI * t) - 1.0f) / 2.0f; -} - -// ============================================================================ -// 指数缓动 (Exponential) -// ============================================================================ - -float easeInExpo(float t) { - return t == 0.0f ? 0.0f : std::pow(2.0f, 10.0f * (t - 1.0f)); -} - -float easeOutExpo(float t) { - return t == 1.0f ? 1.0f : 1.0f - std::pow(2.0f, -10.0f * t); -} - -float easeInOutExpo(float t) { - if (t == 0.0f) return 0.0f; - if (t == 1.0f) return 1.0f; - return t < 0.5f - ? std::pow(2.0f, 20.0f * t - 10.0f) / 2.0f - : (2.0f - std::pow(2.0f, -20.0f * t + 10.0f)) / 2.0f; -} - -// ============================================================================ -// 圆形缓动 (Circular) -// ============================================================================ - -float easeInCirc(float t) { - return 1.0f - std::sqrt(1.0f - std::pow(t, 2.0f)); -} - -float easeOutCirc(float t) { - return std::sqrt(1.0f - std::pow(t - 1.0f, 2.0f)); -} - -float easeInOutCirc(float t) { - return t < 0.5f - ? (1.0f - std::sqrt(1.0f - std::pow(2.0f * t, 2.0f))) / 2.0f - : (std::sqrt(1.0f - std::pow(-2.0f * t + 2.0f, 2.0f)) + 1.0f) / 2.0f; -} - -// ============================================================================ -// 回震缓动 (Back) -// ============================================================================ - -float easeInBack(float t) { - const float c1 = 1.70158f; - const float c3 = c1 + 1.0f; - return c3 * t * t * t - c1 * t * t; -} - -float easeOutBack(float t) { - const float c1 = 1.70158f; - const float c3 = c1 + 1.0f; - return 1.0f + c3 * std::pow(t - 1.0f, 3.0f) + c1 * std::pow(t - 1.0f, 2.0f); -} - -float easeInOutBack(float t) { - const float c1 = 1.70158f; - const float c2 = c1 * 1.525f; - return t < 0.5f - ? (std::pow(2.0f * t, 2.0f) * ((c2 + 1.0f) * 2.0f * t - c2)) / 2.0f - : (std::pow(2.0f * t - 2.0f, 2.0f) * ((c2 + 1.0f) * (t * 2.0f - 2.0f) + c2) + 2.0f) / 2.0f; -} - -// ============================================================================ -// 弹性缓动 (Elastic) -// ============================================================================ - -float easeInElastic(float t) { - const float c4 = (2.0f * M_PI) / 3.0f; - if (t == 0.0f) return 0.0f; - if (t == 1.0f) return 1.0f; - return -std::pow(2.0f, 10.0f * t - 10.0f) * std::sin((t * 10.0f - 10.75f) * c4); -} - -float easeOutElastic(float t) { - const float c4 = (2.0f * M_PI) / 3.0f; - if (t == 0.0f) return 0.0f; - if (t == 1.0f) return 1.0f; - return std::pow(2.0f, -10.0f * t) * std::sin((t * 10.0f - 0.75f) * c4) + 1.0f; -} - -float easeInOutElastic(float t) { - const float c5 = (2.0f * M_PI) / 4.5f; - if (t == 0.0f) return 0.0f; - if (t == 1.0f) return 1.0f; - return t < 0.5f - ? -(std::pow(2.0f, 20.0f * t - 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f - : (std::pow(2.0f, -20.0f * t + 10.0f) * std::sin((20.0f * t - 11.125f) * c5)) / 2.0f + 1.0f; -} - -// ============================================================================ -// 弹跳缓动 (Bounce) -// ============================================================================ - -namespace { -float easeOutBounceInternal(float t) { - const float n1 = 7.5625f; - const float d1 = 2.75f; - - if (t < 1.0f / d1) { - return n1 * t * t; - } else if (t < 2.0f / d1) { - t -= 1.5f / d1; - return n1 * t * t + 0.75f; - } else if (t < 2.5f / d1) { - t -= 2.25f / d1; - return n1 * t * t + 0.9375f; - } else { - t -= 2.625f / d1; - return n1 * t * t + 0.984375f; - } -} -} - -float easeInBounce(float t) { - return 1.0f - easeOutBounceInternal(1.0f - t); -} - -float easeOutBounce(float t) { - return easeOutBounceInternal(t); -} - -float easeInOutBounce(float t) { - return t < 0.5f - ? (1.0f - easeOutBounceInternal(1.0f - 2.0f * t)) / 2.0f - : (1.0f + easeOutBounceInternal(2.0f * t - 1.0f)) / 2.0f; -} - -} // namespace extra2d diff --git a/Extra2D/src/action/finite_time_action.cpp b/Extra2D/src/action/finite_time_action.cpp deleted file mode 100644 index 41d6a7e..0000000 --- a/Extra2D/src/action/finite_time_action.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "extra2d/action/finite_time_action.h" - -namespace extra2d { - -FiniteTimeAction::FiniteTimeAction(float duration) - : duration_(duration) { -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/als_parser.cpp b/Extra2D/src/animation/als_parser.cpp deleted file mode 100644 index 73808ad..0000000 --- a/Extra2D/src/animation/als_parser.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include - -namespace extra2d { - -AlsParseResult AlsParser::parse(const std::string &filePath) { - AlsParseResult result; - - // TODO: 实现实际的 ALS 文件格式解析 - // 当前为框架实现,需要根据实际 ALS 文件格式补充解析逻辑 - // - // 解析流程: - // 1. 读取 ALS 文件内容 - // 2. 解析子动画列表: - // - 子动画 ANI 文件路径 - // - 层级 zOrder - // - 偏移量 - // 3. 组装 AlsLayerInfo 数组 - - result.success = true; - result.errorMessage = - "ALS parser framework ready - actual format parsing to be implemented"; - - return result; -} - -AlsParseResult AlsParser::parseFromMemory(const std::string &content, - const std::string &basePath) { - AlsParseResult result; - - // TODO: 从内存内容解析 - result.success = true; - - return result; -} - -std::string AlsParser::resolvePath(const std::string &relativePath) const { - if (!basePath_.empty() && !relativePath.empty() && relativePath[0] != '/') { - return basePath_ + "/" + relativePath; - } - return relativePath; -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/ani_binary_parser.cpp b/Extra2D/src/animation/ani_binary_parser.cpp deleted file mode 100644 index e7c035a..0000000 --- a/Extra2D/src/animation/ani_binary_parser.cpp +++ /dev/null @@ -1,313 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -namespace { - -// 简易二进制缓冲区读取器 -class BufferReader { -public: - BufferReader(const uint8_t *data, size_t length) - : data_(data), length_(length), pos_(0) {} - - template T read() { - if (pos_ + sizeof(T) > length_) { - return T{}; - } - T value; - std::memcpy(&value, data_ + pos_, sizeof(T)); - pos_ += sizeof(T); - return value; - } - - std::string readAsciiString(int32_t len) { - if (len <= 0 || pos_ + static_cast(len) > length_) { - return ""; - } - std::string result(reinterpret_cast(data_ + pos_), len); - pos_ += len; - return result; - } - - bool hasRemaining() const { return pos_ < length_; } - size_t remaining() const { return length_ - pos_; } - -private: - const uint8_t *data_; - size_t length_; - size_t pos_; -}; - -// 字符串转小写 -void toLower(std::string &s) { - for (auto &c : s) { - c = static_cast(std::tolower(static_cast(c))); - } -} - -} // anonymous namespace - -AniParseResult AniBinaryParser::parse(const uint8_t *data, size_t length) { - AniParseResult result; - result.clip = AnimationClip::create(); - - if (!data || length < 4) { - result.success = false; - result.errorMessage = "Invalid binary ANI data"; - return result; - } - - BufferReader reader(data, length); - - // 读取帧数和资源数 - uint16_t frameCount = reader.read(); - uint16_t resourceCount = reader.read(); - - // 读取精灵路径列表 - std::vector sprites; - sprites.reserve(resourceCount); - for (uint16_t i = 0; i < resourceCount; ++i) { - int32_t len = reader.read(); - std::string path = reader.readAsciiString(len); - toLower(path); - sprites.push_back(std::move(path)); - } - - // 读取全局参数 - uint16_t globalParamCount = reader.read(); - for (uint16_t j = 0; j < globalParamCount; ++j) { - uint16_t type = reader.read(); - switch (type) { - case static_cast(AniNodeType::Loop): - if (reader.read()) { - result.clip->setLooping(true); - } - break; - case static_cast(AniNodeType::Shadow): - if (reader.read()) { - result.clip->globalProperties().set(FramePropertyKey::Shadow, true); - } - break; - default: - break; - } - } - - // 逐帧解析 - for (uint16_t i = 0; i < frameCount; ++i) { - AnimationFrame frame; - - // 碰撞盒 - uint16_t boxCount = reader.read(); - for (uint16_t j = 0; j < boxCount; ++j) { - uint16_t boxType = reader.read(); - std::array box; - for (int m = 0; m < 6; ++m) { - box[m] = reader.read(); - } - if (boxType == static_cast(AniNodeType::DamageBox)) { - frame.damageBoxes.push_back(box); - } else if (boxType == static_cast(AniNodeType::AttackBox)) { - frame.attackBoxes.push_back(box); - } - } - - // 图片 ID 和参数 - uint16_t imgId = reader.read(); - uint16_t imgParam = reader.read(); - (void)imgParam; - - if (imgId < sprites.size()) { - frame.texturePath = sprites[imgId]; - frame.textureIndex = imgId; - - std::string resolvedPath = resolvePath(frame.texturePath); - auto spriteFrame = SpriteFrameCache::getInstance().getOrCreateFromFile( - resolvedPath, frame.textureIndex); - frame.spriteFrame = spriteFrame; - } - - // 位置 - frame.offset = Vec2(static_cast(reader.read()), - static_cast(reader.read())); - - // 帧属性 - uint16_t propertyCount = reader.read(); - for (uint16_t m = 0; m < propertyCount; ++m) { - uint16_t propType = reader.read(); - AniNodeType nodeType = static_cast(propType); - - switch (nodeType) { - case AniNodeType::Loop: - frame.properties.set(FramePropertyKey::Loop, - static_cast(reader.read())); - break; - - case AniNodeType::Shadow: - frame.properties.set(FramePropertyKey::Shadow, - static_cast(reader.read())); - break; - - case AniNodeType::Interpolation: - frame.properties.withInterpolation( - static_cast(reader.read())); - break; - - case AniNodeType::Coord: - frame.properties.set(FramePropertyKey::Coord, - static_cast(reader.read())); - break; - - case AniNodeType::ImageRate: { - float rateX = reader.read(); - float rateY = reader.read(); - frame.properties.withImageRate(Vec2(rateX, rateY)); - break; - } - - case AniNodeType::ImageRotate: - frame.properties.withImageRotate( - static_cast(reader.read())); - break; - - case AniNodeType::RGBA: { - uint32_t rgba = reader.read(); - uint8_t a = static_cast((rgba >> 24) & 0xFF); - uint8_t r = static_cast((rgba >> 16) & 0xFF); - uint8_t g = static_cast((rgba >> 8) & 0xFF); - uint8_t b = static_cast((rgba) & 0xFF); - frame.properties.withColorTint(Color::fromRGBA(r, g, b, a)); - break; - } - - case AniNodeType::GraphicEffect: { - uint16_t effectType = reader.read(); - frame.properties.set(FramePropertyKey::GraphicEffect, - static_cast(effectType)); - // MONOCHROME 额外读取 rgb - if (effectType == 5) { - reader.read(); // r - reader.read(); // g - reader.read(); // b - } - // SPACEDISTORT 额外读取 pos - else if (effectType == 6) { - reader.read(); // x - reader.read(); // y - } - break; - } - - case AniNodeType::Delay: - frame.delay = static_cast(reader.read()); - break; - - case AniNodeType::DamageType: - frame.properties.set(FramePropertyKey::DamageType, - static_cast(reader.read())); - break; - - case AniNodeType::PlaySound: { - int32_t len = reader.read(); - std::string soundPath = reader.readAsciiString(len); - frame.properties.withPlaySound(resolvePath(soundPath)); - break; - } - - case AniNodeType::SetFlag: - frame.properties.withSetFlag(reader.read()); - break; - - case AniNodeType::FlipType: - frame.properties.set(FramePropertyKey::FlipType, - static_cast(reader.read())); - break; - - case AniNodeType::LoopStart: - frame.properties.set(FramePropertyKey::LoopStart, true); - break; - - case AniNodeType::LoopEnd: - frame.properties.set(FramePropertyKey::LoopEnd, reader.read()); - break; - - case AniNodeType::Clip: { - std::vector clipRegion = { - static_cast(reader.read()), - static_cast(reader.read()), - static_cast(reader.read()), - static_cast(reader.read())}; - frame.properties.set(FramePropertyKey::ClipRegion, clipRegion); - break; - } - - default: - // 未知类型 - 跳过 - break; - } - } - - result.clip->addFrame(std::move(frame)); - } - - result.success = true; - return result; -} - -AniParseResult AniBinaryParser::parseFromFile(const std::string &filePath) { - std::ifstream file(filePath, std::ios::binary | std::ios::ate); - if (!file.is_open()) { - AniParseResult result; - result.success = false; - result.errorMessage = "Cannot open binary ANI file: " + filePath; - return result; - } - - auto size = file.tellg(); - file.seekg(0, std::ios::beg); - - std::vector buffer(static_cast(size)); - file.read(reinterpret_cast(buffer.data()), size); - file.close(); - - // 提取基础路径 - auto lastSlash = filePath.find_last_of("/\\"); - if (lastSlash != std::string::npos && basePath_.empty()) { - basePath_ = filePath.substr(0, lastSlash); - } - - auto result = parse(buffer.data(), buffer.size()); - - if (result.clip) { - result.clip->setSourcePath(filePath); - std::string name = (lastSlash != std::string::npos) - ? filePath.substr(lastSlash + 1) - : filePath; - result.clip->setName(name); - } - - return result; -} - -std::string -AniBinaryParser::resolvePath(const std::string &relativePath) const { - std::string resolved = relativePath; - if (pathResolver_) { - resolved = pathResolver_(relativePath); - } - - if (!basePath_.empty() && !resolved.empty() && resolved[0] != '/') { - if (resolved.size() < 2 || resolved[1] != ':') { - resolved = basePath_ + "/" + resolved; - } - } - - return resolved; -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/ani_parser.cpp b/Extra2D/src/animation/ani_parser.cpp deleted file mode 100644 index 29fd3b1..0000000 --- a/Extra2D/src/animation/ani_parser.cpp +++ /dev/null @@ -1,428 +0,0 @@ -#include -#include -#include -#include -#include - -namespace extra2d { - -namespace { - -std::string trim(const std::string &s) { - auto start = s.find_first_not_of(" \t\r\n"); - if (start == std::string::npos) - return ""; - auto end = s.find_last_not_of(" \t\r\n"); - return s.substr(start, end - start + 1); -} - -std::string stripBackticks(const std::string &s) { - std::string t = trim(s); - if (t.size() >= 2 && t.front() == '`' && t.back() == '`') { - return t.substr(1, t.size() - 2); - } - return t; -} - -std::vector splitWhitespace(const std::string &s) { - std::vector tokens; - std::istringstream iss(s); - std::string token; - while (iss >> token) { - tokens.push_back(token); - } - return tokens; -} - -bool lineStartsWith(const std::string &trimmed, const std::string &tag) { - return trimmed.find(tag) == 0; -} - -std::string valueAfterTag(const std::string &trimmed, const std::string &tag) { - auto pos = trimmed.find(tag); - if (pos == std::string::npos) - return ""; - return trim(trimmed.substr(pos + tag.size())); -} - -// 读取标签后的值,如果同行没有则从流中读取下一行 -std::string readValue(const std::string &trimmed, const std::string &tag, - std::istringstream &stream) { - std::string val = valueAfterTag(trimmed, tag); - if (val.empty()) { - std::string nextLine; - if (std::getline(stream, nextLine)) { - val = trim(nextLine); - } - } - return val; -} - -} // anonymous namespace - -AniParseResult AniParser::parse(const std::string &filePath) { - AniParseResult result; - - std::ifstream file(filePath); - if (!file.is_open()) { - result.success = false; - result.errorMessage = "Cannot open ANI file: " + filePath; - return result; - } - - std::string content((std::istreambuf_iterator(file)), - std::istreambuf_iterator()); - file.close(); - - // 提取基础路径 - auto lastSlash = filePath.find_last_of("/\\"); - std::string basePath; - if (lastSlash != std::string::npos) { - basePath = filePath.substr(0, lastSlash); - } - - if (basePath_.empty() && !basePath.empty()) { - basePath_ = basePath; - } - - result = parseFromMemory(content, basePath_.empty() ? basePath : basePath_); - - if (result.clip) { - result.clip->setSourcePath(filePath); - std::string name = (lastSlash != std::string::npos) - ? filePath.substr(lastSlash + 1) - : filePath; - result.clip->setName(name); - } - - return result; -} - -AniParseResult AniParser::parseFromMemory(const std::string &content, - const std::string &basePath) { - AniParseResult result; - result.clip = AnimationClip::create(); - - if (!basePath.empty() && basePath_.empty()) { - basePath_ = basePath; - } - - std::istringstream stream(content); - std::string line; - - AnimationFrame currentFrame; - bool inFrame = false; - bool hasCurrentFrame = false; - - // [IMAGE] 标签的多行读取状态 - bool pendingImage = false; - std::string imagePath; - - auto finalizeFrame = [&]() { - if (!hasCurrentFrame) - return; - if (!currentFrame.texturePath.empty()) { - std::string resolvedPath = resolvePath(currentFrame.texturePath); - auto spriteFrame = SpriteFrameCache::getInstance().getOrCreateFromFile( - resolvedPath, currentFrame.textureIndex); - currentFrame.spriteFrame = spriteFrame; - } - result.clip->addFrame(std::move(currentFrame)); - currentFrame = AnimationFrame(); - hasCurrentFrame = false; - }; - - while (std::getline(stream, line)) { - std::string trimmed = trim(line); - - if (trimmed.empty()) - continue; - if (trimmed[0] == '#') - continue; - - // [FRAME MAX] - if (lineStartsWith(trimmed, "[FRAME MAX]")) { - readValue(trimmed, "[FRAME MAX]", stream); - continue; - } - - // [FRAMEXXX] - 新帧开始 - if (trimmed.size() >= 7 && trimmed.substr(0, 6) == "[FRAME" && - trimmed.back() == ']' && !lineStartsWith(trimmed, "[FRAME MAX]")) { - finalizeFrame(); - currentFrame = AnimationFrame(); - inFrame = true; - hasCurrentFrame = true; - pendingImage = false; - continue; - } - - if (!inFrame) { - // 全局属性 - if (lineStartsWith(trimmed, "[LOOP]")) { - result.clip->setLooping(true); - } else if (lineStartsWith(trimmed, "[SHADOW]")) { - result.clip->globalProperties().set(FramePropertyKey::Shadow, true); - } - continue; - } - - // === 帧内属性解析 === - - // [IMAGE] - 图片路径和索引(跨行) - if (lineStartsWith(trimmed, "[IMAGE]")) { - std::string val = valueAfterTag(trimmed, "[IMAGE]"); - if (!val.empty()) { - auto tokens = splitWhitespace(val); - imagePath = stripBackticks(tokens[0]); - if (tokens.size() >= 2) { - currentFrame.texturePath = imagePath; - currentFrame.textureIndex = std::stoi(tokens[1]); - pendingImage = false; - } else { - pendingImage = true; - } - } else { - pendingImage = true; - imagePath.clear(); - } - continue; - } - - // [IMAGE] 后续行 - if (pendingImage) { - if (imagePath.empty()) { - imagePath = stripBackticks(trimmed); - } else { - currentFrame.texturePath = imagePath; - currentFrame.textureIndex = std::stoi(trimmed); - pendingImage = false; - } - continue; - } - - // [IMAGE POS] - if (lineStartsWith(trimmed, "[IMAGE POS]")) { - std::string val = readValue(trimmed, "[IMAGE POS]", stream); - auto tokens = splitWhitespace(val); - if (tokens.size() >= 2) { - currentFrame.offset = Vec2(static_cast(std::stoi(tokens[0])), - static_cast(std::stoi(tokens[1]))); - } - continue; - } - - // [DELAY] - if (lineStartsWith(trimmed, "[DELAY]")) { - std::string val = readValue(trimmed, "[DELAY]", stream); - currentFrame.delay = static_cast(std::stoi(trim(val))); - continue; - } - - // [DAMAGE TYPE] - if (lineStartsWith(trimmed, "[DAMAGE TYPE]")) { - std::string val = readValue(trimmed, "[DAMAGE TYPE]", stream); - val = stripBackticks(val); - int damageType = 0; - if (val == "SUPERARMOR") - damageType = 1; - else if (val == "UNBREAKABLE") - damageType = 2; - currentFrame.properties.set(FramePropertyKey::DamageType, damageType); - continue; - } - - // [DAMAGE BOX] - if (lineStartsWith(trimmed, "[DAMAGE BOX]")) { - std::string val = readValue(trimmed, "[DAMAGE BOX]", stream); - auto tokens = splitWhitespace(val); - if (tokens.size() >= 6) { - std::array box; - for (int i = 0; i < 6; ++i) { - box[i] = std::stoi(tokens[i]); - } - currentFrame.damageBoxes.push_back(box); - } - continue; - } - - // [ATTACK BOX] - if (lineStartsWith(trimmed, "[ATTACK BOX]")) { - std::string val = readValue(trimmed, "[ATTACK BOX]", stream); - auto tokens = splitWhitespace(val); - if (tokens.size() >= 6) { - std::array box; - for (int i = 0; i < 6; ++i) { - box[i] = std::stoi(tokens[i]); - } - currentFrame.attackBoxes.push_back(box); - } - continue; - } - - // [SET FLAG] - if (lineStartsWith(trimmed, "[SET FLAG]")) { - std::string val = readValue(trimmed, "[SET FLAG]", stream); - currentFrame.properties.withSetFlag(std::stoi(trim(val))); - continue; - } - - // [PLAY SOUND] - if (lineStartsWith(trimmed, "[PLAY SOUND]")) { - std::string val = readValue(trimmed, "[PLAY SOUND]", stream); - currentFrame.properties.withPlaySound(resolvePath(stripBackticks(val))); - continue; - } - - // [IMAGE RATE] - if (lineStartsWith(trimmed, "[IMAGE RATE]")) { - std::string val = readValue(trimmed, "[IMAGE RATE]", stream); - auto tokens = splitWhitespace(val); - if (tokens.size() >= 2) { - currentFrame.properties.withImageRate( - Vec2(std::stof(tokens[0]), std::stof(tokens[1]))); - } - continue; - } - - // [IMAGE ROTATE] - if (lineStartsWith(trimmed, "[IMAGE ROTATE]")) { - std::string val = readValue(trimmed, "[IMAGE ROTATE]", stream); - currentFrame.properties.withImageRotate(std::stof(trim(val))); - continue; - } - - // [RGBA] - if (lineStartsWith(trimmed, "[RGBA]")) { - std::string val = readValue(trimmed, "[RGBA]", stream); - auto tokens = splitWhitespace(val); - if (tokens.size() >= 4) { - Color color = - Color::fromRGBA(static_cast(std::stoi(tokens[0])), - static_cast(std::stoi(tokens[1])), - static_cast(std::stoi(tokens[2])), - static_cast(std::stoi(tokens[3]))); - currentFrame.properties.withColorTint(color); - } - continue; - } - - // [INTERPOLATION] - if (lineStartsWith(trimmed, "[INTERPOLATION]")) { - currentFrame.properties.withInterpolation(true); - continue; - } - - // [LOOP] - if (lineStartsWith(trimmed, "[LOOP]")) { - currentFrame.properties.set(FramePropertyKey::Loop, true); - continue; - } - - // [SHADOW] - if (lineStartsWith(trimmed, "[SHADOW]")) { - currentFrame.properties.set(FramePropertyKey::Shadow, true); - continue; - } - - // [FLIP TYPE] - if (lineStartsWith(trimmed, "[FLIP TYPE]")) { - std::string val = readValue(trimmed, "[FLIP TYPE]", stream); - val = stripBackticks(val); - int flipType = 0; - if (val == "HORIZON") - flipType = 1; - else if (val == "VERTICAL") - flipType = 2; - else if (val == "ALL") - flipType = 3; - else - flipType = std::stoi(val); - currentFrame.properties.set(FramePropertyKey::FlipType, flipType); - continue; - } - - // [COORD] - if (lineStartsWith(trimmed, "[COORD]")) { - std::string val = readValue(trimmed, "[COORD]", stream); - currentFrame.properties.set(FramePropertyKey::Coord, - std::stoi(trim(val))); - continue; - } - - // [GRAPHIC EFFECT] - if (lineStartsWith(trimmed, "[GRAPHIC EFFECT]")) { - std::string val = readValue(trimmed, "[GRAPHIC EFFECT]", stream); - val = stripBackticks(val); - int effectType = 0; - if (val == "DODGE") - effectType = 1; - else if (val == "LINEARDODGE") - effectType = 2; - else if (val == "DARK") - effectType = 3; - else if (val == "XOR") - effectType = 4; - else if (val == "MONOCHROME") - effectType = 5; - else if (val == "SPACEDISTORT") - effectType = 6; - else - effectType = std::stoi(val); - currentFrame.properties.set(FramePropertyKey::GraphicEffect, effectType); - continue; - } - - // [CLIP] - if (lineStartsWith(trimmed, "[CLIP]")) { - std::string val = readValue(trimmed, "[CLIP]", stream); - auto tokens = splitWhitespace(val); - if (tokens.size() >= 4) { - std::vector clipRegion = { - std::stoi(tokens[0]), std::stoi(tokens[1]), std::stoi(tokens[2]), - std::stoi(tokens[3])}; - currentFrame.properties.set(FramePropertyKey::ClipRegion, clipRegion); - } - continue; - } - - // [LOOP START] - if (lineStartsWith(trimmed, "[LOOP START]")) { - currentFrame.properties.set(FramePropertyKey::LoopStart, true); - continue; - } - - // [LOOP END] - if (lineStartsWith(trimmed, "[LOOP END]")) { - std::string val = readValue(trimmed, "[LOOP END]", stream); - currentFrame.properties.set(FramePropertyKey::LoopEnd, - std::stoi(trim(val))); - continue; - } - - // 未识别标签 - 静默忽略 - } - - // 保存最后一帧 - finalizeFrame(); - - result.success = true; - return result; -} - -std::string AniParser::resolvePath(const std::string &relativePath) const { - std::string resolved = relativePath; - if (pathResolver_) { - resolved = pathResolver_(relativePath); - } - - if (!basePath_.empty() && !resolved.empty() && resolved[0] != '/') { - if (resolved.size() < 2 || resolved[1] != ':') { - resolved = basePath_ + "/" + resolved; - } - } - - return resolved; -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/animated_sprite.cpp b/Extra2D/src/animation/animated_sprite.cpp deleted file mode 100644 index 4619df3..0000000 --- a/Extra2D/src/animation/animated_sprite.cpp +++ /dev/null @@ -1,290 +0,0 @@ -#include -#include - -namespace extra2d { - -const std::vector> AnimatedSprite::emptyBoxes_; -const std::string AnimatedSprite::emptyString_; - -AnimatedSprite::AnimatedSprite() { - controller_.setFrameChangeCallback( - [this](size_t oldIdx, size_t newIdx, const AnimationFrame &frame) { - onFrameChanged(oldIdx, newIdx, frame); - }); -} - -// ------ 静态工厂 ------ - -Ptr AnimatedSprite::create() { - return makePtr(); -} - -Ptr AnimatedSprite::create(Ptr clip) { - auto sprite = makePtr(); - sprite->setAnimationClip(std::move(clip)); - return sprite; -} - -Ptr AnimatedSprite::create(const std::string &aniFilePath) { - auto sprite = makePtr(); - sprite->loadAnimation(aniFilePath); - return sprite; -} - -// ------ 动画绑定 ------ - -void AnimatedSprite::setAnimationClip(Ptr clip) { - controller_.setClip(clip); - if (clip && !clip->empty()) { - applyFrame(clip->getFrame(0)); - } -} - -void AnimatedSprite::loadAnimation(const std::string &aniFilePath) { - auto clip = AnimationCache::getInstance().loadClip(aniFilePath); - if (clip) { - setAnimationClip(clip); - } -} - -Ptr AnimatedSprite::getAnimationClip() const { - return controller_.getClip(); -} - -// ------ 动画字典 ------ - -void AnimatedSprite::addAnimation(const std::string &name, - Ptr clip) { - if (clip) { - animations_[name] = std::move(clip); - } -} - -void AnimatedSprite::play(const std::string &name, bool loop) { - auto it = animations_.find(name); - if (it == animations_.end()) - return; - - currentAnimationName_ = name; - // 精灵图动画不应覆盖节点的 position/scale/rotation - applyFrameTransform_ = false; - setAnimationClip(it->second); - setLooping(loop); - play(); -} - -bool AnimatedSprite::hasAnimation(const std::string &name) const { - return animations_.find(name) != animations_.end(); -} - -Ptr AnimatedSprite::getAnimation(const std::string &name) const { - auto it = animations_.find(name); - if (it != animations_.end()) - return it->second; - return nullptr; -} - -const std::string &AnimatedSprite::getCurrentAnimationName() const { - return currentAnimationName_; -} - -// ------ 播放控制 ------ - -void AnimatedSprite::play() { controller_.play(); } -void AnimatedSprite::pause() { controller_.pause(); } -void AnimatedSprite::resume() { controller_.resume(); } -void AnimatedSprite::stop() { controller_.stop(); } -void AnimatedSprite::reset() { controller_.reset(); } -bool AnimatedSprite::isPlaying() const { return controller_.isPlaying(); } -bool AnimatedSprite::isPaused() const { return controller_.isPaused(); } -bool AnimatedSprite::isStopped() const { return controller_.isStopped(); } - -// ------ 属性控制 ------ - -void AnimatedSprite::setLooping(bool loop) { controller_.setLooping(loop); } -bool AnimatedSprite::isLooping() const { return controller_.isLooping(); } -void AnimatedSprite::setPlaybackSpeed(float speed) { - controller_.setPlaybackSpeed(speed); -} -float AnimatedSprite::getPlaybackSpeed() const { - return controller_.getPlaybackSpeed(); -} - -// ------ 帧控制 ------ - -void AnimatedSprite::setFrameIndex(size_t index) { - controller_.setFrameIndex(index); -} -size_t AnimatedSprite::getCurrentFrameIndex() const { - return controller_.getCurrentFrameIndex(); -} -size_t AnimatedSprite::getTotalFrames() const { - return controller_.getTotalFrames(); -} -void AnimatedSprite::nextFrame() { controller_.nextFrame(); } -void AnimatedSprite::prevFrame() { controller_.prevFrame(); } - -// ------ 帧范围限制 ------ - -void AnimatedSprite::setFrameRange(int start, int end) { - frameRangeStart_ = start; - frameRangeEnd_ = end; - - // 确保当前帧在新的范围内 - auto clip = controller_.getClip(); - if (clip && !clip->empty()) { - size_t currentFrame = controller_.getCurrentFrameIndex(); - size_t minFrame = static_cast(start); - size_t maxFrame = - (end < 0) ? clip->getFrameCount() - 1 : static_cast(end); - - if (currentFrame < minFrame || currentFrame > maxFrame) { - controller_.setFrameIndex(minFrame); - } - } -} - -std::pair AnimatedSprite::getFrameRange() const { - return {frameRangeStart_, frameRangeEnd_}; -} - -void AnimatedSprite::clearFrameRange() { - frameRangeStart_ = 0; - frameRangeEnd_ = -1; -} - -bool AnimatedSprite::hasFrameRange() const { return frameRangeEnd_ >= 0; } - -// ------ 回调 ------ - -void AnimatedSprite::setCompletionCallback( - AnimationController::CompletionCallback cb) { - controller_.setCompletionCallback(std::move(cb)); -} - -void AnimatedSprite::setKeyframeCallback( - AnimationController::KeyframeCallback cb) { - controller_.setKeyframeCallback(std::move(cb)); -} - -void AnimatedSprite::setSoundTriggerCallback( - AnimationController::SoundTriggerCallback cb) { - controller_.setSoundTriggerCallback(std::move(cb)); -} - -// ------ 碰撞盒访问 ------ - -const std::vector> & -AnimatedSprite::getCurrentDamageBoxes() const { - auto clip = controller_.getClip(); - if (!clip || clip->empty()) - return emptyBoxes_; - return clip->getFrame(controller_.getCurrentFrameIndex()).damageBoxes; -} - -const std::vector> & -AnimatedSprite::getCurrentAttackBoxes() const { - auto clip = controller_.getClip(); - if (!clip || clip->empty()) - return emptyBoxes_; - return clip->getFrame(controller_.getCurrentFrameIndex()).attackBoxes; -} - -// ------ 生命周期 ------ - -void AnimatedSprite::onEnter() { - Sprite::onEnter(); - if (autoPlay_ && controller_.getClip() && !controller_.getClip()->empty()) { - play(); - } -} - -void AnimatedSprite::onUpdate(float dt) { - Sprite::onUpdate(dt); - - // 保存更新前的帧索引 - size_t prevFrameIdx = controller_.getCurrentFrameIndex(); - - controller_.update(dt); - - // 应用帧范围限制 - if (hasFrameRange() && controller_.isPlaying()) { - size_t currentFrame = controller_.getCurrentFrameIndex(); - size_t minFrame = static_cast(frameRangeStart_); - size_t maxFrame = static_cast(frameRangeEnd_); - - auto clip = controller_.getClip(); - if (clip && !clip->empty()) { - // 确保范围有效 - if (maxFrame >= clip->getFrameCount()) { - maxFrame = clip->getFrameCount() - 1; - } - - // 如果超出范围,回到起始帧 - if (currentFrame < minFrame || currentFrame > maxFrame) { - controller_.setFrameIndex(minFrame); - } - } - } - - // 插值处理 - if (controller_.isInterpolating()) { - auto clip = controller_.getClip(); - if (clip) { - size_t idx = controller_.getCurrentFrameIndex(); - if (idx + 1 < clip->getFrameCount()) { - auto props = InterpolationEngine::interpolate( - clip->getFrame(idx), clip->getFrame(idx + 1), - controller_.getInterpolationFactor()); - // 仅更新插值属性,不覆盖帧纹理 - Sprite::setColor(props.color); - } - } - } -} - -// ------ 内部方法 ------ - -void AnimatedSprite::onFrameChanged(size_t /*oldIdx*/, size_t /*newIdx*/, - const AnimationFrame &frame) { - applyFrame(frame); -} - -void AnimatedSprite::applyFrame(const AnimationFrame &frame) { - // 更新纹理 - if (frame.spriteFrame && frame.spriteFrame->isValid()) { - Sprite::setTexture(frame.spriteFrame->getTexture()); - Sprite::setTextureRect(frame.spriteFrame->getRect()); - } - - // 帧变换仅在 ANI 动画模式下应用;精灵图模式跳过,避免覆盖节点世界坐标 - if (applyFrameTransform_) { - // 应用帧偏移(作为精灵位置) - Node::setPosition(frame.offset); - - // 应用缩放 - Vec2 scale = frame.getEffectiveScale(); - Node::setScale(scale); - - // 应用旋转 - float rotation = frame.getEffectiveRotation(); - Node::setRotation(rotation); - - // 应用翻转 - auto flipType = frame.properties.get(FramePropertyKey::FlipType); - if (flipType.has_value()) { - int flip = flipType.value(); - Sprite::setFlipX(flip == 1 || flip == 3); - Sprite::setFlipY(flip == 2 || flip == 3); - } else { - Sprite::setFlipX(false); - Sprite::setFlipY(false); - } - } - - // 颜色始终应用 - Color color = frame.getEffectiveColor(); - Sprite::setColor(color); -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/animation_cache.cpp b/Extra2D/src/animation/animation_cache.cpp deleted file mode 100644 index 95bcff2..0000000 --- a/Extra2D/src/animation/animation_cache.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include -#include -#include - -namespace extra2d { - -namespace { - -// 检测文件是否为文本格式 ANI -// 文本格式以 '#' 或 '[' 开头(跳过空白后) -bool isTextFormat(const std::string &filePath) { - std::ifstream file(filePath, std::ios::binary); - if (!file.is_open()) - return false; - - // 读取前几个字节判断 - char buf[16] = {}; - file.read(buf, sizeof(buf)); - auto bytesRead = file.gcount(); - file.close(); - - // 跳过 BOM 和空白 - size_t pos = 0; - // UTF-8 BOM - if (bytesRead >= 3 && static_cast(buf[0]) == 0xEF && - static_cast(buf[1]) == 0xBB && - static_cast(buf[2]) == 0xBF) { - pos = 3; - } - - // 跳过空白 - while (pos < static_cast(bytesRead) && - (buf[pos] == ' ' || buf[pos] == '\t' || buf[pos] == '\r' || - buf[pos] == '\n')) { - ++pos; - } - - if (pos >= static_cast(bytesRead)) - return false; - - // 文本 ANI 文件以 '#' (注释/PVF_File) 或 '[' (标签) 开头 - return buf[pos] == '#' || buf[pos] == '['; -} - -} // anonymous namespace - -Ptr AnimationCache::loadClip(const std::string &aniFilePath) { - // 先检查缓存 - { - std::lock_guard lock(mutex_); - auto it = clips_.find(aniFilePath); - if (it != clips_.end()) { - return it->second; - } - } - - // 提取基础路径 - std::string basePath; - auto lastSlash = aniFilePath.find_last_of("/\\"); - if (lastSlash != std::string::npos) { - basePath = aniFilePath.substr(0, lastSlash); - } - - AniParseResult result; - - if (isTextFormat(aniFilePath)) { - // 文本格式 - AniParser parser; - if (pathResolver_) { - parser.setPathResolver(pathResolver_); - } - if (!basePath.empty()) { - parser.setBasePath(basePath); - } - result = parser.parse(aniFilePath); - } else { - // 二进制格式 - AniBinaryParser parser; - if (pathResolver_) { - parser.setPathResolver(pathResolver_); - } - if (!basePath.empty()) { - parser.setBasePath(basePath); - } - result = parser.parseFromFile(aniFilePath); - } - - if (!result.success || !result.clip) { - return nullptr; - } - - // 添加到缓存 - { - std::lock_guard lock(mutex_); - clips_[aniFilePath] = result.clip; - } - - return result.clip; -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/animation_clip.cpp b/Extra2D/src/animation/animation_clip.cpp deleted file mode 100644 index a37a281..0000000 --- a/Extra2D/src/animation/animation_clip.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -namespace extra2d { - -// AnimationClip 的实现全部在头文件中以内联方式完成 -// 此文件保留用于未来可能需要的非内联实现 - -} // namespace extra2d diff --git a/Extra2D/src/animation/animation_controller.cpp b/Extra2D/src/animation/animation_controller.cpp deleted file mode 100644 index 8466510..0000000 --- a/Extra2D/src/animation/animation_controller.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include - -namespace extra2d { - -void AnimationController::setClip(Ptr clip) { - clip_ = std::move(clip); - currentFrameIndex_ = 0; - accumulatedTime_ = 0.0f; - interpolating_ = false; - interpolationFactor_ = 0.0f; - state_ = AnimPlayState::Stopped; -} - -void AnimationController::play() { - if (!clip_ || clip_->empty()) - return; - state_ = AnimPlayState::Playing; -} - -void AnimationController::pause() { - if (state_ == AnimPlayState::Playing) { - state_ = AnimPlayState::Paused; - } -} - -void AnimationController::resume() { - if (state_ == AnimPlayState::Paused) { - state_ = AnimPlayState::Playing; - } -} - -void AnimationController::stop() { - state_ = AnimPlayState::Stopped; - accumulatedTime_ = 0.0f; - interpolating_ = false; - interpolationFactor_ = 0.0f; -} - -void AnimationController::reset() { - stop(); - if (clip_ && !clip_->empty()) { - advanceFrame(0); - } -} - -void AnimationController::setFrameIndex(size_t index) { - if (!clip_ || index >= clip_->getFrameCount()) - return; - accumulatedTime_ = 0.0f; - advanceFrame(index); -} - -void AnimationController::nextFrame() { - if (!clip_ || clip_->empty()) - return; - size_t next = currentFrameIndex_ + 1; - if (next >= clip_->getFrameCount()) { - if (isLooping()) { - next = 0; - } else { - return; - } - } - accumulatedTime_ = 0.0f; - advanceFrame(next); -} - -void AnimationController::prevFrame() { - if (!clip_ || clip_->empty()) - return; - if (currentFrameIndex_ > 0) { - accumulatedTime_ = 0.0f; - advanceFrame(currentFrameIndex_ - 1); - } else if (isLooping()) { - accumulatedTime_ = 0.0f; - advanceFrame(clip_->getFrameCount() - 1); - } -} - -void AnimationController::update(float dt) { - if (state_ != AnimPlayState::Playing) - return; - if (!clip_ || clip_->empty()) - return; - - // 累加时间(转换为毫秒) - float dt_ms = dt * 1000.0f * playbackSpeed_; - accumulatedTime_ += dt_ms; - - const AnimationFrame ¤tFrame = clip_->getFrame(currentFrameIndex_); - float frameDelay = currentFrame.delay; - - // 更新插值状态 - updateInterpolation(); - - // 循环处理:支持一次跳过多帧(与原始 ANI 系统行为一致) - while (accumulatedTime_ >= frameDelay) { - accumulatedTime_ -= frameDelay; - - size_t totalFrames = clip_->getFrameCount(); - - if (currentFrameIndex_ < totalFrames - 1) { - // 推进到下一帧 - advanceFrame(currentFrameIndex_ + 1); - } else { - // 最后一帧播放完毕 - if (isLooping()) { - advanceFrame(0); - } else { - // 动画结束 - state_ = AnimPlayState::Stopped; - if (onComplete_) { - onComplete_(); - } - return; - } - } - - // 更新下一帧的延迟 - frameDelay = clip_->getFrame(currentFrameIndex_).delay; - } - - // 更新插值因子 - updateInterpolation(); -} - -size_t AnimationController::getTotalFrames() const { - return clip_ ? clip_->getFrameCount() : 0; -} - -const AnimationFrame &AnimationController::getCurrentFrame() const { - assert(clip_ && currentFrameIndex_ < clip_->getFrameCount()); - return clip_->getFrame(currentFrameIndex_); -} - -bool AnimationController::isLooping() const { - if (hasLoopOverride_) - return loopOverride_; - return clip_ ? clip_->isLooping() : false; -} - -void AnimationController::setLooping(bool loop) { - hasLoopOverride_ = true; - loopOverride_ = loop; -} - -void AnimationController::advanceFrame(size_t newIndex) { - if (!clip_ || newIndex >= clip_->getFrameCount()) - return; - - size_t oldIndex = currentFrameIndex_; - currentFrameIndex_ = newIndex; - - const AnimationFrame &frame = clip_->getFrame(newIndex); - - // 触发帧变更回调 - if (onFrameChange_) { - onFrameChange_(oldIndex, newIndex, frame); - } - - // 处理帧属性(关键帧、音效等) - processFrameProperties(frame); -} - -void AnimationController::processFrameProperties(const AnimationFrame &frame) { - const auto &props = frame.properties; - - // 关键帧回调 - if (props.has(FramePropertyKey::SetFlag)) { - auto flagIndex = props.get(FramePropertyKey::SetFlag); - if (flagIndex.has_value() && onKeyframe_) { - onKeyframe_(flagIndex.value()); - } - } - - // 音效触发 - if (props.has(FramePropertyKey::PlaySound)) { - auto soundPath = props.get(FramePropertyKey::PlaySound); - if (soundPath.has_value() && onSoundTrigger_) { - onSoundTrigger_(soundPath.value()); - } - } -} - -void AnimationController::updateInterpolation() { - if (!clip_ || clip_->empty()) { - interpolating_ = false; - interpolationFactor_ = 0.0f; - return; - } - - const AnimationFrame ¤tFrame = clip_->getFrame(currentFrameIndex_); - - if (currentFrame.hasInterpolation() && - currentFrameIndex_ + 1 < clip_->getFrameCount()) { - interpolating_ = true; - float frameDelay = currentFrame.delay; - interpolationFactor_ = - (frameDelay > 0.0f) - ? math::clamp(accumulatedTime_ / frameDelay, 0.0f, 1.0f) - : 0.0f; - } else { - interpolating_ = false; - interpolationFactor_ = 0.0f; - } -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/animation_frame.cpp b/Extra2D/src/animation/animation_frame.cpp deleted file mode 100644 index f4396dd..0000000 --- a/Extra2D/src/animation/animation_frame.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -namespace extra2d { - -// AnimationFrame 的实现全部在头文件中以内联方式完成 -// 此文件保留用于未来可能需要的非内联实现 - -} // namespace extra2d diff --git a/Extra2D/src/animation/animation_node.cpp b/Extra2D/src/animation/animation_node.cpp deleted file mode 100644 index 3b8788d..0000000 --- a/Extra2D/src/animation/animation_node.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include -#include - -namespace extra2d { - -const std::vector> AnimationNode::emptyBoxes_; - -// ============================================================================ -// 构造 -// ============================================================================ - -AnimationNode::AnimationNode() { setupControllerCallbacks(); } - -// ============================================================================ -// 静态工厂 -// ============================================================================ - -Ptr AnimationNode::create() { return makePtr(); } - -Ptr AnimationNode::create(Ptr clip) { - auto node = makePtr(); - node->setClip(std::move(clip)); - return node; -} - -Ptr AnimationNode::create(const std::string &aniFilePath) { - auto node = makePtr(); - node->loadFromFile(aniFilePath); - return node; -} - -// ============================================================================ -// 动画数据 -// ============================================================================ - -void AnimationNode::setClip(Ptr clip) { - controller_.setClip(clip); - if (clip && !clip->empty()) { - // 预加载所有帧的 SpriteFrame - std::vector frames; - frames.reserve(clip->getFrameCount()); - for (size_t i = 0; i < clip->getFrameCount(); ++i) { - frames.push_back(clip->getFrame(i)); - } - frameRenderer_.preloadFrames(frames); - } else { - frameRenderer_.releaseFrames(); - } -} - -Ptr AnimationNode::getClip() const { - return controller_.getClip(); -} - -bool AnimationNode::loadFromFile(const std::string &aniFilePath) { - auto clip = AnimationCache::getInstance().loadClip(aniFilePath); - if (clip) { - setClip(clip); - return true; - } - return false; -} - -// ============================================================================ -// 播放控制 -// ============================================================================ - -void AnimationNode::play() { controller_.play(); } -void AnimationNode::pause() { controller_.pause(); } -void AnimationNode::resume() { controller_.resume(); } -void AnimationNode::stop() { controller_.stop(); } -void AnimationNode::reset() { controller_.reset(); } - -bool AnimationNode::isPlaying() const { return controller_.isPlaying(); } -bool AnimationNode::isPaused() const { return controller_.isPaused(); } -bool AnimationNode::isStopped() const { return controller_.isStopped(); } - -void AnimationNode::setPlaybackSpeed(float speed) { - controller_.setPlaybackSpeed(speed); -} -float AnimationNode::getPlaybackSpeed() const { - return controller_.getPlaybackSpeed(); -} -void AnimationNode::setLooping(bool loop) { controller_.setLooping(loop); } -bool AnimationNode::isLooping() const { return controller_.isLooping(); } - -// ============================================================================ -// 帧控制 -// ============================================================================ - -void AnimationNode::setFrameIndex(size_t index) { - controller_.setFrameIndex(index); -} -size_t AnimationNode::getCurrentFrameIndex() const { - return controller_.getCurrentFrameIndex(); -} -size_t AnimationNode::getTotalFrames() const { - return controller_.getTotalFrames(); -} - -// ============================================================================ -// 事件回调 -// ============================================================================ - -void AnimationNode::setKeyframeCallback(KeyframeHitCallback callback) { - controller_.setKeyframeCallback( - [this, cb = std::move(callback)](int flagIndex) { - if (cb) - cb(flagIndex); - AnimationEvent evt; - evt.type = AnimationEventType::KeyframeHit; - evt.frameIndex = controller_.getCurrentFrameIndex(); - evt.keyframeFlag = flagIndex; - evt.source = this; - dispatchEvent(evt); - }); -} - -void AnimationNode::setCompletionCallback(AnimationCompleteCallback callback) { - controller_.setCompletionCallback([this, cb = std::move(callback)]() { - if (cb) - cb(); - AnimationEvent evt; - evt.type = AnimationEventType::AnimationEnd; - evt.frameIndex = controller_.getCurrentFrameIndex(); - evt.source = this; - dispatchEvent(evt); - }); -} - -void AnimationNode::setFrameChangeCallback( - AnimationController::FrameChangeCallback callback) { - // 保存外部回调,在 setupControllerCallbacks 中已设置内部回调 - // 需要重新绑定,将两者合并 - controller_.setFrameChangeCallback( - [this, cb = std::move(callback)](size_t oldIdx, size_t newIdx, - const AnimationFrame &frame) { - if (cb) - cb(oldIdx, newIdx, frame); - AnimationEvent evt; - evt.type = AnimationEventType::FrameChanged; - evt.frameIndex = newIdx; - evt.previousFrameIndex = oldIdx; - evt.source = this; - dispatchEvent(evt); - }); -} - -void AnimationNode::addEventListener(AnimationEventCallback callback) { - eventListeners_.push_back(std::move(callback)); -} - -// ============================================================================ -// 视觉属性 -// ============================================================================ - -void AnimationNode::setTintColor(const Color &color) { tintColor_ = color; } - -// ============================================================================ -// 碰撞盒 -// ============================================================================ - -const std::vector> & -AnimationNode::getCurrentDamageBoxes() const { - auto clip = controller_.getClip(); - if (!clip || clip->empty()) - return emptyBoxes_; - return clip->getFrame(controller_.getCurrentFrameIndex()).damageBoxes; -} - -const std::vector> & -AnimationNode::getCurrentAttackBoxes() const { - auto clip = controller_.getClip(); - if (!clip || clip->empty()) - return emptyBoxes_; - return clip->getFrame(controller_.getCurrentFrameIndex()).attackBoxes; -} - -// ============================================================================ -// 查询 -// ============================================================================ - -Size AnimationNode::getMaxFrameSize() const { - return frameRenderer_.getMaxFrameSize(); -} - -Rect AnimationNode::getBoundingBox() const { - Size size = frameRenderer_.getMaxFrameSize(); - Vec2 pos = getPosition(); - Vec2 anchor = getAnchor(); - return Rect{{pos.x - size.width * anchor.x, pos.y - size.height * anchor.y}, - size}; -} - -// ============================================================================ -// 生命周期 -// ============================================================================ - -void AnimationNode::onEnter() { - Node::onEnter(); - if (autoPlay_ && controller_.getClip() && !controller_.getClip()->empty()) { - play(); - } -} - -void AnimationNode::onExit() { Node::onExit(); } - -void AnimationNode::onUpdate(float dt) { - Node::onUpdate(dt); - controller_.update(dt); -} - -void AnimationNode::onDraw(RenderBackend &renderer) { - auto clip = controller_.getClip(); - if (!clip || clip->empty()) - return; - - size_t idx = controller_.getCurrentFrameIndex(); - const auto &frame = clip->getFrame(idx); - Vec2 pos = getPosition(); - - if (controller_.isInterpolating() && idx + 1 < clip->getFrameCount()) { - auto props = InterpolationEngine::interpolate( - frame, clip->getFrame(idx + 1), controller_.getInterpolationFactor()); - - frameRenderer_.renderInterpolated(renderer, frame, idx, props, pos, - getOpacity(), tintColor_, flipX_, flipY_); - } else { - frameRenderer_.renderFrame(renderer, frame, idx, pos, getOpacity(), - tintColor_, flipX_, flipY_); - } -} - -// ============================================================================ -// 内部方法 -// ============================================================================ - -void AnimationNode::setupControllerCallbacks() { - controller_.setFrameChangeCallback( - [this](size_t oldIdx, size_t newIdx, const AnimationFrame &frame) { - AnimationEvent evt; - evt.type = AnimationEventType::FrameChanged; - evt.frameIndex = newIdx; - evt.previousFrameIndex = oldIdx; - evt.source = this; - dispatchEvent(evt); - }); - - controller_.setSoundTriggerCallback([this](const std::string &path) { - AnimationEvent evt; - evt.type = AnimationEventType::SoundTrigger; - evt.frameIndex = controller_.getCurrentFrameIndex(); - evt.soundPath = path; - evt.source = this; - dispatchEvent(evt); - }); -} - -void AnimationNode::dispatchEvent(const AnimationEvent &event) { - for (auto &listener : eventListeners_) { - if (listener) - listener(event); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/composite_animation.cpp b/Extra2D/src/animation/composite_animation.cpp deleted file mode 100644 index 07b186a..0000000 --- a/Extra2D/src/animation/composite_animation.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#include -#include - -namespace extra2d { - -// ============================================================================ -// 静态工厂 -// ============================================================================ - -Ptr CompositeAnimation::create() { - return makePtr(); -} - -Ptr -CompositeAnimation::create(const std::string &alsFilePath) { - auto comp = makePtr(); - comp->loadFromFile(alsFilePath); - return comp; -} - -// ============================================================================ -// 加载 ALS 文件 -// ============================================================================ - -bool CompositeAnimation::loadFromFile(const std::string &alsFilePath) { - AlsParser parser; - auto result = parser.parse(alsFilePath); - if (!result.success) - return false; - - // 清除现有图层 - for (auto &entry : layers_) { - if (entry.node) { - removeChild(entry.node); - } - } - layers_.clear(); - - // 创建每个图层 - for (const auto &layer : result.layers) { - auto node = AnimationNode::create(); - if (node->loadFromFile(layer.aniPath)) { - node->setPosition(layer.offset); - addLayer(node, layer.zOrder); - } - } - - return !layers_.empty(); -} - -// ============================================================================ -// 图层管理 -// ============================================================================ - -void CompositeAnimation::addLayer(Ptr node, int zOrder) { - if (!node) - return; - - node->setZOrder(zOrder); - addChild(node); - layers_.push_back({node, zOrder}); -} - -void CompositeAnimation::removeLayer(size_t index) { - if (index >= layers_.size()) - return; - - auto &entry = layers_[index]; - if (entry.node) { - removeChild(entry.node); - } - layers_.erase(layers_.begin() + static_cast(index)); -} - -Ptr CompositeAnimation::getLayer(size_t index) const { - if (index >= layers_.size()) - return nullptr; - return layers_[index].node; -} - -Ptr CompositeAnimation::getMainLayer() const { - if (layers_.empty()) - return nullptr; - return layers_[0].node; -} - -size_t CompositeAnimation::getLayerCount() const { return layers_.size(); } - -// ============================================================================ -// 统一播放控制 -// ============================================================================ - -void CompositeAnimation::play() { - for (auto &entry : layers_) { - if (entry.node) - entry.node->play(); - } -} - -void CompositeAnimation::pause() { - for (auto &entry : layers_) { - if (entry.node) - entry.node->pause(); - } -} - -void CompositeAnimation::resume() { - for (auto &entry : layers_) { - if (entry.node) - entry.node->resume(); - } -} - -void CompositeAnimation::stop() { - for (auto &entry : layers_) { - if (entry.node) - entry.node->stop(); - } -} - -void CompositeAnimation::reset() { - for (auto &entry : layers_) { - if (entry.node) - entry.node->reset(); - } -} - -void CompositeAnimation::setPlaybackSpeed(float speed) { - for (auto &entry : layers_) { - if (entry.node) - entry.node->setPlaybackSpeed(speed); - } -} - -void CompositeAnimation::setLooping(bool loop) { - for (auto &entry : layers_) { - if (entry.node) - entry.node->setLooping(loop); - } -} - -bool CompositeAnimation::isPlaying() const { - auto main = getMainLayer(); - return main ? main->isPlaying() : false; -} - -bool CompositeAnimation::isStopped() const { - auto main = getMainLayer(); - return main ? main->isStopped() : true; -} - -// ============================================================================ -// 事件回调(绑定到主图层) -// ============================================================================ - -void CompositeAnimation::setKeyframeCallback(KeyframeHitCallback callback) { - auto main = getMainLayer(); - if (main) - main->setKeyframeCallback(std::move(callback)); -} - -void CompositeAnimation::setCompletionCallback( - AnimationCompleteCallback callback) { - auto main = getMainLayer(); - if (main) - main->setCompletionCallback(std::move(callback)); -} - -void CompositeAnimation::addEventListener(AnimationEventCallback callback) { - auto main = getMainLayer(); - if (main) - main->addEventListener(std::move(callback)); -} - -// ============================================================================ -// 视觉属性(应用到所有图层) -// ============================================================================ - -void CompositeAnimation::setTintColor(const Color &color) { - for (auto &entry : layers_) { - if (entry.node) - entry.node->setTintColor(color); - } -} - -void CompositeAnimation::setFlipX(bool flip) { - for (auto &entry : layers_) { - if (entry.node) - entry.node->setFlipX(flip); - } -} - -void CompositeAnimation::setFlipY(bool flip) { - for (auto &entry : layers_) { - if (entry.node) - entry.node->setFlipY(flip); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/frame_property.cpp b/Extra2D/src/animation/frame_property.cpp deleted file mode 100644 index 03736fc..0000000 --- a/Extra2D/src/animation/frame_property.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#include - -namespace extra2d { - -// ============================================================================ -// FramePropertySet 实现 -// ============================================================================ - -void FramePropertySet::set(FramePropertyKey key, FramePropertyValue value) { - properties_[key] = value; -} - -void FramePropertySet::set(FramePropertyKey key, const std::string& value) { - FramePropertyValue pv; - pv.type = PropertyValueType::String; - pv.data.stringIndex = allocateString(value); - properties_[key] = pv; -} - -void FramePropertySet::set(FramePropertyKey key, const std::vector& value) { - FramePropertyValue pv; - pv.type = PropertyValueType::IntVector; - pv.data.vectorIndex = allocateVector(value); - properties_[key] = pv; -} - -void FramePropertySet::setCustom(const std::string &key, std::any value) { - customProperties_[key] = std::move(value); -} - -bool FramePropertySet::has(FramePropertyKey key) const { - return properties_.find(key) != properties_.end(); -} - -bool FramePropertySet::hasCustom(const std::string &key) const { - return customProperties_.find(key) != customProperties_.end(); -} - -void FramePropertySet::remove(FramePropertyKey key) { - properties_.erase(key); -} - -void FramePropertySet::removeCustom(const std::string &key) { - customProperties_.erase(key); -} - -void FramePropertySet::clear() { - properties_.clear(); - customProperties_.clear(); - stringPool_.clear(); - vectorPool_.clear(); - nextStringIndex_ = 0; - nextVectorIndex_ = 0; -} - -std::optional FramePropertySet::getCustom(const std::string &key) const { - auto it = customProperties_.find(key); - if (it == customProperties_.end()) - return std::nullopt; - return it->second; -} - -// ============================================================================ -// 字符串池和vector池管理 -// ============================================================================ - -uint32_t FramePropertySet::allocateString(const std::string& str) { - // 查找是否已存在相同字符串 - for (uint32_t i = 0; i < stringPool_.size(); ++i) { - if (stringPool_[i] == str) { - return i; - } - } - // 分配新字符串 - uint32_t index = static_cast(stringPool_.size()); - stringPool_.push_back(str); - return index; -} - -uint32_t FramePropertySet::allocateVector(const std::vector& vec) { - // 查找是否已存在相同vector - for (uint32_t i = 0; i < vectorPool_.size(); ++i) { - if (vectorPool_[i] == vec) { - return i; - } - } - // 分配新vector - uint32_t index = static_cast(vectorPool_.size()); - vectorPool_.push_back(vec); - return index; -} - -const std::string* FramePropertySet::getString(uint32_t index) const { - if (index < stringPool_.size()) { - return &stringPool_[index]; - } - return nullptr; -} - -const std::vector* FramePropertySet::getVector(uint32_t index) const { - if (index < vectorPool_.size()) { - return &vectorPool_[index]; - } - return nullptr; -} - -// ============================================================================ -// 模板特化实现 -// ============================================================================ - -template <> std::optional FramePropertySet::get(FramePropertyKey key) const { - auto it = properties_.find(key); - if (it == properties_.end()) return std::nullopt; - if (it->second.type == PropertyValueType::Bool) { - return it->second.data.boolValue; - } - return std::nullopt; -} - -template <> std::optional FramePropertySet::get(FramePropertyKey key) const { - auto it = properties_.find(key); - if (it == properties_.end()) return std::nullopt; - if (it->second.type == PropertyValueType::Int) { - return it->second.data.intValue; - } - return std::nullopt; -} - -template <> std::optional FramePropertySet::get(FramePropertyKey key) const { - auto it = properties_.find(key); - if (it == properties_.end()) return std::nullopt; - if (it->second.type == PropertyValueType::Float) { - return it->second.data.floatValue; - } - return std::nullopt; -} - -template <> std::optional FramePropertySet::get(FramePropertyKey key) const { - auto it = properties_.find(key); - if (it == properties_.end()) return std::nullopt; - if (it->second.type == PropertyValueType::Vec2) { - return it->second.data.vec2Value; - } - return std::nullopt; -} - -template <> std::optional FramePropertySet::get(FramePropertyKey key) const { - auto it = properties_.find(key); - if (it == properties_.end()) return std::nullopt; - if (it->second.type == PropertyValueType::Color) { - return it->second.data.colorValue; - } - return std::nullopt; -} - -template <> std::optional FramePropertySet::get(FramePropertyKey key) const { - auto it = properties_.find(key); - if (it == properties_.end()) return std::nullopt; - if (it->second.type == PropertyValueType::String) { - const std::string* str = getString(it->second.data.stringIndex); - if (str) return *str; - } - return std::nullopt; -} - -template <> std::optional> FramePropertySet::get>(FramePropertyKey key) const { - auto it = properties_.find(key); - if (it == properties_.end()) return std::nullopt; - if (it->second.type == PropertyValueType::IntVector) { - const std::vector* vec = getVector(it->second.data.vectorIndex); - if (vec) return *vec; - } - return std::nullopt; -} - -// ============================================================================ -// 链式 API 实现 -// ============================================================================ - -FramePropertySet &FramePropertySet::withSetFlag(int index) { - set(FramePropertyKey::SetFlag, FramePropertyValue(index)); - return *this; -} - -FramePropertySet &FramePropertySet::withPlaySound(const std::string &path) { - set(FramePropertyKey::PlaySound, path); - return *this; -} - -FramePropertySet &FramePropertySet::withImageRate(const Vec2 &scale) { - set(FramePropertyKey::ImageRate, FramePropertyValue(scale)); - return *this; -} - -FramePropertySet &FramePropertySet::withImageRotate(float degrees) { - set(FramePropertyKey::ImageRotate, FramePropertyValue(degrees)); - return *this; -} - -FramePropertySet &FramePropertySet::withColorTint(const Color &color) { - set(FramePropertyKey::ColorTint, FramePropertyValue(color)); - return *this; -} - -FramePropertySet &FramePropertySet::withInterpolation(bool enabled) { - set(FramePropertyKey::Interpolation, FramePropertyValue(enabled)); - return *this; -} - -FramePropertySet &FramePropertySet::withBlendLinearDodge(bool enabled) { - set(FramePropertyKey::BlendLinearDodge, FramePropertyValue(enabled)); - return *this; -} - -FramePropertySet &FramePropertySet::withLoop(bool enabled) { - set(FramePropertyKey::Loop, FramePropertyValue(enabled)); - return *this; -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/frame_renderer.cpp b/Extra2D/src/animation/frame_renderer.cpp deleted file mode 100644 index 5e4ce2d..0000000 --- a/Extra2D/src/animation/frame_renderer.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include - -namespace extra2d { - -// ============================================================================ -// 预加载 -// ============================================================================ - -bool FrameRenderer::preloadFrames(const std::vector &frames) { - releaseFrames(); - spriteFrames_.reserve(frames.size()); - maxFrameSize_ = Size{0.0f, 0.0f}; - - for (const auto &frame : frames) { - Ptr sf = frame.spriteFrame; - - // 如果帧自身没有 SpriteFrame,尝试通过缓存获取 - if (!sf && !frame.texturePath.empty()) { - sf = SpriteFrameCache::getInstance().getOrCreateFromFile( - frame.texturePath, frame.textureIndex); - } - - spriteFrames_.push_back(sf); - - // 更新最大帧尺寸 - if (sf && sf->isValid()) { - const auto &rect = sf->getRect(); - if (rect.size.width > maxFrameSize_.width) - maxFrameSize_.width = rect.size.width; - if (rect.size.height > maxFrameSize_.height) - maxFrameSize_.height = rect.size.height; - } - } - - return true; -} - -void FrameRenderer::releaseFrames() { - spriteFrames_.clear(); - maxFrameSize_ = Size{0.0f, 0.0f}; -} - -// ============================================================================ -// 渲染当前帧 -// ============================================================================ - -void FrameRenderer::renderFrame(RenderBackend &renderer, - const AnimationFrame &frame, size_t frameIndex, - const Vec2 &position, float nodeOpacity, - const Color &tintColor, bool flipX, - bool flipY) { - if (frameIndex >= spriteFrames_.size()) - return; - - auto sf = spriteFrames_[frameIndex]; - if (!sf || !sf->isValid()) - return; - - BlendMode blend = mapBlendMode(frame.properties); - Vec2 scale = frame.getEffectiveScale(); - float rotation = frame.getEffectiveRotation(); - Color frameColor = frame.getEffectiveColor(); - - // 合并帧颜色和节点染色 - Color finalTint{tintColor.r * frameColor.r, tintColor.g * frameColor.g, - tintColor.b * frameColor.b, - tintColor.a * frameColor.a * nodeOpacity}; - - drawSpriteFrame(renderer, sf, position, frame.offset, scale, rotation, 1.0f, - finalTint, flipX, flipY, blend); -} - -// ============================================================================ -// 渲染插值帧 -// ============================================================================ - -void FrameRenderer::renderInterpolated( - RenderBackend &renderer, const AnimationFrame &fromFrame, size_t fromIndex, - const InterpolatedProperties &props, const Vec2 &position, - float nodeOpacity, const Color &tintColor, bool flipX, bool flipY) { - if (fromIndex >= spriteFrames_.size()) - return; - - auto sf = spriteFrames_[fromIndex]; - if (!sf || !sf->isValid()) - return; - - BlendMode blend = mapBlendMode(fromFrame.properties); - - Color finalTint{tintColor.r * props.color.r, tintColor.g * props.color.g, - tintColor.b * props.color.b, - tintColor.a * props.color.a * nodeOpacity}; - - drawSpriteFrame(renderer, sf, position, props.position, props.scale, - props.rotation, 1.0f, finalTint, flipX, flipY, blend); -} - -// ============================================================================ -// 混合模式映射 -// ============================================================================ - -BlendMode FrameRenderer::mapBlendMode(const FramePropertySet &props) { - if (props.has(FramePropertyKey::BlendAdditive)) { - auto val = props.get(FramePropertyKey::BlendAdditive); - if (val.has_value() && val.value()) - return BlendMode::Additive; - } - if (props.has(FramePropertyKey::BlendLinearDodge)) { - auto val = props.get(FramePropertyKey::BlendLinearDodge); - if (val.has_value() && val.value()) - return BlendMode::Additive; // 线性减淡 ≈ 加法混合 - } - return BlendMode::Alpha; -} - -// ============================================================================ -// 查询 -// ============================================================================ - -Ptr FrameRenderer::getSpriteFrame(size_t frameIndex) const { - if (frameIndex >= spriteFrames_.size()) - return nullptr; - return spriteFrames_[frameIndex]; -} - -// ============================================================================ -// 内部绘制 -// ============================================================================ - -void FrameRenderer::drawSpriteFrame(RenderBackend &renderer, - Ptr sf, const Vec2 &position, - const Vec2 &offset, const Vec2 &scale, - float rotation, float opacity, - const Color &tint, bool flipX, bool flipY, - BlendMode blend) { - if (!sf || !sf->isValid()) - return; - - auto texture = sf->getTexture(); - if (!texture) - return; - - renderer.setBlendMode(blend); - - const Rect &srcRect = sf->getRect(); - - // 计算目标矩形 - float w = srcRect.size.width * std::abs(scale.x); - float h = srcRect.size.height * std::abs(scale.y); - - Vec2 finalPos{position.x + offset.x, position.y + offset.y}; - - // 处理翻转(通过缩放符号) - float flipScaleX = flipX ? -1.0f : 1.0f; - float flipScaleY = flipY ? -1.0f : 1.0f; - - // 锚点由 RenderBackend 在绘制时处理,这里只传递位置和尺寸 - // 使用中心锚点(0.5, 0.5),所以 destRect 从 finalPos 开始,不预偏移 - Rect destRect{{finalPos.x, finalPos.y}, {w * flipScaleX, h * flipScaleY}}; - - Color finalColor{tint.r, tint.g, tint.b, tint.a * opacity}; - - renderer.drawSprite(*texture, destRect, srcRect, finalColor, rotation, - Vec2{0.5f, 0.5f}); -} - -} // namespace extra2d diff --git a/Extra2D/src/animation/interpolation_engine.cpp b/Extra2D/src/animation/interpolation_engine.cpp deleted file mode 100644 index d0799bc..0000000 --- a/Extra2D/src/animation/interpolation_engine.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -namespace extra2d { - -// InterpolationEngine 的实现全部在头文件中以静态内联方式完成 -// 此文件保留用于未来可能需要的非内联实现 - -} // namespace extra2d diff --git a/Extra2D/src/animation/sprite_frame.cpp b/Extra2D/src/animation/sprite_frame.cpp deleted file mode 100644 index 2b72137..0000000 --- a/Extra2D/src/animation/sprite_frame.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -namespace extra2d { - -// SpriteFrame 的实现全部在头文件中以内联方式完成 -// 此文件保留用于未来可能需要的非内联实现 - -} // namespace extra2d diff --git a/Extra2D/src/animation/sprite_frame_cache.cpp b/Extra2D/src/animation/sprite_frame_cache.cpp deleted file mode 100644 index 0a08b67..0000000 --- a/Extra2D/src/animation/sprite_frame_cache.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -namespace extra2d { - -// SpriteFrameCache 的实现全部在头文件中以内联方式完成 -// 此文件保留用于未来可能需要的非内联实现 - -} // namespace extra2d diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index dbec1a4..6763b11 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -342,9 +341,6 @@ void Application::mainLoop() { } void Application::update() { - // Update action manager (single update per frame) - ActionManager::getInstance()->update(deltaTime_); - if (timerManager_) { timerManager_->update(deltaTime_); } diff --git a/Extra2D/src/effects/custom_effect_manager.cpp b/Extra2D/src/effects/custom_effect_manager.cpp deleted file mode 100644 index 5c9114e..0000000 --- a/Extra2D/src/effects/custom_effect_manager.cpp +++ /dev/null @@ -1,918 +0,0 @@ -#include -#include -#include -#include -#include - -namespace extra2d { - -using json = nlohmann::json; - -// ============================================================================ -// JSON序列化辅助函数 -// ============================================================================ - -/** - * @brief 将Vec2转换为JSON数组 - */ -static json vec2ToJson(const Vec2 &v) { return json::array({v.x, v.y}); } - -/** - * @brief 从JSON数组解析Vec2 - */ -static Vec2 jsonToVec2(const json &j) { - if (j.is_array() && j.size() >= 2) { - return Vec2(j[0].get(), j[1].get()); - } - return Vec2(); -} - -/** - * @brief 将Color转换为JSON数组 - */ -static json colorToJson(const Color &c) { - return json::array({c.r, c.g, c.b, c.a}); -} - -/** - * @brief 从JSON数组解析Color - */ -static Color jsonToColor(const json &j) { - if (j.is_array() && j.size() >= 4) { - return Color(j[0].get(), j[1].get(), j[2].get(), - j[3].get()); - } - return Colors::White; -} - -/** - * @brief 将EmitterConfig转换为JSON - */ -static json emitterConfigToJson(const EmitterConfig &config) { - json j; - j["emissionRate"] = config.emissionRate; - j["life"] = json::array({config.minLife, config.maxLife}); - j["startSize"] = json::array({config.minStartSize, config.maxStartSize}); - j["endSize"] = json::array({config.minEndSize, config.maxEndSize}); - j["velocity"] = json::object({{"min", vec2ToJson(config.minVelocity)}, - {"max", vec2ToJson(config.maxVelocity)}}); - j["acceleration"] = vec2ToJson(config.acceleration); - j["startColor"] = colorToJson(config.startColor); - j["endColor"] = colorToJson(config.endColor); - j["blendMode"] = static_cast(config.blendMode); - j["shape"] = static_cast(config.shape); - j["shapeRadius"] = config.shapeRadius; - return j; -} - -/** - * @brief 从JSON解析EmitterConfig - */ -static EmitterConfig jsonToEmitterConfig(const json &j) { - EmitterConfig config; - - if (j.contains("emissionRate")) { - config.emissionRate = j["emissionRate"].get(); - } - if (j.contains("life") && j["life"].is_array() && j["life"].size() >= 2) { - config.minLife = j["life"][0].get(); - config.maxLife = j["life"][1].get(); - } - if (j.contains("startSize") && j["startSize"].is_array() && - j["startSize"].size() >= 2) { - config.minStartSize = j["startSize"][0].get(); - config.maxStartSize = j["startSize"][1].get(); - } - if (j.contains("endSize") && j["endSize"].is_array() && - j["endSize"].size() >= 2) { - config.minEndSize = j["endSize"][0].get(); - config.maxEndSize = j["endSize"][1].get(); - } - if (j.contains("velocity")) { - const auto &vel = j["velocity"]; - if (vel.contains("min")) { - config.minVelocity = jsonToVec2(vel["min"]); - } - if (vel.contains("max")) { - config.maxVelocity = jsonToVec2(vel["max"]); - } - } - if (j.contains("acceleration")) { - config.acceleration = jsonToVec2(j["acceleration"]); - } - if (j.contains("startColor")) { - config.startColor = jsonToColor(j["startColor"]); - } - if (j.contains("endColor")) { - config.endColor = jsonToColor(j["endColor"]); - } - if (j.contains("blendMode")) { - config.blendMode = static_cast(j["blendMode"].get()); - } - if (j.contains("shape")) { - config.shape = static_cast(j["shape"].get()); - } - if (j.contains("shapeRadius")) { - config.shapeRadius = j["shapeRadius"].get(); - } - - return config; -} - -/** - * @brief 将CustomEffectConfig转换为JSON - */ -static json effectConfigToJson(const CustomEffectConfig &config) { - json j; - j["name"] = config.name; - j["type"] = static_cast(config.type); - j["description"] = config.description; - j["duration"] = config.duration; - j["loop"] = config.loop; - j["delay"] = config.delay; - - if (config.type == CustomEffectType::Particle) { - j["emitter"] = emitterConfigToJson(config.emitterConfig); - } else if (config.type == CustomEffectType::PostProcess) { - j["shaderVert"] = config.shaderVertPath; - j["shaderFrag"] = config.shaderFragPath; - j["params"] = config.shaderParams; - } - - return j; -} - -/** - * @brief 从JSON解析CustomEffectConfig - */ -static CustomEffectConfig jsonToEffectConfig(const json &j) { - CustomEffectConfig config; - - if (j.contains("name")) { - config.name = j["name"].get(); - } - if (j.contains("type")) { - config.type = static_cast(j["type"].get()); - } - if (j.contains("description")) { - config.description = j["description"].get(); - } - if (j.contains("duration")) { - config.duration = j["duration"].get(); - } - if (j.contains("loop")) { - config.loop = j["loop"].get(); - } - if (j.contains("delay")) { - config.delay = j["delay"].get(); - } - if (j.contains("emitter")) { - config.emitterConfig = jsonToEmitterConfig(j["emitter"]); - } - if (j.contains("shaderVert")) { - config.shaderVertPath = j["shaderVert"].get(); - } - if (j.contains("shaderFrag")) { - config.shaderFragPath = j["shaderFrag"].get(); - } - if (j.contains("params")) { - for (auto &[key, value] : j["params"].items()) { - config.shaderParams[key] = value.get(); - } - } - - return config; -} - -// ============================================================================ -// CustomEffect实现 -// ============================================================================ - -CustomEffect::CustomEffect(const CustomEffectConfig &config) - : config_(config) {} - -bool CustomEffect::init() { return true; } - -void CustomEffect::update(float dt) { - if (!playing_ || paused_ || finished_) - return; - - // 处理延迟 - if (delayTimer_ < config_.delay) { - delayTimer_ += dt; - return; - } - - elapsedTime_ += dt; - - // 检查是否结束 - if (config_.duration > 0 && elapsedTime_ >= config_.duration) { - if (config_.loop) { - elapsedTime_ = 0.0f; - } else { - finished_ = true; - playing_ = false; - } - } -} - -void CustomEffect::render(RenderBackend &renderer) { - // 基类不渲染任何内容 -} - -void CustomEffect::shutdown() { - playing_ = false; - paused_ = false; - finished_ = true; -} - -void CustomEffect::play() { - if (finished_) { - reset(); - } - playing_ = true; - paused_ = false; -} - -void CustomEffect::pause() { paused_ = true; } - -void CustomEffect::stop() { - playing_ = false; - paused_ = false; -} - -void CustomEffect::reset() { - elapsedTime_ = 0.0f; - delayTimer_ = 0.0f; - finished_ = false; - playing_ = false; - paused_ = false; -} - -// ============================================================================ -// CustomParticleEffect实现 -// ============================================================================ - -CustomParticleEffect::CustomParticleEffect(const CustomEffectConfig &config) - : CustomEffect(config) {} - -bool CustomParticleEffect::init() { - particleSystem_ = ParticleSystem::create(); - if (!particleSystem_) { - E2D_ERROR("创建粒子系统失败"); - return false; - } - - emitter_ = particleSystem_->addEmitter(config_.emitterConfig); - if (!emitter_) { - E2D_ERROR("创建粒子发射器失败"); - return false; - } - - // 初始化时启动发射器 - emitter_->start(); - - return true; -} - -void CustomParticleEffect::play() { - CustomEffect::play(); - if (emitter_) { - emitter_->start(); - } -} - -void CustomParticleEffect::stop() { - CustomEffect::stop(); - if (emitter_) { - emitter_->stop(); - } -} - -void CustomParticleEffect::update(float dt) { - CustomEffect::update(dt); - - if (particleSystem_) { - particleSystem_->setPosition(position_); - particleSystem_->onUpdate(dt); - } -} - -void CustomParticleEffect::render(RenderBackend &renderer) { - if (particleSystem_) { - particleSystem_->onDraw(renderer); - } -} - -void CustomParticleEffect::shutdown() { - if (emitter_) { - emitter_->stop(); - emitter_.reset(); - } - if (particleSystem_) { - particleSystem_->removeAllEmitters(); - particleSystem_.reset(); - } - CustomEffect::shutdown(); -} - -// ============================================================================ -// CustomPostProcessEffect实现 -// ============================================================================ - -CustomPostProcessEffect::CustomPostProcessEffect( - const CustomEffectConfig &config) - : CustomEffect(config), PostProcessEffect(config.name) {} - -bool CustomPostProcessEffect::init() { - if (!config_.shaderVertPath.empty() && !config_.shaderFragPath.empty()) { - if (!loadShaderFromFile(config_.shaderVertPath, config_.shaderFragPath)) { - E2D_ERROR("加载后处理Shader失败"); - return false; - } - } - - runtimeParams_ = config_.shaderParams; - return true; -} - -void CustomPostProcessEffect::update(float dt) { CustomEffect::update(dt); } - -void CustomPostProcessEffect::shutdown() { - PostProcessEffect::shutdown(); - CustomEffect::shutdown(); -} - -void CustomPostProcessEffect::onShaderBind(GLShader &shader) { - for (const auto &[name, value] : runtimeParams_) { - shader.setFloat(name, value); - } -} - -void CustomPostProcessEffect::setParam(const std::string &name, float value) { - runtimeParams_[name] = value; -} - -float CustomPostProcessEffect::getParam(const std::string &name) const { - auto it = runtimeParams_.find(name); - if (it != runtimeParams_.end()) { - return it->second; - } - return 0.0f; -} - -// ============================================================================ -// CustomEffectFactory实现 -// ============================================================================ - -CustomEffectFactory &CustomEffectFactory::getInstance() { - static CustomEffectFactory instance; - return instance; -} - -void CustomEffectFactory::registerEffect(const std::string &typeName, - EffectCreator creator) { - creators_[typeName] = creator; - E2D_INFO("注册自定义特效类型: {}", typeName); -} - -Ptr -CustomEffectFactory::create(const std::string &typeName, - const CustomEffectConfig &config) { - auto it = creators_.find(typeName); - if (it != creators_.end()) { - return it->second(config); - } - - // 默认创建器 - if (typeName == "Particle") { - return std::make_shared(config); - } else if (typeName == "PostProcess") { - return std::make_shared(config); - } - - E2D_ERROR("未知的特效类型: {}", typeName); - return nullptr; -} - -bool CustomEffectFactory::isRegistered(const std::string &typeName) const { - return creators_.find(typeName) != creators_.end(); -} - -std::vector CustomEffectFactory::getRegisteredTypes() const { - std::vector types; - for (const auto &[name, _] : creators_) { - types.push_back(name); - } - return types; -} - -// ============================================================================ -// CustomEffectManager实现 -// ============================================================================ - -CustomEffectManager &CustomEffectManager::getInstance() { - static CustomEffectManager instance; - return instance; -} - -bool CustomEffectManager::init() { - E2D_INFO("初始化自定义特效管理器..."); - - // 注册默认特效类型 - auto &factory = E2D_CUSTOM_EFFECT_FACTORY(); - factory.registerEffect("Particle", [](const CustomEffectConfig &config) { - return std::make_shared(config); - }); - factory.registerEffect("PostProcess", [](const CustomEffectConfig &config) { - return std::make_shared(config); - }); - - E2D_INFO("自定义特效管理器初始化完成"); - return true; -} - -void CustomEffectManager::shutdown() { - E2D_INFO("关闭自定义特效管理器..."); - stopAll(); - activeEffects_.clear(); - configs_.clear(); -} - -bool CustomEffectManager::loadFromFile(const std::string &filepath) { - std::ifstream file(filepath); - if (!file.is_open()) { - E2D_ERROR("无法打开特效配置文件: {}", filepath); - return false; - } - - try { - // 尝试解析为JSON - json j; - file >> j; - file.close(); - - if (j.is_array()) { - // 多个特效配置数组 - for (const auto &effectJson : j) { - auto config = jsonToEffectConfig(effectJson); - if (!config.name.empty()) { - registerConfig(config.name, config); - } - } - } else if (j.is_object()) { - // 单个特效配置 - auto config = jsonToEffectConfig(j); - if (!config.name.empty()) { - registerConfig(config.name, config); - } - } - - E2D_INFO("从JSON文件加载特效配置: {}", filepath); - return true; - - } catch (const json::exception &e) { - // JSON解析失败,回退到文本格式 - file.close(); - return loadFromTextFile(filepath); - } -} - -bool CustomEffectManager::loadFromTextFile(const std::string &filepath) { - std::ifstream file(filepath); - if (!file.is_open()) { - E2D_ERROR("无法打开特效配置文件: {}", filepath); - return false; - } - - // 简化格式:每行一个特效配置 - // 格式: EFFECT name type - // PARAM key value - // END - - std::string line; - CustomEffectConfig currentConfig; - bool inEffect = false; - - while (std::getline(file, line)) { - // 跳过空行和注释 - if (line.empty() || line[0] == '#') - continue; - - std::istringstream iss(line); - std::string cmd; - iss >> cmd; - - if (cmd == "EFFECT") { - // 开始新特效 - if (inEffect) { - // 保存上一个特效 - registerConfig(currentConfig.name, currentConfig); - } - inEffect = true; - currentConfig = CustomEffectConfig(); - - std::string type; - iss >> currentConfig.name >> type; - - if (type == "Particle") { - currentConfig.type = CustomEffectType::Particle; - } else if (type == "PostProcess") { - currentConfig.type = CustomEffectType::PostProcess; - } else { - currentConfig.type = CustomEffectType::Particle; - } - } else if (cmd == "DESC") { - std::getline(iss, currentConfig.description); - // 去除前导空格 - if (!currentConfig.description.empty() && - currentConfig.description[0] == ' ') { - currentConfig.description = currentConfig.description.substr(1); - } - } else if (cmd == "DURATION") { - iss >> currentConfig.duration; - } else if (cmd == "LOOP") { - std::string val; - iss >> val; - currentConfig.loop = (val == "true" || val == "1"); - } else if (cmd == "EMISSION") { - iss >> currentConfig.emitterConfig.emissionRate; - } else if (cmd == "LIFE") { - iss >> currentConfig.emitterConfig.minLife >> - currentConfig.emitterConfig.maxLife; - } else if (cmd == "SIZE_START") { - iss >> currentConfig.emitterConfig.minStartSize >> - currentConfig.emitterConfig.maxStartSize; - } else if (cmd == "SIZE_END") { - iss >> currentConfig.emitterConfig.minEndSize >> - currentConfig.emitterConfig.maxEndSize; - } else if (cmd == "VELOCITY") { - iss >> currentConfig.emitterConfig.minVelocity.x >> - currentConfig.emitterConfig.minVelocity.y >> - currentConfig.emitterConfig.maxVelocity.x >> - currentConfig.emitterConfig.maxVelocity.y; - } else if (cmd == "ACCEL") { - iss >> currentConfig.emitterConfig.acceleration.x >> - currentConfig.emitterConfig.acceleration.y; - } else if (cmd == "COLOR_START") { - iss >> currentConfig.emitterConfig.startColor.r >> - currentConfig.emitterConfig.startColor.g >> - currentConfig.emitterConfig.startColor.b >> - currentConfig.emitterConfig.startColor.a; - } else if (cmd == "COLOR_END") { - iss >> currentConfig.emitterConfig.endColor.r >> - currentConfig.emitterConfig.endColor.g >> - currentConfig.emitterConfig.endColor.b >> - currentConfig.emitterConfig.endColor.a; - } else if (cmd == "BLEND") { - std::string mode; - iss >> mode; - if (mode == "Additive") { - currentConfig.emitterConfig.blendMode = BlendMode::Additive; - } else if (mode == "Alpha") { - currentConfig.emitterConfig.blendMode = BlendMode::Alpha; - } else { - currentConfig.emitterConfig.blendMode = BlendMode::None; - } - } else if (cmd == "END") { - // 结束当前特效 - if (inEffect) { - registerConfig(currentConfig.name, currentConfig); - inEffect = false; - } - } - } - - // 保存最后一个特效 - if (inEffect) { - registerConfig(currentConfig.name, currentConfig); - } - - file.close(); - E2D_INFO("从文本文件加载特效配置: {}", filepath); - return true; -} - -bool CustomEffectManager::saveToFile(const std::string &name, - const std::string &filepath, - bool useJson) { - auto it = configs_.find(name); - if (it == configs_.end()) { - E2D_ERROR("特效配置不存在: {}", name); - return false; - } - - std::ofstream file(filepath); - if (!file.is_open()) { - E2D_ERROR("无法创建文件: {}", filepath); - return false; - } - - if (useJson) { - // 保存为JSON格式 - json j = effectConfigToJson(it->second); - file << j.dump(2); // 缩进2个空格,更易读 - E2D_INFO("保存特效配置到JSON文件: {}", filepath); - } else { - // 保存为文本格式 - const auto &config = it->second; - - file << "# Easy2D Custom Effect Config\n"; - file << "# Generated automatically\n\n"; - - file << "EFFECT " << config.name << " "; - if (config.type == CustomEffectType::Particle) { - file << "Particle\n"; - } else if (config.type == CustomEffectType::PostProcess) { - file << "PostProcess\n"; - } else { - file << "Particle\n"; - } - - file << "DESC " << config.description << "\n"; - file << "DURATION " << config.duration << "\n"; - file << "LOOP " << (config.loop ? "true" : "false") << "\n"; - - if (config.type == CustomEffectType::Particle) { - const auto &ec = config.emitterConfig; - file << "EMISSION " << ec.emissionRate << "\n"; - file << "LIFE " << ec.minLife << " " << ec.maxLife << "\n"; - file << "SIZE_START " << ec.minStartSize << " " << ec.maxStartSize - << "\n"; - file << "SIZE_END " << ec.minEndSize << " " << ec.maxEndSize << "\n"; - file << "VELOCITY " << ec.minVelocity.x << " " << ec.minVelocity.y << " " - << ec.maxVelocity.x << " " << ec.maxVelocity.y << "\n"; - file << "ACCEL " << ec.acceleration.x << " " << ec.acceleration.y << "\n"; - file << "COLOR_START " << ec.startColor.r << " " << ec.startColor.g << " " - << ec.startColor.b << " " << ec.startColor.a << "\n"; - file << "COLOR_END " << ec.endColor.r << " " << ec.endColor.g << " " - << ec.endColor.b << " " << ec.endColor.a << "\n"; - - file << "BLEND "; - switch (ec.blendMode) { - case BlendMode::Additive: - file << "Additive\n"; - break; - case BlendMode::Alpha: - file << "Alpha\n"; - break; - default: - file << "None\n"; - break; - } - } - - file << "END\n"; - E2D_INFO("保存特效配置到文本文件: {}", filepath); - } - - file.close(); - return true; -} - -bool CustomEffectManager::saveAllToFile(const std::string &filepath) { - std::ofstream file(filepath); - if (!file.is_open()) { - E2D_ERROR("无法创建文件: {}", filepath); - return false; - } - - json effectsArray = json::array(); - for (const auto &[name, config] : configs_) { - effectsArray.push_back(effectConfigToJson(config)); - } - - file << effectsArray.dump(2); - file.close(); - - E2D_INFO("保存所有特效配置到: {}", filepath); - return true; -} - -void CustomEffectManager::registerConfig(const std::string &name, - const CustomEffectConfig &config) { - configs_[name] = config; - E2D_INFO("注册特效配置: {}", name); -} - -CustomEffectConfig *CustomEffectManager::getConfig(const std::string &name) { - auto it = configs_.find(name); - if (it != configs_.end()) { - return &it->second; - } - return nullptr; -} - -void CustomEffectManager::removeConfig(const std::string &name) { - configs_.erase(name); -} - -std::vector CustomEffectManager::getConfigNames() const { - std::vector names; - for (const auto &[name, _] : configs_) { - names.push_back(name); - } - return names; -} - -Ptr CustomEffectManager::createEffect(const std::string &name) { - auto config = getConfig(name); - if (!config) { - E2D_ERROR("特效配置不存在: {}", name); - return nullptr; - } - - return createEffectFromConfig(*config); -} - -Ptr -CustomEffectManager::createEffectFromConfig(const CustomEffectConfig &config) { - std::string typeName; - switch (config.type) { - case CustomEffectType::Particle: - typeName = "Particle"; - break; - case CustomEffectType::PostProcess: - typeName = "PostProcess"; - break; - default: - typeName = "Particle"; - break; - } - - auto effect = E2D_CUSTOM_EFFECT_FACTORY().create(typeName, config); - if (effect && effect->init()) { - activeEffects_.push_back(effect); - return effect; - } - - return nullptr; -} - -void CustomEffectManager::destroyEffect(Ptr effect) { - if (!effect) - return; - - effect->shutdown(); - - auto it = std::find(activeEffects_.begin(), activeEffects_.end(), effect); - if (it != activeEffects_.end()) { - activeEffects_.erase(it); - } -} - -void CustomEffectManager::update(float dt) { - for (auto &effect : activeEffects_) { - if (effect->isPlaying()) { - effect->update(dt); - } - } - - // 清理已完成的特效 - activeEffects_.erase(std::remove_if(activeEffects_.begin(), - activeEffects_.end(), - [](const Ptr &effect) { - return effect->isFinished(); - }), - activeEffects_.end()); -} - -void CustomEffectManager::render(RenderBackend &renderer) { - for (auto &effect : activeEffects_) { - if (effect->isPlaying()) { - effect->render(renderer); - } - } -} - -void CustomEffectManager::stopAll() { - for (auto &effect : activeEffects_) { - effect->stop(); - } -} - -Ptr CustomEffectManager::play(const std::string &name, - const Vec2 &position) { - auto effect = createEffect(name); - if (effect) { - effect->setPosition(position); - effect->play(); - } - return effect; -} - -void CustomEffectManager::playOneShot(const std::string &name, - const Vec2 &position) { - auto effect = play(name, position); - if (effect) { - // 设置非循环,播放一次后自动销毁 - effect->play(); - } -} - -// ============================================================================ -// EffectBuilder实现 -// ============================================================================ - -CustomEffectConfig EffectBuilder::Particle(const std::string &name) { - CustomEffectConfig config; - config.name = name; - config.type = CustomEffectType::Particle; - config.duration = -1.0f; - config.loop = true; - config.delay = 0.0f; - - // 默认粒子配置 - config.emitterConfig.emissionRate = 100.0f; - config.emitterConfig.minLife = 1.0f; - config.emitterConfig.maxLife = 2.0f; - config.emitterConfig.minStartSize = 10.0f; - config.emitterConfig.maxStartSize = 20.0f; - config.emitterConfig.minEndSize = 0.0f; - config.emitterConfig.maxEndSize = 5.0f; - config.emitterConfig.minVelocity = Vec2(-50.0f, -50.0f); - config.emitterConfig.maxVelocity = Vec2(50.0f, 50.0f); - config.emitterConfig.acceleration = Vec2(0.0f, 0.0f); - config.emitterConfig.startColor = Colors::White; - config.emitterConfig.endColor = Colors::Transparent; - config.emitterConfig.blendMode = BlendMode::Additive; - - return config; -} - -CustomEffectConfig EffectBuilder::Fire(const std::string &name) { - CustomEffectConfig config = Particle(name); - config.emitterConfig = ParticlePreset::Fire(); - return config; -} - -CustomEffectConfig EffectBuilder::Smoke(const std::string &name) { - CustomEffectConfig config = Particle(name); - config.emitterConfig = ParticlePreset::Smoke(); - return config; -} - -CustomEffectConfig EffectBuilder::Explosion(const std::string &name) { - CustomEffectConfig config = Particle(name); - config.emitterConfig = ParticlePreset::Explosion(); - config.duration = 2.0f; - config.loop = false; - return config; -} - -CustomEffectConfig EffectBuilder::Magic(const std::string &name) { - CustomEffectConfig config = Particle(name); - config.emitterConfig = ParticlePreset::Magic(); - return config; -} - -CustomEffectConfig EffectBuilder::Sparkle(const std::string &name) { - CustomEffectConfig config = Particle(name); - config.emitterConfig = ParticlePreset::Sparkle(); - return config; -} - -CustomEffectConfig EffectBuilder::Bloom(const std::string &name) { - CustomEffectConfig config; - config.name = name; - config.type = CustomEffectType::PostProcess; - config.duration = -1.0f; - config.loop = true; - config.shaderParams["intensity"] = 1.5f; - config.shaderParams["threshold"] = 0.8f; - return config; -} - -CustomEffectConfig EffectBuilder::Blur(const std::string &name) { - CustomEffectConfig config; - config.name = name; - config.type = CustomEffectType::PostProcess; - config.duration = -1.0f; - config.loop = true; - config.shaderParams["radius"] = 2.0f; - return config; -} - -CustomEffectConfig EffectBuilder::Vignette(const std::string &name) { - CustomEffectConfig config; - config.name = name; - config.type = CustomEffectType::PostProcess; - config.duration = -1.0f; - config.loop = true; - config.shaderParams["intensity"] = 0.5f; - return config; -} - -CustomEffectConfig EffectBuilder::ColorGrading(const std::string &name) { - CustomEffectConfig config; - config.name = name; - config.type = CustomEffectType::PostProcess; - config.duration = -1.0f; - config.loop = true; - config.shaderParams["brightness"] = 1.0f; - config.shaderParams["contrast"] = 1.0f; - config.shaderParams["saturation"] = 1.0f; - return config; -} - -} // namespace extra2d diff --git a/Extra2D/src/effects/particle_system.cpp b/Extra2D/src/effects/particle_system.cpp deleted file mode 100644 index e9bdd9c..0000000 --- a/Extra2D/src/effects/particle_system.cpp +++ /dev/null @@ -1,468 +0,0 @@ -#include -#include -#include -#include - -namespace extra2d { - -// ============================================================================ -// 形状生成函数类型和查找表 -// ============================================================================ -using ShapeGenerator = Vec2 (ParticleEmitter::*)(); - -// 形状生成函数指针数组,索引对应 EmitterConfig::Shape 枚举值 -static constexpr ShapeGenerator SHAPE_GENERATORS[] = { - &ParticleEmitter::randomPointShape, // Shape::Point = 0 - &ParticleEmitter::randomCircleShape, // Shape::Circle = 1 - &ParticleEmitter::randomRectangleShape, // Shape::Rectangle = 2 - &ParticleEmitter::randomConeShape // Shape::Cone = 3 -}; - -static constexpr size_t SHAPE_GENERATOR_COUNT = sizeof(SHAPE_GENERATORS) / sizeof(SHAPE_GENERATORS[0]); - -// ============================================================================ -// ParticleEmitter实现 -// ============================================================================ - -ParticleEmitter::ParticleEmitter() : rng_(static_cast(std::random_device{}())) {} - -bool ParticleEmitter::init(size_t maxParticles) { - particles_.resize(maxParticles); - activeCount_ = 0; - return true; -} - -void ParticleEmitter::shutdown() { - particles_.clear(); - activeCount_ = 0; -} - -void ParticleEmitter::start() { - emitting_ = true; - emissionTime_ = 0.0f; -} - -void ParticleEmitter::stop() { emitting_ = false; } - -void ParticleEmitter::burst(int count) { - for (int i = 0; i < count && activeCount_ < particles_.size(); ++i) { - emitParticle(); - } -} - -void ParticleEmitter::reset() { - for (auto &particle : particles_) { - particle.active = false; - } - activeCount_ = 0; - emissionTimer_ = 0.0f; - emissionTime_ = 0.0f; -} - -void ParticleEmitter::update(float dt) { - // 发射新粒子 - if (emitting_) { - emissionTime_ += dt; - - // 检查持续时间 - if (config_.emissionDuration > 0 && - emissionTime_ >= config_.emissionDuration) { - emitting_ = false; - } - - emissionTimer_ += dt; - float emissionInterval = 1.0f / config_.emissionRate; - - while (emissionTimer_ >= emissionInterval && - activeCount_ < particles_.size()) { - emitParticle(); - emissionTimer_ -= emissionInterval; - } - } - - // 更新活跃粒子 - size_t newActiveCount = 0; - for (size_t i = 0; i < activeCount_; ++i) { - auto &p = particles_[i]; - - if (!p.active) - continue; - - // 更新生命周期 - p.life -= dt; - if (p.life <= 0.0f) { - p.active = false; - continue; - } - - // 更新物理 - p.velocity += p.acceleration * dt; - p.position += p.velocity * dt; - p.rotation += p.angularVelocity * dt; - - // 更新大小 - p.size += p.sizeDelta * dt; - if (p.size < 0.0f) - p.size = 0.0f; - - // 更新颜色 - p.color += p.colorDelta * dt; - - // 保持活跃 - if (newActiveCount != i) { - particles_[newActiveCount] = p; - } - newActiveCount++; - } - - activeCount_ = newActiveCount; -} - -void ParticleEmitter::render(RenderBackend &renderer) { - if (activeCount_ == 0) - return; - - // 设置混合模式 - renderer.setBlendMode(config_.blendMode); - - // 渲染所有活跃粒子 - if (config_.texture) { - // 使用纹理批量渲染 - renderer.beginSpriteBatch(); - - for (size_t i = 0; i < activeCount_; ++i) { - const auto &p = particles_[i]; - if (!p.active) - continue; - - // 计算目标矩形 - // 锚点由 RenderBackend 在绘制时处理,这里只传递位置和尺寸 - Rect destRect(p.position.x, p.position.y, p.size, p.size); - - renderer.drawSprite( - *config_.texture, destRect, - Rect(0, 0, config_.texture->getWidth(), config_.texture->getHeight()), - p.color, p.rotation, Vec2(0.5f, 0.5f)); - } - - renderer.endSpriteBatch(); - } else { - // 没有纹理,使用圆形填充渲染 - for (size_t i = 0; i < activeCount_; ++i) { - const auto &p = particles_[i]; - if (!p.active) - continue; - - // 渲染圆形粒子 - renderer.fillCircle(p.position, p.size * 0.5f, p.color); - } - } -} - -void ParticleEmitter::emitParticle() { - if (activeCount_ >= particles_.size()) - return; - - Particle &p = particles_[activeCount_]; - p.active = true; - - // 位置 - p.position = position_ + randomPointInShape(); - - // 速度 - p.velocity = randomVelocity(); - - // 加速度 - p.acceleration = config_.acceleration; - - // 旋转 - p.rotation = randomFloat(config_.minRotation, config_.maxRotation); - p.angularVelocity = - randomFloat(config_.minAngularVelocity, config_.maxAngularVelocity); - - // 大小 - float startSize = randomFloat(config_.minStartSize, config_.maxStartSize); - float endSize = randomFloat(config_.minEndSize, config_.maxEndSize); - p.size = startSize; - - // 生命周期 - p.maxLife = randomFloat(config_.minLife, config_.maxLife); - p.life = p.maxLife; - - // 计算每帧变化量 - if (p.maxLife > 0.0f) { - p.sizeDelta = (endSize - startSize) / p.maxLife; - p.colorDelta = (config_.endColor - config_.startColor) / p.maxLife; - } - - // 颜色 - p.color = config_.startColor; - - activeCount_++; -} - -float ParticleEmitter::randomFloat(float min, float max) { - return rng_.nextFloat(min, max); -} - -Vec2 ParticleEmitter::randomPointInShape() { - // 使用查找表替代 switch - size_t shapeIndex = static_cast(config_.shape); - if (shapeIndex < SHAPE_GENERATOR_COUNT) { - return (this->*SHAPE_GENERATORS[shapeIndex])(); - } - return Vec2::Zero(); -} - -Vec2 ParticleEmitter::randomPointShape() { - return Vec2::Zero(); -} - -Vec2 ParticleEmitter::randomCircleShape() { - float angle = randomFloat(0.0f, 2.0f * 3.14159265359f); - float radius = randomFloat(0.0f, config_.shapeRadius); - return Vec2(std::cos(angle) * radius, std::sin(angle) * radius); -} - -Vec2 ParticleEmitter::randomRectangleShape() { - return Vec2( - randomFloat(-config_.shapeSize.x * 0.5f, config_.shapeSize.x * 0.5f), - randomFloat(-config_.shapeSize.y * 0.5f, config_.shapeSize.y * 0.5f)); -} - -Vec2 ParticleEmitter::randomConeShape() { - float angle = - randomFloat(-config_.coneAngle * 0.5f, config_.coneAngle * 0.5f); - float radius = randomFloat(0.0f, config_.shapeRadius); - float rad = angle * 3.14159265359f / 180.0f; - return Vec2(std::cos(rad) * radius, std::sin(rad) * radius); -} - -Vec2 ParticleEmitter::randomVelocity() { - return Vec2(randomFloat(config_.minVelocity.x, config_.maxVelocity.x), - randomFloat(config_.minVelocity.y, config_.maxVelocity.y)); -} - -// ============================================================================ -// ParticleSystem实现 -// ============================================================================ - -ParticleSystem::ParticleSystem() {} - -Ptr ParticleSystem::create() { - return std::make_shared(); -} - -Ptr ParticleSystem::addEmitter(const EmitterConfig &config) { - auto emitter = std::make_shared(); - emitter->setConfig(config); - emitter->init(1000); // 默认最大1000个粒子 - emitters_.push_back(emitter); - return emitter; -} - -void ParticleSystem::removeEmitter(Ptr emitter) { - auto it = std::find(emitters_.begin(), emitters_.end(), emitter); - if (it != emitters_.end()) { - (*it)->shutdown(); - emitters_.erase(it); - } -} - -void ParticleSystem::removeAllEmitters() { - for (auto &emitter : emitters_) { - emitter->shutdown(); - } - emitters_.clear(); -} - -void ParticleSystem::startAll() { - for (auto &emitter : emitters_) { - emitter->start(); - } -} - -void ParticleSystem::stopAll() { - for (auto &emitter : emitters_) { - emitter->stop(); - } -} - -void ParticleSystem::resetAll() { - for (auto &emitter : emitters_) { - emitter->reset(); - } -} - -void ParticleSystem::onUpdate(float dt) { - // 获取粒子系统的世界位置 - auto worldPos = convertToWorldSpace(Vec2::Zero()); - - for (auto &emitter : emitters_) { - // 更新发射器位置为粒子系统的世界位置 - emitter->setPosition(worldPos); - // 更新发射器 - emitter->update(dt); - } -} - -void ParticleSystem::onDraw(RenderBackend &renderer) { - for (auto &emitter : emitters_) { - emitter->render(renderer); - } -} - -// ============================================================================ -// 预设实现 -// ============================================================================ - -EmitterConfig ParticlePreset::Fire() { - EmitterConfig config; - config.emissionRate = 200.0f; - config.minLife = 0.5f; - config.maxLife = 1.5f; - config.minStartSize = 20.0f; - config.maxStartSize = 40.0f; - config.minEndSize = 5.0f; - config.maxEndSize = 10.0f; - config.minVelocity = Vec2(-30.0f, -150.0f); // 向上(负y) - config.maxVelocity = Vec2(30.0f, -50.0f); - config.acceleration = Vec2(0.0f, 0.0f); - config.startColor = Color(1.0f, 0.8f, 0.2f, 1.0f); // 黄色 - config.endColor = Color(1.0f, 0.2f, 0.0f, 0.0f); // 红色透明 - config.blendMode = BlendMode::Additive; - return config; -} - -EmitterConfig ParticlePreset::Smoke() { - EmitterConfig config; - config.emissionRate = 50.0f; - config.minLife = 2.0f; - config.maxLife = 4.0f; - config.minStartSize = 30.0f; - config.maxStartSize = 60.0f; - config.minEndSize = 80.0f; - config.maxEndSize = 120.0f; - config.minVelocity = Vec2(-20.0f, -60.0f); // 向上(负y) - config.maxVelocity = Vec2(20.0f, -30.0f); - config.acceleration = Vec2(0.0f, -10.0f); // 向上加速度 - config.startColor = Color(0.5f, 0.5f, 0.5f, 0.5f); // 灰色半透明 - config.endColor = Color(0.3f, 0.3f, 0.3f, 0.0f); // 深灰透明 - config.blendMode = BlendMode::Alpha; - return config; -} - -EmitterConfig ParticlePreset::Explosion() { - EmitterConfig config; - config.emissionRate = 1000.0f; - config.emissionDuration = 0.1f; // 瞬间爆发 - config.minLife = 0.5f; - config.maxLife = 1.5f; - config.minStartSize = 10.0f; - config.maxStartSize = 30.0f; - config.minEndSize = 0.0f; - config.maxEndSize = 5.0f; - config.minVelocity = Vec2(-300.0f, -300.0f); - config.maxVelocity = Vec2(300.0f, 300.0f); - config.acceleration = Vec2(0.0f, -50.0f); - config.startColor = Color(1.0f, 1.0f, 0.5f, 1.0f); // 亮黄 - config.endColor = Color(1.0f, 0.3f, 0.0f, 0.0f); // 橙红透明 - config.blendMode = BlendMode::Additive; - return config; -} - -EmitterConfig ParticlePreset::Sparkle() { - EmitterConfig config; - config.emissionRate = 20.0f; - config.minLife = 0.2f; - config.maxLife = 0.8f; - config.minStartSize = 2.0f; - config.maxStartSize = 5.0f; - config.minEndSize = 0.0f; - config.maxEndSize = 2.0f; - config.minVelocity = Vec2(-10.0f, -10.0f); - config.maxVelocity = Vec2(10.0f, 10.0f); - config.acceleration = Vec2(0.0f, 0.0f); - config.startColor = Color(1.0f, 1.0f, 1.0f, 1.0f); // 白色 - config.endColor = Color(1.0f, 1.0f, 1.0f, 0.0f); // 透明 - config.blendMode = BlendMode::Additive; - return config; -} - -EmitterConfig ParticlePreset::Rain() { - EmitterConfig config; - config.emissionRate = 500.0f; - config.minLife = 1.0f; - config.maxLife = 2.0f; - config.minStartSize = 2.0f; - config.maxStartSize = 4.0f; - config.minEndSize = 2.0f; - config.maxEndSize = 4.0f; - config.minVelocity = Vec2(-100.0f, -400.0f); - config.maxVelocity = Vec2(100.0f, -600.0f); - config.acceleration = Vec2(0.0f, -100.0f); - config.startColor = Color(0.7f, 0.8f, 1.0f, 0.6f); // 淡蓝 - config.endColor = Color(0.7f, 0.8f, 1.0f, 0.3f); // 淡蓝半透明 - config.shape = EmitterConfig::Shape::Rectangle; - config.shapeSize = Vec2(800.0f, 100.0f); // 在顶部区域发射 - config.blendMode = BlendMode::Alpha; - return config; -} - -EmitterConfig ParticlePreset::Snow() { - EmitterConfig config; - config.emissionRate = 100.0f; - config.minLife = 3.0f; - config.maxLife = 6.0f; - config.minStartSize = 5.0f; - config.maxStartSize = 10.0f; - config.minEndSize = 5.0f; - config.maxEndSize = 10.0f; - config.minVelocity = Vec2(-30.0f, -30.0f); - config.maxVelocity = Vec2(30.0f, -80.0f); - config.acceleration = Vec2(0.0f, 0.0f); - config.startColor = Color(1.0f, 1.0f, 1.0f, 0.8f); // 白色 - config.endColor = Color(1.0f, 1.0f, 1.0f, 0.8f); // 白色 - config.shape = EmitterConfig::Shape::Rectangle; - config.shapeSize = Vec2(800.0f, 100.0f); - config.blendMode = BlendMode::Alpha; - return config; -} - -EmitterConfig ParticlePreset::Magic() { - EmitterConfig config; - config.emissionRate = 100.0f; - config.minLife = 1.0f; - config.maxLife = 2.0f; - config.minStartSize = 5.0f; - config.maxStartSize = 15.0f; - config.minEndSize = 0.0f; - config.maxEndSize = 5.0f; - config.minVelocity = Vec2(-50.0f, -50.0f); // 主要向上 - config.maxVelocity = Vec2(50.0f, -50.0f); - config.acceleration = Vec2(0.0f, -20.0f); // 向上加速度 - config.startColor = Color(0.5f, 0.2f, 1.0f, 1.0f); // 紫色 - config.endColor = Color(0.2f, 0.8f, 1.0f, 0.0f); // 青色透明 - config.blendMode = BlendMode::Additive; - return config; -} - -EmitterConfig ParticlePreset::Bubbles() { - EmitterConfig config; - config.emissionRate = 30.0f; - config.minLife = 2.0f; - config.maxLife = 4.0f; - config.minStartSize = 5.0f; - config.maxStartSize = 15.0f; - config.minEndSize = 5.0f; - config.maxEndSize = 15.0f; - config.minVelocity = Vec2(-20.0f, 20.0f); - config.maxVelocity = Vec2(20.0f, 60.0f); - config.acceleration = Vec2(0.0f, 30.0f); - config.startColor = Color(0.8f, 0.9f, 1.0f, 0.4f); // 淡蓝透明 - config.endColor = Color(0.8f, 0.9f, 1.0f, 0.1f); // 更透明 - config.blendMode = BlendMode::Alpha; - return config; -} - -} // namespace extra2d diff --git a/Extra2D/src/effects/post_process.cpp b/Extra2D/src/effects/post_process.cpp deleted file mode 100644 index 72c378c..0000000 --- a/Extra2D/src/effects/post_process.cpp +++ /dev/null @@ -1,389 +0,0 @@ -#include -#include -#include -#include - -#include - -namespace extra2d { - -// ============================================================================ -// 静态成员初始化 -// ============================================================================ -GLuint PostProcessEffect::quadVao_ = 0; -GLuint PostProcessEffect::quadVbo_ = 0; -bool PostProcessEffect::quadInitialized_ = false; - -// ============================================================================ -// PostProcessEffect实现 -// ============================================================================ - -PostProcessEffect::PostProcessEffect(const std::string &name) : name_(name) {} - -bool PostProcessEffect::init() { - initQuad(); - valid_ = true; - return true; -} - -void PostProcessEffect::shutdown() { - shader_.reset(); - valid_ = false; -} - -void PostProcessEffect::apply(const Texture &source, RenderTarget &target, - RenderBackend &renderer) { - if (!enabled_ || !valid_) - return; - - target.bind(); - - if (shader_) { - shader_->bind(); - shader_->setInt("u_texture", 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, - static_cast( - reinterpret_cast(source.getNativeHandle()))); - - onShaderBind(*shader_); - } - - renderFullscreenQuad(); - - if (shader_) { - shader_->unbind(); - } - - target.unbind(); -} - -bool PostProcessEffect::loadShader(const std::string &vertSource, - const std::string &fragSource) { - shader_ = std::make_shared(); - if (!shader_->compileFromSource(vertSource.c_str(), fragSource.c_str())) { - E2D_ERROR("后处理效果 '{}' 加载Shader失败", name_); - shader_.reset(); - return false; - } - return true; -} - -bool PostProcessEffect::loadShaderFromFile(const std::string &vertPath, - const std::string &fragPath) { - shader_ = std::make_shared(); - if (!shader_->compileFromFile(vertPath, fragPath)) { - E2D_ERROR("后处理效果 '{}' 从文件加载Shader失败", name_); - shader_.reset(); - return false; - } - return true; -} - -void PostProcessEffect::initQuad() { - if (quadInitialized_) - return; - - // 全屏四边形顶点数据(位置和纹理坐标) - float quadVertices[] = {// 位置 // 纹理坐标 - -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f}; - - glGenVertexArrays(1, &quadVao_); - glGenBuffers(1, &quadVbo_); - - glBindVertexArray(quadVao_); - glBindBuffer(GL_ARRAY_BUFFER, quadVbo_); - glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, - GL_STATIC_DRAW); - - // 位置属性 - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); - - // 纹理坐标属性 - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), - (void *)(2 * sizeof(float))); - - glBindVertexArray(0); - - quadInitialized_ = true; -} - -void PostProcessEffect::destroyQuad() { - if (quadVao_ != 0) { - glDeleteVertexArrays(1, &quadVao_); - quadVao_ = 0; - } - if (quadVbo_ != 0) { - glDeleteBuffers(1, &quadVbo_); - quadVbo_ = 0; - } - quadInitialized_ = false; -} - -void PostProcessEffect::renderFullscreenQuad() { - if (!quadInitialized_) { - initQuad(); - } - - glBindVertexArray(quadVao_); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); -} - -// ============================================================================ -// PostProcessStack实现 -// ============================================================================ - -PostProcessStack::PostProcessStack() = default; - -PostProcessStack::~PostProcessStack() { shutdown(); } - -bool PostProcessStack::init(int width, int height) { - E2D_INFO("初始化后处理栈..."); - - width_ = width; - height_ = height; - - // 创建两个渲染目标用于乒乓渲染 - RenderTargetConfig config; - config.width = width; - config.height = height; - config.hasDepthBuffer = false; - config.autoResize = false; - - renderTargetA_ = std::make_shared(); - renderTargetB_ = std::make_shared(); - - if (!renderTargetA_->init(config)) { - E2D_ERROR("创建后处理渲染目标A失败"); - return false; - } - - if (!renderTargetB_->init(config)) { - E2D_ERROR("创建后处理渲染目标B失败"); - return false; - } - - valid_ = true; - E2D_INFO("后处理栈初始化成功"); - return true; -} - -void PostProcessStack::shutdown() { - E2D_INFO("关闭后处理栈..."); - - clearEffects(); - - if (renderTargetA_) { - renderTargetA_->shutdown(); - renderTargetA_.reset(); - } - - if (renderTargetB_) { - renderTargetB_->shutdown(); - renderTargetB_.reset(); - } - - valid_ = false; -} - -void PostProcessStack::addEffect(Ptr effect) { - if (effect && effect->init()) { - effects_.push_back(effect); - E2D_INFO("添加后处理效果: {}", effect->getName()); - } -} - -void PostProcessStack::insertEffect(size_t index, - Ptr effect) { - if (effect && effect->init() && index <= effects_.size()) { - effects_.insert(effects_.begin() + index, effect); - E2D_INFO("插入后处理效果 '{}' 到位置 {}", effect->getName(), index); - } -} - -void PostProcessStack::removeEffect(const std::string &name) { - for (auto it = effects_.begin(); it != effects_.end(); ++it) { - if ((*it)->getName() == name) { - (*it)->shutdown(); - effects_.erase(it); - E2D_INFO("移除后处理效果: {}", name); - return; - } - } -} - -void PostProcessStack::removeEffect(size_t index) { - if (index < effects_.size()) { - effects_[index]->shutdown(); - effects_.erase(effects_.begin() + index); - } -} - -Ptr PostProcessStack::getEffect(const std::string &name) { - for (auto &effect : effects_) { - if (effect->getName() == name) { - return effect; - } - } - return nullptr; -} - -Ptr PostProcessStack::getEffect(size_t index) { - if (index < effects_.size()) { - return effects_[index]; - } - return nullptr; -} - -void PostProcessStack::clearEffects() { - for (auto &effect : effects_) { - effect->shutdown(); - } - effects_.clear(); -} - -void PostProcessStack::beginCapture() { - if (!valid_) - return; - - renderTargetA_->bind(); - renderTargetA_->clear(Colors::Black); - capturing_ = true; -} - -void PostProcessStack::endCapture(RenderBackend &renderer) { - if (!valid_ || !capturing_) - return; - - renderTargetA_->unbind(); - - // 应用所有后处理效果 - if (effects_.empty()) { - // 没有效果,直接渲染到屏幕 - // 这里需要渲染renderTargetA_的纹理到屏幕 - capturing_ = false; - return; - } - - // 乒乓渲染 - RenderTarget *readTarget = renderTargetA_.get(); - RenderTarget *writeTarget = renderTargetB_.get(); - - for (size_t i = 0; i < effects_.size(); ++i) { - auto &effect = effects_[i]; - - if (effect->isEnabled()) { - effect->apply(*readTarget->getColorTexture(), *writeTarget, renderer); - } - - // 交换读写目标 - std::swap(readTarget, writeTarget); - } - - // 最终结果在readTarget中(因为最后一次交换) - // 这里应该将结果渲染到屏幕 - - capturing_ = false; -} - -void PostProcessStack::process(const Texture &source, RenderTarget &target, - RenderBackend &renderer) { - if (!valid_) - return; - - RenderTarget *readTarget = nullptr; - RenderTarget *writeTarget = nullptr; - - // 确定读写目标 - if (target.getFBO() == renderTargetA_->getFBO()) { - readTarget = renderTargetB_.get(); - writeTarget = renderTargetA_.get(); - } else { - readTarget = renderTargetA_.get(); - writeTarget = renderTargetB_.get(); - } - - // 首先将源纹理复制到读目标 - readTarget->bind(); - // 这里需要渲染源纹理到readTarget - readTarget->unbind(); - - // 应用效果 - for (auto &effect : effects_) { - if (effect->isEnabled()) { - effect->apply(*readTarget->getColorTexture(), *writeTarget, renderer); - } - std::swap(readTarget, writeTarget); - } - - // 将最终结果复制到目标 - readTarget->blitTo(target, true, false); -} - -void PostProcessStack::resize(int width, int height) { - if (width_ == width && height_ == height) - return; - - width_ = width; - height_ = height; - - if (renderTargetA_) { - renderTargetA_->resize(width, height); - } - - if (renderTargetB_) { - renderTargetB_->resize(width, height); - } -} - -// ============================================================================ -// PostProcessManager实现 -// ============================================================================ - -PostProcessManager &PostProcessManager::getInstance() { - static PostProcessManager instance; - return instance; -} - -void PostProcessManager::init(int width, int height) { - if (initialized_) - return; - - E2D_INFO("初始化后处理管理器..."); - mainStack_.init(width, height); - initialized_ = true; -} - -void PostProcessManager::shutdown() { - if (!initialized_) - return; - - E2D_INFO("关闭后处理管理器..."); - mainStack_.shutdown(); - initialized_ = false; -} - -void PostProcessManager::resize(int width, int height) { - if (initialized_) { - mainStack_.resize(width, height); - } -} - -void PostProcessManager::beginFrame() { - if (initialized_) { - mainStack_.beginCapture(); - } -} - -void PostProcessManager::endFrame(RenderBackend &renderer) { - if (initialized_) { - mainStack_.endCapture(renderer); - } -} - -} // namespace extra2d diff --git a/Extra2D/src/scene/node.cpp b/Extra2D/src/scene/node.cpp index ec24190..86d6d3c 100644 --- a/Extra2D/src/scene/node.cpp +++ b/Extra2D/src/scene/node.cpp @@ -1,7 +1,5 @@ #include #include -#include -#include #include #include #include @@ -13,8 +11,6 @@ Node::Node() = default; Node::~Node() { removeAllChildren(); - stopAllActions(); - ActionManager::getInstance()->removeAllActionsFromTarget(this); } void Node::addChild(Ptr child) { @@ -403,46 +399,6 @@ void Node::updateSpatialIndex() { } } -// ============================================================================ -// 动作系统 - 新接口 -// ============================================================================ - -Action* Node::runAction(Action* action) { - if (!action) { - return nullptr; - } - ActionManager::getInstance()->addAction(action, this); - return action; -} - -void Node::stopAllActions() { - ActionManager::getInstance()->removeAllActionsFromTarget(this); -} - -void Node::stopAction(Action* action) { - ActionManager::getInstance()->removeAction(action); -} - -void Node::stopActionByTag(int tag) { - ActionManager::getInstance()->removeActionByTag(tag, this); -} - -void Node::stopActionsByFlags(unsigned int flags) { - ActionManager::getInstance()->removeActionsByFlags(flags, this); -} - -Action* Node::getActionByTag(int tag) { - return ActionManager::getInstance()->getActionByTag(tag, this); -} - -size_t Node::getActionCount() const { - return ActionManager::getInstance()->getActionCount(const_cast(this)); -} - -bool Node::isRunningActions() const { - return getActionCount() > 0; -} - void Node::update(float dt) { onUpdate(dt); } void Node::render(RenderBackend &renderer) { diff --git a/examples/collision_demo/main.cpp b/examples/collision_demo/main.cpp deleted file mode 100644 index 9b0c105..0000000 --- a/examples/collision_demo/main.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include - -using namespace extra2d; - -// ============================================================================ -// 碰撞测试节点 - 有实际边界框 -// ============================================================================ -class CollisionBox : public Node { -public: - CollisionBox(float width, float height, const Color &color) - : width_(width), height_(height), color_(color), isColliding_(false) { - // 启用空间索引,这是碰撞检测的关键 - setSpatialIndexed(true); - } - - void setColliding(bool colliding) { isColliding_ = colliding; } - - Rect getBoundingBox() const override { - // 返回实际的矩形边界 - Vec2 pos = getPosition(); - return Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_); - } - - void onRender(RenderBackend &renderer) override { - Vec2 pos = getPosition(); - - // 绘制填充矩形 - Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.8f) : color_; - renderer.fillRect( - Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_), - fillColor); - - // 绘制边框 - Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f) - : Color(1.0f, 1.0f, 1.0f, 0.5f); - float borderWidth = isColliding_ ? 3.0f : 2.0f; - renderer.drawRect( - Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_), - borderColor, borderWidth); - } - -private: - float width_, height_; - Color color_; - bool isColliding_; -}; - -// ============================================================================ -// 碰撞检测场景 -// ============================================================================ -class CollisionDemoScene : public Scene { -public: - void onEnter() override { - E2D_LOG_INFO("CollisionDemoScene::onEnter - 碰撞检测演示"); - - // 设置背景色 - setBackgroundColor(Color(0.05f, 0.05f, 0.1f, 1.0f)); - - // 获取屏幕中心 - auto &app = Application::instance(); - float centerX = app.getConfig().width / 2.0f; - float centerY = app.getConfig().height / 2.0f; - - // 创建静态碰撞框 - createStaticBoxes(centerX, centerY); - - // 创建移动的中心方块 - centerBox_ = - makePtr(80.0f, 80.0f, Color(0.2f, 0.6f, 1.0f, 0.8f)); - centerBox_->setPosition(Vec2(centerX, centerY)); - addChild(centerBox_); - - // 加载字体并创建UI - loadFonts(); - - E2D_LOG_INFO("创建了 {} 个碰撞框", boxes_.size() + 1); - } - - void onUpdate(float dt) override { - Scene::onUpdate(dt); - - // 旋转中心方块 - rotationAngle_ += rotationSpeed_ * dt; - if (rotationAngle_ >= 360.0f) - rotationAngle_ -= 360.0f; - - // 让中心方块沿圆形路径移动 - float radius = 150.0f; - float rad = rotationAngle_ * 3.14159f / 180.0f; - - auto &app = Application::instance(); - Vec2 center = - Vec2(app.getConfig().width / 2.0f, app.getConfig().height / 2.0f); - - centerBox_->setPosition(Vec2(center.x + std::cos(rad) * radius, - center.y + std::sin(rad) * radius)); - centerBox_->setRotation(rotationAngle_); - - // 执行碰撞检测 - performCollisionDetection(); - - // 更新UI文本 - updateUI(); - - // 检查退出按键 - auto &input = Application::instance().input(); - if (input.isButtonPressed(GamepadButton::Start)) { - E2D_LOG_INFO("退出应用"); - Application::instance().quit(); - } - } - -private: - /** - * @brief 加载字体资源并创建UI文本 - */ - void loadFonts() { - auto &resources = Application::instance().resources(); - titleFont_ = resources.loadFont("assets/font.ttf", 60, true); - infoFont_ = resources.loadFont("assets/font.ttf", 28, true); - - // 创建标题文本 - titleText_ = Text::create("碰撞检测演示", titleFont_); - titleText_->setPosition(50.0f, 30.0f); - titleText_->setTextColor(Color(1.0f, 1.0f, 1.0f, 1.0f)); - addChild(titleText_); - - // 创建说明文本 - descText_ = Text::create("蓝色方块旋转并检测碰撞", infoFont_); - descText_->setPosition(50.0f, 80.0f); - descText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(descText_); - - collideHintText_ = Text::create("红色 = 检测到碰撞", infoFont_); - collideHintText_->setPosition(50.0f, 105.0f); - collideHintText_->setTextColor(Color(1.0f, 0.5f, 0.5f, 1.0f)); - addChild(collideHintText_); - - // 创建动态统计文本 - collisionText_ = Text::create("", infoFont_); - collisionText_->setPosition(50.0f, 150.0f); - collisionText_->setTextColor(Color(1.0f, 1.0f, 0.5f, 1.0f)); - addChild(collisionText_); - - fpsText_ = Text::create("", infoFont_); - fpsText_->setPosition(50.0f, 175.0f); - fpsText_->setTextColor(Color(0.8f, 1.0f, 0.8f, 1.0f)); - addChild(fpsText_); - - // 创建退出提示文本 - float screenHeight = static_cast(Application::instance().getConfig().height); - exitHintText_ = Text::create("按 + 键退出", infoFont_); - exitHintText_->setPosition(50.0f, screenHeight - 50.0f); - exitHintText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(exitHintText_); - } - - /** - * @brief 更新UI文本 - */ - void updateUI() { - auto &app = Application::instance(); - - // 使用 setFormat 更新动态文本 - collisionText_->setFormat("碰撞数: %zu", collisionCount_); - fpsText_->setFormat("FPS: %u", app.fps()); - } - - /** - * @brief 创建静态碰撞框 - */ - void createStaticBoxes(float centerX, float centerY) { - // 创建围绕中心的静态碰撞框 - std::vector> positions = { - {Vec2(centerX - 200, centerY - 150), Color(0.3f, 1.0f, 0.3f, 0.7f)}, - {Vec2(centerX + 200, centerY - 150), Color(1.0f, 0.3f, 0.3f, 0.7f)}, - {Vec2(centerX - 200, centerY + 150), Color(0.3f, 0.3f, 1.0f, 0.7f)}, - {Vec2(centerX + 200, centerY + 150), Color(1.0f, 1.0f, 0.3f, 0.7f)}, - {Vec2(centerX, centerY - 220), Color(1.0f, 0.3f, 1.0f, 0.7f)}, - {Vec2(centerX, centerY + 220), Color(0.3f, 1.0f, 1.0f, 0.7f)}, - }; - - for (const auto &[pos, color] : positions) { - auto box = makePtr(70.0f, 70.0f, color); - box->setPosition(pos); - addChild(box); - boxes_.push_back(box); - } - } - - /** - * @brief 执行碰撞检测 - */ - void performCollisionDetection() { - // 清除之前的碰撞状态 - centerBox_->setColliding(false); - for (auto &box : boxes_) { - box->setColliding(false); - } - - // 使用空间索引进行碰撞检测 - auto collisions = queryCollisions(); - - collisionCount_ = collisions.size(); - - // 标记碰撞的节点 - for (const auto &[nodeA, nodeB] : collisions) { - if (auto boxA = dynamic_cast(nodeA)) { - boxA->setColliding(true); - } - if (auto boxB = dynamic_cast(nodeB)) { - boxB->setColliding(true); - } - } - } - - Ptr centerBox_; - std::vector> boxes_; - float rotationAngle_ = 0.0f; - float rotationSpeed_ = 60.0f; // 旋转速度(度/秒) - size_t collisionCount_ = 0; - - // 字体资源 - Ptr titleFont_; - Ptr infoFont_; - - // UI 文本组件 - Ptr titleText_; - Ptr descText_; - Ptr collideHintText_; - Ptr collisionText_; - Ptr fpsText_; - Ptr exitHintText_; -}; - -// ============================================================================ -// 程序入口 -// ============================================================================ - -int main(int argc, char **argv) -{ - // 初始化日志系统 - Logger::init(); - Logger::setLevel(LogLevel::Debug); - - E2D_LOG_INFO("========================"); - E2D_LOG_INFO("Easy2D 碰撞检测演示"); - E2D_LOG_INFO("========================"); - - // 获取应用实例 - auto &app = Application::instance(); - - // 配置应用 - AppConfig config; - config.title = "Easy2D - 碰撞检测演示"; - config.width = 1280; - config.height = 720; - config.vsync = true; - config.fpsLimit = 60; - - // 初始化应用 - if (!app.init(config)) { - E2D_LOG_ERROR("应用初始化失败!"); - return -1; - } - - // 进入场景 - app.enterScene(makePtr()); - - E2D_LOG_INFO("开始主循环..."); - - // 运行应用 - app.run(); - - E2D_LOG_INFO("应用结束"); - - return 0; -} diff --git a/examples/collision_demo/romfs/assets/font.ttf b/examples/collision_demo/romfs/assets/font.ttf deleted file mode 100644 index 8997148..0000000 Binary files a/examples/collision_demo/romfs/assets/font.ttf and /dev/null differ diff --git a/examples/collision_demo/xmake.lua b/examples/collision_demo/xmake.lua deleted file mode 100644 index abf2f2e..0000000 --- a/examples/collision_demo/xmake.lua +++ /dev/null @@ -1,76 +0,0 @@ --- ============================================== --- Collision Demo 示例 - Xmake 构建脚本 --- 支持平台: MinGW (Windows), Nintendo Switch --- ============================================== - --- 获取当前脚本所在目录(示例根目录) -local example_dir = os.scriptdir() - --- 可执行文件目标 -target("collision_demo") - set_kind("binary") - add_files("main.cpp") - add_includedirs("../../Extra2D/include") - add_deps("extra2d") - - -- 使用与主项目相同的平台配置 - if is_plat("switch") then - set_targetdir("../../build/examples/collision_demo") - - -- 构建后生成 NRO 文件 - after_build(function (target) - local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro" - local elf_file = target:targetfile() - local output_dir = path.directory(elf_file) - local nacp_file = path.join(output_dir, "collision_demo.nacp") - local nro_file = path.join(output_dir, "collision_demo.nro") - local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe") - local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe") - - if os.isfile(nacptool) and os.isfile(elf2nro) then - os.vrunv(nacptool, {"--create", "Collision Demo", "Extra2D Team", "1.0.0", nacp_file}) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs}) - else - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file}) - end - print("Generated NRO: " .. nro_file) - end - end) - - -- 打包时将 NRO 文件复制到 package 目录 - after_package(function (target) - local nro_file = path.join(target:targetdir(), "collision_demo.nro") - local package_dir = target:packagedir() - if os.isfile(nro_file) and package_dir then - os.cp(nro_file, package_dir) - print("Copied NRO to package: " .. package_dir) - end - end) - - elseif is_plat("mingw") then - set_targetdir("../../build/examples/collision_demo") - add_ldflags("-mwindows", {force = true}) - - -- 复制资源到输出目录 - after_build(function (target) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - local target_dir = path.directory(target:targetfile()) - local assets_dir = path.join(target_dir, "assets") - - -- 创建 assets 目录 - if not os.isdir(assets_dir) then - os.mkdir(assets_dir) - end - - -- 复制所有资源文件(包括子目录) - os.cp(path.join(romfs, "assets/**"), assets_dir) - print("Copied assets from " .. romfs .. " to " .. assets_dir) - else - print("Warning: romfs directory not found at " .. romfs) - end - end) - end -target_end() diff --git a/examples/flappy_bird/BaseScene.cpp b/examples/flappy_bird/BaseScene.cpp deleted file mode 100644 index eb29595..0000000 --- a/examples/flappy_bird/BaseScene.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// ============================================================================ -// BaseScene.cpp - Flappy Bird 基础场景实现 -// ============================================================================ - -#include "BaseScene.h" -#include -#include - -namespace flappybird { - -BaseScene::BaseScene() { - // 设置背景颜色为黑色(窗口四周会显示这个颜色) - setBackgroundColor(extra2d::Color(0.0f, 0.0f, 0.0f, 1.0f)); -} - -void BaseScene::onEnter() { - extra2d::Scene::onEnter(); - // 计算并更新视口 - updateViewport(); -} - -void BaseScene::updateViewport() { - auto &app = extra2d::Application::instance(); - float windowWidth = static_cast(app.window().getWidth()); - float windowHeight = static_cast(app.window().getHeight()); - - // 计算游戏内容在窗口中的居中位置 - // 保持游戏原始宽高比,进行"黑边"适配 - float scaleX = windowWidth / GAME_WIDTH; - float scaleY = windowHeight / GAME_HEIGHT; - // 使用较小的缩放比例,确保游戏内容完整显示在窗口中 - float scale = std::min(scaleX, scaleY); - - scaledGameWidth_ = GAME_WIDTH * scale; - scaledGameHeight_ = GAME_HEIGHT * scale; - // 计算居中偏移,使游戏内容在窗口中水平和垂直居中 - viewportOffsetX_ = (windowWidth - scaledGameWidth_) * 0.5f; - viewportOffsetY_ = (windowHeight - scaledGameHeight_) * 0.5f; - - // 设置视口大小为游戏逻辑分辨率 - setViewportSize(GAME_WIDTH, GAME_HEIGHT); - - // 创建并设置相机 - auto camera = extra2d::makePtr(); - // 设置正交投影,覆盖整个游戏逻辑区域 - // 注意:对于2D游戏,Y轴向下增长,所以bottom > top - camera->setViewport(0.0f, GAME_WIDTH, GAME_HEIGHT, 0.0f); - setCamera(camera); -} - -void BaseScene::onRender(extra2d::RenderBackend &renderer) { - // 检查窗口大小是否改变,如果改变则更新视口 - auto &app = extra2d::Application::instance(); - float currentWindowWidth = static_cast(app.window().getWidth()); - float currentWindowHeight = static_cast(app.window().getHeight()); - - // 如果窗口大小改变,重新计算视口 - float expectedWidth = scaledGameWidth_ + viewportOffsetX_ * 2.0f; - float expectedHeight = scaledGameHeight_ + viewportOffsetY_ * 2.0f; - if (std::abs(currentWindowWidth - expectedWidth) > 1.0f || - std::abs(currentWindowHeight - expectedHeight) > 1.0f) { - E2D_LOG_INFO("BaseScene::onRender - window size changed from ({} x {}) to " - "({} x {}), updating viewport", - expectedWidth, expectedHeight, currentWindowWidth, - currentWindowHeight); - updateViewport(); - } - - // 设置视口为居中区域 - renderer.setViewport( - static_cast(viewportOffsetX_), static_cast(viewportOffsetY_), - static_cast(scaledGameWidth_), static_cast(scaledGameHeight_)); - - // 调用父类的 onRender 进行实际渲染 - extra2d::Scene::onRender(renderer); -} - -void BaseScene::renderContent(extra2d::RenderBackend &renderer) { - // 如果视口参数未初始化(onEnter 还没被调用),先初始化 - if (scaledGameWidth_ <= 0.0f || scaledGameHeight_ <= 0.0f) { - updateViewport(); - } - - // 检查窗口大小是否改变 - auto &app = extra2d::Application::instance(); - float currentWindowWidth = static_cast(app.window().getWidth()); - float currentWindowHeight = static_cast(app.window().getHeight()); - - float expectedWidth = scaledGameWidth_ + viewportOffsetX_ * 2.0f; - float expectedHeight = scaledGameHeight_ + viewportOffsetY_ * 2.0f; - if (std::abs(currentWindowWidth - expectedWidth) > 1.0f || - std::abs(currentWindowHeight - expectedHeight) > 1.0f) { - updateViewport(); - } - - // 检查当前场景是否作为 TransitionScene 的子场景被渲染 - bool isChildOfTransition = false; - if (auto parent = getParent()) { - if (dynamic_cast(parent.get())) { - isChildOfTransition = true; - } - } - - if (isChildOfTransition) { - // 作为 TransitionScene 的子场景时,需要设置正确的投影矩阵 - // 使用游戏逻辑分辨率作为投影区域,让 TransitionScene 控制整体视口 - auto camera = getActiveCamera(); - if (camera) { - // 设置投影矩阵覆盖整个游戏逻辑区域 - renderer.setViewProjection(camera->getViewProjectionMatrix()); - } - // 渲染场景内容(投影矩阵已设置,直接渲染) - batchUpdateTransforms(); - renderer.beginSpriteBatch(); - render(renderer); - renderer.endSpriteBatch(); - } else { - // 正常渲染时,调用父类的 renderContent 处理视口和投影 - renderer.setViewport(static_cast(viewportOffsetX_), - static_cast(viewportOffsetY_), - static_cast(scaledGameWidth_), - static_cast(scaledGameHeight_)); - extra2d::Scene::renderContent(renderer); - } -} - -} // namespace flappybird diff --git a/examples/flappy_bird/BaseScene.h b/examples/flappy_bird/BaseScene.h deleted file mode 100644 index 7189f8a..0000000 --- a/examples/flappy_bird/BaseScene.h +++ /dev/null @@ -1,57 +0,0 @@ -// ============================================================================ -// BaseScene.h - Flappy Bird 基础场景类 -// 描述: 提供统一的居中视口适配功能,所有游戏场景都应继承此类 -// ============================================================================ - -#pragma once - -#include - -namespace flappybird { - -// 游戏逻辑分辨率(原始 Flappy Bird 尺寸) -static constexpr float GAME_WIDTH = 288.0f; -static constexpr float GAME_HEIGHT = 512.0f; - -/** - * @brief Flappy Bird 基础场景类 - * 所有游戏场景都应继承此类,以获得统一的居中视口适配功能 - */ -class BaseScene : public extra2d::Scene { -public: - /** - * @brief 构造函数 - */ - BaseScene(); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 渲染时调用,设置居中视口 - * @param renderer 渲染后端 - */ - void onRender(extra2d::RenderBackend &renderer) override; - - /** - * @brief 渲染场景内容,确保视口正确设置 - * @param renderer 渲染后端 - */ - void renderContent(extra2d::RenderBackend &renderer) override; - -protected: - /** - * @brief 更新视口计算,使游戏内容在窗口中居中显示 - */ - void updateViewport(); - - // 视口适配参数(用于在窗口中居中显示游戏内容) - float scaledGameWidth_ = 0.0f; // 缩放后的游戏宽度 - float scaledGameHeight_ = 0.0f; // 缩放后的游戏高度 - float viewportOffsetX_ = 0.0f; // 视口水平偏移 - float viewportOffsetY_ = 0.0f; // 视口垂直偏移 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/GameOverLayer.cpp b/examples/flappy_bird/GameOverLayer.cpp deleted file mode 100644 index 1ca508a..0000000 --- a/examples/flappy_bird/GameOverLayer.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// ============================================================================ -// GameOverLayer.cpp - 游戏结束层实现 -// ============================================================================ - -#include "GameOverLayer.h" -#include "BaseScene.h" -#include "GameScene.h" -#include "Number.h" -#include "ResLoader.h" -#include "StartScene.h" - -namespace flappybird { - -GameOverLayer::GameOverLayer(int score) : score_(score) { - // 注意:不要在构造函数中创建子节点 - // 因为此时 weak_from_this() 还不能使用 -} - -void GameOverLayer::onEnter() { - Node::onEnter(); - - // 在 onEnter 中初始化,此时 weak_from_this() 可用 - // 使用游戏逻辑分辨率 - float screenWidth = GAME_WIDTH; - float screenHeight = GAME_HEIGHT; - - // 整体居中(x 坐标相对于屏幕中心) - setPosition(extra2d::Vec2(screenWidth / 2.0f, screenHeight)); - - // 显示 "Game Over" 文字(y=120,从顶部开始) - auto gameOverFrame = ResLoader::getKeyFrame("text_game_over"); - if (gameOverFrame) { - auto gameOver = extra2d::Sprite::create(gameOverFrame->getTexture(), - gameOverFrame->getRect()); - gameOver->setAnchor(extra2d::Vec2(0.5f, 0.0f)); - gameOver->setPosition(extra2d::Vec2(0.0f, 120.0f)); // x=0 表示相对于中心点 - addChild(gameOver); - } - - // 初始化得分面板 - initPanel(score_, screenHeight); - - // 初始化按钮 - initButtons(); - - // 创建向上移动的动画(从屏幕底部移动到正常位置) - auto moveAction = extra2d::MoveBy::create(1.0f, extra2d::Vec2(0.0f, -screenHeight)); - moveAction->setCompletionCallback([this]() { - animationDone_ = true; - if (restartBtn_) - restartBtn_->setEnabled(true); - if (menuBtn_) - menuBtn_->setEnabled(true); - if (shareBtn_) - shareBtn_->setEnabled(true); - }); - runAction(moveAction); -} - -void GameOverLayer::initPanel(int score, float screenHeight) { - // 显示得分板(在屏幕中间) - auto panelFrame = ResLoader::getKeyFrame("score_panel"); - if (!panelFrame) - return; - - auto panel = - extra2d::Sprite::create(panelFrame->getTexture(), panelFrame->getRect()); - panel->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - panel->setPosition( - extra2d::Vec2(0.0f, screenHeight / 2.0f)); // x=0 表示相对于中心点 - addChild(panel); - - // 获取最高分(从存储中读取) - static int bestScore = 0; - if (score > bestScore) { - bestScore = score; - } - - // 显示 "New" 标记(如果破了记录) - if (score >= bestScore && score > 0) { - auto newFrame = ResLoader::getKeyFrame("new"); - if (newFrame) { - auto newSprite = - extra2d::Sprite::create(newFrame->getTexture(), newFrame->getRect()); - newSprite->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - // 调整位置使其在面板内部,靠近 BEST 分数 - newSprite->setPosition( - extra2d::Vec2(30.0f, 25.0f)); // 相对于面板的坐标,在 BEST 右侧 - panel->addChild(newSprite); - } - } - - // 显示奖牌 - auto medalFrame = getMedal(score); - if (medalFrame) { - auto medal = extra2d::Sprite::create(medalFrame->getTexture(), - medalFrame->getRect()); - medal->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - medal->setPosition(extra2d::Vec2(54.0f, 68.0f)); // 相对于面板的坐标 - panel->addChild(medal); - } - - // 显示本局得分 - auto scoreNumber = extra2d::makePtr(); - scoreNumber->setLittleNumber(score); - scoreNumber->setPosition( - extra2d::Vec2(80.0f, -15.0f)); // 相对于面板的坐标,右侧对齐 - panel->addChild(scoreNumber); - - // 显示最高分 - auto bestNumber = extra2d::makePtr(); - bestNumber->setLittleNumber(bestScore); - bestNumber->setPosition( - extra2d::Vec2(80.0f, 25.0f)); // 相对于面板的坐标,右侧对齐 - panel->addChild(bestNumber); -} - -void GameOverLayer::initButtons() { - auto restartFrame = ResLoader::getKeyFrame("button_restart"); - if (restartFrame) { - restartBtn_ = extra2d::Button::create(); - restartBtn_->setBackgroundImage(restartFrame->getTexture(), - restartFrame->getRect()); - restartBtn_->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - restartBtn_->setPosition(extra2d::Vec2(0.0f, 360.0f)); - restartBtn_->setEnabled(false); - restartBtn_->setOnClick([]() { - ResLoader::playMusic(MusicType::Click); - auto &app = extra2d::Application::instance(); - app.scenes().replaceScene(extra2d::makePtr(), - extra2d::TransitionType::Fade, 0.5f); - }); - addChild(restartBtn_); - } - - auto menuFrame = ResLoader::getKeyFrame("button_menu"); - if (menuFrame) { - menuBtn_ = extra2d::Button::create(); - menuBtn_->setBackgroundImage(menuFrame->getTexture(), menuFrame->getRect()); - menuBtn_->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - menuBtn_->setPosition(extra2d::Vec2(0.0f, 420.0f)); - menuBtn_->setEnabled(false); - menuBtn_->setOnClick([]() { - ResLoader::playMusic(MusicType::Click); - auto &app = extra2d::Application::instance(); - app.scenes().replaceScene(extra2d::makePtr(), - extra2d::TransitionType::Fade, 0.5f); - }); - addChild(menuBtn_); - } - - auto shareFrame = ResLoader::getKeyFrame("button_share"); - if (shareFrame) { - shareBtn_ = extra2d::Button::create(); - shareBtn_->setBackgroundImage(shareFrame->getTexture(), - shareFrame->getRect()); - shareBtn_->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - shareBtn_->setPosition(extra2d::Vec2(0.0f, 460.0f)); - shareBtn_->setEnabled(false); - shareBtn_->setOnClick([]() { ResLoader::playMusic(MusicType::Click); }); - addChild(shareBtn_); - } -} - -void GameOverLayer::onUpdate(float dt) { - Node::onUpdate(dt); - - if (!animationDone_) - return; - - auto &input = extra2d::Application::instance().input(); - - if (input.isButtonPressed(extra2d::GamepadButton::A)) { - ResLoader::playMusic(MusicType::Click); - auto &app = extra2d::Application::instance(); - app.scenes().replaceScene(extra2d::makePtr(), - extra2d::TransitionType::Fade, 0.5f); - } - - if (input.isButtonPressed(extra2d::GamepadButton::B)) { - ResLoader::playMusic(MusicType::Click); - auto &app = extra2d::Application::instance(); - app.scenes().replaceScene(extra2d::makePtr(), - extra2d::TransitionType::Fade, 0.5f); - } -} - -extra2d::Ptr GameOverLayer::getMedal(int score) { - if (score < 10) { - return nullptr; // 无奖牌 - } else if (score < 20) { - return ResLoader::getKeyFrame("medals_0"); // 铜牌 - } else if (score < 30) { - return ResLoader::getKeyFrame("medals_1"); // 银牌 - } else if (score < 50) { - return ResLoader::getKeyFrame("medals_2"); // 金牌 - } else { - return ResLoader::getKeyFrame("medals_3"); // 钻石奖牌 - } -} - -} // namespace flappybird diff --git a/examples/flappy_bird/GameOverLayer.h b/examples/flappy_bird/GameOverLayer.h deleted file mode 100644 index 4feda4d..0000000 --- a/examples/flappy_bird/GameOverLayer.h +++ /dev/null @@ -1,62 +0,0 @@ -// ============================================================================ -// GameOverLayer.h - 游戏结束层 -// 描述: 显示游戏结束界面、得分和奖牌 -// ============================================================================ - -#pragma once - -#include - -namespace flappybird { - -/** - * @brief 游戏结束层类 - * 显示游戏结束后的得分面板和按钮 - */ -class GameOverLayer : public extra2d::Node { -public: - /** - * @brief 构造函数 - * @param score 本局得分 - */ - GameOverLayer(int score); - - /** - * @brief 进入场景时调用 - */ - void onEnter() override; - - /** - * @brief 每帧更新时调用 - * @param dt 时间间隔 - */ - void onUpdate(float dt) override; - -private: - /** - * @brief 初始化得分面板 - * @param score 本局得分 - * @param screenHeight 屏幕高度 - */ - void initPanel(int score, float screenHeight); - - /** - * @brief 初始化按钮 - */ - void initButtons(); - - /** - * @brief 根据得分获取奖牌 - * @param score 得分 - * @return 奖牌精灵帧 - */ - extra2d::Ptr getMedal(int score); - - int score_ = 0; // 本局得分 - bool animationDone_ = false; // 动画是否完成 - extra2d::Ptr restartBtn_; // 重新开始按钮 - extra2d::Ptr menuBtn_; // 菜单按钮 - extra2d::Ptr shareBtn_; // 分享按钮 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/GameScene.cpp b/examples/flappy_bird/GameScene.cpp deleted file mode 100644 index 7b27383..0000000 --- a/examples/flappy_bird/GameScene.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// ============================================================================ -// GameScene.cpp - 游戏主场景实现 -// ============================================================================ - -#include "GameScene.h" -#include "GameOverLayer.h" -#include "ResLoader.h" -#include "input.h" - -namespace flappybird { - -GameScene::GameScene() { - // 基类 BaseScene 已经处理了视口设置和背景颜色 -} - -void GameScene::onEnter() { - BaseScene::onEnter(); - - // 游戏坐标系:使用游戏逻辑分辨率 - float screenWidth = GAME_WIDTH; - float screenHeight = GAME_HEIGHT; - - // 添加背景(使用左上角锚点,与原游戏一致) - auto bgFrame = ResLoader::getKeyFrame("bg_day"); - if (bgFrame) { - auto background = - extra2d::Sprite::create(bgFrame->getTexture(), bgFrame->getRect()); - background->setAnchor(extra2d::Vec2(0.0f, 0.0f)); - background->setPosition(extra2d::Vec2(0.0f, 0.0f)); - addChild(background); - } - - // 添加水管(初始时隐藏,游戏开始后才显示) - auto pipes = extra2d::makePtr(); - pipes_ = pipes.get(); - pipes->setVisible(false); - addChild(pipes); - - // 添加小鸟(在屏幕中间偏左位置) - auto bird = extra2d::makePtr(); - bird->setPosition( - extra2d::Vec2(screenWidth / 2.0f - 50.0f, screenHeight / 2.0f)); - bird_ = bird.get(); - addChild(bird); - - // 添加地面 - auto ground = extra2d::makePtr(); - ground_ = ground.get(); - addChild(ground); - - // 添加得分(屏幕顶部中央) - auto scoreNumber = extra2d::makePtr(); - scoreNumber->setPosition(extra2d::Vec2(screenWidth / 2.0f, 50.0f)); - scoreNumber->setNumber(0); - scoreNumber_ = scoreNumber.get(); - addChild(scoreNumber); - - // 添加 ready 图片(屏幕中央偏上) - auto readyFrame = ResLoader::getKeyFrame("text_ready"); - if (readyFrame) { - readySprite_ = extra2d::Sprite::create(readyFrame->getTexture(), - readyFrame->getRect()); - readySprite_->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - readySprite_->setPosition( - extra2d::Vec2(screenWidth / 2.0f, screenHeight / 2.0f - 70.0f)); - addChild(readySprite_); - } - - // 添加教程图片(屏幕中央偏下) - auto tutorialFrame = ResLoader::getKeyFrame("tutorial"); - if (tutorialFrame) { - tutorialSprite_ = extra2d::Sprite::create(tutorialFrame->getTexture(), - tutorialFrame->getRect()); - tutorialSprite_->setAnchor(extra2d::Vec2(0.5f, 0.5f)); - tutorialSprite_->setPosition( - extra2d::Vec2(screenWidth / 2.0f, screenHeight / 2.0f + 30.0f)); - addChild(tutorialSprite_); - } - - // 播放转场音效 - ResLoader::playMusic(MusicType::Swoosh); - - // 初始化状态 - started_ = false; - score_ = 0; -} - -void GameScene::onUpdate(float dt) { - if (!gameOver_) { - if (!bird_) - return; - - auto &input = extra2d::Application::instance().input(); - - if (input.isButtonPressed(extra2d::GamepadButton::A) || - input.isMousePressed(extra2d::MouseButton::Left)) { - if (!started_) { - started_ = true; - startGame(); - } - bird_->jump(); - } - - if (started_) { - bird_->fall(dt); - - if (pipes_) { - Pipe *firstPipe = pipes_->getPipe(0); - if (firstPipe && !firstPipe->scored) { - float birdX = bird_->getPosition().x; - float pipeX = firstPipe->getPosition().x; - if (pipeX <= birdX) { - score_++; - scoreNumber_->setNumber(score_); - firstPipe->scored = true; - ResLoader::playMusic(MusicType::Point); - } - } - } - - if (bird_->isLiving() && checkCollision()) { - onHit(); - } - - if (bird_->isLiving() && GAME_HEIGHT - bird_->getPosition().y <= 123.0f) { - bird_->setPosition( - extra2d::Vec2(bird_->getPosition().x, GAME_HEIGHT - 123.0f)); - bird_->setStatus(Bird::Status::Still); - onHit(); - } - } - } - - BaseScene::onUpdate(dt); -} - -void GameScene::startGame() { - // 隐藏 ready 和 tutorial 图片 - if (readySprite_) { - readySprite_->setVisible(false); - } - if (tutorialSprite_) { - tutorialSprite_->setVisible(false); - } - - // 显示并开始移动水管 - if (pipes_) { - pipes_->setVisible(true); - pipes_->start(); - } - - // 设置小鸟状态 - if (bird_) { - bird_->setStatus(Bird::Status::StartToFly); - } -} - -bool GameScene::checkCollision() { - if (!bird_ || !pipes_) - return false; - - extra2d::Rect birdBox = bird_->getBoundingBox(); - - // 检查与每个水管的碰撞 - for (int i = 0; i < 3; ++i) { - Pipe *pipe = pipes_->getPipe(i); - if (!pipe) - continue; - - // 检查与上水管的碰撞 - extra2d::Rect topBox = pipe->getTopPipeBox(); - if (birdBox.intersects(topBox)) { - return true; - } - - // 检查与下水管的碰撞 - extra2d::Rect bottomBox = pipe->getBottomPipeBox(); - if (birdBox.intersects(bottomBox)) { - return true; - } - } - - return false; -} - -void GameScene::onHit() { - if (!bird_->isLiving()) - return; - - // 小鸟死亡 - bird_->die(); - - // 停止地面滚动 - if (ground_) { - ground_->stop(); - } - - // 停止水管移动 - if (pipes_) { - pipes_->stop(); - } - - // 停止小鸟动画 - if (bird_) { - bird_->setStatus(Bird::Status::Still); - } - - // 隐藏得分 - if (scoreNumber_) { - scoreNumber_->setVisible(false); - } - - gameOver(); -} - -void GameScene::gameOver() { - if (gameOver_) - return; - - started_ = false; - gameOver_ = true; - - auto gameOverLayer = extra2d::makePtr(score_); - addChild(gameOverLayer); -} - -} // namespace flappybird diff --git a/examples/flappy_bird/GameScene.h b/examples/flappy_bird/GameScene.h deleted file mode 100644 index 0e6fa3b..0000000 --- a/examples/flappy_bird/GameScene.h +++ /dev/null @@ -1,72 +0,0 @@ -// ============================================================================ -// GameScene.h - 游戏主场景 -// 描述: 游戏的核心场景,包含小鸟、水管、地面和得分系统 -// ============================================================================ - -#pragma once - -#include "BaseScene.h" -#include "Bird.h" -#include "Pipes.h" -#include "Ground.h" -#include "Number.h" - -namespace flappybird { - -/** - * @brief 游戏主场景类 - * 游戏的核心场景,处理游戏逻辑、碰撞检测和得分 - */ -class GameScene : public BaseScene { -public: - /** - * @brief 构造函数 - */ - GameScene(); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 每帧更新时调用 - * @param dt 时间间隔(秒) - */ - void onUpdate(float dt) override; - -private: - /** - * @brief 开始游戏 - */ - void startGame(); - - /** - * @brief 处理碰撞事件 - */ - void onHit(); - - /** - * @brief 游戏结束 - */ - void gameOver(); - - /** - * @brief 检查小鸟与水管的碰撞 - * @return 是否发生碰撞 - */ - bool checkCollision(); - - Bird* bird_ = nullptr; // 小鸟 - Pipes* pipes_ = nullptr; // 水管管理器 - Ground* ground_ = nullptr; // 地面 - Number* scoreNumber_ = nullptr; // 得分显示 - extra2d::Ptr readySprite_; // "Get Ready" 提示 - extra2d::Ptr tutorialSprite_; // 操作教程提示 - - bool started_ = false; // 游戏是否已开始 - bool gameOver_ = false; // 游戏是否已结束 - int score_ = 0; // 当前得分 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/Number.cpp b/examples/flappy_bird/Number.cpp deleted file mode 100644 index 39f09e1..0000000 --- a/examples/flappy_bird/Number.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// ============================================================================ -// Number.cpp - 数字显示类实现 -// ============================================================================ - -#include "Number.h" -#include "ResLoader.h" - -namespace flappybird { - -Number::Number() : number_(0) { -} - -void Number::setNumber(int number) { - number_ = number; - createNumberSprites(number, "number_big_"); -} - -void Number::setLittleNumber(int number) { - number_ = number; - createNumberSprites(number, "number_medium_"); -} - -void Number::createNumberSprites(int number, const std::string& prefix) { - // 清除之前的数字精灵 - removeAllChildren(); - - // 获取数字 0 的高度作为参考 - auto zeroFrame = ResLoader::getKeyFrame(prefix + "0"); - float digitHeight = zeroFrame ? zeroFrame->getRect().size.height : 36.0f; - - // 收集所有数字位 - std::vector digits; - if (number == 0) { - digits.push_back(0); - } else { - while (number > 0) { - digits.push_back(number % 10); - number /= 10; - } - } - - // 计算总宽度 - float totalWidth = 0.0f; - std::vector digitWidths; - for (int digit : digits) { - auto frame = ResLoader::getKeyFrame(prefix + std::to_string(digit)); - float width = frame ? frame->getRect().size.width : 24.0f; - digitWidths.push_back(width); - totalWidth += width; - } - - // 创建数字精灵并居中排列 - float currentX = -totalWidth / 2.0f; - for (size_t i = 0; i < digits.size(); ++i) { - auto frame = ResLoader::getKeyFrame(prefix + std::to_string(digits[i])); - if (frame) { - auto digitSprite = extra2d::Sprite::create(frame->getTexture(), frame->getRect()); - digitSprite->setAnchor(extra2d::Vec2(0.0f, 0.0f)); - digitSprite->setPosition(extra2d::Vec2(currentX, -digitHeight / 2.0f)); - addChild(digitSprite); - } - currentX += digitWidths[i]; - } -} - -} // namespace flappybird diff --git a/examples/flappy_bird/Number.h b/examples/flappy_bird/Number.h deleted file mode 100644 index db75767..0000000 --- a/examples/flappy_bird/Number.h +++ /dev/null @@ -1,52 +0,0 @@ -// ============================================================================ -// Number.h - 数字显示类 -// 描述: 将整数数字转换为精灵图片显示 -// ============================================================================ - -#pragma once - -#include - -namespace flappybird { - -/** - * @brief 数字显示类 - * 用于显示得分,将整数转换为对应的数字图片 - */ -class Number : public extra2d::Node { -public: - /** - * @brief 构造函数 - */ - Number(); - - /** - * @brief 设置显示的数字(大号) - * @param number 要显示的数字 - */ - void setNumber(int number); - - /** - * @brief 设置显示的数字(小号) - * @param number 要显示的数字 - */ - void setLittleNumber(int number); - - /** - * @brief 获取当前数字 - * @return 当前数字 - */ - int getNumber() const { return number_; } - -private: - /** - * @brief 创建数字精灵 - * @param number 数字值 - * @param prefix 数字图片前缀("number_big_" 或 "number_medium_") - */ - void createNumberSprites(int number, const std::string& prefix); - - int number_ = 0; // 当前数字 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/ResLoader.cpp b/examples/flappy_bird/ResLoader.cpp deleted file mode 100644 index 7b63a18..0000000 --- a/examples/flappy_bird/ResLoader.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// ============================================================================ -// ResLoader.cpp - 资源加载器实现 -// ============================================================================ - -#include "ResLoader.h" -#include - -namespace flappybird { - -extra2d::Ptr ResLoader::atlasTexture_; -std::map ResLoader::imageMap_; -std::map> ResLoader::soundMap_; - -void ResLoader::init() { - auto &resources = extra2d::Application::instance().resources(); - - // 加载图集纹理 - atlasTexture_ = resources.loadTexture("assets/images/atlas.png"); - if (!atlasTexture_) { - E2D_LOG_ERROR("无法加载图集纹理 atlas.png"); - return; - } - - // 使用资源管理器加载 JSON 文件 - std::string jsonContent = resources.loadJsonFile("assets/images/atlas.json"); - if (jsonContent.empty()) { - E2D_LOG_ERROR("无法加载 atlas.json 文件"); - return; - } - - // 解析 JSON 图集数据 - try { - nlohmann::json jsonData = nlohmann::json::parse(jsonContent); - - for (const auto &sprite : jsonData["sprites"]) { - std::string name = sprite["name"]; - float x = sprite["x"]; - float y = sprite["y"]; - float width = sprite["width"]; - float height = sprite["height"]; - - ImageInfo info = {width, height, x, y}; - imageMap_[name] = info; - } - - E2D_LOG_INFO("成功加载 {} 个精灵帧", imageMap_.size()); - } catch (const std::exception &e) { - E2D_LOG_ERROR("解析 atlas.json 失败: {}", e.what()); - return; - } - - // 加载音效 - soundMap_[MusicType::Click] = resources.loadSound("assets/sound/click.wav"); - soundMap_[MusicType::Hit] = resources.loadSound("assets/sound/hit.wav"); - soundMap_[MusicType::Fly] = resources.loadSound("assets/sound/fly.wav"); - soundMap_[MusicType::Point] = resources.loadSound("assets/sound/point.wav"); - soundMap_[MusicType::Swoosh] = resources.loadSound("assets/sound/swoosh.wav"); - - E2D_LOG_INFO("资源加载完成"); -} - -extra2d::Ptr -ResLoader::getKeyFrame(const std::string &name) { - auto it = imageMap_.find(name); - if (it == imageMap_.end()) { - E2D_LOG_WARN("找不到精灵帧: %s", name.c_str()); - return nullptr; - } - - const ImageInfo &info = it->second; - E2D_LOG_INFO("加载精灵帧: name={}, w={}, h={}, x={}, y={}", name, info.width, - info.height, info.x, info.y); - - // 检查纹理尺寸 - if (atlasTexture_) { - E2D_LOG_INFO("图集纹理尺寸: {}x{}", atlasTexture_->getWidth(), - atlasTexture_->getHeight()); - } - - return extra2d::makePtr( - atlasTexture_, extra2d::Rect(info.x, info.y, info.width, info.height)); -} - -void ResLoader::playMusic(MusicType type) { - auto it = soundMap_.find(type); - if (it == soundMap_.end()) { - E2D_LOG_WARN("ResLoader::playMusic: sound type not found"); - return; - } - if (!it->second) { - E2D_LOG_WARN("ResLoader::playMusic: sound pointer is null"); - return; - } - if (!it->second->play()) { - E2D_LOG_WARN("ResLoader::playMusic: failed to play sound"); - } -} - -} // namespace flappybird diff --git a/examples/flappy_bird/ResLoader.h b/examples/flappy_bird/ResLoader.h deleted file mode 100644 index c22301e..0000000 --- a/examples/flappy_bird/ResLoader.h +++ /dev/null @@ -1,63 +0,0 @@ -// ============================================================================ -// ResLoader.h - 资源加载器 -// 描述: 管理游戏资源的加载和访问 -// ============================================================================ - -#pragma once - -#include -#include -#include - -namespace flappybird { - -/** - * @brief 音频类型枚举 - */ -enum class MusicType { - Click, // 按键声音 - Hit, // 小鸟死亡声音 - Fly, // 小鸟飞翔声音 - Point, // 得分声音 - Swoosh // 转场声音 -}; - -/** - * @brief 资源加载器类 - * 管理纹理图集、精灵帧和音频资源的加载 - */ -class ResLoader { -public: - /** - * @brief 初始化资源加载器 - */ - static void init(); - - /** - * @brief 获取精灵帧 - * @param name 帧名称 - * @return 精灵帧指针 - */ - static extra2d::Ptr getKeyFrame(const std::string& name); - - /** - * @brief 播放音效 - * @param type 音效类型 - */ - static void playMusic(MusicType type); - -private: - /** - * @brief 图片信息结构 - * 对应 atlas.txt 格式: 元素名 width height x y - */ - struct ImageInfo { - float width, height, x, y; - }; - - static extra2d::Ptr atlasTexture_; // 图集纹理 - static std::map imageMap_; // 图片信息映射 - static std::map> soundMap_; // 音效映射 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/SplashScene.cpp b/examples/flappy_bird/SplashScene.cpp deleted file mode 100644 index f45a23e..0000000 --- a/examples/flappy_bird/SplashScene.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// ============================================================================ -// SplashScene.cpp - 启动场景实现 -// ============================================================================ - -#include "SplashScene.h" -#include "ResLoader.h" -#include "StartScene.h" -#include - -namespace flappybird { - -SplashScene::SplashScene() { - // 基类 BaseScene 已经处理了视口设置和背景颜色 -} - -void SplashScene::onEnter() { - BaseScene::onEnter(); - - // 尝试加载 splash 图片 - auto splashFrame = ResLoader::getKeyFrame("splash"); - if (splashFrame) { - auto splash = extra2d::Sprite::create(splashFrame->getTexture(), - splashFrame->getRect()); - splash->setAnchor(0.5f, 0.5f); - // splash 图片是全屏的(288x512),将其中心放在游戏区域中心 - splash->setPosition(GAME_WIDTH / 2.0f, GAME_HEIGHT / 2.0f); - addChild(splash); - } - // 播放转场音效 - ResLoader::playMusic(MusicType::Swoosh); -} - -void SplashScene::onUpdate(float dt) { - BaseScene::onUpdate(dt); - - // 计时 - timer_ += dt; - if (timer_ >= delay_) { - gotoStartScene(); - } -} - -void SplashScene::gotoStartScene() { - auto &app = extra2d::Application::instance(); - app.scenes().replaceScene(extra2d::makePtr(), - extra2d::TransitionType::Fade, 2.0f); -} - -} // namespace flappybird diff --git a/examples/flappy_bird/SplashScene.h b/examples/flappy_bird/SplashScene.h deleted file mode 100644 index 57e64e6..0000000 --- a/examples/flappy_bird/SplashScene.h +++ /dev/null @@ -1,44 +0,0 @@ -// ============================================================================ -// SplashScene.h - 启动场景 -// 描述: 显示游戏 Logo,2秒后自动跳转到开始场景 -// ============================================================================ - -#pragma once - -#include "BaseScene.h" - -namespace flappybird { - -/** - * @brief 启动场景类 - * 显示游戏 Logo,短暂延迟后进入主菜单 - */ -class SplashScene : public BaseScene { -public: - /** - * @brief 构造函数 - */ - SplashScene(); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 每帧更新时调用 - * @param dt 时间间隔(秒) - */ - void onUpdate(float dt) override; - -private: - /** - * @brief 跳转到开始场景 - */ - void gotoStartScene(); - - float timer_ = 0.0f; // 计时器 - const float delay_ = 2.0f; // 延迟时间(秒) -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/StartScene.cpp b/examples/flappy_bird/StartScene.cpp deleted file mode 100644 index ae43a51..0000000 --- a/examples/flappy_bird/StartScene.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// ============================================================================ -// StartScene.cpp - 开始菜单场景实现 -// ============================================================================ - -#include "StartScene.h" -#include "Bird.h" -#include "GameScene.h" -#include "Ground.h" -#include "ResLoader.h" -#include "extra2d/event/input_codes.h" - -namespace flappybird { - -StartScene::StartScene() { - // 基类 BaseScene 已经处理了视口设置和背景颜色 -} - -void StartScene::onEnter() { - BaseScene::onEnter(); - - // 使用游戏逻辑分辨率 - float screenWidth = GAME_WIDTH; - float screenHeight = GAME_HEIGHT; - - // 添加背景(使用左上角锚点) - auto bgFrame = ResLoader::getKeyFrame("bg_day"); - if (bgFrame) { - auto background = - extra2d::Sprite::create(bgFrame->getTexture(), bgFrame->getRect()); - background->setAnchor(0.0f, 0.0f); - background->setPosition(0.0f, 0.0f); - addChild(background); - E2D_LOG_INFO("背景已添加: size={} x {}", bgFrame->getRect().size.width, - bgFrame->getRect().size.height); - } else { - E2D_LOG_ERROR("无法加载背景图片"); - } - - // 添加地面 - auto ground = extra2d::makePtr(); - addChild(ground); - - // 添加标题图片(在上方) - auto titleFrame = ResLoader::getKeyFrame("title"); - if (titleFrame) { - auto title = extra2d::Sprite::create(titleFrame->getTexture(), - titleFrame->getRect()); - title->setAnchor(0.5f, 0.5f); - // 标题在屏幕上方 - title->setPosition(screenWidth / 2.0f, 150.0f); - addChild(title); - E2D_LOG_INFO("标题已添加: size={} x {}", titleFrame->getRect().size.width, - titleFrame->getRect().size.height); - } else { - E2D_LOG_ERROR("无法加载标题图片"); - } - - // 添加小鸟(在标题下方) - auto bird = extra2d::makePtr(); - bird->setAnchor(0.5f, 0.5f); - bird->setPosition(screenWidth / 2.0f, screenHeight / 2.0f); - bird->setStatus(Bird::Status::Idle); - addChild(bird); - - // 添加开始按钮 - 在小鸟下方 - auto playFrame = ResLoader::getKeyFrame("button_play"); - if (playFrame) { - float btnWidth = playFrame->getRect().size.width; - float btnHeight = playFrame->getRect().size.height; - - playBtn_ = extra2d::Button::create(); - playBtn_->setBackgroundImage(playFrame->getTexture(), playFrame->getRect()); - // 使用世界坐标,中心锚点 - playBtn_->setAnchor(0.5f, 0.5f); - // PLAY 按钮在小鸟下方 - playBtn_->setPosition(screenWidth / 2.0f, - screenHeight - playBtn_->getSize().height - 100.0f); - playBtn_->setOnClick([this]() { - ResLoader::playMusic(MusicType::Click); - startGame(); - }); - addChild(playBtn_); - } - - // 添加分享按钮 - 在 PLAY 按钮下方,靠近地面 - auto shareFrame = ResLoader::getKeyFrame("button_share"); - if (shareFrame) { - float btnWidth = shareFrame->getRect().size.width; - float btnHeight = shareFrame->getRect().size.height; - - shareBtn_ = extra2d::Button::create(); - shareBtn_->setBackgroundImage(shareFrame->getTexture(), - shareFrame->getRect()); - // 使用世界坐标,中心锚点 - shareBtn_->setAnchor(0.5f, 0.5f); - // SHARE 按钮在 PLAY 按钮下方,靠近地面 - shareBtn_->setPosition(screenWidth / 2.0f, - screenHeight - shareBtn_->getSize().height - 80.0f); - shareBtn_->setOnClick([this]() { - ResLoader::playMusic(MusicType::Click); - // 分享功能暂不实现 - }); - addChild(shareBtn_); - } - - // 添加 copyright 图片(在底部) - auto copyrightFrame = ResLoader::getKeyFrame("brand_copyright"); - if (copyrightFrame) { - auto copyright = extra2d::Sprite::create(copyrightFrame->getTexture(), - copyrightFrame->getRect()); - copyright->setAnchor(0.5f, 0.5f); - // Copyright 在屏幕底部 - copyright->setPosition(screenWidth / 2.0f, screenHeight - 20.0f); - addChild(copyright); - } - - // 播放转场音效 - ResLoader::playMusic(MusicType::Swoosh); -} - -void StartScene::onUpdate(float dt) { - BaseScene::onUpdate(dt); - - // 检测 A 键或空格开始游戏 - auto &input = extra2d::Application::instance().input(); - if (input.isButtonPressed(extra2d::GamepadButton::A)) { - ResLoader::playMusic(MusicType::Click); - startGame(); - } - - // 检测 BACK 键退出游戏 - if (input.isButtonPressed(extra2d::GamepadButton::Start)) { - ResLoader::playMusic(MusicType::Click); - auto &app = extra2d::Application::instance(); - app.quit(); - } -} - -void StartScene::startGame() { - auto &app = extra2d::Application::instance(); - app.scenes().replaceScene(extra2d::makePtr(), - extra2d::TransitionType::Fade, 0.5f); -} - -} // namespace flappybird diff --git a/examples/flappy_bird/StartScene.h b/examples/flappy_bird/StartScene.h deleted file mode 100644 index 01cc56d..0000000 --- a/examples/flappy_bird/StartScene.h +++ /dev/null @@ -1,53 +0,0 @@ -// ============================================================================ -// StartScene.h - 开始菜单场景 -// 描述: 显示游戏标题、开始按钮和版权信息 -// ============================================================================ - -#pragma once - -#include "BaseScene.h" - -namespace flappybird { - -/** - * @brief 开始场景类 - * 游戏主菜单,包含开始游戏按钮和版权信息 - */ -class StartScene : public BaseScene { -public: - /** - * @brief 构造函数 - */ - StartScene(); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 每帧更新时调用 - * @param dt 时间间隔(秒) - */ - void onUpdate(float dt) override; - -private: - /** - * @brief 创建菜单按钮 - */ - void createMenuButtons(); - - /** - * @brief 开始游戏 - */ - void startGame(); - - extra2d::Ptr playBtn_; // 开始按钮 - extra2d::Ptr shareBtn_; // 分享按钮 - extra2d::Ptr title_; // 标题精灵 - float titleFinalY_ = 0.0f; // 标题最终Y位置 - float titleAnimTime_ = 0.0f; // 标题动画时间 - static constexpr float TITLE_ANIM_DURATION = 0.5f; // 标题动画持续时间 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/bird.cpp b/examples/flappy_bird/bird.cpp deleted file mode 100644 index a7cdae9..0000000 --- a/examples/flappy_bird/bird.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// ============================================================================ -// Bird.cpp - 小鸟类实现 -// ============================================================================ - -#include "Bird.h" -#include "ResLoader.h" - -namespace flappybird { - -Bird::Bird() { - // 注意:不要在构造函数中调用 initAnimations() - // 因为此时 weak_from_this() 还不能使用 - setStatus(Status::Idle); -} - -void Bird::onEnter() { - Node::onEnter(); - // 在 onEnter 中初始化动画,此时 weak_from_this() 可用 - if (!animSprite_) { - initAnimations(); - } -} - -Bird::~Bird() = default; - -void Bird::initAnimations() { - // 随机选择小鸟颜色(0-2) - int colorMode = extra2d::randomInt(0, 2); - std::string prefix = "bird" + std::to_string(colorMode) + "_"; - - // 创建动画片段 - auto clip = extra2d::AnimationClip::create("bird_fly"); - - // 添加动画帧序列: 0 -> 1 -> 2 -> 1 - // 注意:每个颜色只有 0, 1, 2 三个帧,没有 3 - int frameSequence[] = {0, 1, 2, 1}; - for (int frameIndex : frameSequence) { - auto frameSprite = ResLoader::getKeyFrame(prefix + std::to_string(frameIndex)); - if (frameSprite) { - extra2d::AnimationFrame frame; - frame.spriteFrame = frameSprite; - frame.delay = 100.0f; // 100毫秒 = 0.1秒 - clip->addFrame(std::move(frame)); - } else { - E2D_LOG_WARN("无法加载动画帧: {}{}", prefix, frameIndex); - } - } - - // 创建动画精灵 - if (clip->getFrameCount() > 0) { - clip->setLooping(true); - animSprite_ = extra2d::AnimatedSprite::create(clip); - // 精灵图动画不应应用帧变换(避免覆盖节点位置) - animSprite_->setApplyFrameTransform(false); - animSprite_->play(); - addChild(animSprite_); - E2D_LOG_INFO("小鸟动画创建成功: 颜色={}, 帧数={}, running={}, animSprite父节点={}", - colorMode, clip->getFrameCount(), isRunning(), - animSprite_->getParent() ? "有" : "无"); - } else { - E2D_LOG_ERROR("小鸟动画创建失败: 没有找到任何动画帧"); - } -} - -void Bird::onUpdate(float dt) { - extra2d::Node::onUpdate(dt); - - // 处理闲置动画(上下浮动) - if (status_ == Status::Idle) { - idleTimer_ += dt; - idleOffset_ = std::sin(idleTimer_ * 5.0f) * 4.0f; - } -} - -void Bird::onRender(extra2d::RenderBackend& renderer) { - // 动画精灵会自动渲染,这里只需要处理旋转和偏移 - if (animSprite_) { - animSprite_->setRotation(rotation_); - - // 应用闲置偏移 - if (status_ == Status::Idle) { - animSprite_->setPosition(extra2d::Vec2(0.0f, idleOffset_)); - } else { - animSprite_->setPosition(extra2d::Vec2(0.0f, 0.0f)); - } - } - - // 调用父类的 onRender 来渲染子节点 - Node::onRender(renderer); -} - -void Bird::fall(float dt) { - if (!living_) return; - - // 更新垂直位置 - extra2d::Vec2 pos = getPosition(); - pos.y += speed_ * dt; - setPosition(pos); - - // 应用重力 - speed_ += gravity * dt; - - // 限制顶部边界 - if (pos.y < 0) { - pos.y = 0; - setPosition(pos); - speed_ = 0; - } - - // 根据速度计算旋转角度 - // 上升时抬头(-15度),下降时低头(最大90度) - if (speed_ < 0) { - rotation_ = -15.0f; - } else { - rotation_ = std::min(90.0f, speed_ * 0.15f); - } -} - -void Bird::jump() { - if (!living_) return; - - // 给小鸟向上的速度 - speed_ = -jumpSpeed; - - // 设置状态为飞行 - setStatus(Status::Fly); - - // 播放音效 - ResLoader::playMusic(MusicType::Fly); -} - -void Bird::die() { - living_ = false; - - // 播放死亡音效 - ResLoader::playMusic(MusicType::Hit); -} - -void Bird::setStatus(Status status) { - status_ = status; - - switch (status) { - case Status::Still: - // 停止所有动画 - if (animSprite_) { - animSprite_->pause(); - } - break; - - case Status::Idle: - // 开始闲置动画 - if (animSprite_) { - animSprite_->setPlaybackSpeed(1.0f); // 正常速度 - animSprite_->play(); - } - idleTimer_ = 0.0f; - break; - - case Status::StartToFly: - // 停止闲置动画,加速翅膀扇动 - idleOffset_ = 0.0f; - if (animSprite_) { - animSprite_->setPlaybackSpeed(2.0f); // 2倍速度 = 0.05秒每帧 - } - break; - - case Status::Fly: - // 飞行状态 - break; - - default: - break; - } -} - -extra2d::Rect Bird::getBoundingBox() const { - extra2d::Vec2 pos = getPosition(); - // 小鸟碰撞框大小约为 24x24 - float halfSize = 12.0f; - return extra2d::Rect( - pos.x - halfSize, - pos.y - halfSize, - halfSize * 2.0f, - halfSize * 2.0f - ); -} - -} // namespace flappybird diff --git a/examples/flappy_bird/bird.h b/examples/flappy_bird/bird.h deleted file mode 100644 index 560b815..0000000 --- a/examples/flappy_bird/bird.h +++ /dev/null @@ -1,116 +0,0 @@ -// ============================================================================ -// Bird.h - 小鸟类 -// 描述: 玩家控制的小鸟角色,包含飞行动画和物理效果 -// ============================================================================ - -#pragma once - -#include - -namespace flappybird { - -/** - * @brief 小鸟类 - * 游戏主角,包含飞行动画、重力模拟和状态管理 - */ -class Bird : public extra2d::Node { -public: - /** - * @brief 小鸟状态枚举 - */ - enum class Status { - Still, // 静止不动 - Idle, // 上下浮动(菜单展示) - StartToFly, // 开始飞行 - Fly // 飞行中 - }; - - /** - * @brief 构造函数 - */ - Bird(); - - /** - * @brief 析构函数 - */ - ~Bird(); - - /** - * @brief 每帧更新 - * @param dt 时间间隔(秒) - */ - void onUpdate(float dt) override; - - /** - * @brief 渲染小鸟 - * @param renderer 渲染后端 - */ - void onRender(extra2d::RenderBackend& renderer) override; - - /** - * @brief 进入场景时调用 - */ - void onEnter() override; - - /** - * @brief 模拟下落 - * @param dt 时间间隔(秒) - */ - void fall(float dt); - - /** - * @brief 跳跃 - */ - void jump(); - - /** - * @brief 死亡 - */ - void die(); - - /** - * @brief 设置小鸟状态 - * @param status 新状态 - */ - void setStatus(Status status); - - /** - * @brief 获取当前状态 - * @return 当前状态 - */ - Status getStatus() const { return status_; } - - /** - * @brief 检查是否存活 - * @return 是否存活 - */ - bool isLiving() const { return living_; } - - /** - * @brief 获取边界框(用于碰撞检测) - * @return 边界框 - */ - extra2d::Rect getBoundingBox() const override; - -private: - /** - * @brief 初始化动画 - */ - void initAnimations(); - - bool living_ = true; // 是否存活 - float speed_ = 0.0f; // 垂直速度 - float rotation_ = 0.0f; // 旋转角度 - Status status_ = Status::Idle; // 当前状态 - - // 动画相关 - extra2d::Ptr animSprite_; // 动画精灵 - float idleTimer_ = 0.0f; // 闲置动画计时器 - float idleOffset_ = 0.0f; // 闲置偏移量 - - // 物理常量 - static constexpr float gravity = 1440.0f; // 重力加速度 - static constexpr float jumpSpeed = 432.0f; // 跳跃初速度 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/ground.cpp b/examples/flappy_bird/ground.cpp deleted file mode 100644 index 1592b68..0000000 --- a/examples/flappy_bird/ground.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// ============================================================================ -// Ground.cpp - 地面类实现 -// ============================================================================ - -#include "Ground.h" -#include "ResLoader.h" -#include "BaseScene.h" - -namespace flappybird { - -Ground::Ground() { - moving_ = true; - - // 使用游戏逻辑高度,而不是窗口高度 - float screenHeight = GAME_HEIGHT; - - // 获取地面纹理帧 - auto landFrame = ResLoader::getKeyFrame("land"); - if (!landFrame) return; - - // 获取地面纹理和矩形 - auto texture = landFrame->getTexture(); - auto rect = landFrame->getRect(); - float groundWidth = rect.size.width; - float groundHeight = rect.size.height; - - // 创建第一块地面 - ground1_ = extra2d::Sprite::create(texture, rect); - ground1_->setAnchor(extra2d::Vec2(0.0f, 1.0f)); // 锚点设在左下角 - ground1_->setPosition(extra2d::Vec2(0.0f, screenHeight)); - addChild(ground1_); - - // 创建第二块地面,紧挨在第一块右边 - ground2_ = extra2d::Sprite::create(texture, rect); - ground2_->setAnchor(extra2d::Vec2(0.0f, 1.0f)); - ground2_->setPosition(extra2d::Vec2(groundWidth - 1.0f, screenHeight)); - addChild(ground2_); -} - -void Ground::onUpdate(float dt) { - extra2d::Node::onUpdate(dt); - - if (!moving_) return; - if (!ground1_ || !ground2_) return; - - // 获取地面宽度(从纹理矩形获取) - float groundWidth = ground1_->getTextureRect().size.width; - - // 移动两块地面 - extra2d::Vec2 pos1 = ground1_->getPosition(); - extra2d::Vec2 pos2 = ground2_->getPosition(); - - pos1.x -= speed * dt; - pos2.x -= speed * dt; - - // 当地面完全移出屏幕左侧时,重置到右侧 - if (pos1.x <= -groundWidth) { - pos1.x = pos2.x + groundWidth - 1.0f; - } - if (pos2.x <= -groundWidth) { - pos2.x = pos1.x + groundWidth - 1.0f; - } - - ground1_->setPosition(pos1); - ground2_->setPosition(pos2); -} - -void Ground::stop() { - moving_ = false; -} - -float Ground::getHeight() const { - auto landFrame = ResLoader::getKeyFrame("land"); - return landFrame ? landFrame->getRect().size.height : 112.0f; -} - -} // namespace flappybird diff --git a/examples/flappy_bird/ground.h b/examples/flappy_bird/ground.h deleted file mode 100644 index 1f23986..0000000 --- a/examples/flappy_bird/ground.h +++ /dev/null @@ -1,48 +0,0 @@ -// ============================================================================ -// Ground.h - 地面类 -// 描述: 游戏底部不断向左滚动的地面 -// ============================================================================ - -#pragma once - -#include - -namespace flappybird { - -/** - * @brief 地面类 - * 游戏底部的滚动地面,由两块地面拼接而成 - */ -class Ground : public extra2d::Node { -public: - /** - * @brief 构造函数 - */ - Ground(); - - /** - * @brief 每帧更新 - * @param dt 时间间隔(秒) - */ - void onUpdate(float dt) override; - - /** - * @brief 停止地面滚动 - */ - void stop(); - - /** - * @brief 获取地面高度 - * @return 地面高度 - */ - float getHeight() const; - -private: - extra2d::Ptr ground1_; // 第一块地面 - extra2d::Ptr ground2_; // 第二块地面 - - static constexpr float speed = 120.0f; // 滚动速度(像素/秒) - bool moving_ = true; // 是否正在滚动 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/main.cpp b/examples/flappy_bird/main.cpp deleted file mode 100644 index 14cff1e..0000000 --- a/examples/flappy_bird/main.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// ============================================================================ -// FlappyBird - Extra2D 示例程序 -// 作者: Extra2D Team -// 描述: 经典的 Flappy Bird 游戏实现 -// ============================================================================ - -#include "ResLoader.h" -#include "SplashScene.h" -#include - -using namespace extra2d; - -/** - * @brief 程序入口 - */ -int main(int argc, char **argv) { - // 初始化日志系统 - Logger::init(); - Logger::setLevel(LogLevel::Debug); - - E2D_LOG_INFO("========================"); - E2D_LOG_INFO("Extra2D FlappyBird"); - E2D_LOG_INFO("========================"); - - // 获取应用实例 - auto &app = Application::instance(); - - // 配置应用 - AppConfig config; - config.title = "Extra2D - FlappyBird"; - config.width = 1280; // 窗口宽度 (720P 分辨率) - config.height = 720; // 窗口高度 (720P 分辨率) - config.vsync = true; - config.fpsLimit = 60; - - // 初始化应用 - if (!app.init(config)) { - E2D_LOG_ERROR("应用初始化失败!"); - return -1; - } - - // 初始化资源加载器 - flappybird::ResLoader::init(); - - // 进入启动场景 - app.enterScene(makePtr()); - - E2D_LOG_INFO("开始主循环..."); - app.run(); - - E2D_LOG_INFO("应用结束"); - return 0; -} diff --git a/examples/flappy_bird/pipe.cpp b/examples/flappy_bird/pipe.cpp deleted file mode 100644 index 1f7432a..0000000 --- a/examples/flappy_bird/pipe.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// ============================================================================ -// Pipe.cpp - 水管类实现 -// ============================================================================ - -#include "Pipe.h" -#include "ResLoader.h" -#include "BaseScene.h" - -namespace flappybird { - -Pipe::Pipe() { - scored = false; - // 注意:不要在构造函数中创建子节点 - // 因为此时 weak_from_this() 还不能使用 -} - -void Pipe::onEnter() { - Node::onEnter(); - - // 在 onEnter 中创建子节点,此时 weak_from_this() 可用 - if (!topPipe_ && !bottomPipe_) { - // 使用游戏逻辑高度 - float screenHeight = GAME_HEIGHT; - - // 获取地面高度 - auto landFrame = ResLoader::getKeyFrame("land"); - float landHeight = landFrame ? landFrame->getRect().size.height : 112.0f; - - // 随机生成水管高度 - // 范围:与屏幕顶部最小距离不小于 100 像素 - // 与屏幕底部最小距离不小于地面上方 100 像素 - float minHeight = 100.0f; - float maxHeight = screenHeight - landHeight - 100.0f - gapHeight_; - float height = static_cast(extra2d::randomInt(static_cast(minHeight), static_cast(maxHeight))); - - // 创建上水管 - auto topFrame = ResLoader::getKeyFrame("pipe_above"); - if (topFrame) { - topPipe_ = extra2d::Sprite::create(topFrame->getTexture(), topFrame->getRect()); - topPipe_->setAnchor(extra2d::Vec2(0.5f, 1.0f)); // 锚点设在底部中心 - topPipe_->setPosition(extra2d::Vec2(0.0f, height - gapHeight_ / 2.0f)); - addChild(topPipe_); - } - - // 创建下水管 - auto bottomFrame = ResLoader::getKeyFrame("pipe_below"); - if (bottomFrame) { - bottomPipe_ = extra2d::Sprite::create(bottomFrame->getTexture(), bottomFrame->getRect()); - bottomPipe_->setAnchor(extra2d::Vec2(0.5f, 0.0f)); // 锚点设在顶部中心 - bottomPipe_->setPosition(extra2d::Vec2(0.0f, height + gapHeight_ / 2.0f)); - addChild(bottomPipe_); - } - } -} - -Pipe::~Pipe() = default; - -extra2d::Rect Pipe::getBoundingBox() const { - // 返回整个水管的边界框(包含上下两根) - extra2d::Vec2 pos = getPosition(); - - // 水管宽度约为 52 - float pipeWidth = 52.0f; - float halfWidth = pipeWidth / 2.0f; - - // 使用游戏逻辑高度 - float screenHeight = GAME_HEIGHT; - - return extra2d::Rect( - pos.x - halfWidth, - 0.0f, - pipeWidth, - screenHeight - ); -} - -extra2d::Rect Pipe::getTopPipeBox() const { - if (!topPipe_) return extra2d::Rect(); - - extra2d::Vec2 pos = getPosition(); - extra2d::Vec2 topPos = topPipe_->getPosition(); - - // 上水管尺寸 - float pipeWidth = 52.0f; - float pipeHeight = 320.0f; - - return extra2d::Rect( - pos.x - pipeWidth / 2.0f, - pos.y + topPos.y - pipeHeight, - pipeWidth, - pipeHeight - ); -} - -extra2d::Rect Pipe::getBottomPipeBox() const { - if (!bottomPipe_) return extra2d::Rect(); - - extra2d::Vec2 pos = getPosition(); - extra2d::Vec2 bottomPos = bottomPipe_->getPosition(); - - // 下水管尺寸 - float pipeWidth = 52.0f; - float pipeHeight = 320.0f; - - return extra2d::Rect( - pos.x - pipeWidth / 2.0f, - pos.y + bottomPos.y, - pipeWidth, - pipeHeight - ); -} - -} // namespace flappybird diff --git a/examples/flappy_bird/pipe.h b/examples/flappy_bird/pipe.h deleted file mode 100644 index 3ceda9b..0000000 --- a/examples/flappy_bird/pipe.h +++ /dev/null @@ -1,59 +0,0 @@ -// ============================================================================ -// Pipe.h - 水管类 -// 描述: 游戏中的障碍物,由上下两根水管组成 -// ============================================================================ - -#pragma once - -#include - -namespace flappybird { - -/** - * @brief 水管类 - * 由上下两根水管组成的障碍物 - */ -class Pipe : public extra2d::Node { -public: - /** - * @brief 构造函数 - */ - Pipe(); - - /** - * @brief 析构函数 - */ - ~Pipe(); - - /** - * @brief 进入场景时调用 - */ - void onEnter() override; - - /** - * @brief 获取边界框(用于碰撞检测) - * @return 边界框 - */ - extra2d::Rect getBoundingBox() const override; - - /** - * @brief 获取上水管边界框 - * @return 边界框 - */ - extra2d::Rect getTopPipeBox() const; - - /** - * @brief 获取下水管边界框 - * @return 边界框 - */ - extra2d::Rect getBottomPipeBox() const; - - bool scored = false; // 是否已计分 - -private: - extra2d::Ptr topPipe_; // 上水管 - extra2d::Ptr bottomPipe_; // 下水管 - float gapHeight_ = 120.0f; // 间隙高度 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/pipes.cpp b/examples/flappy_bird/pipes.cpp deleted file mode 100644 index 7326175..0000000 --- a/examples/flappy_bird/pipes.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// ============================================================================ -// Pipes.cpp - 水管管理器实现 -// ============================================================================ - -#include "Pipes.h" -#include "BaseScene.h" - -namespace flappybird { - -Pipes::Pipes() { - pipeCount_ = 0; - moving_ = false; - - // 初始化水管数组 - for (int i = 0; i < maxPipes; ++i) { - pipes_[i] = nullptr; - } - - // 注意:不要在构造函数中添加水管 - // 因为此时 weak_from_this() 还不能使用 -} - -void Pipes::onEnter() { - Node::onEnter(); - // 在 onEnter 中初始化水管,此时 weak_from_this() 可用 - if (pipeCount_ == 0) { - addPipe(); - addPipe(); - addPipe(); - } -} - -Pipes::~Pipes() = default; - -void Pipes::onUpdate(float dt) { - extra2d::Node::onUpdate(dt); - - if (!moving_) return; - - // 移动所有水管 - for (int i = 0; i < pipeCount_; ++i) { - if (pipes_[i]) { - extra2d::Vec2 pos = pipes_[i]->getPosition(); - pos.x -= pipeSpeed * dt; - pipes_[i]->setPosition(pos); - } - } - - // 检查最前面的水管是否移出屏幕 - if (pipes_[0] && pipes_[0]->getPosition().x <= -30.0f) { - // 移除第一个水管(通过名称查找并移除) - // 由于 removeChild 需要 Ptr,我们使用 removeChildByName 或直接操作 - // 这里我们直接移除第一个子节点(假设它是水管) - auto children = getChildren(); - if (!children.empty()) { - removeChild(children[0]); - } - - // 将后面的水管前移 - for (int i = 0; i < pipeCount_ - 1; ++i) { - pipes_[i] = pipes_[i + 1]; - } - pipes_[pipeCount_ - 1] = nullptr; - pipeCount_--; - - // 添加新水管 - addPipe(); - } -} - -void Pipes::addPipe() { - if (pipeCount_ >= maxPipes) return; - - // 创建新水管 - auto pipe = extra2d::makePtr(); - - // 设置水管位置 - if (pipeCount_ == 0) { - // 第一个水管在屏幕外 130 像素处 - pipe->setPosition(extra2d::Vec2( - GAME_WIDTH + 130.0f, - 0.0f - )); - } else { - // 其他水管在前一个水管后方 - float prevX = pipes_[pipeCount_ - 1]->getPosition().x; - pipe->setPosition(extra2d::Vec2(prevX + pipeSpacing, 0.0f)); - } - - // 保存水管指针 - pipes_[pipeCount_] = pipe.get(); - pipeCount_++; - - // 添加到场景 - addChild(pipe); -} - -void Pipes::start() { - moving_ = true; -} - -void Pipes::stop() { - moving_ = false; -} - -} // namespace flappybird diff --git a/examples/flappy_bird/pipes.h b/examples/flappy_bird/pipes.h deleted file mode 100644 index 11dd88d..0000000 --- a/examples/flappy_bird/pipes.h +++ /dev/null @@ -1,71 +0,0 @@ -// ============================================================================ -// Pipes.h - 水管管理器 -// 描述: 管理多个水管的生成、移动和回收 -// ============================================================================ - -#pragma once - -#include -#include "Pipe.h" - -namespace flappybird { - -/** - * @brief 水管管理器类 - * 管理游戏中的所有水管,负责生成、移动和回收 - */ -class Pipes : public extra2d::Node { -public: - /** - * @brief 构造函数 - */ - Pipes(); - - /** - * @brief 析构函数 - */ - ~Pipes(); - - /** - * @brief 每帧更新 - * @param dt 时间间隔(秒) - */ - void onUpdate(float dt) override; - - /** - * @brief 进入场景时调用 - */ - void onEnter() override; - - /** - * @brief 开始移动水管 - */ - void start(); - - /** - * @brief 停止移动水管 - */ - void stop(); - - /** - * @brief 获取当前水管数组 - * @return 水管指针数组 - */ - Pipe* getPipe(int index) { return (index >= 0 && index < 3) ? pipes_[index] : nullptr; } - -private: - /** - * @brief 添加一根新水管 - */ - void addPipe(); - - static constexpr int maxPipes = 3; // 最大水管数量 - static constexpr float pipeSpeed = 120.0f; // 水管移动速度(像素/秒) - static constexpr float pipeSpacing = 200.0f; // 水管间距 - - Pipe* pipes_[maxPipes]; // 水管数组 - int pipeCount_ = 0; // 当前水管数量 - bool moving_ = false; // 是否正在移动 -}; - -} // namespace flappybird diff --git a/examples/flappy_bird/romfs/assets/images/atlas.json b/examples/flappy_bird/romfs/assets/images/atlas.json deleted file mode 100644 index ce507c0..0000000 --- a/examples/flappy_bird/romfs/assets/images/atlas.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "sprites": [ - {"name": "splash", "width": 288, "height": 512, "x": 292, "y": 515}, - {"name": "bg_day", "width": 288, "height": 512, "x": 0, "y": 0}, - {"name": "bg_night", "width": 288, "height": 512, "x": 292, "y": 0}, - {"name": "bird0_0", "width": 34, "height": 24, "x": 5, "y": 982}, - {"name": "bird0_1", "width": 34, "height": 24, "x": 61, "y": 982}, - {"name": "bird0_2", "width": 34, "height": 24, "x": 117, "y": 982}, - {"name": "bird1_0", "width": 34, "height": 24, "x": 173, "y": 982}, - {"name": "bird1_1", "width": 34, "height": 24, "x": 229, "y": 658}, - {"name": "bird1_2", "width": 34, "height": 24, "x": 229, "y": 710}, - {"name": "bird2_0", "width": 34, "height": 24, "x": 229, "y": 762}, - {"name": "bird2_1", "width": 34, "height": 24, "x": 229, "y": 814}, - {"name": "bird2_2", "width": 34, "height": 24, "x": 229, "y": 866}, - {"name": "black", "width": 32, "height": 32, "x": 584, "y": 412}, - {"name": "blink_00", "width": 10, "height": 10, "x": 276, "y": 682}, - {"name": "blink_01", "width": 10, "height": 10, "x": 276, "y": 734}, - {"name": "blink_02", "width": 10, "height": 10, "x": 276, "y": 786}, - {"name": "brand_copyright", "width": 126, "height": 14, "x": 884, "y": 182}, - {"name": "button_ok", "width": 80, "height": 28, "x": 924, "y": 84}, - {"name": "button_pause", "width": 26, "height": 28, "x": 242, "y": 612}, - {"name": "button_rate", "width": 74, "height": 48, "x": 924, "y": 0}, - {"name": "button_resume", "width": 26, "height": 28, "x": 668, "y": 284}, - {"name": "button_score", "width": 116, "height": 70, "x": 822, "y": 234}, - {"name": "button_restart", "width": 116, "height": 70, "x": 702, "y": 316}, - {"name": "button_share_big", "width": 116, "height": 70, "x": 822, "y": 316}, - {"name": "button_play", "width": 116, "height": 70, "x": 702, "y": 234}, - {"name": "button_menu", "width": 80, "height": 28, "x": 924, "y": 52}, - {"name": "button_share", "width": 80, "height": 28, "x": 584, "y": 284}, - {"name": "land", "width": 336, "height": 112, "x": 584, "y": 0}, - {"name": "medals_0", "width": 44, "height": 44, "x": 224, "y": 954}, - {"name": "medals_1", "width": 44, "height": 44, "x": 224, "y": 906}, - {"name": "medals_2", "width": 44, "height": 44, "x": 242, "y": 564}, - {"name": "medals_3", "width": 44, "height": 44, "x": 242, "y": 516}, - {"name": "new", "width": 32, "height": 14, "x": 224, "y": 1002}, - {"name": "number_big_0", "width": 24, "height": 36, "x": 992, "y": 120}, - {"name": "number_big_1", "width": 16, "height": 36, "x": 272, "y": 910}, - {"name": "number_big_2", "width": 24, "height": 36, "x": 584, "y": 320}, - {"name": "number_big_3", "width": 24, "height": 36, "x": 612, "y": 320}, - {"name": "number_big_4", "width": 24, "height": 36, "x": 640, "y": 320}, - {"name": "number_big_5", "width": 24, "height": 36, "x": 668, "y": 320}, - {"name": "number_big_6", "width": 24, "height": 36, "x": 584, "y": 368}, - {"name": "number_big_7", "width": 24, "height": 36, "x": 612, "y": 368}, - {"name": "number_big_8", "width": 24, "height": 36, "x": 640, "y": 368}, - {"name": "number_big_9", "width": 24, "height": 36, "x": 668, "y": 368}, - {"name": "number_medium_0", "width": 16, "height": 20, "x": 272, "y": 612}, - {"name": "number_medium_1", "width": 16, "height": 20, "x": 272, "y": 954}, - {"name": "number_medium_2", "width": 16, "height": 20, "x": 272, "y": 978}, - {"name": "number_medium_3", "width": 16, "height": 20, "x": 260, "y": 1002}, - {"name": "number_medium_4", "width": 16, "height": 20, "x": 1002, "y": 0}, - {"name": "number_medium_5", "width": 16, "height": 20, "x": 1002, "y": 24}, - {"name": "number_medium_6", "width": 16, "height": 20, "x": 1008, "y": 52}, - {"name": "number_medium_7", "width": 16, "height": 20, "x": 1008, "y": 84}, - {"name": "number_medium_8", "width": 16, "height": 20, "x": 584, "y": 484}, - {"name": "number_medium_9", "width": 16, "height": 20, "x": 620, "y": 412}, - {"name": "number_small_0", "width": 12, "height": 14, "x": 276, "y": 646}, - {"name": "number_small_1", "width": 12, "height": 14, "x": 276, "y": 664}, - {"name": "number_small_2", "width": 12, "height": 14, "x": 276, "y": 698}, - {"name": "number_small_3", "width": 12, "height": 14, "x": 276, "y": 716}, - {"name": "number_small_4", "width": 12, "height": 14, "x": 276, "y": 750}, - {"name": "number_small_5", "width": 12, "height": 14, "x": 276, "y": 768}, - {"name": "number_small_6", "width": 12, "height": 14, "x": 276, "y": 802}, - {"name": "number_small_7", "width": 12, "height": 14, "x": 276, "y": 820}, - {"name": "number_small_8", "width": 12, "height": 14, "x": 276, "y": 854}, - {"name": "number_small_9", "width": 12, "height": 14, "x": 276, "y": 872}, - {"name": "number_context_10", "width": 12, "height": 14, "x": 992, "y": 164}, - {"name": "pipe_above_2", "width": 52, "height": 320, "x": 0, "y": 646}, - {"name": "pipe_below_2", "width": 52, "height": 320, "x": 56, "y": 646}, - {"name": "pipe_above", "width": 52, "height": 320, "x": 112, "y": 646}, - {"name": "pipe_below", "width": 52, "height": 320, "x": 168, "y": 646}, - {"name": "score_panel", "width": 238, "height": 126, "x": 0, "y": 516}, - {"name": "text_game_over", "width": 204, "height": 54, "x": 784, "y": 116}, - {"name": "text_ready", "width": 196, "height": 62, "x": 584, "y": 116}, - {"name": "title", "width": 178, "height": 48, "x": 702, "y": 182}, - {"name": "tutorial", "width": 114, "height": 98, "x": 584, "y": 182}, - {"name": "white", "width": 32, "height": 32, "x": 584, "y": 448} - ] -} \ No newline at end of file diff --git a/examples/flappy_bird/romfs/assets/images/atlas.png b/examples/flappy_bird/romfs/assets/images/atlas.png deleted file mode 100644 index 05e5ce1..0000000 Binary files a/examples/flappy_bird/romfs/assets/images/atlas.png and /dev/null differ diff --git a/examples/flappy_bird/romfs/assets/sound/click.wav b/examples/flappy_bird/romfs/assets/sound/click.wav deleted file mode 100644 index aef35cd..0000000 Binary files a/examples/flappy_bird/romfs/assets/sound/click.wav and /dev/null differ diff --git a/examples/flappy_bird/romfs/assets/sound/fly.wav b/examples/flappy_bird/romfs/assets/sound/fly.wav deleted file mode 100644 index 940adf2..0000000 Binary files a/examples/flappy_bird/romfs/assets/sound/fly.wav and /dev/null differ diff --git a/examples/flappy_bird/romfs/assets/sound/hit.wav b/examples/flappy_bird/romfs/assets/sound/hit.wav deleted file mode 100644 index 0fe1cf7..0000000 Binary files a/examples/flappy_bird/romfs/assets/sound/hit.wav and /dev/null differ diff --git a/examples/flappy_bird/romfs/assets/sound/point.wav b/examples/flappy_bird/romfs/assets/sound/point.wav deleted file mode 100644 index eb3961a..0000000 Binary files a/examples/flappy_bird/romfs/assets/sound/point.wav and /dev/null differ diff --git a/examples/flappy_bird/romfs/assets/sound/swoosh.wav b/examples/flappy_bird/romfs/assets/sound/swoosh.wav deleted file mode 100644 index e218eac..0000000 Binary files a/examples/flappy_bird/romfs/assets/sound/swoosh.wav and /dev/null differ diff --git a/examples/flappy_bird/xmake.lua b/examples/flappy_bird/xmake.lua deleted file mode 100644 index 9959fce..0000000 --- a/examples/flappy_bird/xmake.lua +++ /dev/null @@ -1,77 +0,0 @@ --- ============================================== --- FlappyBird 示例 - Xmake 构建脚本 --- 支持平台: MinGW (Windows), Nintendo Switch --- ============================================== - --- 获取当前脚本所在目录(示例根目录) -local example_dir = os.scriptdir() - --- 可执行文件目标 -target("flappy_bird") - set_kind("binary") - add_files("*.cpp") - add_includedirs("../../Extra2D/include") - add_includedirs("../../Extra2D/include/json") - add_deps("extra2d") - - -- 使用与主项目相同的平台配置 - if is_plat("switch") then - set_targetdir("../../build/examples/flappy_bird") - - -- 构建后生成 NRO 文件 - after_build(function (target) - local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro" - local elf_file = target:targetfile() - local output_dir = path.directory(elf_file) - local nacp_file = path.join(output_dir, "flappy_bird.nacp") - local nro_file = path.join(output_dir, "flappy_bird.nro") - local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe") - local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe") - - if os.isfile(nacptool) and os.isfile(elf2nro) then - os.vrunv(nacptool, {"--create", "FlappyBird", "Extra2D Team", "1.0.0", nacp_file}) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs}) - else - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file}) - end - print("Generated NRO: " .. nro_file) - end - end) - - -- 打包时将 NRO 文件复制到 package 目录 - after_package(function (target) - local nro_file = path.join(target:targetdir(), "flappy_bird.nro") - local package_dir = target:packagedir() - if os.isfile(nro_file) and package_dir then - os.cp(nro_file, package_dir) - print("Copied NRO to package: " .. package_dir) - end - end) - - elseif is_plat("mingw") then - set_targetdir("../../build/examples/flappy_bird") - -- add_ldflags("-mwindows", {force = true}) - - -- 复制资源到输出目录 - after_build(function (target) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - local target_dir = path.directory(target:targetfile()) - local assets_dir = path.join(target_dir, "assets") - - -- 创建 assets 目录 - if not os.isdir(assets_dir) then - os.mkdir(assets_dir) - end - - -- 复制所有资源文件(包括子目录) - os.cp(path.join(romfs, "assets/**"), assets_dir) - print("Copied assets from " .. romfs .. " to " .. assets_dir) - else - print("Warning: romfs directory not found at " .. romfs) - end - end) - end -target_end() diff --git a/examples/push_box/BaseScene.cpp b/examples/push_box/BaseScene.cpp deleted file mode 100644 index 888af25..0000000 --- a/examples/push_box/BaseScene.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// ============================================================================ -// BaseScene.cpp - Push Box 基础场景实现 -// ============================================================================ - -#include "BaseScene.h" -#include -#include - -namespace pushbox { - -BaseScene::BaseScene() { - // 设置背景颜色为黑色(窗口四周会显示这个颜色) - setBackgroundColor(extra2d::Color(0.0f, 0.0f, 0.0f, 1.0f)); -} - -void BaseScene::onEnter() { - extra2d::Scene::onEnter(); - // 计算并更新视口 - updateViewport(); -} - -/** - * @brief 更新视口计算,使游戏内容在窗口中居中显示 - */ -void BaseScene::updateViewport() { - auto &app = extra2d::Application::instance(); - float windowWidth = static_cast(app.window().getWidth()); - float windowHeight = static_cast(app.window().getHeight()); - - // 计算游戏内容在窗口中的居中位置 - // 保持游戏原始宽高比,进行"黑边"适配 - float scaleX = windowWidth / GAME_WIDTH; - float scaleY = windowHeight / GAME_HEIGHT; - // 使用较小的缩放比例,确保游戏内容完整显示在窗口中 - float scale = std::min(scaleX, scaleY); - - scaledGameWidth_ = GAME_WIDTH * scale; - scaledGameHeight_ = GAME_HEIGHT * scale; - // 计算居中偏移,使游戏内容在窗口中水平和垂直居中 - viewportOffsetX_ = (windowWidth - scaledGameWidth_) * 0.5f; - viewportOffsetY_ = (windowHeight - scaledGameHeight_) * 0.5f; - - // 设置视口大小为游戏逻辑分辨率 - setViewportSize(GAME_WIDTH, GAME_HEIGHT); - - // 创建并设置相机 - auto camera = extra2d::makePtr(); - // 设置正交投影,覆盖整个游戏逻辑区域 - // 注意:对于2D游戏,Y轴向下增长,所以bottom > top - camera->setViewport(0.0f, GAME_WIDTH, GAME_HEIGHT, 0.0f); - setCamera(camera); -} - -/** - * @brief 渲染时调用,设置居中视口 - * @param renderer 渲染后端 - */ -void BaseScene::onRender(extra2d::RenderBackend &renderer) { - // 检查窗口大小是否改变,如果改变则更新视口 - auto &app = extra2d::Application::instance(); - float currentWindowWidth = static_cast(app.window().getWidth()); - float currentWindowHeight = static_cast(app.window().getHeight()); - - // 如果窗口大小改变,重新计算视口 - float expectedWidth = scaledGameWidth_ + viewportOffsetX_ * 2.0f; - float expectedHeight = scaledGameHeight_ + viewportOffsetY_ * 2.0f; - if (std::abs(currentWindowWidth - expectedWidth) > 1.0f || - std::abs(currentWindowHeight - expectedHeight) > 1.0f) { - E2D_LOG_INFO("BaseScene::onRender - window size changed from ({} x {}) to " - "({} x {}), updating viewport", - expectedWidth, expectedHeight, currentWindowWidth, - currentWindowHeight); - updateViewport(); - } - - // 设置视口为居中区域 - renderer.setViewport( - static_cast(viewportOffsetX_), static_cast(viewportOffsetY_), - static_cast(scaledGameWidth_), static_cast(scaledGameHeight_)); - - // 调用父类的 onRender 进行实际渲染 - extra2d::Scene::onRender(renderer); -} - -/** - * @brief 渲染场景内容,确保视口正确设置 - * @param renderer 渲染后端 - */ -void BaseScene::renderContent(extra2d::RenderBackend &renderer) { - // 如果视口参数未初始化(onEnter 还没被调用),先初始化 - if (scaledGameWidth_ <= 0.0f || scaledGameHeight_ <= 0.0f) { - updateViewport(); - } - - // 检查窗口大小是否改变 - auto &app = extra2d::Application::instance(); - float currentWindowWidth = static_cast(app.window().getWidth()); - float currentWindowHeight = static_cast(app.window().getHeight()); - - float expectedWidth = scaledGameWidth_ + viewportOffsetX_ * 2.0f; - float expectedHeight = scaledGameHeight_ + viewportOffsetY_ * 2.0f; - if (std::abs(currentWindowWidth - expectedWidth) > 1.0f || - std::abs(currentWindowHeight - expectedHeight) > 1.0f) { - updateViewport(); - } - - // 检查当前场景是否作为 TransitionScene 的子场景被渲染 - bool isChildOfTransition = false; - if (auto parent = getParent()) { - if (dynamic_cast(parent.get())) { - isChildOfTransition = true; - } - } - - if (isChildOfTransition) { - // 作为 TransitionScene 的子场景时,需要设置正确的投影矩阵 - // 使用游戏逻辑分辨率作为投影区域,让 TransitionScene 控制整体视口 - auto camera = getActiveCamera(); - if (camera) { - // 设置投影矩阵覆盖整个游戏逻辑区域 - renderer.setViewProjection(camera->getViewProjectionMatrix()); - } - // 渲染场景内容(投影矩阵已设置,直接渲染) - batchUpdateTransforms(); - renderer.beginSpriteBatch(); - render(renderer); - renderer.endSpriteBatch(); - } else { - // 正常渲染时,调用父类的 renderContent 处理视口和投影 - renderer.setViewport(static_cast(viewportOffsetX_), - static_cast(viewportOffsetY_), - static_cast(scaledGameWidth_), - static_cast(scaledGameHeight_)); - extra2d::Scene::renderContent(renderer); - } -} - -} // namespace pushbox diff --git a/examples/push_box/BaseScene.h b/examples/push_box/BaseScene.h deleted file mode 100644 index 2799ee0..0000000 --- a/examples/push_box/BaseScene.h +++ /dev/null @@ -1,57 +0,0 @@ -// ============================================================================ -// BaseScene.h - Push Box 基础场景类 -// 描述: 提供统一的居中视口适配功能,所有游戏场景都应继承此类 -// ============================================================================ - -#pragma once - -#include - -namespace pushbox { - -// 游戏逻辑分辨率 -static constexpr float GAME_WIDTH = 640.0f; -static constexpr float GAME_HEIGHT = 480.0f; - -/** - * @brief Push Box 基础场景类 - * 所有游戏场景都应继承此类,以获得统一的居中视口适配功能 - */ -class BaseScene : public extra2d::Scene { -public: - /** - * @brief 构造函数 - */ - BaseScene(); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 渲染时调用,设置居中视口 - * @param renderer 渲染后端 - */ - void onRender(extra2d::RenderBackend &renderer) override; - - /** - * @brief 渲染场景内容,确保视口正确设置 - * @param renderer 渲染后端 - */ - void renderContent(extra2d::RenderBackend &renderer) override; - -protected: - /** - * @brief 更新视口计算,使游戏内容在窗口中居中显示 - */ - void updateViewport(); - - // 视口适配参数(用于在窗口中居中显示游戏内容) - float scaledGameWidth_ = 0.0f; // 缩放后的游戏宽度 - float scaledGameHeight_ = 0.0f; // 缩放后的游戏高度 - float viewportOffsetX_ = 0.0f; // 视口水平偏移 - float viewportOffsetY_ = 0.0f; // 视口垂直偏移 -}; - -} // namespace pushbox diff --git a/examples/push_box/PlayScene.cpp b/examples/push_box/PlayScene.cpp deleted file mode 100644 index 429fac2..0000000 --- a/examples/push_box/PlayScene.cpp +++ /dev/null @@ -1,487 +0,0 @@ -// ============================================================================ -// PlayScene.cpp - Push Box 游戏场景实现 -// ============================================================================ - -#include "PlayScene.h" - -#include "StartScene.h" -#include "SuccessScene.h" -#include "audio_manager.h" -#include "storage.h" -#include -#include - -namespace pushbox { - -/** - * @brief 加载字体 - * @param size 字体大小 - */ -static extra2d::Ptr loadFont(int size) { - auto &resources = extra2d::Application::instance().resources(); - auto font = resources.loadFont("assets/font.ttf", size); - return font; -} - -PlayScene::PlayScene(int level) : BaseScene() { - auto &app = extra2d::Application::instance(); - auto &resources = app.resources(); - - E2D_LOG_INFO("PlayScene: Loading textures..."); - - texWall_ = resources.loadTexture("assets/images/wall.gif"); - texPoint_ = resources.loadTexture("assets/images/point.gif"); - texFloor_ = resources.loadTexture("assets/images/floor.gif"); - texBox_ = resources.loadTexture("assets/images/box.gif"); - texBoxInPoint_ = resources.loadTexture("assets/images/boxinpoint.gif"); - - texMan_[1] = resources.loadTexture("assets/images/player/manup.gif"); - texMan_[2] = resources.loadTexture("assets/images/player/mandown.gif"); - texMan_[3] = resources.loadTexture("assets/images/player/manleft.gif"); - texMan_[4] = resources.loadTexture("assets/images/player/manright.gif"); - - texManPush_[1] = resources.loadTexture("assets/images/player/manhandup.gif"); - texManPush_[2] = - resources.loadTexture("assets/images/player/manhanddown.gif"); - texManPush_[3] = - resources.loadTexture("assets/images/player/manhandleft.gif"); - texManPush_[4] = - resources.loadTexture("assets/images/player/manhandright.gif"); - - font28_ = loadFont(28); - font20_ = loadFont(20); - - // 使用游戏逻辑分辨率 - float screenW = GAME_WIDTH; - float screenH = GAME_HEIGHT; - - // 计算游戏区域居中偏移 - float offsetX = (screenW - GAME_WIDTH) / 2.0f; - float offsetY = (screenH - GAME_HEIGHT) / 2.0f; - - // 音效开关按钮(使用 Button 的切换模式) - auto soundOn = resources.loadTexture("assets/images/soundon.png"); - auto soundOff = resources.loadTexture("assets/images/soundoff.png"); - if (soundOn && soundOff) { - soundBtn_ = extra2d::Button::create(); - soundBtn_->setToggleMode(true); - soundBtn_->setStateBackgroundImage(soundOff, soundOn); - soundBtn_->setOn(g_SoundOpen); - soundBtn_->setAnchor(0.0f, 0.0f); - soundBtn_->setPosition(offsetX + 50.0f, offsetY + 50.0f); - soundBtn_->setOnStateChange([](bool isOn) { - g_SoundOpen = isOn; - AudioManager::instance().setEnabled(isOn); - }); - addChild(soundBtn_); - } - - levelText_ = extra2d::Text::create("", font28_); - levelText_->setPosition(offsetX + 520.0f, offsetY + 30.0f); - levelText_->setTextColor(extra2d::Colors::White); - addChild(levelText_); - - stepText_ = extra2d::Text::create("", font20_); - stepText_->setPosition(offsetX + 520.0f, offsetY + 100.0f); - stepText_->setTextColor(extra2d::Colors::White); - addChild(stepText_); - - bestText_ = extra2d::Text::create("", font20_); - bestText_->setPosition(offsetX + 520.0f, offsetY + 140.0f); - bestText_->setTextColor(extra2d::Colors::White); - addChild(bestText_); - - // 创建菜单文本(使用颜色变化指示选中) - restartText_ = extra2d::Text::create("Y键重开", font20_); - restartText_->setPosition(offsetX + 520.0f, offsetY + 290.0f); - addChild(restartText_); - - soundToggleText_ = extra2d::Text::create("X键切换音效", font20_); - soundToggleText_->setPosition(offsetX + 520.0f, offsetY + 330.0f); - addChild(soundToggleText_); - - // 撤销提示(对象池使用示例) - undoText_ = extra2d::Text::create("Z键撤销", font20_); - undoText_->setPosition(offsetX + 520.0f, offsetY + 370.0f); - addChild(undoText_); - - mapLayer_ = extra2d::makePtr(); - mapLayer_->setAnchor(0.0f, 0.0f); - mapLayer_->setPosition(0.0f, 0.0f); - addChild(mapLayer_); - - setLevel(level); -} - -void PlayScene::onEnter() { - BaseScene::onEnter(); - if (soundBtn_) { - soundBtn_->setOn(g_SoundOpen); - } - updateMenuColors(); -} - -/** - * @brief 更新菜单颜色 - */ -void PlayScene::updateMenuColors() { - // 选中的项用红色,未选中的用白色 - if (restartText_) { - restartText_->setTextColor(menuIndex_ == 0 ? extra2d::Colors::Red - : extra2d::Colors::White); - } - if (soundToggleText_) { - soundToggleText_->setTextColor(menuIndex_ == 1 ? extra2d::Colors::Red - : extra2d::Colors::White); - } - if (undoText_) { - undoText_->setTextColor(menuIndex_ == 2 ? extra2d::Colors::Red - : extra2d::Colors::White); - } -} - -void PlayScene::onUpdate(float dt) { - BaseScene::onUpdate(dt); - - auto &app = extra2d::Application::instance(); - auto &input = app.input(); - - // B 键返回主菜单 - if (input.isButtonPressed(extra2d::GamepadButton::B)) { - app.scenes().replaceScene(extra2d::makePtr(), - extra2d::TransitionType::Fade, 0.5f); - return; - } - - // Y 键重开 - if (input.isButtonPressed(extra2d::GamepadButton::Y)) { - setLevel(g_CurrentLevel); - return; - } - - // X键直接切换音效(备用,按钮也可点击切换) - if (input.isButtonPressed(extra2d::GamepadButton::X)) { - g_SoundOpen = !g_SoundOpen; - AudioManager::instance().setEnabled(g_SoundOpen); - if (soundBtn_) { - soundBtn_->setOn(g_SoundOpen); - } - return; - } - - // Z 键撤销(对象池使用示例) - if (input.isKeyPressed(extra2d::Key::Z)) { - undoMove(); - return; - } - - // A 键执行选中的菜单项 - if (input.isButtonPressed(extra2d::GamepadButton::A)) { - executeMenuItem(); - return; - } - - // 方向键移动 - if (input.isButtonPressed(extra2d::GamepadButton::DPadUp)) { - move(0, -1, 1); - flush(); - } else if (input.isButtonPressed(extra2d::GamepadButton::DPadDown)) { - move(0, 1, 2); - flush(); - } else if (input.isButtonPressed(extra2d::GamepadButton::DPadLeft)) { - move(-1, 0, 3); - flush(); - } else if (input.isButtonPressed(extra2d::GamepadButton::DPadRight)) { - move(1, 0, 4); - flush(); - } else { - return; - } - - // 检查是否通关 - for (int i = 0; i < map_.width; i++) { - for (int j = 0; j < map_.height; j++) { - Piece p = map_.value[j][i]; - if (p.type == TYPE::Box && p.isPoint == false) { - return; - } - } - } - - gameOver(); -} - -/** - * @brief 执行选中的菜单项 - */ -void PlayScene::executeMenuItem() { - switch (menuIndex_) { - case 0: // 重开 - setLevel(g_CurrentLevel); - break; - case 1: // 切换音效 - g_SoundOpen = !g_SoundOpen; - AudioManager::instance().setEnabled(g_SoundOpen); - if (soundBtn_) { - soundBtn_->setOn(g_SoundOpen); - } - break; - case 2: // 撤销 - undoMove(); - break; - } -} - -/** - * @brief 刷新地图显示 - */ -void PlayScene::flush() { - mapLayer_->removeAllChildren(); - - int tileW = texFloor_ ? texFloor_->getWidth() : 32; - int tileH = texFloor_ ? texFloor_->getHeight() : 32; - - // 使用游戏逻辑分辨率 - float gameWidth = GAME_WIDTH; - float gameHeight = GAME_HEIGHT; - float baseOffsetX = 0.0f; - float baseOffsetY = 0.0f; - - // 在 12x12 网格中居中地图 - float mapOffsetX = static_cast((12.0f - map_.width) / 2.0f) * tileW; - float mapOffsetY = static_cast((12.0f - map_.height) / 2.0f) * tileH; - - float offsetX = baseOffsetX + mapOffsetX; - float offsetY = baseOffsetY + mapOffsetY; - - for (int i = 0; i < map_.width; i++) { - for (int j = 0; j < map_.height; j++) { - Piece piece = map_.value[j][i]; - - extra2d::Ptr tex; - - if (piece.type == TYPE::Wall) { - tex = texWall_; - } else if (piece.type == TYPE::Ground && piece.isPoint) { - tex = texPoint_; - } else if (piece.type == TYPE::Ground) { - tex = texFloor_; - } else if (piece.type == TYPE::Box && piece.isPoint) { - tex = texBoxInPoint_; - } else if (piece.type == TYPE::Box) { - tex = texBox_; - } else if (piece.type == TYPE::Man && g_Pushing) { - tex = texManPush_[g_Direct]; - } else if (piece.type == TYPE::Man) { - tex = texMan_[g_Direct]; - } else { - continue; - } - - if (!tex) { - continue; - } - - auto sprite = extra2d::Sprite::create(tex); - sprite->setAnchor(0.0f, 0.0f); - sprite->setPosition(offsetX + static_cast(i * tileW), - offsetY + static_cast(j * tileH)); - mapLayer_->addChild(sprite); - } - } -} - -/** - * @brief 设置关卡 - * @param level 关卡编号 - */ -void PlayScene::setLevel(int level) { - g_CurrentLevel = level; - saveCurrentLevel(g_CurrentLevel); - - // 清空移动历史(智能指针自动回收到对象池) - while (!moveHistory_.empty()) { - moveHistory_.pop(); - } - - if (levelText_) { - levelText_->setText("第" + std::to_string(level) + "关"); - } - - setStep(0); - - int bestStep = loadBestStep(level, 0); - if (bestText_) { - if (bestStep != 0) { - bestText_->setText("最佳" + std::to_string(bestStep) + "步"); - } else { - bestText_->setText(""); - } - } - - // 深拷贝地图数据 - Map &sourceMap = g_Maps[level - 1]; - map_.width = sourceMap.width; - map_.height = sourceMap.height; - map_.roleX = sourceMap.roleX; - map_.roleY = sourceMap.roleY; - for (int i = 0; i < 12; i++) { - for (int j = 0; j < 12; j++) { - map_.value[i][j] = sourceMap.value[i][j]; - } - } - - g_Direct = 2; - g_Pushing = false; - flush(); -} - -/** - * @brief 设置步数 - * @param step 步数 - */ -void PlayScene::setStep(int step) { - step_ = step; - if (stepText_) { - stepText_->setText("当前" + std::to_string(step) + "步"); - } -} - -/** - * @brief 移动玩家 - * @param dx X方向偏移 - * @param dy Y方向偏移 - * @param direct 方向(1=上,2=下,3=左,4=右) - */ -void PlayScene::move(int dx, int dy, int direct) { - int targetX = dx + map_.roleX; - int targetY = dy + map_.roleY; - g_Direct = direct; - - if (targetX < 0 || targetX >= map_.width || targetY < 0 || - targetY >= map_.height) { - return; - } - - if (map_.value[targetY][targetX].type == TYPE::Wall) { - return; - } - - // 使用对象池创建移动记录(自动管理内存) - auto record = E2D_MAKE_POOLED(MoveRecord, map_.roleX, map_.roleY, targetX, targetY, false); - - if (map_.value[targetY][targetX].type == TYPE::Ground) { - g_Pushing = false; - map_.value[map_.roleY][map_.roleX].type = TYPE::Ground; - map_.value[targetY][targetX].type = TYPE::Man; - AudioManager::instance().playManMove(); - } else if (map_.value[targetY][targetX].type == TYPE::Box) { - g_Pushing = true; - - int boxX = 0; - int boxY = 0; - switch (g_Direct) { - case 1: - boxX = targetX; - boxY = targetY - 1; - break; - case 2: - boxX = targetX; - boxY = targetY + 1; - break; - case 3: - boxX = targetX - 1; - boxY = targetY; - break; - case 4: - boxX = targetX + 1; - boxY = targetY; - break; - default: - return; - } - - if (boxX < 0 || boxX >= map_.width || boxY < 0 || boxY >= map_.height) { - return; - } - - if (map_.value[boxY][boxX].type == TYPE::Wall || - map_.value[boxY][boxX].type == TYPE::Box) { - return; - } - - // 记录箱子移动 - record->pushedBox = true; - record->boxFromX = targetX; - record->boxFromY = targetY; - record->boxToX = boxX; - record->boxToY = boxY; - - map_.value[boxY][boxX].type = TYPE::Box; - map_.value[targetY][targetX].type = TYPE::Man; - map_.value[map_.roleY][map_.roleX].type = TYPE::Ground; - - AudioManager::instance().playBoxMove(); - } else { - return; - } - - // 保存移动记录到历史栈 - moveHistory_.push(record); - - map_.roleX = targetX; - map_.roleY = targetY; - setStep(step_ + 1); -} - -/** - * @brief 游戏通关 - */ -void PlayScene::gameOver() { - int bestStep = loadBestStep(g_CurrentLevel, 0); - if (bestStep == 0 || step_ < bestStep) { - saveBestStep(g_CurrentLevel, step_); - } - - if (g_CurrentLevel == MAX_LEVEL) { - extra2d::Application::instance().scenes().pushScene( - extra2d::makePtr(), extra2d::TransitionType::Fade, 0.5f); - return; - } - - setLevel(g_CurrentLevel + 1); -} - -/** - * @brief 撤销上一步移动(对象池使用示例) - * 智能指针离开作用域时自动回收到对象池 - */ -void PlayScene::undoMove() { - if (moveHistory_.empty()) { - E2D_LOG_INFO("No moves to undo"); - return; - } - - auto record = moveHistory_.top(); - moveHistory_.pop(); - - // 恢复玩家位置 - map_.value[map_.roleY][map_.roleX].type = TYPE::Ground; - map_.value[record->fromY][record->fromX].type = TYPE::Man; - map_.roleX = record->fromX; - map_.roleY = record->fromY; - - // 如果推了箱子,恢复箱子位置 - if (record->pushedBox) { - map_.value[record->boxToY][record->boxToX].type = TYPE::Ground; - map_.value[record->boxFromY][record->boxFromX].type = TYPE::Box; - } - - // record 智能指针离开作用域后自动回收到对象池 - setStep(step_ - 1); - flush(); - - E2D_LOG_INFO("Undo move, step: {}", step_); -} - -} // namespace pushbox diff --git a/examples/push_box/PlayScene.h b/examples/push_box/PlayScene.h deleted file mode 100644 index 3cfdad8..0000000 --- a/examples/push_box/PlayScene.h +++ /dev/null @@ -1,113 +0,0 @@ -// ============================================================================ -// PlayScene.h - Push Box 游戏场景 -// ============================================================================ - -#pragma once - -#include "BaseScene.h" -#include "data.h" -#include -#include -#include - -namespace pushbox { - -/** - * @brief Push Box 游戏场景 - */ -class PlayScene : public BaseScene { -public: - /** - * @brief 构造函数 - * @param level 关卡编号 - */ - explicit PlayScene(int level); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 每帧更新 - * @param dt 帧间隔时间 - */ - void onUpdate(float dt) override; - -private: - /** - * @brief 更新菜单颜色 - */ - void updateMenuColors(); - - /** - * @brief 执行选中的菜单项 - */ - void executeMenuItem(); - - /** - * @brief 刷新地图显示 - */ - void flush(); - - /** - * @brief 设置关卡 - * @param level 关卡编号 - */ - void setLevel(int level); - - /** - * @brief 设置步数 - * @param step 步数 - */ - void setStep(int step); - - /** - * @brief 移动玩家 - * @param dx X方向偏移 - * @param dy Y方向偏移 - * @param direct 方向(1=上,2=下,3=左,4=右) - */ - void move(int dx, int dy, int direct); - - /** - * @brief 游戏通关 - */ - void gameOver(); - - /** - * @brief 撤销上一步移动(对象池使用示例) - */ - void undoMove(); - - int step_ = 0; - int menuIndex_ = 0; - Map map_{}; - - extra2d::Ptr font28_; - extra2d::Ptr font20_; - - extra2d::Ptr levelText_; - extra2d::Ptr stepText_; - extra2d::Ptr bestText_; - extra2d::Ptr restartText_; - extra2d::Ptr soundToggleText_; - extra2d::Ptr undoText_; - extra2d::Ptr mapLayer_; - - extra2d::Ptr soundBtn_; - - extra2d::Ptr texWall_; - extra2d::Ptr texPoint_; - extra2d::Ptr texFloor_; - extra2d::Ptr texBox_; - extra2d::Ptr texBoxInPoint_; - - extra2d::Ptr texMan_[5]; - extra2d::Ptr texManPush_[5]; - - // 对象池使用示例:使用智能指针管理 MoveRecord - std::stack> moveHistory_; -}; - -} // namespace pushbox diff --git a/examples/push_box/StartScene.cpp b/examples/push_box/StartScene.cpp deleted file mode 100644 index deae581..0000000 --- a/examples/push_box/StartScene.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// ============================================================================ -// StartScene.cpp - Push Box 开始场景实现 -// ============================================================================ - -#include "StartScene.h" - -#include "PlayScene.h" -#include "audio_manager.h" -#include "data.h" -#include - -namespace pushbox { - -StartScene::StartScene() : BaseScene() { - // BaseScene 已处理视口设置 -} - -/** - * @brief 加载菜单字体 - */ -static extra2d::Ptr loadMenuFont() { - auto &resources = extra2d::Application::instance().resources(); - auto font = resources.loadFont("assets/font.ttf", 28, true); - return font; -} - -void StartScene::onEnter() { - BaseScene::onEnter(); - - auto &app = extra2d::Application::instance(); - auto &resources = app.resources(); - - if (getChildren().empty()) { - // 使用游戏逻辑分辨率 - float screenW = GAME_WIDTH; - float screenH = GAME_HEIGHT; - - auto bgTex = resources.loadTexture("assets/images/start.jpg"); - if (bgTex) { - auto background = extra2d::Sprite::create(bgTex); - float bgWidth = static_cast(bgTex->getWidth()); - float bgHeight = static_cast(bgTex->getHeight()); - float offsetX = (screenW - bgWidth) / 2.0f; - float offsetY = (screenH - bgHeight) / 2.0f; - - background->setAnchor(0.0f, 0.0f); - background->setPosition(offsetX, offsetY); - addChild(background); - - float centerX = screenW / 2.0f; - - font_ = loadMenuFont(); - if (!font_) { - return; - } - - // 创建菜单按钮(使用 Button 实现文本居中) - startBtn_ = extra2d::Button::create(); - startBtn_->setFont(font_); - startBtn_->setText("新游戏"); - startBtn_->setTextColor(extra2d::Colors::Black); - startBtn_->setBackgroundColor(extra2d::Colors::Transparent, - extra2d::Colors::Transparent, - extra2d::Colors::Transparent); - startBtn_->setBorder(extra2d::Colors::Transparent, 0.0f); - startBtn_->setPadding(extra2d::Vec2(0.0f, 0.0f)); - startBtn_->setCustomSize(200.0f, 40.0f); - startBtn_->setAnchor(0.5f, 0.5f); - startBtn_->setPosition(centerX, offsetY + 260.0f); - addChild(startBtn_); - - resumeBtn_ = extra2d::Button::create(); - resumeBtn_->setFont(font_); - resumeBtn_->setText("继续关卡"); - resumeBtn_->setTextColor(extra2d::Colors::Black); - resumeBtn_->setBackgroundColor(extra2d::Colors::Transparent, - extra2d::Colors::Transparent, - extra2d::Colors::Transparent); - resumeBtn_->setBorder(extra2d::Colors::Transparent, 0.0f); - resumeBtn_->setPadding(extra2d::Vec2(0.0f, 0.0f)); - resumeBtn_->setCustomSize(200.0f, 40.0f); - resumeBtn_->setAnchor(0.5f, 0.5f); - resumeBtn_->setPosition(centerX, offsetY + 300.0f); - addChild(resumeBtn_); - - exitBtn_ = extra2d::Button::create(); - exitBtn_->setFont(font_); - exitBtn_->setText("退出"); - exitBtn_->setTextColor(extra2d::Colors::Black); - exitBtn_->setBackgroundColor(extra2d::Colors::Transparent, - extra2d::Colors::Transparent, - extra2d::Colors::Transparent); - exitBtn_->setBorder(extra2d::Colors::Transparent, 0.0f); - exitBtn_->setPadding(extra2d::Vec2(0.0f, 0.0f)); - exitBtn_->setCustomSize(200.0f, 40.0f); - exitBtn_->setAnchor(0.5f, 0.5f); - exitBtn_->setPosition(centerX, offsetY + 340.0f); - addChild(exitBtn_); - - // 音效开关按钮(使用 Button 的切换模式) - auto soundOn = resources.loadTexture("assets/images/soundon.png"); - auto soundOff = resources.loadTexture("assets/images/soundoff.png"); - if (soundOn && soundOff) { - soundBtn_ = extra2d::Button::create(); - soundBtn_->setToggleMode(true); - soundBtn_->setStateBackgroundImage(soundOff, soundOn); - soundBtn_->setOn(g_SoundOpen); - soundBtn_->setAnchor(0.0f, 0.0f); - soundBtn_->setPosition(offsetX + 50.0f, offsetY + 50.0f); - soundBtn_->setOnStateChange([](bool isOn) { - g_SoundOpen = isOn; - AudioManager::instance().setEnabled(isOn); - }); - addChild(soundBtn_); - } - } - } - - // 始终有3个菜单项 - menuCount_ = 3; - updateMenuColors(); -} - -void StartScene::onUpdate(float dt) { - BaseScene::onUpdate(dt); - - auto &app = extra2d::Application::instance(); - auto &input = app.input(); - - // 方向键上下切换选择 - if (input.isButtonPressed(extra2d::GamepadButton::DPadUp)) { - selectedIndex_ = (selectedIndex_ - 1 + menuCount_) % menuCount_; - updateMenuColors(); - } else if (input.isButtonPressed(extra2d::GamepadButton::DPadDown)) { - selectedIndex_ = (selectedIndex_ + 1) % menuCount_; - updateMenuColors(); - } - - // A键确认 - if (input.isButtonPressed(extra2d::GamepadButton::A)) { - executeMenuItem(); - } - - // X键切换音效(备用,按钮也可点击切换) - if (input.isButtonPressed(extra2d::GamepadButton::X)) { - g_SoundOpen = !g_SoundOpen; - AudioManager::instance().setEnabled(g_SoundOpen); - if (soundBtn_) { - soundBtn_->setOn(g_SoundOpen); - } - } -} - -/** - * @brief 更新菜单颜色 - */ -void StartScene::updateMenuColors() { - // 根据选中状态更新按钮文本颜色 - // 选中的项用红色,未选中的用黑色,禁用的项用深灰色 - - if (startBtn_) { - startBtn_->setTextColor(selectedIndex_ == 0 ? extra2d::Colors::Red - : extra2d::Colors::Black); - } - - if (resumeBtn_) { - // "继续关卡"始终显示,但当 g_CurrentLevel == 1 时禁用(深灰色) - if (g_CurrentLevel > 1) { - // 可用状态:选中为红色,未选中为黑色 - resumeBtn_->setTextColor(selectedIndex_ == 1 ? extra2d::Colors::Red - : extra2d::Colors::Black); - } else { - // 禁用状态:深灰色 (RGB: 80, 80, 80) - resumeBtn_->setTextColor(extra2d::Color(80, 80, 80, 255)); - } - } - - if (exitBtn_) { - exitBtn_->setTextColor(selectedIndex_ == 2 ? extra2d::Colors::Red - : extra2d::Colors::Black); - } -} - -/** - * @brief 执行选中的菜单项 - */ -void StartScene::executeMenuItem() { - // 始终有3个选项,但"继续关卡"(索引1)在 g_CurrentLevel == 1 时禁用 - switch (selectedIndex_) { - case 0: - startNewGame(); - break; - case 1: - // 只有当 g_CurrentLevel > 1 时才能选择"继续关卡" - if (g_CurrentLevel > 1) { - continueGame(); - } - break; - case 2: - exitGame(); - break; - } -} - -/** - * @brief 开始新游戏 - */ -void StartScene::startNewGame() { - extra2d::Application::instance().scenes().replaceScene( - extra2d::makePtr(1), extra2d::TransitionType::Fade, 0.5f); -} - -/** - * @brief 继续游戏 - */ -void StartScene::continueGame() { - extra2d::Application::instance().scenes().replaceScene( - extra2d::makePtr(g_CurrentLevel), - extra2d::TransitionType::Fade, 0.5f); -} - -/** - * @brief 退出游戏 - */ -void StartScene::exitGame() { extra2d::Application::instance().quit(); } - -} // namespace pushbox diff --git a/examples/push_box/StartScene.h b/examples/push_box/StartScene.h deleted file mode 100644 index c792d78..0000000 --- a/examples/push_box/StartScene.h +++ /dev/null @@ -1,68 +0,0 @@ -// ============================================================================ -// StartScene.h - Push Box 开始场景 -// ============================================================================ - -#pragma once - -#include "BaseScene.h" -#include - -namespace pushbox { - -/** - * @brief Push Box 开始场景(主菜单) - */ -class StartScene : public BaseScene { -public: - /** - * @brief 构造函数 - */ - StartScene(); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 每帧更新 - * @param dt 帧间隔时间 - */ - void onUpdate(float dt) override; - -private: - /** - * @brief 更新菜单颜色 - */ - void updateMenuColors(); - - /** - * @brief 执行选中的菜单项 - */ - void executeMenuItem(); - - /** - * @brief 开始新游戏 - */ - void startNewGame(); - - /** - * @brief 继续游戏 - */ - void continueGame(); - - /** - * @brief 退出游戏 - */ - void exitGame(); - - extra2d::Ptr font_; - extra2d::Ptr startBtn_; - extra2d::Ptr resumeBtn_; - extra2d::Ptr exitBtn_; - extra2d::Ptr soundBtn_; - int selectedIndex_ = 0; - int menuCount_ = 3; -}; - -} // namespace pushbox diff --git a/examples/push_box/SuccessScene.cpp b/examples/push_box/SuccessScene.cpp deleted file mode 100644 index bbe9e4b..0000000 --- a/examples/push_box/SuccessScene.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ============================================================================ -// SuccessScene.cpp - Push Box 通关场景实现 -// ============================================================================ - -#include "SuccessScene.h" - -#include - -namespace pushbox { - -SuccessScene::SuccessScene() : BaseScene() { - // BaseScene 已处理视口设置 -} - -/** - * @brief 加载菜单字体 - */ -static extra2d::Ptr loadMenuFont() { - auto& resources = extra2d::Application::instance().resources(); - auto font = resources.loadFont("assets/font.ttf", 28); - return font; -} - -void SuccessScene::onEnter() { - BaseScene::onEnter(); - - auto& app = extra2d::Application::instance(); - auto& resources = app.resources(); - - if (getChildren().empty()) { - // 使用游戏逻辑分辨率 - float screenW = GAME_WIDTH; - float screenH = GAME_HEIGHT; - - auto bgTex = resources.loadTexture("assets/images/success.jpg"); - if (bgTex) { - auto background = extra2d::Sprite::create(bgTex); - float bgWidth = static_cast(bgTex->getWidth()); - float bgHeight = static_cast(bgTex->getHeight()); - float offsetX = (screenW - bgWidth) / 2.0f; - float offsetY = (screenH - bgHeight) / 2.0f; - - background->setAnchor(0.0f, 0.0f); - background->setPosition(offsetX, offsetY); - addChild(background); - - float centerX = screenW / 2.0f; - - auto font = loadMenuFont(); - if (font) { - // 创建按钮文本(仅显示,不响应鼠标) - auto backText = extra2d::Text::create("回主菜单", font); - backText->setPosition(centerX, offsetY + 350.0f); - backText->setTextColor(extra2d::Colors::Black); - addChild(backText); - - // 创建选择指示器(箭头) - selectorText_ = extra2d::Text::create(">", font); - selectorText_->setTextColor(extra2d::Colors::Red); - selectorText_->setPosition(centerX - 80.0f, offsetY + 350.0f); - addChild(selectorText_); - } - } - } -} - -void SuccessScene::onUpdate(float dt) { - BaseScene::onUpdate(dt); - - auto& app = extra2d::Application::instance(); - auto& input = app.input(); - - // A键确认返回主菜单 - if (input.isButtonPressed(extra2d::GamepadButton::A)) { - auto& scenes = extra2d::Application::instance().scenes(); - scenes.popScene(extra2d::TransitionType::Fade, 0.5f); - scenes.popScene(extra2d::TransitionType::Fade, 0.5f); - } -} - -} // namespace pushbox diff --git a/examples/push_box/SuccessScene.h b/examples/push_box/SuccessScene.h deleted file mode 100644 index 2158e48..0000000 --- a/examples/push_box/SuccessScene.h +++ /dev/null @@ -1,37 +0,0 @@ -// ============================================================================ -// SuccessScene.h - Push Box 通关场景 -// ============================================================================ - -#pragma once - -#include "BaseScene.h" -#include - -namespace pushbox { - -/** - * @brief Push Box 通关场景 - */ -class SuccessScene : public BaseScene { -public: - /** - * @brief 构造函数 - */ - SuccessScene(); - - /** - * @brief 场景进入时调用 - */ - void onEnter() override; - - /** - * @brief 每帧更新 - * @param dt 帧间隔时间 - */ - void onUpdate(float dt) override; - -private: - extra2d::Ptr selectorText_; -}; - -} // namespace pushbox diff --git a/examples/push_box/audio_manager.cpp b/examples/push_box/audio_manager.cpp deleted file mode 100644 index 92a8667..0000000 --- a/examples/push_box/audio_manager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "audio_manager.h" - -#include "storage.h" - -namespace pushbox { - -// ============================================================================ -// 单例实现 -// ============================================================================ -AudioManager& AudioManager::instance() { - static AudioManager instance; - return instance; -} - -// ============================================================================ -// 初始化音频资源 -// ============================================================================ -void AudioManager::init() { - if (initialized_) { - return; - } - - auto& resources = extra2d::Application::instance().resources(); - - // 加载音效资源 - background_ = resources.loadSound("pushbox_bg", "assets/audio/background.wav"); - manMove_ = resources.loadSound("pushbox_manmove", "assets/audio/manmove.wav"); - boxMove_ = resources.loadSound("pushbox_boxmove", "assets/audio/boxmove.wav"); - - // 设置背景音乐循环播放 - if (background_) { - background_->setLooping(true); - } - - // 从存储中读取音效设置 - enabled_ = g_SoundOpen; - - initialized_ = true; - - // 如果音效开启,播放背景音乐 - if (enabled_ && background_) { - background_->play(); - } -} - -// ============================================================================ -// 启用/禁用音效 -// ============================================================================ -void AudioManager::setEnabled(bool enabled) { - enabled_ = enabled; - g_SoundOpen = enabled; - saveSoundOpen(enabled); - - if (!background_) { - return; - } - - if (enabled_) { - background_->resume(); - } else { - background_->pause(); - } -} - -// ============================================================================ -// 播放角色移动音效 -// ============================================================================ -void AudioManager::playManMove() { - if (!enabled_ || !manMove_) { - return; - } - manMove_->play(); -} - -// ============================================================================ -// 播放箱子移动音效 -// ============================================================================ -void AudioManager::playBoxMove() { - if (!enabled_ || !boxMove_) { - return; - } - boxMove_->play(); -} - -// ============================================================================ -// 背景音乐控制 -// ============================================================================ -void AudioManager::playBackground() { - if (background_) { - background_->play(); - } -} - -void AudioManager::pauseBackground() { - if (background_) { - background_->pause(); - } -} - -void AudioManager::resumeBackground() { - if (background_) { - background_->resume(); - } -} - -void AudioManager::stopBackground() { - if (background_) { - background_->stop(); - } -} - -} // namespace pushbox diff --git a/examples/push_box/audio_manager.h b/examples/push_box/audio_manager.h deleted file mode 100644 index d9c6e19..0000000 --- a/examples/push_box/audio_manager.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "data.h" -#include - -namespace pushbox { - -// ============================================================================ -// 全局音频管理器 - 单例模式,不依赖场景生命周期 -// ============================================================================ -class AudioManager { -public: - // 获取单例实例 - static AudioManager& instance(); - - // 初始化音频资源 - void init(); - - // 启用/禁用音效 - void setEnabled(bool enabled); - bool isEnabled() const { return enabled_; } - - // 播放音效 - void playManMove(); - void playBoxMove(); - - // 背景音乐控制 - void playBackground(); - void pauseBackground(); - void resumeBackground(); - void stopBackground(); - -private: - AudioManager() = default; - ~AudioManager() = default; - AudioManager(const AudioManager&) = delete; - AudioManager& operator=(const AudioManager&) = delete; - - bool initialized_ = false; - bool enabled_ = true; - - extra2d::Ptr background_; - extra2d::Ptr manMove_; - extra2d::Ptr boxMove_; -}; - -} // namespace pushbox diff --git a/examples/push_box/data.cpp b/examples/push_box/data.cpp deleted file mode 100644 index 33902f1..0000000 --- a/examples/push_box/data.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "data.h" - -namespace pushbox { - -int g_CurrentLevel = 1; -bool g_SoundOpen = true; -int g_Direct = 2; -bool g_Pushing = false; - -Map g_Maps[MAX_LEVEL] = { - { - 8, 8, 4, 4, - { - {{Empty}, {Empty}, {Wall}, {Wall}, {Wall}, {Empty}, {Empty}, {Empty}}, - {{Empty}, {Empty}, {Wall}, {Ground, true}, {Wall}, {Empty}, {Empty}, {Empty}}, - {{Empty}, {Empty}, {Wall}, {Ground}, {Wall}, {Wall}, {Wall}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Box}, {Ground}, {Box}, {Ground, true}, {Wall}}, - {{Wall}, {Ground, true}, {Ground}, {Box}, {Man}, {Wall}, {Wall}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Wall}, {Box}, {Wall}, {Empty}, {Empty}}, - {{Empty}, {Empty}, {Empty}, {Wall}, {Ground, true}, {Wall}, {Empty}, {Empty}}, - {{Empty}, {Empty}, {Empty}, {Wall}, {Wall}, {Wall}, {Empty}, {Empty}}, - }, - }, - { - 9, 9, 1, 1, - { - {{Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}, {Empty}, {Empty}, {Empty}}, - {{Wall}, {Man}, {Ground}, {Ground}, {Wall}, {Empty}, {Empty}, {Empty}, {Empty}}, - {{Wall}, {Ground}, {Box}, {Box}, {Wall}, {Empty}, {Wall}, {Wall}, {Wall}}, - {{Wall}, {Ground}, {Box}, {Ground}, {Wall}, {Empty}, {Wall}, {Ground, true}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Ground}, {Wall}, {Wall}, {Wall}, {Ground, true}, {Wall}}, - {{Empty}, {Wall}, {Wall}, {Ground}, {Ground}, {Ground}, {Ground}, {Ground, true}, {Wall}}, - {{Empty}, {Wall}, {Ground}, {Ground}, {Ground}, {Wall}, {Ground}, {Ground}, {Wall}}, - {{Empty}, {Wall}, {Ground}, {Ground}, {Ground}, {Wall}, {Wall}, {Wall}, {Wall}}, - {{Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}, {Empty}, {Empty}}, - }, - }, - { - 10, 7, 3, 3, - { - {{Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}, {Empty}}, - {{Empty}, {Wall}, {Ground}, {Ground}, {Ground}, {Ground}, {Ground}, {Wall}, {Wall}, {Wall}}, - {{Wall}, {Wall}, {Box}, {Wall}, {Wall}, {Wall}, {Ground}, {Ground}, {Ground}, {Wall}}, - {{Wall}, {Ground}, {Ground}, {Man}, {Box}, {Ground}, {Ground}, {Box}, {Ground}, {Wall}}, - {{Wall}, {Ground}, {Ground, true}, {Ground, true}, {Wall}, {Ground}, {Box}, {Ground}, {Wall}, {Wall}}, - {{Wall}, {Wall}, {Ground, true}, {Ground, true}, {Wall}, {Ground}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}}, - }, - }, - { - 6, 8, 1, 2, - { - {{Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}}, - {{Wall}, {Wall}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Wall}, {Man}, {Box}, {Ground}, {Wall}, {Empty}}, - {{Wall}, {Wall}, {Box}, {Ground}, {Wall}, {Wall}}, - {{Wall}, {Wall}, {Ground}, {Box}, {Ground}, {Wall}}, - {{Wall}, {Ground, true}, {Box}, {Ground}, {Ground}, {Wall}}, - {{Wall}, {Ground, true}, {Ground, true}, {Box, true}, {Ground, true}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}}, - }, - }, - { - 8, 8, 2, 2, - { - {{Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}, {Empty}}, - {{Empty}, {Wall}, {Ground}, {Ground}, {Wall}, {Wall}, {Wall}, {Empty}}, - {{Empty}, {Wall}, {Man}, {Box}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Wall}, {Wall}, {Wall}, {Ground}, {Wall}, {Ground}, {Wall}, {Wall}}, - {{Wall}, {Ground, true}, {Wall}, {Ground}, {Wall}, {Ground}, {Ground}, {Wall}}, - {{Wall}, {Ground, true}, {Box}, {Ground}, {Ground}, {Wall}, {Ground}, {Wall}}, - {{Wall}, {Ground, true}, {Ground}, {Ground}, {Ground}, {Box}, {Ground}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}}, - }, - }, - { - 10, 8, 8, 1, - { - {{Empty}, {Empty}, {Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}}, - {{Empty}, {Empty}, {Wall}, {Wall}, {Ground}, {Ground}, {Wall}, {Ground}, {Man}, {Wall}}, - {{Empty}, {Empty}, {Wall}, {Ground}, {Ground}, {Ground}, {Wall}, {Ground}, {Ground}, {Wall}}, - {{Empty}, {Empty}, {Wall}, {Box}, {Ground}, {Box}, {Ground}, {Box}, {Ground}, {Wall}}, - {{Empty}, {Empty}, {Wall}, {Ground}, {Box}, {Wall}, {Wall}, {Ground}, {Ground}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Ground}, {Box}, {Ground}, {Wall}, {Ground}, {Wall}, {Wall}}, - {{Wall}, {Ground, true}, {Ground, true}, {Ground, true}, {Ground, true}, {Ground, true}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}}, - }, - }, - { - 10, 7, 8, 3, - { - {{Empty}, {Empty}, {Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}}, - {{Empty}, {Wall}, {Wall}, {Wall}, {Ground}, {Ground}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Wall}, {Wall}, {Ground, true}, {Ground}, {Box}, {Wall}, {Wall}, {Ground}, {Wall}, {Wall}}, - {{Wall}, {Ground, true}, {Ground, true}, {Box}, {Ground}, {Box}, {Ground}, {Ground}, {Man}, {Wall}}, - {{Wall}, {Ground, true}, {Ground, true}, {Ground}, {Box}, {Ground}, {Box}, {Ground}, {Wall}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Empty}, {Empty}, {Empty}, {Empty}, {Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}}, - }, - }, - { - 11, 9, 8, 7, - { - {{Empty}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Empty}}, - {{Empty}, {Wall}, {Ground}, {Ground}, {Wall}, {Wall}, {Ground}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Empty}, {Wall}, {Ground}, {Ground}, {Ground}, {Box}, {Ground}, {Ground}, {Ground}, {Wall}, {Empty}}, - {{Empty}, {Wall}, {Box}, {Ground}, {Wall}, {Wall}, {Wall}, {Ground}, {Box}, {Wall}, {Empty}}, - {{Empty}, {Wall}, {Ground}, {Wall}, {Ground, true}, {Ground, true}, {Ground, true}, {Wall}, {Ground}, {Wall}, {Empty}}, - {{Wall}, {Wall}, {Ground}, {Wall}, {Ground, true}, {Ground, true}, {Ground, true}, {Wall}, {Ground}, {Wall}, {Wall}}, - {{Wall}, {Ground}, {Box}, {Ground}, {Ground}, {Box}, {Ground}, {Ground}, {Box}, {Ground}, {Wall}}, - {{Wall}, {Ground}, {Ground}, {Ground}, {Ground}, {Ground}, {Wall}, {Ground}, {Man}, {Ground}, {Wall}}, - {{Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}, {Wall}}, - }, - }, -}; - -} // namespace pushbox diff --git a/examples/push_box/data.h b/examples/push_box/data.h deleted file mode 100644 index 5d3801f..0000000 --- a/examples/push_box/data.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#define MAX_LEVEL 8 -#define GAME_WIDTH 640.0f -#define GAME_HEIGHT 480.0f - -namespace pushbox { - -enum TYPE { Empty, Wall, Ground, Box, Man }; - -struct Piece { - TYPE type; - bool isPoint; -}; - -struct Map { - int width; - int height; - int roleX; - int roleY; - Piece value[12][12]; -}; - -/** - * @brief 移动记录 - 用于撤销功能(对象池示例) - * 这个结构体演示如何使用对象池管理小对象 - */ -struct MoveRecord { - int fromX, fromY; - int toX, toY; - int boxFromX, boxFromY; - int boxToX, boxToY; - bool pushedBox; - - MoveRecord() = default; - MoveRecord(int fx, int fy, int tx, int ty, bool pushed = false) - : fromX(fx), fromY(fy), toX(tx), toY(ty) - , boxFromX(-1), boxFromY(-1), boxToX(-1), boxToY(-1) - , pushedBox(pushed) {} -}; - -extern Map g_Maps[MAX_LEVEL]; -extern int g_CurrentLevel; -extern bool g_SoundOpen; -extern int g_Direct; -extern bool g_Pushing; - -} // namespace pushbox diff --git a/examples/push_box/main.cpp b/examples/push_box/main.cpp deleted file mode 100644 index 3c3f5d4..0000000 --- a/examples/push_box/main.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include "StartScene.h" -#include "data.h" -#include "storage.h" -#include "audio_manager.h" - -using namespace extra2d; - -// ============================================================================ -// 程序入口 -// ============================================================================ - -int main(int argc, char **argv) -{ - Logger::init(); - Logger::setLevel(LogLevel::Debug); - - E2D_LOG_INFO("========================"); - E2D_LOG_INFO("Extra2D push_box"); - E2D_LOG_INFO("========================"); - - auto &app = Application::instance(); - - AppConfig config; - config.title = "Extra2D - push_box"; - config.width = 1280; - config.height = 720; - config.vsync = true; - config.fpsLimit = 60; - - if (!app.init(config)) { - E2D_LOG_ERROR("应用初始化失败!"); - return -1; - } - - // 初始化存储系统 - pushbox::initStorage("sdmc:/"); - pushbox::g_CurrentLevel = pushbox::loadCurrentLevel(1); - if (pushbox::g_CurrentLevel > MAX_LEVEL) { - pushbox::g_CurrentLevel = 1; - } - pushbox::g_SoundOpen = pushbox::loadSoundOpen(true); - - // 初始化全局音频管理器 - pushbox::AudioManager::instance().init(); - - // 进入开始场景(主界面) - app.enterScene(makePtr()); - - E2D_LOG_INFO("开始主循环..."); - app.run(); - - E2D_LOG_INFO("应用结束"); - return 0; -} diff --git a/examples/push_box/romfs/assets/audio/background.wav b/examples/push_box/romfs/assets/audio/background.wav deleted file mode 100644 index 8b669c6..0000000 Binary files a/examples/push_box/romfs/assets/audio/background.wav and /dev/null differ diff --git a/examples/push_box/romfs/assets/audio/boxmove.wav b/examples/push_box/romfs/assets/audio/boxmove.wav deleted file mode 100644 index cda7cc0..0000000 Binary files a/examples/push_box/romfs/assets/audio/boxmove.wav and /dev/null differ diff --git a/examples/push_box/romfs/assets/audio/manmove.wav b/examples/push_box/romfs/assets/audio/manmove.wav deleted file mode 100644 index c13f1bd..0000000 Binary files a/examples/push_box/romfs/assets/audio/manmove.wav and /dev/null differ diff --git a/examples/push_box/romfs/assets/font.ttf b/examples/push_box/romfs/assets/font.ttf deleted file mode 100644 index 8997148..0000000 Binary files a/examples/push_box/romfs/assets/font.ttf and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/box.gif b/examples/push_box/romfs/assets/images/box.gif deleted file mode 100644 index 4c835fd..0000000 Binary files a/examples/push_box/romfs/assets/images/box.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/boxinpoint.gif b/examples/push_box/romfs/assets/images/boxinpoint.gif deleted file mode 100644 index a12c967..0000000 Binary files a/examples/push_box/romfs/assets/images/boxinpoint.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/floor.gif b/examples/push_box/romfs/assets/images/floor.gif deleted file mode 100644 index 321d058..0000000 Binary files a/examples/push_box/romfs/assets/images/floor.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/mandown.gif b/examples/push_box/romfs/assets/images/player/mandown.gif deleted file mode 100644 index 98ccb72..0000000 Binary files a/examples/push_box/romfs/assets/images/player/mandown.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/manhanddown.gif b/examples/push_box/romfs/assets/images/player/manhanddown.gif deleted file mode 100644 index 6e1ba0f..0000000 Binary files a/examples/push_box/romfs/assets/images/player/manhanddown.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/manhandleft.gif b/examples/push_box/romfs/assets/images/player/manhandleft.gif deleted file mode 100644 index 18326bf..0000000 Binary files a/examples/push_box/romfs/assets/images/player/manhandleft.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/manhandright.gif b/examples/push_box/romfs/assets/images/player/manhandright.gif deleted file mode 100644 index e6e3618..0000000 Binary files a/examples/push_box/romfs/assets/images/player/manhandright.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/manhandup.gif b/examples/push_box/romfs/assets/images/player/manhandup.gif deleted file mode 100644 index 33e4a79..0000000 Binary files a/examples/push_box/romfs/assets/images/player/manhandup.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/manleft.gif b/examples/push_box/romfs/assets/images/player/manleft.gif deleted file mode 100644 index e875c28..0000000 Binary files a/examples/push_box/romfs/assets/images/player/manleft.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/manright.gif b/examples/push_box/romfs/assets/images/player/manright.gif deleted file mode 100644 index bf94f12..0000000 Binary files a/examples/push_box/romfs/assets/images/player/manright.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/player/manup.gif b/examples/push_box/romfs/assets/images/player/manup.gif deleted file mode 100644 index 7211b42..0000000 Binary files a/examples/push_box/romfs/assets/images/player/manup.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/point.gif b/examples/push_box/romfs/assets/images/point.gif deleted file mode 100644 index b81cbe4..0000000 Binary files a/examples/push_box/romfs/assets/images/point.gif and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/soundoff.png b/examples/push_box/romfs/assets/images/soundoff.png deleted file mode 100644 index 246abd1..0000000 Binary files a/examples/push_box/romfs/assets/images/soundoff.png and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/soundon.png b/examples/push_box/romfs/assets/images/soundon.png deleted file mode 100644 index 592edb4..0000000 Binary files a/examples/push_box/romfs/assets/images/soundon.png and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/start.jpg b/examples/push_box/romfs/assets/images/start.jpg deleted file mode 100644 index 0a2e7e8..0000000 Binary files a/examples/push_box/romfs/assets/images/start.jpg and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/success.jpg b/examples/push_box/romfs/assets/images/success.jpg deleted file mode 100644 index f12b5ac..0000000 Binary files a/examples/push_box/romfs/assets/images/success.jpg and /dev/null differ diff --git a/examples/push_box/romfs/assets/images/wall.gif b/examples/push_box/romfs/assets/images/wall.gif deleted file mode 100644 index 448dc5e..0000000 Binary files a/examples/push_box/romfs/assets/images/wall.gif and /dev/null differ diff --git a/examples/push_box/storage.cpp b/examples/push_box/storage.cpp deleted file mode 100644 index cf153aa..0000000 --- a/examples/push_box/storage.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "storage.h" - -#include -#include - -namespace pushbox { - -static extra2d::DataStore g_store; -static std::filesystem::path g_filePath; -static bool g_loaded = false; - -static void ensureLoaded() { - if (g_loaded) { - return; - } - if (!g_filePath.empty()) { - g_store.load(g_filePath.string()); - } - g_loaded = true; -} - -void initStorage(const std::filesystem::path& baseDir) { - // Nintendo Switch 标准保存位置:/save/ 或 sdmc:/switch/ - // 优先使用 /save/ 目录(官方存档位置) - std::filesystem::path saveDir; - - // 检查是否存在 /save/ 目录(这是 NS 官方存档目录) - if (std::filesystem::exists("/save/")) { - saveDir = "/save/"; - } else if (std::filesystem::exists("/switch/")) { - // 备用:使用 /switch/ 目录 - saveDir = "/switch/push_box/"; - std::filesystem::create_directories(saveDir); - } else { - // 开发环境:使用 sdmc:/switch/ - saveDir = baseDir / "switch/push_box/"; - std::filesystem::create_directories(saveDir); - } - - g_filePath = saveDir / "pushbox.ini"; - g_store.load(g_filePath.string()); - g_loaded = true; -} - -int loadCurrentLevel(int defaultValue) { - ensureLoaded(); - int level = g_store.getInt("game", "level", defaultValue); - if (level < 1) { - level = 1; - } - return level; -} - -void saveCurrentLevel(int level) { - ensureLoaded(); - g_store.setInt("game", "level", level); - if (!g_filePath.empty()) { - g_store.save(g_filePath.string()); - } -} - -bool loadSoundOpen(bool defaultValue) { - ensureLoaded(); - return g_store.getBool("game", "sound", defaultValue); -} - -void saveSoundOpen(bool open) { - ensureLoaded(); - g_store.setBool("game", "sound", open); - if (!g_filePath.empty()) { - g_store.save(g_filePath.string()); - } -} - -int loadBestStep(int level, int defaultValue) { - ensureLoaded(); - std::string key = "level" + std::to_string(level); - return g_store.getInt("best", key, defaultValue); -} - -void saveBestStep(int level, int step) { - ensureLoaded(); - std::string key = "level" + std::to_string(level); - g_store.setInt("best", key, step); - if (!g_filePath.empty()) { - g_store.save(g_filePath.string()); - } -} - -std::filesystem::path storageFilePath() { return g_filePath; } - -} // namespace pushbox diff --git a/examples/push_box/storage.h b/examples/push_box/storage.h deleted file mode 100644 index d766a8a..0000000 --- a/examples/push_box/storage.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -namespace pushbox { - -void initStorage(const std::filesystem::path& baseDir); - -int loadCurrentLevel(int defaultValue = 1); -void saveCurrentLevel(int level); - -bool loadSoundOpen(bool defaultValue = true); -void saveSoundOpen(bool open); - -int loadBestStep(int level, int defaultValue = 0); -void saveBestStep(int level, int step); - -std::filesystem::path storageFilePath(); - -} // namespace pushbox diff --git a/examples/push_box/xmake.lua b/examples/push_box/xmake.lua deleted file mode 100644 index f550124..0000000 --- a/examples/push_box/xmake.lua +++ /dev/null @@ -1,76 +0,0 @@ --- ============================================== --- Push Box 示例 - Xmake 构建脚本 --- 支持平台: MinGW (Windows), Nintendo Switch --- ============================================== - --- 获取当前脚本所在目录(示例根目录) -local example_dir = os.scriptdir() - --- 可执行文件目标 -target("push_box") - set_kind("binary") - add_files("*.cpp") - add_includedirs("../../Extra2D/include") - add_deps("extra2d") - - -- 使用与主项目相同的平台配置 - if is_plat("switch") then - set_targetdir("../../build/examples/push_box") - - -- 构建后生成 NRO 文件 - after_build(function (target) - local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro" - local elf_file = target:targetfile() - local output_dir = path.directory(elf_file) - local nacp_file = path.join(output_dir, "push_box.nacp") - local nro_file = path.join(output_dir, "push_box.nro") - local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe") - local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe") - - if os.isfile(nacptool) and os.isfile(elf2nro) then - os.vrunv(nacptool, {"--create", "Push Box", "Extra2D Team", "1.0.0", nacp_file}) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs}) - else - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file}) - end - print("Generated NRO: " .. nro_file) - end - end) - - -- 打包时将 NRO 文件复制到 package 目录 - after_package(function (target) - local nro_file = path.join(target:targetdir(), "push_box.nro") - local package_dir = target:packagedir() - if os.isfile(nro_file) and package_dir then - os.cp(nro_file, package_dir) - print("Copied NRO to package: " .. package_dir) - end - end) - - elseif is_plat("mingw") then - set_targetdir("../../build/examples/push_box") - -- add_ldflags("-mwindows", {force = true}) - - -- 复制资源到输出目录 - after_build(function (target) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - local target_dir = path.directory(target:targetfile()) - local assets_dir = path.join(target_dir, "assets") - - -- 创建 assets 目录 - if not os.isdir(assets_dir) then - os.mkdir(assets_dir) - end - - -- 复制所有资源文件(包括子目录) - os.cp(path.join(romfs, "assets/**"), assets_dir) - print("Copied assets from " .. romfs .. " to " .. assets_dir) - else - print("Warning: romfs directory not found at " .. romfs) - end - end) - end -target_end() diff --git a/examples/spatial_index_demo/main.cpp b/examples/spatial_index_demo/main.cpp deleted file mode 100644 index 4fa89d8..0000000 --- a/examples/spatial_index_demo/main.cpp +++ /dev/null @@ -1,536 +0,0 @@ -#include -#include - -using namespace extra2d; - -// ============================================================================ -// 性能统计 -// ============================================================================ -struct PerformanceStats { - double updateTime = 0.0; - double collisionTime = 0.0; - double renderTime = 0.0; - size_t collisionCount = 0; - size_t nodeCount = 0; - const char *strategyName = "Unknown"; -}; - -// ============================================================================ -// 碰撞节点 - 使用引擎自带的空间索引功能 -// ============================================================================ -class PhysicsNode : public Node { -public: - PhysicsNode(float size, const Color &color, int id) - : size_(size), color_(color), id_(id), isColliding_(false) { - // 启用引擎自带的空间索引功能 - // 这是关键:设置 spatialIndexed_ = true 让节点参与空间索引 - setSpatialIndexed(true); - - // 随机速度 - std::random_device rd; - std::mt19937 gen(rd() + id); - std::uniform_real_distribution velDist(-150.0f, 150.0f); - velocity_ = Vec2(velDist(gen), velDist(gen)); - } - - void setColliding(bool colliding) { isColliding_ = colliding; } - bool isColliding() const { return isColliding_; } - int getId() const { return id_; } - - // 必须实现 getBoundingBox() 才能参与空间索引碰撞检测 - Rect getBoundingBox() const override { - Vec2 pos = getPosition(); - return Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_); - } - - void update(float dt, float screenWidth, float screenHeight) { - Vec2 pos = getPosition(); - pos = pos + velocity_ * dt; - - // 边界反弹 - if (pos.x < size_ / 2 || pos.x > screenWidth - size_ / 2) { - velocity_.x = -velocity_.x; - pos.x = std::clamp(pos.x, size_ / 2, screenWidth - size_ / 2); - } - if (pos.y < size_ / 2 || pos.y > screenHeight - size_ / 2) { - velocity_.y = -velocity_.y; - pos.y = std::clamp(pos.y, size_ / 2, screenHeight - size_ / 2); - } - - setPosition(pos); - } - - void onRender(RenderBackend &renderer) override { - Vec2 pos = getPosition(); - - // 碰撞时变红色 - Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.9f) : color_; - renderer.fillRect(Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_), - fillColor); - - // 绘制边框 - Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f) - : Color(0.3f, 0.3f, 0.3f, 0.5f); - renderer.drawRect(Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_), - borderColor, 1.0f); - } - -private: - float size_; - Color color_; - int id_; - bool isColliding_; - Vec2 velocity_; -}; - -// ============================================================================ -// 空间索引演示场景 -// ============================================================================ -class SpatialIndexDemoScene : public Scene { -public: - void onEnter() override { - // 必须先调用父类的 onEnter(),这样才能正确设置 running_ 状态 - // 并触发子节点的 onAttachToScene,将节点注册到空间索引 - Scene::onEnter(); - - E2D_LOG_INFO("SpatialIndexDemoScene::onEnter - 引擎空间索引演示"); - - auto &app = Application::instance(); - screenWidth_ = static_cast(app.getConfig().width); - screenHeight_ = static_cast(app.getConfig().height); - - // 设置背景色 - setBackgroundColor(Color(0.05f, 0.05f, 0.1f, 1.0f)); - - // 创建100个碰撞节点 - createPhysicsNodes(100); - - // 加载字体 - loadFonts(); - - E2D_LOG_INFO("空间索引已启用: {}", isSpatialIndexingEnabled()); - } - - void onExit() override { - // 显式移除所有子节点,确保在场景析构前正确清理空间索引 - // 这必须在 Scene::onExit() 之前调用,因为 onExit() 会将 running_ 设为 false - removeAllChildren(); - - Scene::onExit(); - } - - void onUpdate(float dt) override { - Scene::onUpdate(dt); - - auto startTime = std::chrono::high_resolution_clock::now(); - - // 更新所有物理节点位置 - for (const auto &child : getChildren()) { - if (auto node = dynamic_cast(child.get())) { - node->update(dt, screenWidth_, screenHeight_); - } - } - - auto updateEndTime = std::chrono::high_resolution_clock::now(); - stats_.updateTime = - std::chrono::duration(updateEndTime - startTime) - .count(); - - // 使用引擎自带的空间索引进行碰撞检测 - performCollisionDetection(); - - auto collisionEndTime = std::chrono::high_resolution_clock::now(); - stats_.collisionTime = std::chrono::duration( - collisionEndTime - updateEndTime) - .count(); - - // 统计物理节点数量 - stats_.nodeCount = getPhysicsNodeCount(); - - // 获取当前使用的空间索引策略 - stats_.strategyName = getSpatialManager().getStrategyName(); - - // 检查退出按键 - auto &input = Application::instance().input(); - if (input.isButtonPressed(GamepadButton::Start)) { - E2D_LOG_INFO("退出应用"); - Application::instance().quit(); - } - - // 按 A 键添加节点 - if (input.isButtonPressed(GamepadButton::A)) { - addNodes(100); - } - - // 按 B 键减少节点 - if (input.isButtonPressed(GamepadButton::B)) { - removeNodes(100); - } - - // 按 X 键切换空间索引策略 - if (input.isButtonPressed(GamepadButton::X)) { - toggleSpatialStrategy(); - } - } - - void onRender(RenderBackend &renderer) override { - auto renderStart = std::chrono::high_resolution_clock::now(); - - Scene::onRender(renderer); - - auto renderEnd = std::chrono::high_resolution_clock::now(); - stats_.renderTime = - std::chrono::duration(renderEnd - renderStart) - .count(); - - // 更新UI文本内容 - updateUI(); - - // 绘制图例方块(Text组件会自动渲染) - drawLegend(renderer); - } - -private: - /** - * @brief 加载字体资源并创建UI文本组件 - */ - void loadFonts() { - auto &resources = Application::instance().resources(); - - titleFont_ = resources.loadFont("assets/font.ttf", 28, true); - infoFont_ = resources.loadFont("assets/font.ttf", 16, true); - - // 创建标题文本 - titleText_ = Text::create("引擎空间索引演示", titleFont_); - titleText_->setPosition(30.0f, 20.0f); - titleText_->setTextColor(Color(1.0f, 1.0f, 1.0f, 1.0f)); - addChild(titleText_); - - float x = 30.0f; - float y = 60.0f; - float lineHeight = 22.0f; - - // 创建统计信息文本 - nodeCountText_ = Text::create("", infoFont_); - nodeCountText_->setPosition(x, y); - nodeCountText_->setTextColor(Color(0.9f, 0.9f, 0.9f, 1.0f)); - addChild(nodeCountText_); - y += lineHeight; - - strategyText_ = Text::create("", infoFont_); - strategyText_->setPosition(x, y); - strategyText_->setTextColor(Color(0.5f, 1.0f, 0.5f, 1.0f)); - addChild(strategyText_); - y += lineHeight; - - collisionText_ = Text::create("", infoFont_); - collisionText_->setPosition(x, y); - collisionText_->setTextColor(Color(1.0f, 0.5f, 0.5f, 1.0f)); - addChild(collisionText_); - y += lineHeight; - - updateTimeText_ = Text::create("", infoFont_); - updateTimeText_->setPosition(x, y); - updateTimeText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(updateTimeText_); - y += lineHeight; - - collisionTimeText_ = Text::create("", infoFont_); - collisionTimeText_->setPosition(x, y); - collisionTimeText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(collisionTimeText_); - y += lineHeight; - - renderTimeText_ = Text::create("", infoFont_); - renderTimeText_->setPosition(x, y); - renderTimeText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(renderTimeText_); - y += lineHeight; - - fpsText_ = Text::create("", infoFont_); - fpsText_->setPosition(x, y); - fpsText_->setTextColor(Color(0.5f, 1.0f, 0.5f, 1.0f)); - addChild(fpsText_); - y += lineHeight * 1.5f; - - // 创建操作说明文本 - helpTitleText_ = Text::create("操作说明:", infoFont_); - helpTitleText_->setPosition(x, y); - helpTitleText_->setTextColor(Color(1.0f, 1.0f, 0.5f, 1.0f)); - addChild(helpTitleText_); - y += lineHeight; - - helpAddText_ = Text::create("A键 - 添加100个节点", infoFont_); - helpAddText_->setPosition(x + 10, y); - helpAddText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(helpAddText_); - y += lineHeight; - - helpRemoveText_ = Text::create("B键 - 移除100个节点", infoFont_); - helpRemoveText_->setPosition(x + 10, y); - helpRemoveText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(helpRemoveText_); - y += lineHeight; - - helpToggleText_ = Text::create("X键 - 切换索引策略", infoFont_); - helpToggleText_->setPosition(x + 10, y); - helpToggleText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(helpToggleText_); - y += lineHeight; - - helpExitText_ = Text::create("+键 - 退出程序", infoFont_); - helpExitText_->setPosition(x + 10, y); - helpExitText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(helpExitText_); - - // 创建图例文本 - float legendX = screenWidth_ - 200.0f; - float legendY = 20.0f; - - legendTitleText_ = Text::create("图例:", infoFont_); - legendTitleText_->setPosition(legendX, legendY); - legendTitleText_->setTextColor(Color(1.0f, 1.0f, 1.0f, 1.0f)); - addChild(legendTitleText_); - legendY += 25.0f; - - legendNormalText_ = Text::create("- 正常", infoFont_); - legendNormalText_->setPosition(legendX + 20.0f, legendY); - legendNormalText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(legendNormalText_); - legendY += 25.0f; - - legendCollidingText_ = Text::create("- 碰撞中", infoFont_); - legendCollidingText_->setPosition(legendX + 20.0f, legendY); - legendCollidingText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); - addChild(legendCollidingText_); - } - - /** - * @brief 创建指定数量的物理节点 - */ - void createPhysicsNodes(size_t count) { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution posX(50.0f, screenWidth_ - 50.0f); - std::uniform_real_distribution posY(50.0f, screenHeight_ - 50.0f); - std::uniform_real_distribution colorR(0.2f, 0.9f); - std::uniform_real_distribution colorG(0.2f, 0.9f); - std::uniform_real_distribution colorB(0.2f, 0.9f); - - for (size_t i = 0; i < count; ++i) { - Color color(colorR(gen), colorG(gen), colorB(gen), 0.7f); - auto node = makePtr(20.0f, color, static_cast(i)); - node->setPosition(Vec2(posX(gen), posY(gen))); - addChild(node); - } - } - - /** - * @brief 获取物理节点数量 - */ - size_t getPhysicsNodeCount() const { - size_t count = 0; - for (const auto &child : getChildren()) { - if (dynamic_cast(child.get())) { - ++count; - } - } - return count; - } - - /** - * @brief 获取所有物理节点 - */ - std::vector getPhysicsNodes() const { - std::vector nodes; - for (const auto &child : getChildren()) { - if (auto node = dynamic_cast(child.get())) { - nodes.push_back(node); - } - } - return nodes; - } - - /** - * @brief 添加节点 - */ - void addNodes(size_t count) { - size_t currentCount = getPhysicsNodeCount(); - if (currentCount + count > 5000) { - E2D_LOG_WARN("节点数量已达上限(5000)"); - return; - } - createPhysicsNodes(count); - E2D_LOG_INFO("添加 {} 个节点,当前总数: {}", count, getPhysicsNodeCount()); - } - - /** - * @brief 移除节点 - */ - void removeNodes(size_t count) { - auto physicsNodes = getPhysicsNodes(); - if (count >= physicsNodes.size()) { - count = physicsNodes.size(); - } - if (count == 0) - return; - - // 从后往前移除指定数量的节点 - for (size_t i = 0; i < count; ++i) { - // 找到最后一个物理节点对应的子节点并移除 - for (auto it = getChildren().rbegin(); it != getChildren().rend(); ++it) { - if (dynamic_cast(it->get())) { - removeChild(*it); - break; - } - } - } - E2D_LOG_INFO("移除 {} 个节点,当前总数: {}", count, getPhysicsNodeCount()); - } - - /** - * @brief 切换空间索引策略 - */ - void toggleSpatialStrategy() { - auto &spatialManager = getSpatialManager(); - SpatialStrategy currentStrategy = spatialManager.getCurrentStrategy(); - - if (currentStrategy == SpatialStrategy::QuadTree) { - spatialManager.setStrategy(SpatialStrategy::SpatialHash); - E2D_LOG_INFO("切换到空间哈希策略"); - } else { - spatialManager.setStrategy(SpatialStrategy::QuadTree); - E2D_LOG_INFO("切换到四叉树策略"); - } - } - - /** - * @brief 使用引擎自带的空间索引进行碰撞检测 - * - * 关键方法: - * - Scene::queryCollisions() - 查询场景中所有碰撞的节点对 - * - SpatialManager::queryCollisions() - 空间管理器的碰撞检测 - */ - void performCollisionDetection() { - // 清除之前的碰撞状态 - for (const auto &child : getChildren()) { - if (auto node = dynamic_cast(child.get())) { - node->setColliding(false); - } - } - - // 使用引擎自带的空间索引进行碰撞检测 - // 这是核心:Scene::queryCollisions() 会自动使用 SpatialManager - auto collisions = queryCollisions(); - - stats_.collisionCount = collisions.size(); - - // 标记碰撞的节点 - for (const auto &[nodeA, nodeB] : collisions) { - if (auto boxA = dynamic_cast(nodeA)) { - boxA->setColliding(true); - } - if (auto boxB = dynamic_cast(nodeB)) { - boxB->setColliding(true); - } - } - } - - /** - * @brief 更新UI文本内容 - */ - void updateUI() { - if (!nodeCountText_) - return; - - auto &app = Application::instance(); - - // 使用 setFormat 格式化文本 - nodeCountText_->setFormat("节点数量: %zu", stats_.nodeCount); - strategyText_->setFormat("索引策略: %s", stats_.strategyName); - collisionText_->setFormat("碰撞对数: %zu", stats_.collisionCount); - updateTimeText_->setFormat("更新时间: %.2f ms", stats_.updateTime); - collisionTimeText_->setFormat("碰撞检测: %.2f ms", stats_.collisionTime); - renderTimeText_->setFormat("渲染时间: %.2f ms", stats_.renderTime); - fpsText_->setFormat("FPS: %u", app.fps()); - } - - /** - * @brief 绘制图例方块 - */ - void drawLegend(RenderBackend &renderer) { - float legendX = screenWidth_ - 200.0f; - float legendY = 20.0f + 25.0f; // 在标题下方 - - // 绘制正常状态方块 - renderer.fillRect(Rect(legendX, legendY, 15.0f, 15.0f), - Color(0.5f, 0.5f, 0.9f, 0.7f)); - legendY += 25.0f; - - // 绘制碰撞状态方块 - renderer.fillRect(Rect(legendX, legendY, 15.0f, 15.0f), - Color(1.0f, 0.2f, 0.2f, 0.9f)); - } - - PerformanceStats stats_; - float screenWidth_ = 1280.0f; - float screenHeight_ = 720.0f; - - Ptr titleFont_; - Ptr infoFont_; - - // UI 文本组件 - Ptr titleText_; - Ptr nodeCountText_; - Ptr strategyText_; - Ptr collisionText_; - Ptr updateTimeText_; - Ptr collisionTimeText_; - Ptr renderTimeText_; - Ptr fpsText_; - Ptr helpTitleText_; - Ptr helpAddText_; - Ptr helpRemoveText_; - Ptr helpToggleText_; - Ptr helpExitText_; - Ptr legendTitleText_; - Ptr legendNormalText_; - Ptr legendCollidingText_; -}; - -// ============================================================================ -// 程序入口 -// ============================================================================ - -int main(int argc, char **argv) { - Logger::init(); - Logger::setLevel(LogLevel::Debug); - - E2D_LOG_INFO("========================"); - E2D_LOG_INFO("Easy2D 引擎空间索引演示"); - E2D_LOG_INFO("========================"); - - auto &app = Application::instance(); - - AppConfig config; - config.title = "Easy2D - 引擎空间索引演示"; - config.width = 1280; - config.height = 720; - config.vsync = true; - config.fpsLimit = 60; - - if (!app.init(config)) { - E2D_LOG_ERROR("应用初始化失败!"); - return -1; - } - - app.enterScene(makePtr()); - - E2D_LOG_INFO("开始主循环..."); - - app.run(); - - E2D_LOG_INFO("应用结束"); - - return 0; -} diff --git a/examples/spatial_index_demo/romfs/assets/font.ttf b/examples/spatial_index_demo/romfs/assets/font.ttf deleted file mode 100644 index 8997148..0000000 Binary files a/examples/spatial_index_demo/romfs/assets/font.ttf and /dev/null differ diff --git a/examples/spatial_index_demo/xmake.lua b/examples/spatial_index_demo/xmake.lua deleted file mode 100644 index 307b3e9..0000000 --- a/examples/spatial_index_demo/xmake.lua +++ /dev/null @@ -1,76 +0,0 @@ --- ============================================== --- Spatial Index Demo 示例 - Xmake 构建脚本 --- 支持平台: MinGW (Windows), Nintendo Switch --- ============================================== - --- 获取当前脚本所在目录(示例根目录) -local example_dir = os.scriptdir() - --- 可执行文件目标 -target("spatial_index_demo") - set_kind("binary") - add_files("main.cpp") - add_includedirs("../../Extra2D/include") - add_deps("extra2d") - - -- 使用与主项目相同的平台配置 - if is_plat("switch") then - set_targetdir("../../build/examples/spatial_index_demo") - - -- 构建后生成 NRO 文件 - after_build(function (target) - local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro" - local elf_file = target:targetfile() - local output_dir = path.directory(elf_file) - local nacp_file = path.join(output_dir, "spatial_index_demo.nacp") - local nro_file = path.join(output_dir, "spatial_index_demo.nro") - local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe") - local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe") - - if os.isfile(nacptool) and os.isfile(elf2nro) then - os.vrunv(nacptool, {"--create", "Spatial Index Demo", "Extra2D Team", "1.0.0", nacp_file}) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs}) - else - os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file}) - end - print("Generated NRO: " .. nro_file) - end - end) - - -- 打包时将 NRO 文件复制到 package 目录 - after_package(function (target) - local nro_file = path.join(target:targetdir(), "spatial_index_demo.nro") - local package_dir = target:packagedir() - if os.isfile(nro_file) and package_dir then - os.cp(nro_file, package_dir) - print("Copied NRO to package: " .. package_dir) - end - end) - - elseif is_plat("mingw") then - set_targetdir("../../build/examples/spatial_index_demo") - add_ldflags("-mwindows", {force = true}) - - -- 复制资源到输出目录 - after_build(function (target) - local romfs = path.join(example_dir, "romfs") - if os.isdir(romfs) then - local target_dir = path.directory(target:targetfile()) - local assets_dir = path.join(target_dir, "assets") - - -- 创建 assets 目录 - if not os.isdir(assets_dir) then - os.mkdir(assets_dir) - end - - -- 复制所有资源文件(包括子目录) - os.cp(path.join(romfs, "assets/**"), assets_dir) - print("Copied assets from " .. romfs .. " to " .. assets_dir) - else - print("Warning: romfs directory not found at " .. romfs) - end - end) - end -target_end() diff --git a/xmake.lua b/xmake.lua index 33fd5ff..f13e49c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -88,10 +88,6 @@ define_extra2d_engine() -- 示例程序目标(作为子项目) if is_config("examples","true") then includes("examples/hello_world", {rootdir = "examples/hello_world"}) - includes("examples/spatial_index_demo", {rootdir = "examples/spatial_index_demo"}) - includes("examples/collision_demo", {rootdir = "examples/collision_demo"}) - includes("examples/push_box", {rootdir = "examples/push_box"}) - includes("examples/flappy_bird", {rootdir = "examples/flappy_bird"}) end -- ==============================================