refactor(engine): 重构引擎架构,引入模块化系统和事件总线
重构引擎核心架构,移除旧的服务和调度器系统,引入基于事件总线的模块化设计: 1. 新增 Context 类作为引擎核心上下文,管理模块和插件生命周期 2. 新增 IModule 接口和 ModuleRegistry 实现模块注册与管理 3. 新增 IPlugin 接口和 PluginLoader 实现插件动态加载 4. 重构事件系统,扩展触摸和游戏手柄事件 5. 移除 Director、Scheduler 和 Service 等旧系统 6. 重构文件、窗口等平台模块为独立模块 7. 更新应用类以使用新架构 8. 优化随机数生成器为非单例设计 9. 添加详细的模块优先级常量 10. 改进代码组织结构和文档注释 此次重构提高了代码的可维护性和扩展性,支持运行时模块和插件动态加载,为后续功能扩展奠定基础。
This commit is contained in:
parent
5a3d0cd9de
commit
e68ce87638
|
|
@ -2,9 +2,13 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class Context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 应用程序配置
|
* @brief 应用程序配置
|
||||||
*/
|
*/
|
||||||
|
|
@ -24,34 +28,40 @@ struct AppConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 应用程序主控类
|
* @brief 应用程序主控类
|
||||||
*
|
*
|
||||||
* 管理应用程序生命周期、窗口和主循环
|
* 管理应用程序生命周期、窗口和主循环
|
||||||
|
* 基于新的 Context 架构,支持多实例
|
||||||
*/
|
*/
|
||||||
class Application {
|
class Application {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 获取单例实例
|
* @brief 创建应用程序实例
|
||||||
*/
|
*/
|
||||||
static Application& instance();
|
static std::unique_ptr<Application> create();
|
||||||
|
|
||||||
Application(const Application&) = delete;
|
Application(const Application&) = delete;
|
||||||
Application& operator=(const Application&) = delete;
|
Application& operator=(const Application&) = delete;
|
||||||
|
|
||||||
|
Application(Application&&) noexcept;
|
||||||
|
Application& operator=(Application&&) noexcept;
|
||||||
|
|
||||||
|
~Application();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 初始化应用程序
|
* @brief 初始化应用程序
|
||||||
*/
|
*/
|
||||||
bool init(const AppConfig& config);
|
bool init(const AppConfig& config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 关闭应用程序
|
* @brief 关闭应用程序
|
||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 运行主循环
|
* @brief 运行主循环
|
||||||
*/
|
*/
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 请求退出
|
* @brief 请求退出
|
||||||
*/
|
*/
|
||||||
|
|
@ -61,17 +71,17 @@ public:
|
||||||
* @brief 暂停应用
|
* @brief 暂停应用
|
||||||
*/
|
*/
|
||||||
void pause();
|
void pause();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 恢复应用
|
* @brief 恢复应用
|
||||||
*/
|
*/
|
||||||
void resume();
|
void resume();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 是否暂停
|
* @brief 是否暂停
|
||||||
*/
|
*/
|
||||||
bool isPaused() const { return paused_; }
|
bool isPaused() const { return paused_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 是否运行中
|
* @brief 是否运行中
|
||||||
*/
|
*/
|
||||||
|
|
@ -81,12 +91,12 @@ public:
|
||||||
* @brief 获取帧时间
|
* @brief 获取帧时间
|
||||||
*/
|
*/
|
||||||
float deltaTime() const { return deltaTime_; }
|
float deltaTime() const { return deltaTime_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取总运行时间
|
* @brief 获取总运行时间
|
||||||
*/
|
*/
|
||||||
float totalTime() const { return totalTime_; }
|
float totalTime() const { return totalTime_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前 FPS
|
* @brief 获取当前 FPS
|
||||||
*/
|
*/
|
||||||
|
|
@ -97,13 +107,19 @@ public:
|
||||||
*/
|
*/
|
||||||
const AppConfig& getConfig() const { return config_; }
|
const AppConfig& getConfig() const { return config_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取引擎上下文
|
||||||
|
*/
|
||||||
|
Context* getContext() const { return context_.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application() = default;
|
Application();
|
||||||
~Application();
|
|
||||||
|
|
||||||
void mainLoop();
|
void mainLoop();
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
|
std::unique_ptr<Context> context_;
|
||||||
|
|
||||||
AppConfig config_;
|
AppConfig config_;
|
||||||
|
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
|
|
@ -119,6 +135,4 @@ private:
|
||||||
int32 currentFps_ = 0;
|
int32 currentFps_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define APP extra2d::Application::instance()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <types/base/types.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class ModuleRegistry;
|
||||||
|
class PluginLoader;
|
||||||
|
class TimerModule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 引擎上下文
|
||||||
|
*
|
||||||
|
* 管理引擎的核心生命周期,包含模块注册表、插件加载器和定时器模块
|
||||||
|
* 非单例设计,支持创建多个独立的引擎实例
|
||||||
|
*/
|
||||||
|
class Context {
|
||||||
|
public:
|
||||||
|
Context();
|
||||||
|
~Context();
|
||||||
|
|
||||||
|
// 禁止拷贝
|
||||||
|
Context(const Context&) = delete;
|
||||||
|
Context& operator=(const Context&) = delete;
|
||||||
|
|
||||||
|
// 允许移动
|
||||||
|
Context(Context&&) noexcept;
|
||||||
|
Context& operator=(Context&&) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建上下文(工厂方法)
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<Context> create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化引擎
|
||||||
|
* @return 初始化是否成功
|
||||||
|
*/
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭引擎
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 主循环单帧更新
|
||||||
|
* @param dt 帧间隔时间(秒)
|
||||||
|
*/
|
||||||
|
void tick(float dt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块注册表
|
||||||
|
*/
|
||||||
|
ModuleRegistry& modules();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取插件加载器
|
||||||
|
*/
|
||||||
|
PluginLoader& plugins();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取定时器模块
|
||||||
|
*/
|
||||||
|
TimerModule& timer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取总运行时间
|
||||||
|
*/
|
||||||
|
float totalTime() const { return totalTime_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取帧数
|
||||||
|
*/
|
||||||
|
uint64 frameCount() const { return frameCount_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否正在运行
|
||||||
|
*/
|
||||||
|
bool isRunning() const { return running_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 请求停止引擎
|
||||||
|
*/
|
||||||
|
void stop() { running_ = false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<ModuleRegistry> moduleRegistry_;
|
||||||
|
std::unique_ptr<PluginLoader> pluginLoader_;
|
||||||
|
std::unique_ptr<TimerModule> timerModule_;
|
||||||
|
|
||||||
|
float totalTime_ = 0.0f;
|
||||||
|
uint64 frameCount_ = 0;
|
||||||
|
bool running_ = false;
|
||||||
|
bool inited_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <core/scheduler.h>
|
|
||||||
#include <core/service.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 导演类
|
|
||||||
*
|
|
||||||
* 主循环管理器,协调调度器和服务管理器
|
|
||||||
*/
|
|
||||||
class Director {
|
|
||||||
public:
|
|
||||||
static Director& inst();
|
|
||||||
|
|
||||||
bool init();
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
void mainLoop(float dt);
|
|
||||||
void mainLoopParallel(float dt);
|
|
||||||
|
|
||||||
Scheduler& sched() { return SCHED; }
|
|
||||||
SvcMgr& svcs() { return SVC_MGR; }
|
|
||||||
|
|
||||||
float dt() const { return dt_; }
|
|
||||||
float totalTime() const { return totalTime_; }
|
|
||||||
uint64 frameCount() const { return frameCount_; }
|
|
||||||
|
|
||||||
void pause();
|
|
||||||
void resume();
|
|
||||||
bool isPaused() const { return paused_; }
|
|
||||||
|
|
||||||
void setTimeScale(float scale);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Director() = default;
|
|
||||||
|
|
||||||
float dt_ = 0.0f;
|
|
||||||
float totalTime_ = 0.0f;
|
|
||||||
float fixedAccumulator_ = 0.0f;
|
|
||||||
float fixedDt_ = 1.0f / 60.0f;
|
|
||||||
uint64 frameCount_ = 0;
|
|
||||||
bool paused_ = false;
|
|
||||||
bool inited_ = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DIRECTOR extra2d::Director::inst()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <types/base/types.h>
|
|
||||||
#include <types/ptr/ref_counted.h>
|
|
||||||
#include <types/ptr/intrusive_ptr.h>
|
|
||||||
#include <types/const/priority.h>
|
|
||||||
#include <atomic>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <queue>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
using TimerHdl = uint32;
|
|
||||||
constexpr TimerHdl INVALID_HDL = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 定时器目标接口
|
|
||||||
*
|
|
||||||
* 实现此接口的对象可以接收 update 回调
|
|
||||||
*/
|
|
||||||
class TimerTarget {
|
|
||||||
public:
|
|
||||||
virtual ~TimerTarget() = default;
|
|
||||||
virtual void update(float dt) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 定时器基类
|
|
||||||
*/
|
|
||||||
class Timer : public RefCounted {
|
|
||||||
public:
|
|
||||||
virtual ~Timer() = default;
|
|
||||||
|
|
||||||
virtual void update(float dt) = 0;
|
|
||||||
virtual void trigger() = 0;
|
|
||||||
|
|
||||||
bool isPaused() const { return paused_; }
|
|
||||||
bool isDone() const { return done_; }
|
|
||||||
TimerHdl hdl() const { return hdl_; }
|
|
||||||
|
|
||||||
void pause() { paused_ = true; }
|
|
||||||
void resume() { paused_ = false; }
|
|
||||||
void cancel() { done_ = true; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Timer() = default;
|
|
||||||
|
|
||||||
float elapsed_ = -1.0f;
|
|
||||||
float interval_ = 0.0f;
|
|
||||||
float delay_ = 0.0f;
|
|
||||||
uint32 repeat_ = 0;
|
|
||||||
uint32 timesExecuted_ = 0;
|
|
||||||
bool useDelay_ = false;
|
|
||||||
bool runForever_ = false;
|
|
||||||
bool paused_ = false;
|
|
||||||
bool done_ = false;
|
|
||||||
TimerHdl hdl_ = INVALID_HDL;
|
|
||||||
|
|
||||||
friend class Scheduler;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 调度器
|
|
||||||
*
|
|
||||||
* 基于标准库实现的线程安全调度器,支持定时器和 update 回调
|
|
||||||
*/
|
|
||||||
class Scheduler {
|
|
||||||
public:
|
|
||||||
using Cb = Fn<void(float)>;
|
|
||||||
using VoidCb = Fn<void()>;
|
|
||||||
|
|
||||||
static Scheduler& inst();
|
|
||||||
|
|
||||||
TimerHdl scheduleUpdate(TimerTarget* target, int pri = 0);
|
|
||||||
void unscheduleUpdate(TimerTarget* target);
|
|
||||||
|
|
||||||
TimerHdl schedule(Cb cb, float interval, uint32 repeat = 0, float delay = 0.0f);
|
|
||||||
TimerHdl scheduleOnce(VoidCb cb, float delay);
|
|
||||||
TimerHdl scheduleForever(Cb cb, float interval);
|
|
||||||
|
|
||||||
void unschedule(TimerHdl hdl);
|
|
||||||
void unscheduleAll();
|
|
||||||
void pause(TimerHdl hdl);
|
|
||||||
void resume(TimerHdl hdl);
|
|
||||||
|
|
||||||
void setTimeScale(float scale) { timeScale_ = scale; }
|
|
||||||
float timeScale() const { return timeScale_; }
|
|
||||||
|
|
||||||
void update(float dt);
|
|
||||||
void updateParallel(float dt);
|
|
||||||
|
|
||||||
bool isScheduled(TimerHdl hdl) const;
|
|
||||||
size_t count() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Scheduler() = default;
|
|
||||||
|
|
||||||
struct UpdateEntry {
|
|
||||||
TimerTarget* target;
|
|
||||||
int pri;
|
|
||||||
bool paused;
|
|
||||||
bool markedForDel;
|
|
||||||
|
|
||||||
bool operator<(const UpdateEntry& o) const { return pri > o.pri; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// 线程安全的优先队列包装
|
|
||||||
class SafePriorityQueue {
|
|
||||||
public:
|
|
||||||
void push(const UpdateEntry& entry);
|
|
||||||
bool pop(UpdateEntry& entry);
|
|
||||||
bool empty() const;
|
|
||||||
size_t size() const;
|
|
||||||
void clear();
|
|
||||||
private:
|
|
||||||
mutable std::mutex mutex_;
|
|
||||||
std::priority_queue<UpdateEntry> queue_;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<UpdateEntry> updates_;
|
|
||||||
std::unordered_map<TimerTarget*, size_t> updateIndex_;
|
|
||||||
mutable std::mutex updateIndexMutex_;
|
|
||||||
|
|
||||||
std::unordered_map<TimerHdl, Ptr<Timer>> timers_;
|
|
||||||
mutable std::mutex timersMutex_;
|
|
||||||
|
|
||||||
SafePriorityQueue updateQueue_;
|
|
||||||
|
|
||||||
std::atomic<TimerHdl> nextHdl_{1};
|
|
||||||
std::atomic<float> timeScale_{1.0f};
|
|
||||||
std::atomic<bool> locked_{false};
|
|
||||||
|
|
||||||
TimerHdl genHdl();
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SCHED extra2d::Scheduler::inst()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <types/base/types.h>
|
|
||||||
#include <types/ptr/ref_counted.h>
|
|
||||||
#include <types/ptr/intrusive_ptr.h>
|
|
||||||
#include <types/const/priority.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 服务状态枚举
|
|
||||||
*/
|
|
||||||
enum class SvcState : uint8 {
|
|
||||||
None,
|
|
||||||
Inited,
|
|
||||||
Running,
|
|
||||||
Paused,
|
|
||||||
Shutdown
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 服务基类
|
|
||||||
*
|
|
||||||
* 所有子系统服务都继承此类,提供统一的生命周期管理
|
|
||||||
*/
|
|
||||||
class IService : public RefCounted {
|
|
||||||
public:
|
|
||||||
virtual ~IService() = default;
|
|
||||||
|
|
||||||
virtual bool init() { return true; }
|
|
||||||
virtual void shutdown() {}
|
|
||||||
virtual void pause() {}
|
|
||||||
virtual void resume() {}
|
|
||||||
|
|
||||||
virtual void update(float dt) {}
|
|
||||||
virtual void lateUpdate(float dt) {}
|
|
||||||
virtual void fixedUpdate(float dt) {}
|
|
||||||
|
|
||||||
virtual const char* name() const = 0;
|
|
||||||
virtual int pri() const { return Pri::Default; }
|
|
||||||
|
|
||||||
SvcState state() const { return state_; }
|
|
||||||
bool isInited() const { return state_ >= SvcState::Inited; }
|
|
||||||
bool isEnabled() const { return enabled_; }
|
|
||||||
void setEnabled(bool v) { enabled_ = v; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
IService() = default;
|
|
||||||
SvcState state_ = SvcState::None;
|
|
||||||
bool enabled_ = true;
|
|
||||||
|
|
||||||
friend class SvcMgr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 服务管理器
|
|
||||||
*
|
|
||||||
* 管理所有服务的注册、初始化、更新和关闭
|
|
||||||
*/
|
|
||||||
class SvcMgr {
|
|
||||||
public:
|
|
||||||
static SvcMgr& inst();
|
|
||||||
|
|
||||||
void reg(IService* svc);
|
|
||||||
void unreg(const char* name);
|
|
||||||
Ptr<IService> get(const char* name);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
Ptr<T> getAs(const char* name) {
|
|
||||||
return static_cast<T*>(get(name).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool initAll();
|
|
||||||
void pauseAll();
|
|
||||||
void resumeAll();
|
|
||||||
void shutdownAll();
|
|
||||||
|
|
||||||
void updateAll(float dt);
|
|
||||||
void lateUpdateAll(float dt);
|
|
||||||
void fixedUpdateAll(float dt);
|
|
||||||
|
|
||||||
bool has(const char* name) const;
|
|
||||||
size_t count() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
SvcMgr() = default;
|
|
||||||
|
|
||||||
using SvcMap = std::unordered_map<std::string, Ptr<IService>>;
|
|
||||||
SvcMap svcMap_;
|
|
||||||
mutable std::mutex svcMapMutex_;
|
|
||||||
|
|
||||||
std::vector<Ptr<IService>> sortedSvcs_;
|
|
||||||
|
|
||||||
void sortSvcs();
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SVC_MGR extra2d::SvcMgr::inst()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -137,27 +137,47 @@ DECLARE_EVENT_1(OnMouseWheel, Engine, int32)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 触摸开始事件
|
* @brief 触摸开始事件
|
||||||
* @param touch 触摸点信息
|
* @param touchId 触摸点ID
|
||||||
|
* @param x X 坐标
|
||||||
|
* @param y Y 坐标
|
||||||
*/
|
*/
|
||||||
DECLARE_EVENT_1(OnTouchBegan, Engine, const TouchPoint &)
|
DECLARE_EVENT_3(OnTouchBegin, Engine, int32, int32, int32)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 触摸移动事件
|
* @brief 触摸移动事件
|
||||||
* @param touch 触摸点信息
|
* @param touchId 触摸点ID
|
||||||
|
* @param x X 坐标
|
||||||
|
* @param y Y 坐标
|
||||||
*/
|
*/
|
||||||
DECLARE_EVENT_1(OnTouchMoved, Engine, const TouchPoint &)
|
DECLARE_EVENT_3(OnTouchMove, Engine, int32, int32, int32)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 触摸结束事件
|
* @brief 触摸结束事件
|
||||||
* @param touch 触摸点信息
|
* @param touchId 触摸点ID
|
||||||
|
* @param x X 坐标
|
||||||
|
* @param y Y 坐标
|
||||||
*/
|
*/
|
||||||
DECLARE_EVENT_1(OnTouchEnded, Engine, const TouchPoint &)
|
DECLARE_EVENT_3(OnTouchEnd, Engine, int32, int32, int32)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 触摸取消事件
|
* @brief 触摸取消事件
|
||||||
* @param touch 触摸点信息
|
* @param touchId 触摸点ID
|
||||||
|
* @param x X 坐标
|
||||||
|
* @param y Y 坐标
|
||||||
*/
|
*/
|
||||||
DECLARE_EVENT_1(OnTouchCancelled, Engine, const TouchPoint &)
|
DECLARE_EVENT_3(OnTouchCancel, Engine, int32, int32, int32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 游戏手柄连接事件
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
*/
|
||||||
|
DECLARE_EVENT_1(OnGamepadConnect, Engine, int32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 游戏手柄断开事件
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
*/
|
||||||
|
DECLARE_EVENT_1(OnGamepadDisconnect, Engine, int32)
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 场景事件
|
// 场景事件
|
||||||
|
|
|
||||||
|
|
@ -17,21 +17,24 @@
|
||||||
#include <types/math/vec2.h>
|
#include <types/math/vec2.h>
|
||||||
#include <types/math/vec3.h>
|
#include <types/math/vec3.h>
|
||||||
|
|
||||||
// Core
|
// Context (核心上下文)
|
||||||
#include <core/director.h>
|
#include <context/context.h>
|
||||||
#include <core/scheduler.h>
|
|
||||||
#include <core/service.h>
|
// Module System (新架构 - 基于事件总线)
|
||||||
|
#include <module/imodule.h>
|
||||||
|
#include <module/module_registry.h>
|
||||||
|
|
||||||
|
// Plugin System (新架构 - 基于事件总线)
|
||||||
|
#include <plugin/iplugin.h>
|
||||||
|
#include <plugin/plugin_loader.h>
|
||||||
|
|
||||||
|
// Event System
|
||||||
|
#include <event/event_bus.h>
|
||||||
|
#include <event/event_bus_macros.h>
|
||||||
|
#include <event/events.h>
|
||||||
|
|
||||||
// Platform
|
// Platform
|
||||||
#include <platform/file.h>
|
|
||||||
#include <platform/input.h>
|
|
||||||
#include <platform/sdl2.h>
|
#include <platform/sdl2.h>
|
||||||
#include <platform/window.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Event
|
|
||||||
#include <event/event_bus.h>
|
|
||||||
#include <event/events.h>
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
@ -40,6 +43,13 @@
|
||||||
// Application
|
// Application
|
||||||
#include <app/application.h>
|
#include <app/application.h>
|
||||||
|
|
||||||
|
// 兼容层 - 旧的头文件(将在后续版本中移除)
|
||||||
|
// #include <core/director.h> // 已废弃,使用 Context
|
||||||
|
// #include <module/module.h> // 已废弃,使用 imodule.h
|
||||||
|
// #include <module/module_manager.h> // 已废弃,使用 module_registry.h
|
||||||
|
// #include <plugin/plugin.h> // 已废弃,使用 iplugin.h
|
||||||
|
// #include <plugin/plugin_manager.h> // 已废弃,使用 plugin_loader.h
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <types/base/types.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块类型枚举
|
||||||
|
*/
|
||||||
|
enum class ModuleType : uint8 {
|
||||||
|
Core, // 核心模块(必须)
|
||||||
|
System, // 系统模块(平台相关)
|
||||||
|
Feature, // 功能模块
|
||||||
|
Extension // 扩展模块
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块接口 - 基于事件总线
|
||||||
|
*
|
||||||
|
* 模块不再通过虚函数被调用,而是通过监听事件响应
|
||||||
|
* 模块可以发送事件,但不应直接调用其他模块
|
||||||
|
*
|
||||||
|
* 设计理念:
|
||||||
|
* - 模块是引擎的内置组件,在编译时确定
|
||||||
|
* - 模块之间通过事件总线通信,零直接依赖
|
||||||
|
* - 模块的生命周期由 ModuleRegistry 管理
|
||||||
|
*/
|
||||||
|
class IModule {
|
||||||
|
public:
|
||||||
|
virtual ~IModule() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块名称
|
||||||
|
* @return 模块唯一标识名
|
||||||
|
*/
|
||||||
|
virtual const char* name() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块类型
|
||||||
|
* @return 模块类型,用于分类和优先级管理
|
||||||
|
*/
|
||||||
|
virtual ModuleType type() const { return ModuleType::Feature; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块优先级
|
||||||
|
* @return 优先级数值,越小优先级越高
|
||||||
|
*/
|
||||||
|
virtual int priority() const { return 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化模块
|
||||||
|
*
|
||||||
|
* 在此注册事件监听器
|
||||||
|
* 返回 false 表示初始化失败,引擎将停止
|
||||||
|
*
|
||||||
|
* @return 初始化是否成功
|
||||||
|
*/
|
||||||
|
virtual bool init() { return true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭模块
|
||||||
|
*
|
||||||
|
* 在此执行清理工作
|
||||||
|
* 事件监听器会自动注销(RAII)
|
||||||
|
*/
|
||||||
|
virtual void shutdown() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <module/imodule.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块注册表 - 非单例
|
||||||
|
*
|
||||||
|
* 管理所有模块的生命周期,通过事件总线协调模块间的通信
|
||||||
|
* 支持按优先级排序初始化和关闭
|
||||||
|
*/
|
||||||
|
class ModuleRegistry {
|
||||||
|
public:
|
||||||
|
ModuleRegistry();
|
||||||
|
~ModuleRegistry();
|
||||||
|
|
||||||
|
// 禁止拷贝
|
||||||
|
ModuleRegistry(const ModuleRegistry &) = delete;
|
||||||
|
ModuleRegistry &operator=(const ModuleRegistry &) = delete;
|
||||||
|
|
||||||
|
// 允许移动
|
||||||
|
ModuleRegistry(ModuleRegistry &&) noexcept;
|
||||||
|
ModuleRegistry &operator=(ModuleRegistry &&) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注册模块
|
||||||
|
* @param module 模块实例指针(不由注册表管理生命周期)
|
||||||
|
*/
|
||||||
|
void registerModule(IModule *module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注销模块
|
||||||
|
* @param name 模块名称
|
||||||
|
*/
|
||||||
|
void unregisterModule(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块
|
||||||
|
* @param name 模块名称
|
||||||
|
* @return 模块指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
IModule *getModule(const char *name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查模块是否存在
|
||||||
|
* @param name 模块名称
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
bool hasModule(const char *name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有模块
|
||||||
|
* @return 模块列表
|
||||||
|
*/
|
||||||
|
std::vector<IModule *> getAllModules() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取指定类型的所有模块
|
||||||
|
* @param type 模块类型
|
||||||
|
* @return 模块列表
|
||||||
|
*/
|
||||||
|
std::vector<IModule *> getModulesByType(ModuleType type) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化所有模块(按优先级排序)
|
||||||
|
* @return 是否全部初始化成功
|
||||||
|
*/
|
||||||
|
bool initAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭所有模块(按优先级逆序)
|
||||||
|
*/
|
||||||
|
void shutdownAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取模块数量
|
||||||
|
* @return 模块数量
|
||||||
|
*/
|
||||||
|
size_t getModuleCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 按优先级排序模块
|
||||||
|
*/
|
||||||
|
void sortModules();
|
||||||
|
|
||||||
|
std::vector<IModule *> modules_;
|
||||||
|
std::unordered_map<std::string, IModule *> moduleMap_;
|
||||||
|
bool sorted_ = false;
|
||||||
|
bool inited_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <module/imodule.h>
|
||||||
|
#include <types/base/types.h>
|
||||||
|
#include <types/const/priority.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定时器句柄
|
||||||
|
*/
|
||||||
|
using TimerId = uint32;
|
||||||
|
constexpr TimerId INVALID_TIMER_ID = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定时器回调类型
|
||||||
|
*/
|
||||||
|
using TimerCallback = std::function<void()>;
|
||||||
|
using TimerUpdateCallback = std::function<void(float)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定时器信息
|
||||||
|
*/
|
||||||
|
struct TimerInfo {
|
||||||
|
TimerId id = INVALID_TIMER_ID;
|
||||||
|
float interval = 0.0f; // 间隔时间(秒)
|
||||||
|
float elapsed = 0.0f; // 已过去的时间
|
||||||
|
uint32 repeat = 0; // 重复次数(0表示无限)
|
||||||
|
uint32 executed = 0; // 已执行次数
|
||||||
|
bool paused = false; // 是否暂停
|
||||||
|
bool cancelled = false; // 是否取消
|
||||||
|
TimerCallback callback; // 回调函数
|
||||||
|
TimerUpdateCallback updateCallback; // 带dt的回调
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定时器模块
|
||||||
|
*
|
||||||
|
* 提供定时任务管理功能,作为引擎核心模块运行
|
||||||
|
* 通过事件总线接收更新事件,无需直接依赖
|
||||||
|
*/
|
||||||
|
class TimerModule : public IModule {
|
||||||
|
public:
|
||||||
|
TimerModule();
|
||||||
|
~TimerModule() override;
|
||||||
|
|
||||||
|
// 禁止拷贝
|
||||||
|
TimerModule(const TimerModule&) = delete;
|
||||||
|
TimerModule& operator=(const TimerModule&) = delete;
|
||||||
|
|
||||||
|
// 允许移动
|
||||||
|
TimerModule(TimerModule&&) noexcept;
|
||||||
|
TimerModule& operator=(TimerModule&&) noexcept;
|
||||||
|
|
||||||
|
// IModule 接口实现
|
||||||
|
const char* name() const override { return "Timer"; }
|
||||||
|
ModuleType type() const override { return ModuleType::Core; }
|
||||||
|
int priority() const override { return Pri::System; }
|
||||||
|
bool init() override;
|
||||||
|
void shutdown() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 调度一次性任务
|
||||||
|
* @param delay 延迟时间(秒)
|
||||||
|
* @param callback 回调函数
|
||||||
|
* @return 定时器ID
|
||||||
|
*/
|
||||||
|
TimerId scheduleOnce(float delay, TimerCallback callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 调度重复任务
|
||||||
|
* @param interval 间隔时间(秒)
|
||||||
|
* @param repeat 重复次数(0表示无限)
|
||||||
|
* @param callback 回调函数
|
||||||
|
* @return 定时器ID
|
||||||
|
*/
|
||||||
|
TimerId scheduleRepeat(float interval, uint32 repeat, TimerCallback callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 调度每帧更新任务
|
||||||
|
* @param callback 回调函数(接收dt)
|
||||||
|
* @return 定时器ID
|
||||||
|
*/
|
||||||
|
TimerId scheduleUpdate(TimerUpdateCallback callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 取消定时器
|
||||||
|
* @param id 定时器ID
|
||||||
|
*/
|
||||||
|
void cancel(TimerId id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 暂停定时器
|
||||||
|
* @param id 定时器ID
|
||||||
|
*/
|
||||||
|
void pause(TimerId id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 恢复定时器
|
||||||
|
* @param id 定时器ID
|
||||||
|
*/
|
||||||
|
void resume(TimerId id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置时间缩放
|
||||||
|
* @param scale 时间缩放比例(1.0为正常)
|
||||||
|
*/
|
||||||
|
void setTimeScale(float scale) { timeScale_ = scale; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取时间缩放
|
||||||
|
*/
|
||||||
|
float getTimeScale() const { return timeScale_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 取消所有定时器
|
||||||
|
*/
|
||||||
|
void cancelAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取活动定时器数量
|
||||||
|
*/
|
||||||
|
size_t getActiveCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 更新所有定时器(由 Context 调用)
|
||||||
|
* @param dt 帧间隔时间
|
||||||
|
*/
|
||||||
|
void update(float dt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 生成唯一ID
|
||||||
|
*/
|
||||||
|
TimerId generateId();
|
||||||
|
|
||||||
|
std::unordered_map<TimerId, std::unique_ptr<TimerInfo>> timers_;
|
||||||
|
std::vector<TimerId> pendingRemove_;
|
||||||
|
TimerId nextId_ = 1;
|
||||||
|
float timeScale_ = 1.0f;
|
||||||
|
bool inUpdate_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <module/imodule.h>
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
#include <core/service.h>
|
#include <types/const/priority.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -25,132 +25,136 @@ struct FileData {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
std::vector<uint8> data;
|
std::vector<uint8> data;
|
||||||
std::string error;
|
std::string error;
|
||||||
|
|
||||||
operator bool() const { return ok; }
|
operator bool() const { return ok; }
|
||||||
const uint8* ptr() const { return data.data(); }
|
const uint8* ptr() const { return data.data(); }
|
||||||
size_t size() const { return data.size(); }
|
size_t size() const { return data.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 文件服务
|
* @brief 文件模块
|
||||||
*
|
*
|
||||||
* 提供跨平台文件系统操作
|
* 提供跨平台文件系统操作
|
||||||
|
* 非单例设计,通过 Context 管理生命周期
|
||||||
*/
|
*/
|
||||||
class FileSvc : public IService {
|
class FileModule : public IModule {
|
||||||
public:
|
public:
|
||||||
/**
|
FileModule();
|
||||||
* @brief 获取单例实例
|
~FileModule() override;
|
||||||
*/
|
|
||||||
static FileSvc& inst();
|
// 禁止拷贝
|
||||||
|
FileModule(const FileModule&) = delete;
|
||||||
const char* name() const override { return "FileSvc"; }
|
FileModule& operator=(const FileModule&) = delete;
|
||||||
int pri() const override { return Pri::System; }
|
|
||||||
|
// 允许移动
|
||||||
|
FileModule(FileModule&&) noexcept;
|
||||||
|
FileModule& operator=(FileModule&&) noexcept;
|
||||||
|
|
||||||
|
// IModule 接口实现
|
||||||
|
const char* name() const override { return "File"; }
|
||||||
|
ModuleType type() const override { return ModuleType::System; }
|
||||||
|
int priority() const override { return Pri::File; }
|
||||||
bool init() override;
|
bool init() override;
|
||||||
void shutdown() override;
|
void shutdown() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查文件是否存在
|
* @brief 检查文件是否存在
|
||||||
*/
|
*/
|
||||||
bool exists(const std::string& path) const;
|
bool exists(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否为目录
|
* @brief 检查是否为目录
|
||||||
*/
|
*/
|
||||||
bool isDir(const std::string& path) const;
|
bool isDir(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 读取整个文件到内存
|
* @brief 读取整个文件到内存
|
||||||
*/
|
*/
|
||||||
FileData read(const std::string& path) const;
|
FileData read(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 读取文件为字符串
|
* @brief 读取文件为字符串
|
||||||
*/
|
*/
|
||||||
std::string readString(const std::string& path) const;
|
std::string readString(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 写入数据到文件
|
* @brief 写入数据到文件
|
||||||
*/
|
*/
|
||||||
bool write(const std::string& path, const void* data, size_t size) const;
|
bool write(const std::string& path, const void* data, size_t size) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 写入字符串到文件
|
* @brief 写入字符串到文件
|
||||||
*/
|
*/
|
||||||
bool writeString(const std::string& path, const std::string& content) const;
|
bool writeString(const std::string& path, const std::string& content) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 追加数据到文件
|
* @brief 追加数据到文件
|
||||||
*/
|
*/
|
||||||
bool append(const std::string& path, const void* data, size_t size) const;
|
bool append(const std::string& path, const void* data, size_t size) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 删除文件
|
* @brief 删除文件
|
||||||
*/
|
*/
|
||||||
bool remove(const std::string& path) const;
|
bool remove(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建目录
|
* @brief 创建目录
|
||||||
*/
|
*/
|
||||||
bool mkdir(const std::string& path) const;
|
bool mkdir(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 列出目录内容
|
* @brief 列出目录内容
|
||||||
*/
|
*/
|
||||||
std::vector<FileInfo> listDir(const std::string& path) const;
|
std::vector<FileInfo> listDir(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取文件大小
|
* @brief 获取文件大小
|
||||||
*/
|
*/
|
||||||
int64 fileSize(const std::string& path) const;
|
int64 fileSize(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取文件扩展名
|
* @brief 获取文件扩展名
|
||||||
*/
|
*/
|
||||||
std::string ext(const std::string& path) const;
|
std::string ext(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取文件名(不含路径)
|
* @brief 获取文件名(不含路径)
|
||||||
*/
|
*/
|
||||||
std::string fileName(const std::string& path) const;
|
std::string fileName(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取文件所在目录
|
* @brief 获取文件所在目录
|
||||||
*/
|
*/
|
||||||
std::string dirName(const std::string& path) const;
|
std::string dirName(const std::string& path) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 连接路径
|
* @brief 连接路径
|
||||||
*/
|
*/
|
||||||
std::string join(const std::string& a, const std::string& b) const;
|
std::string join(const std::string& a, const std::string& b) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取可写目录(用于存档等)
|
* @brief 获取可写目录(用于存档等)
|
||||||
*/
|
*/
|
||||||
std::string writableDir() const;
|
std::string writableDir() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置资源根目录
|
* @brief 设置资源根目录
|
||||||
*/
|
*/
|
||||||
void setAssetRoot(const std::string& root) { assetRoot_ = root; }
|
void setAssetRoot(const std::string& root) { assetRoot_ = root; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取资源根目录
|
* @brief 获取资源根目录
|
||||||
*/
|
*/
|
||||||
const std::string& assetRoot() const { return assetRoot_; }
|
const std::string& assetRoot() const { return assetRoot_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取资源完整路径
|
* @brief 获取资源完整路径
|
||||||
*/
|
*/
|
||||||
std::string assetPath(const std::string& relPath) const;
|
std::string assetPath(const std::string& relPath) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileSvc() = default;
|
|
||||||
|
|
||||||
std::string assetRoot_;
|
std::string assetRoot_;
|
||||||
std::string writableDir_;
|
std::string writableDir_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FILE_SVC extra2d::FileSvc::inst()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <core/service.h>
|
#include <functional>
|
||||||
|
#include <module/imodule.h>
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
#include <types/ptr/ref_counted.h>
|
#include <types/const/priority.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -94,47 +95,47 @@ constexpr Key RightAlt = SDL_SCANCODE_RALT;
|
||||||
* @brief 鼠标按键
|
* @brief 鼠标按键
|
||||||
*/
|
*/
|
||||||
enum class MouseBtn : uint8 {
|
enum class MouseBtn : uint8 {
|
||||||
Left = 0,
|
Left = 0,
|
||||||
Middle = 1,
|
Middle = 1,
|
||||||
Right = 2,
|
Right = 2,
|
||||||
X1 = 3,
|
X1 = 3,
|
||||||
X2 = 4,
|
X2 = 4,
|
||||||
Count = 5
|
Count = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 游戏手柄按键
|
* @brief 游戏手柄按键
|
||||||
*/
|
*/
|
||||||
enum class GamepadBtn : uint8 {
|
enum class GamepadBtn : uint8 {
|
||||||
A = 0,
|
A = 0,
|
||||||
B = 1,
|
B = 1,
|
||||||
X = 2,
|
X = 2,
|
||||||
Y = 3,
|
Y = 3,
|
||||||
Back = 4,
|
Back = 4,
|
||||||
Guide = 5,
|
Guide = 5,
|
||||||
Start = 6,
|
Start = 6,
|
||||||
LeftStick = 7,
|
LeftStick = 7,
|
||||||
RightStick = 8,
|
RightStick = 8,
|
||||||
LeftShoulder = 9,
|
LeftShoulder = 9,
|
||||||
RightShoulder = 10,
|
RightShoulder = 10,
|
||||||
DPadUp = 11,
|
DPadUp = 11,
|
||||||
DPadDown = 12,
|
DPadDown = 12,
|
||||||
DPadLeft = 13,
|
DPadLeft = 13,
|
||||||
DPadRight = 14,
|
DPadRight = 14,
|
||||||
Count = 15
|
Count = 15
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 游戏手柄轴
|
* @brief 游戏手柄轴
|
||||||
*/
|
*/
|
||||||
enum class GamepadAxis : uint8 {
|
enum class GamepadAxis : uint8 {
|
||||||
LeftX = 0,
|
LeftX = 0,
|
||||||
LeftY = 1,
|
LeftY = 1,
|
||||||
RightX = 2,
|
RightX = 2,
|
||||||
RightY = 3,
|
RightY = 3,
|
||||||
TriggerLeft = 4,
|
TriggerLeft = 4,
|
||||||
TriggerRight = 5,
|
TriggerRight = 5,
|
||||||
Count = 6
|
Count = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -146,224 +147,232 @@ enum class TouchState : uint8 { None = 0, Began, Moved, Ended, Cancelled };
|
||||||
* @brief 触摸点信息
|
* @brief 触摸点信息
|
||||||
*/
|
*/
|
||||||
struct TouchPoint {
|
struct TouchPoint {
|
||||||
int64 id = 0;
|
int64 id = 0;
|
||||||
float x = 0.0f;
|
float x = 0.0f;
|
||||||
float y = 0.0f;
|
float y = 0.0f;
|
||||||
float prevX = 0.0f;
|
float prevX = 0.0f;
|
||||||
float prevY = 0.0f;
|
float prevY = 0.0f;
|
||||||
float deltaX = 0.0f;
|
float deltaX = 0.0f;
|
||||||
float deltaY = 0.0f;
|
float deltaY = 0.0f;
|
||||||
TouchState state = TouchState::None;
|
TouchState state = TouchState::None;
|
||||||
float pressure = 1.0f;
|
float pressure = 1.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 按键回调类型
|
* @brief 按键回调类型
|
||||||
*/
|
*/
|
||||||
using KeyCb = Fn<void(Key)>;
|
using KeyCb = std::function<void(Key)>;
|
||||||
using MouseBtnCb = Fn<void(MouseBtn, int32 x, int32 y)>;
|
using MouseBtnCb = std::function<void(MouseBtn, int32 x, int32 y)>;
|
||||||
using TouchCb = Fn<void(const TouchPoint &)>;
|
using TouchCb = std::function<void(const TouchPoint&)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 输入服务
|
* @brief 输入模块
|
||||||
*
|
*
|
||||||
* 管理键盘、鼠标、触摸、游戏手柄输入
|
* 管理键盘、鼠标、触摸、游戏手柄输入
|
||||||
|
* 非单例设计,通过 Context 管理生命周期
|
||||||
*/
|
*/
|
||||||
class InputSvc : public IService {
|
class InputModule : public IModule {
|
||||||
public:
|
public:
|
||||||
/**
|
InputModule();
|
||||||
* @brief 获取单例实例
|
~InputModule() override;
|
||||||
*/
|
|
||||||
static InputSvc &inst();
|
|
||||||
|
|
||||||
const char *name() const override { return "InputSvc"; }
|
// 禁止拷贝
|
||||||
int pri() const override { return Pri::Input; }
|
InputModule(const InputModule&) = delete;
|
||||||
|
InputModule& operator=(const InputModule&) = delete;
|
||||||
|
|
||||||
bool init() override;
|
// 允许移动
|
||||||
void shutdown() override;
|
InputModule(InputModule&&) noexcept;
|
||||||
void update(float dt) override;
|
InputModule& operator=(InputModule&&) noexcept;
|
||||||
|
|
||||||
// ========== 键盘 ==========
|
// IModule 接口实现
|
||||||
|
const char* name() const override { return "Input"; }
|
||||||
|
ModuleType type() const override { return ModuleType::System; }
|
||||||
|
int priority() const override { return Pri::Input; }
|
||||||
|
bool init() override;
|
||||||
|
void shutdown() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查按键是否按下
|
* @brief 处理输入事件
|
||||||
*/
|
*/
|
||||||
bool isKeyDown(Key key) const;
|
void processEvent(const SDL_Event& evt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查按键是否刚按下
|
* @brief 更新输入状态(每帧调用)
|
||||||
*/
|
*/
|
||||||
bool isKeyPressed(Key key) const;
|
void update();
|
||||||
|
|
||||||
/**
|
// ========== 键盘 ==========
|
||||||
* @brief 检查按键是否刚释放
|
|
||||||
*/
|
|
||||||
bool isKeyReleased(Key key) const;
|
|
||||||
|
|
||||||
// ========== 鼠标 ==========
|
/**
|
||||||
|
* @brief 检查按键是否按下
|
||||||
|
*/
|
||||||
|
bool isKeyDown(Key key) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取鼠标位置
|
* @brief 检查按键是否刚按下
|
||||||
*/
|
*/
|
||||||
void getMousePos(int32 &x, int32 &y) const;
|
bool isKeyPressed(Key key) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取鼠标位置(浮点)
|
* @brief 检查按键是否刚释放
|
||||||
*/
|
*/
|
||||||
void getMousePos(float &x, float &y) const;
|
bool isKeyReleased(Key key) const;
|
||||||
|
|
||||||
/**
|
// ========== 鼠标 ==========
|
||||||
* @brief 检查鼠标按键是否按下
|
|
||||||
*/
|
|
||||||
bool isMouseBtnDown(MouseBtn btn) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查鼠标按键是否刚按下
|
* @brief 获取鼠标位置
|
||||||
*/
|
*/
|
||||||
bool isMouseBtnPressed(MouseBtn btn) const;
|
void getMousePos(int32& x, int32& y) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查鼠标按键是否刚释放
|
* @brief 获取鼠标位置(浮点)
|
||||||
*/
|
*/
|
||||||
bool isMouseBtnReleased(MouseBtn btn) const;
|
void getMousePos(float& x, float& y) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取鼠标滚轮
|
* @brief 检查鼠标按键是否按下
|
||||||
*/
|
*/
|
||||||
int32 getMouseWheel() const;
|
bool isMouseBtnDown(MouseBtn btn) const;
|
||||||
|
|
||||||
// ========== 触摸 ==========
|
/**
|
||||||
|
* @brief 检查鼠标按键是否刚按下
|
||||||
|
*/
|
||||||
|
bool isMouseBtnPressed(MouseBtn btn) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取触摸点数量
|
* @brief 检查鼠标按键是否刚释放
|
||||||
*/
|
*/
|
||||||
int32 touchCount() const;
|
bool isMouseBtnReleased(MouseBtn btn) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取触摸点
|
* @brief 获取鼠标滚轮
|
||||||
* @param idx 触摸点索引
|
*/
|
||||||
* @return 触摸点信息,无效索引返回 nullptr
|
int32 getMouseWheel() const;
|
||||||
*/
|
|
||||||
const TouchPoint *getTouch(int32 idx) const;
|
|
||||||
|
|
||||||
/**
|
// ========== 触摸 ==========
|
||||||
* @brief 根据 ID 获取触摸点
|
|
||||||
*/
|
|
||||||
const TouchPoint *getTouchById(int64 id) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否有触摸
|
* @brief 获取触摸点数量
|
||||||
*/
|
*/
|
||||||
bool hasTouch() const { return touchCount() > 0; }
|
int32 touchCount() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取所有活跃触摸点
|
* @brief 获取触摸点
|
||||||
*/
|
* @param idx 触摸点索引
|
||||||
const std::vector<TouchPoint> &getTouches() const { return activeTouches_; }
|
* @return 触摸点信息,无效索引返回 nullptr
|
||||||
|
*/
|
||||||
|
const TouchPoint* getTouch(int32 idx) const;
|
||||||
|
|
||||||
// ========== 游戏手柄 ==========
|
/**
|
||||||
|
* @brief 根据 ID 获取触摸点
|
||||||
|
*/
|
||||||
|
const TouchPoint* getTouchById(int64 id) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 连接的游戏手柄数量
|
* @brief 检查是否有触摸
|
||||||
*/
|
*/
|
||||||
int32 gamepadCount() const;
|
bool hasTouch() const { return touchCount() > 0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查手柄按键是否按下
|
* @brief 获取所有活跃触摸点
|
||||||
*/
|
*/
|
||||||
bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const;
|
const std::vector<TouchPoint>& getTouches() const { return activeTouches_; }
|
||||||
|
|
||||||
/**
|
// ========== 游戏手柄 ==========
|
||||||
* @brief 检查手柄按键是否刚按下
|
|
||||||
*/
|
|
||||||
bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取手柄轴值 (-1.0 到 1.0)
|
* @brief 连接的游戏手柄数量
|
||||||
*/
|
*/
|
||||||
float getGamepadAxis(int32 idx, GamepadAxis axis) const;
|
int32 gamepadCount() const;
|
||||||
|
|
||||||
// ========== 回调设置 ==========
|
/**
|
||||||
|
* @brief 检查手柄按键是否按下
|
||||||
|
*/
|
||||||
|
bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置按键按下回调
|
* @brief 检查手柄按键是否刚按下
|
||||||
*/
|
*/
|
||||||
void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); }
|
bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置按键释放回调
|
* @brief 获取手柄轴值 (-1.0 到 1.0)
|
||||||
*/
|
*/
|
||||||
void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); }
|
float getGamepadAxis(int32 idx, GamepadAxis axis) const;
|
||||||
|
|
||||||
/**
|
// ========== 回调设置 ==========
|
||||||
* @brief 设置鼠标按下回调
|
|
||||||
*/
|
|
||||||
void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置鼠标释放回调
|
* @brief 设置按键按下回调
|
||||||
*/
|
*/
|
||||||
void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); }
|
void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置触摸开始回调
|
* @brief 设置按键释放回调
|
||||||
*/
|
*/
|
||||||
void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); }
|
void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置触摸移动回调
|
* @brief 设置鼠标按下回调
|
||||||
*/
|
*/
|
||||||
void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); }
|
void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置触摸结束回调
|
* @brief 设置鼠标释放回调
|
||||||
*/
|
*/
|
||||||
void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); }
|
void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置触摸开始回调
|
||||||
|
*/
|
||||||
|
void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置触摸移动回调
|
||||||
|
*/
|
||||||
|
void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置触摸结束回调
|
||||||
|
*/
|
||||||
|
void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InputSvc() = default;
|
static constexpr int32 KEY_COUNT = SDL_NUM_SCANCODES;
|
||||||
|
static constexpr int32 MAX_GAMEPADS = 4;
|
||||||
|
static constexpr int32 MAX_TOUCHES = 10;
|
||||||
|
|
||||||
static constexpr int32 KEY_COUNT = SDL_NUM_SCANCODES;
|
std::array<uint8, KEY_COUNT> keyState_{};
|
||||||
static constexpr int32 MAX_GAMEPADS = 4;
|
std::array<uint8, KEY_COUNT> keyPrev_{};
|
||||||
static constexpr int32 MAX_TOUCHES = 10;
|
|
||||||
|
|
||||||
std::array<uint8, KEY_COUNT> keyState_{};
|
int32 mouseX_ = 0;
|
||||||
std::array<uint8, KEY_COUNT> keyPrev_{};
|
int32 mouseY_ = 0;
|
||||||
|
int32 mouseWheel_ = 0;
|
||||||
|
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mouseState_{};
|
||||||
|
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mousePrev_{};
|
||||||
|
|
||||||
int32 mouseX_ = 0;
|
std::vector<TouchPoint> activeTouches_;
|
||||||
int32 mouseY_ = 0;
|
std::vector<TouchPoint> endedTouches_;
|
||||||
int32 mouseWheel_ = 0;
|
|
||||||
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mouseState_{};
|
|
||||||
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mousePrev_{};
|
|
||||||
|
|
||||||
std::vector<TouchPoint> activeTouches_;
|
SDL_GameController* gamepads_[MAX_GAMEPADS] = {};
|
||||||
std::vector<TouchPoint> endedTouches_;
|
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)> padState_[MAX_GAMEPADS];
|
||||||
|
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)> padPrev_[MAX_GAMEPADS];
|
||||||
|
|
||||||
SDL_GameController *gamepads_[MAX_GAMEPADS] = {};
|
KeyCb onKeyDown_;
|
||||||
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)>
|
KeyCb onKeyUp_;
|
||||||
padState_[MAX_GAMEPADS];
|
MouseBtnCb onMouseDown_;
|
||||||
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)>
|
MouseBtnCb onMouseUp_;
|
||||||
padPrev_[MAX_GAMEPADS];
|
TouchCb onTouchBegan_;
|
||||||
|
TouchCb onTouchMoved_;
|
||||||
|
TouchCb onTouchEnded_;
|
||||||
|
|
||||||
KeyCb onKeyDown_;
|
void openGamepad(int32 idx);
|
||||||
KeyCb onKeyUp_;
|
void closeGamepad(int32 idx);
|
||||||
MouseBtnCb onMouseDown_;
|
|
||||||
MouseBtnCb onMouseUp_;
|
|
||||||
TouchCb onTouchBegan_;
|
|
||||||
TouchCb onTouchMoved_;
|
|
||||||
TouchCb onTouchEnded_;
|
|
||||||
|
|
||||||
void processEvent(const SDL_Event &evt);
|
void processTouchDown(const SDL_TouchFingerEvent& evt);
|
||||||
void openGamepad(int32 idx);
|
void processTouchUp(const SDL_TouchFingerEvent& evt);
|
||||||
void closeGamepad(int32 idx);
|
void processTouchMotion(const SDL_TouchFingerEvent& evt);
|
||||||
|
|
||||||
void processTouchDown(const SDL_TouchFingerEvent &evt);
|
|
||||||
void processTouchUp(const SDL_TouchFingerEvent &evt);
|
|
||||||
void processTouchMotion(const SDL_TouchFingerEvent &evt);
|
|
||||||
|
|
||||||
friend class WindowSvc;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INPUT_SVC extra2d::InputSvc::inst()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <core/service.h>
|
#include <functional>
|
||||||
|
#include <module/imodule.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
|
#include <types/const/priority.h>
|
||||||
#include <types/math/size.h>
|
#include <types/math/size.h>
|
||||||
#include <types/math/vec2.h>
|
#include <types/math/vec2.h>
|
||||||
#include <types/ptr/ref_counted.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -14,148 +15,154 @@ namespace extra2d {
|
||||||
* @brief 窗口配置
|
* @brief 窗口配置
|
||||||
*/
|
*/
|
||||||
struct WindowCfg {
|
struct WindowCfg {
|
||||||
std::string title = "Extra2D";
|
std::string title = "Extra2D";
|
||||||
int32 width = 1280;
|
int32 width = 1280;
|
||||||
int32 height = 720;
|
int32 height = 720;
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
bool resizable = true;
|
bool resizable = true;
|
||||||
bool vsync = true;
|
bool vsync = true;
|
||||||
int32 glMajor = 3;
|
int32 glMajor = 3;
|
||||||
int32 glMinor = 3;
|
int32 glMinor = 3;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 窗口事件回调
|
* @brief 窗口事件回调
|
||||||
*/
|
*/
|
||||||
using ResizeCb = Fn<void(int32 w, int32 h)>;
|
using ResizeCb = std::function<void(int32 w, int32 h)>;
|
||||||
using CloseCb = Fn<void()>;
|
using CloseCb = std::function<void()>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 窗口服务
|
* @brief 窗口模块
|
||||||
*
|
*
|
||||||
* 管理 SDL2 窗口和 OpenGL 上下文
|
* 管理 SDL2 窗口和 OpenGL 上下文
|
||||||
|
* 非单例设计,通过 Context 管理生命周期
|
||||||
*/
|
*/
|
||||||
class WindowSvc : public IService {
|
class WindowModule : public IModule {
|
||||||
public:
|
public:
|
||||||
/**
|
WindowModule();
|
||||||
* @brief 获取单例实例
|
~WindowModule() override;
|
||||||
*/
|
|
||||||
static WindowSvc &inst();
|
|
||||||
|
|
||||||
const char *name() const override { return "WindowSvc"; }
|
// 禁止拷贝
|
||||||
int pri() const override { return Pri::System; }
|
WindowModule(const WindowModule&) = delete;
|
||||||
|
WindowModule& operator=(const WindowModule&) = delete;
|
||||||
|
|
||||||
bool init() override;
|
// 允许移动
|
||||||
void shutdown() override;
|
WindowModule(WindowModule&&) noexcept;
|
||||||
|
WindowModule& operator=(WindowModule&&) noexcept;
|
||||||
|
|
||||||
/**
|
// IModule 接口实现
|
||||||
* @brief 使用配置创建窗口
|
const char* name() const override { return "Window"; }
|
||||||
*/
|
ModuleType type() const override { return ModuleType::System; }
|
||||||
bool create(const WindowCfg &cfg);
|
int priority() const override { return Pri::Window; }
|
||||||
|
bool init() override;
|
||||||
|
void shutdown() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 处理窗口事件
|
* @brief 使用配置创建窗口
|
||||||
* @return true 继续运行,false 应退出
|
*/
|
||||||
*/
|
bool create(const WindowCfg& cfg);
|
||||||
bool pollEvents();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 交换缓冲区
|
* @brief 处理窗口事件
|
||||||
*/
|
* @return true 继续运行,false 应退出
|
||||||
void swapBuffers();
|
*/
|
||||||
|
bool pollEvents();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 SDL 窗口句柄
|
* @brief 交换缓冲区
|
||||||
*/
|
*/
|
||||||
SDL_Window *handle() const { return window_; }
|
void swapBuffers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取 OpenGL 上下文
|
* @brief 获取 SDL 窗口句柄
|
||||||
*/
|
*/
|
||||||
SDL_GLContext glContext() const { return glCtx_; }
|
SDL_Window* handle() const { return window_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取窗口尺寸
|
* @brief 获取 OpenGL 上下文
|
||||||
*/
|
*/
|
||||||
Size getSize() const;
|
SDL_GLContext glContext() const { return glCtx_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取窗口位置
|
* @brief 获取窗口尺寸
|
||||||
*/
|
*/
|
||||||
Vec2 getPosition() const;
|
Size getSize() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置窗口尺寸
|
* @brief 获取窗口位置
|
||||||
*/
|
*/
|
||||||
void setSize(int32 w, int32 h);
|
Vec2 getPosition() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置窗口标题
|
* @brief 设置窗口尺寸
|
||||||
*/
|
*/
|
||||||
void setTitle(const std::string &title);
|
void setSize(int32 w, int32 h);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置全屏模式
|
* @brief 设置窗口标题
|
||||||
*/
|
*/
|
||||||
void setFullscreen(bool fullscreen);
|
void setTitle(const std::string& title);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否全屏
|
* @brief 设置全屏模式
|
||||||
*/
|
*/
|
||||||
bool isFullscreen() const;
|
void setFullscreen(bool fullscreen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置垂直同步
|
* @brief 检查是否全屏
|
||||||
*/
|
*/
|
||||||
void setVsync(bool vsync);
|
bool isFullscreen() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否垂直同步
|
* @brief 设置垂直同步
|
||||||
*/
|
*/
|
||||||
bool isVsync() const;
|
void setVsync(bool vsync);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 显示/隐藏窗口
|
* @brief 检查是否垂直同步
|
||||||
*/
|
*/
|
||||||
void setVisible(bool visible);
|
bool isVsync() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查窗口是否可见
|
* @brief 显示/隐藏窗口
|
||||||
*/
|
*/
|
||||||
bool isVisible() const;
|
void setVisible(bool visible);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置窗口关闭回调
|
* @brief 检查窗口是否可见
|
||||||
*/
|
*/
|
||||||
void setOnClose(CloseCb cb) { onClose_ = std::move(cb); }
|
bool isVisible() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置窗口大小改变回调
|
* @brief 设置窗口关闭回调
|
||||||
*/
|
*/
|
||||||
void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); }
|
void setOnClose(CloseCb cb) { onClose_ = std::move(cb); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 请求关闭窗口
|
* @brief 设置窗口大小改变回调
|
||||||
*/
|
*/
|
||||||
void requestClose() { shouldClose_ = true; }
|
void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否应该关闭
|
* @brief 请求关闭窗口
|
||||||
*/
|
*/
|
||||||
bool shouldClose() const { return shouldClose_; }
|
void requestClose() { shouldClose_ = true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否应该关闭
|
||||||
|
*/
|
||||||
|
bool shouldClose() const { return shouldClose_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WindowSvc() = default;
|
void handleWindowEvent(const SDL_WindowEvent& evt);
|
||||||
|
|
||||||
SDL_Window *window_ = nullptr;
|
SDL_Window* window_ = nullptr;
|
||||||
SDL_GLContext glCtx_ = nullptr;
|
SDL_GLContext glCtx_ = nullptr;
|
||||||
bool shouldClose_ = false;
|
bool shouldClose_ = false;
|
||||||
bool vsync_ = true;
|
bool vsync_ = true;
|
||||||
|
|
||||||
CloseCb onClose_;
|
CloseCb onClose_;
|
||||||
ResizeCb onResize_;
|
ResizeCb onResize_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define WINDOW extra2d::WindowSvc::inst()
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <types/base/types.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 插件信息结构
|
||||||
|
*/
|
||||||
|
struct PluginInfo {
|
||||||
|
std::string name; // 插件名称
|
||||||
|
std::string version; // 版本号
|
||||||
|
std::string author; // 作者
|
||||||
|
std::string description; // 描述
|
||||||
|
std::vector<std::string> dependencies; // 依赖的插件名称列表
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 插件接口 - 基于事件总线
|
||||||
|
*
|
||||||
|
* 插件是动态加载的扩展组件,可以在运行时加载/卸载
|
||||||
|
* 插件通过事件总线与其他组件通信,零直接依赖
|
||||||
|
*
|
||||||
|
* 设计理念:
|
||||||
|
* - 插件可以在运行时动态加载(动态库)或静态链接
|
||||||
|
* - 插件之间可以声明依赖关系
|
||||||
|
* - 插件通过事件总线发送和接收消息
|
||||||
|
*/
|
||||||
|
class IPlugin {
|
||||||
|
public:
|
||||||
|
virtual ~IPlugin() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取插件信息
|
||||||
|
* @return 插件信息结构
|
||||||
|
*/
|
||||||
|
virtual const PluginInfo& getInfo() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 加载插件
|
||||||
|
*
|
||||||
|
* 在此注册事件监听器
|
||||||
|
* 返回 false 表示加载失败
|
||||||
|
*
|
||||||
|
* @return 加载是否成功
|
||||||
|
*/
|
||||||
|
virtual bool load() { return true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 卸载插件
|
||||||
|
*
|
||||||
|
* 在此执行清理工作
|
||||||
|
* 事件监听器会自动注销(RAII)
|
||||||
|
*/
|
||||||
|
virtual void unload() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取依赖的插件列表
|
||||||
|
* @return 依赖的插件名称列表
|
||||||
|
*/
|
||||||
|
virtual std::vector<std::string> getDependencies() const {
|
||||||
|
return getInfo().dependencies;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 插件导出宏(用于动态库插件)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define EXTRA2D_PLUGIN_EXPORT extern "C" __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define EXTRA2D_PLUGIN_EXPORT extern "C" __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 定义插件导出接口
|
||||||
|
*
|
||||||
|
* 在插件动态库中使用:
|
||||||
|
* EXTRA2D_DEFINE_PLUGIN(MyPluginClass)
|
||||||
|
*/
|
||||||
|
#define EXTRA2D_DEFINE_PLUGIN(PluginClass) \
|
||||||
|
EXTRA2D_PLUGIN_EXPORT extra2d::IPlugin* extra2d_create_plugin() { \
|
||||||
|
return new PluginClass(); \
|
||||||
|
} \
|
||||||
|
EXTRA2D_PLUGIN_EXPORT void extra2d_destroy_plugin(extra2d::IPlugin* plugin) { \
|
||||||
|
delete plugin; \
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <plugin\iplugin.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 插件加载器 - 非单例
|
||||||
|
*
|
||||||
|
* 管理所有插件的生命周期,支持动态加载/卸载
|
||||||
|
* 自动处理插件依赖关系
|
||||||
|
*/
|
||||||
|
class PluginLoader {
|
||||||
|
public:
|
||||||
|
PluginLoader();
|
||||||
|
~PluginLoader();
|
||||||
|
|
||||||
|
// 禁止拷贝
|
||||||
|
PluginLoader(const PluginLoader&) = delete;
|
||||||
|
PluginLoader& operator=(const PluginLoader&) = delete;
|
||||||
|
|
||||||
|
// 允许移动
|
||||||
|
PluginLoader(PluginLoader&&) noexcept;
|
||||||
|
PluginLoader& operator=(PluginLoader&&) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从动态库加载插件
|
||||||
|
* @param path 插件动态库路径
|
||||||
|
* @return 是否加载成功
|
||||||
|
*/
|
||||||
|
bool loadFromLibrary(const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注册内置插件(静态链接)
|
||||||
|
* @param plugin 插件实例指针(不由加载器管理生命周期)
|
||||||
|
*/
|
||||||
|
void registerPlugin(IPlugin* plugin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 卸载插件
|
||||||
|
* @param name 插件名称
|
||||||
|
*/
|
||||||
|
void unloadPlugin(const char* name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取插件
|
||||||
|
* @param name 插件名称
|
||||||
|
* @return 插件指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
IPlugin* getPlugin(const char* name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查插件是否存在
|
||||||
|
* @param name 插件名称
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
bool hasPlugin(const char* name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有插件
|
||||||
|
* @return 插件列表
|
||||||
|
*/
|
||||||
|
std::vector<IPlugin*> getAllPlugins() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化所有插件(自动处理依赖)
|
||||||
|
* @return 是否全部初始化成功
|
||||||
|
*/
|
||||||
|
bool initAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭所有插件(按依赖逆序)
|
||||||
|
*/
|
||||||
|
void shutdownAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取插件数量
|
||||||
|
* @return 插件数量
|
||||||
|
*/
|
||||||
|
size_t getPluginCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 添加插件搜索路径
|
||||||
|
* @param path 搜索路径
|
||||||
|
*/
|
||||||
|
void addSearchPath(const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从目录加载所有插件
|
||||||
|
* @param directory 目录路径
|
||||||
|
* @return 加载成功的插件数量
|
||||||
|
*/
|
||||||
|
size_t loadPluginsFromDirectory(const char* directory);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 插件条目结构
|
||||||
|
*/
|
||||||
|
struct PluginEntry {
|
||||||
|
IPlugin* plugin = nullptr; // 插件实例
|
||||||
|
void* handle = nullptr; // 动态库句柄
|
||||||
|
bool isDynamic = false; // 是否为动态加载
|
||||||
|
bool owned = false; // 是否由加载器管理生命周期
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析插件依赖
|
||||||
|
* @param plugin 插件
|
||||||
|
* @return 依赖是否满足
|
||||||
|
*/
|
||||||
|
bool resolveDependencies(IPlugin* plugin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查依赖是否满足
|
||||||
|
* @param dependencies 依赖列表
|
||||||
|
* @return 是否满足
|
||||||
|
*/
|
||||||
|
bool checkDependencies(const std::vector<std::string>& dependencies);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 按依赖顺序排序插件
|
||||||
|
*/
|
||||||
|
void sortPluginsByDependencies();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 加载动态库
|
||||||
|
* @param path 库路径
|
||||||
|
* @return 库句柄
|
||||||
|
*/
|
||||||
|
void* loadDynamicLibrary(const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 卸载动态库
|
||||||
|
* @param handle 库句柄
|
||||||
|
*/
|
||||||
|
void unloadDynamicLibrary(void* handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取动态库符号
|
||||||
|
* @param handle 库句柄
|
||||||
|
* @param name 符号名称
|
||||||
|
* @return 符号地址
|
||||||
|
*/
|
||||||
|
void* getSymbol(void* handle, const char* name);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, PluginEntry> plugins_;
|
||||||
|
std::vector<std::string> searchPaths_;
|
||||||
|
std::vector<IPlugin*> sortedPlugins_; // 按依赖排序的插件列表
|
||||||
|
bool inited_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -5,10 +5,17 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模块优先级常量
|
||||||
|
*
|
||||||
|
* 数值越小优先级越高,越早初始化,越晚关闭
|
||||||
|
*/
|
||||||
namespace Pri {
|
namespace Pri {
|
||||||
constexpr int32 Min = INT32_MIN;
|
constexpr int32 Min = INT32_MIN;
|
||||||
constexpr int32 System = -1000;
|
constexpr int32 System = -1000;
|
||||||
constexpr int32 Input = -100;
|
constexpr int32 Window = -900; // 窗口模块(最早初始化)
|
||||||
|
constexpr int32 Input = -800; // 输入模块
|
||||||
|
constexpr int32 File = -700; // 文件模块
|
||||||
constexpr int32 Default = 0;
|
constexpr int32 Default = 0;
|
||||||
constexpr int32 Logic = 100;
|
constexpr int32 Logic = 100;
|
||||||
constexpr int32 Anim = 200;
|
constexpr int32 Anim = 200;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ public:
|
||||||
|
|
||||||
Ptr<T>& operator=(T* p) { reset(p); return *this; }
|
Ptr<T>& operator=(T* p) { reset(p); return *this; }
|
||||||
Ptr<T>& operator=(const Ptr<T>& r) { return *this = r.p_; }
|
Ptr<T>& operator=(const Ptr<T>& r) { return *this = r.p_; }
|
||||||
|
template <typename U> Ptr<T>& operator=(const Ptr<U>& r) { return *this = r.get(); }
|
||||||
Ptr<T>& operator=(Ptr<T>&& r) noexcept { Ptr<T>(std::move(r)).swap(*this); return *this; }
|
Ptr<T>& operator=(Ptr<T>&& r) noexcept { Ptr<T>(std::move(r)).swap(*this); return *this; }
|
||||||
|
|
||||||
bool operator==(std::nullptr_t) const { return p_ == nullptr; }
|
bool operator==(std::nullptr_t) const { return p_ == nullptr; }
|
||||||
|
|
|
||||||
|
|
@ -3,84 +3,75 @@
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <types/base/types.h>
|
#include <types/base/types.h>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Random 类 - 随机数生成器
|
// Random 类 - 随机数生成器
|
||||||
|
// 非单例设计,可以创建多个独立的随机数生成器实例
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class Random {
|
class Random {
|
||||||
public:
|
public:
|
||||||
/// 获取单例实例
|
/// 默认构造函数(使用随机种子)
|
||||||
static Random &getInstance();
|
Random();
|
||||||
|
|
||||||
/// 设置随机种子
|
/// 使用指定种子构造
|
||||||
void setSeed(uint32 seed);
|
explicit Random(uint32 seed);
|
||||||
|
|
||||||
/// 使用当前时间作为种子
|
/// 设置随机种子
|
||||||
void randomize();
|
void setSeed(uint32 seed);
|
||||||
|
|
||||||
/// 获取 [0, 1) 范围内的随机浮点数
|
/// 使用当前时间作为种子
|
||||||
float getFloat();
|
void randomize();
|
||||||
|
|
||||||
/// 获取 [min, max] 范围内的随机浮点数
|
/// 获取 [0, 1) 范围内的随机浮点数
|
||||||
float getFloat(float min, float max);
|
float getFloat();
|
||||||
|
|
||||||
/// 获取 [0, max] 范围内的随机整数
|
/// 获取 [min, max] 范围内的随机浮点数
|
||||||
int getInt(int max);
|
float getFloat(float min, float max);
|
||||||
|
|
||||||
/// 获取 [min, max] 范围内的随机整数
|
/// 获取 [0, max] 范围内的随机整数
|
||||||
int getInt(int min, int max);
|
int getInt(int max);
|
||||||
|
|
||||||
/// 获取随机布尔值
|
/// 获取 [min, max] 范围内的随机整数
|
||||||
bool getBool();
|
int getInt(int min, int max);
|
||||||
|
|
||||||
/// 获取随机布尔值(带概率)
|
/// 获取随机布尔值
|
||||||
bool getBool(float probability);
|
bool getBool();
|
||||||
|
|
||||||
/// 获取指定范围内的随机角度(弧度)
|
/// 获取随机布尔值(带概率)
|
||||||
float getAngle();
|
bool getBool(float probability);
|
||||||
|
|
||||||
/// 获取 [-1, 1] 范围内的随机数(用于方向)
|
/// 获取指定范围内的随机角度(弧度)
|
||||||
float getSigned();
|
float getAngle();
|
||||||
|
|
||||||
|
/// 获取 [-1, 1] 范围内的随机数(用于方向)
|
||||||
|
float getSigned();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Random();
|
std::mt19937 generator_;
|
||||||
~Random() = default;
|
std::uniform_real_distribution<float> floatDist_;
|
||||||
|
|
||||||
Random(const Random &) = delete;
|
|
||||||
Random &operator=(const Random &) = delete;
|
|
||||||
|
|
||||||
std::mt19937 generator_;
|
|
||||||
std::uniform_real_distribution<float> floatDist_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 便捷函数
|
// 便捷函数 - 使用全局默认随机数生成器
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/// 获取 [0, 1) 范围内的随机浮点数
|
/// 获取 [0, 1) 范围内的随机浮点数
|
||||||
inline float randomFloat() { return Random::getInstance().getFloat(); }
|
float randomFloat();
|
||||||
|
|
||||||
/// 获取 [min, max] 范围内的随机浮点数
|
/// 获取 [min, max] 范围内的随机浮点数
|
||||||
inline float randomFloat(float min, float max) {
|
float randomFloat(float min, float max);
|
||||||
return Random::getInstance().getFloat(min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取 [0, max] 范围内的随机整数
|
/// 获取 [0, max] 范围内的随机整数
|
||||||
inline int randomInt(int max) { return Random::getInstance().getInt(max); }
|
int randomInt(int max);
|
||||||
|
|
||||||
/// 获取 [min, max] 范围内的随机整数
|
/// 获取 [min, max] 范围内的随机整数
|
||||||
inline int randomInt(int min, int max) {
|
int randomInt(int min, int max);
|
||||||
return Random::getInstance().getInt(min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取随机布尔值
|
/// 获取随机布尔值
|
||||||
inline bool randomBool() { return Random::getInstance().getBool(); }
|
bool randomBool();
|
||||||
|
|
||||||
/// 获取随机布尔值(带概率)
|
/// 获取随机布尔值(带概率)
|
||||||
inline bool randomBool(float probability) {
|
bool randomBool(float probability);
|
||||||
return Random::getInstance().getBool(probability);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
#include <app/application.h>
|
#include <app/application.h>
|
||||||
#include <core/director.h>
|
#include <context/context.h>
|
||||||
#include <core/service.h>
|
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <platform/file.h>
|
|
||||||
#include <platform/input.h>
|
|
||||||
#include <platform/sdl2.h>
|
#include <platform/sdl2.h>
|
||||||
#include <platform/window.h>
|
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
@ -35,12 +30,18 @@ static double getTimeSeconds() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Application &Application::instance() {
|
std::unique_ptr<Application> Application::create() {
|
||||||
static Application instance;
|
return std::unique_ptr<Application>(new Application());
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() { shutdown(); }
|
Application::Application() = default;
|
||||||
|
|
||||||
|
Application::~Application() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::Application(Application&&) noexcept = default;
|
||||||
|
Application& Application::operator=(Application&&) noexcept = default;
|
||||||
|
|
||||||
bool Application::init(const AppConfig &config) {
|
bool Application::init(const AppConfig &config) {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
|
|
@ -70,38 +71,19 @@ bool Application::init(const AppConfig &config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SVC_MGR.reg(&WINDOW);
|
// 创建引擎上下文
|
||||||
SVC_MGR.reg(&INPUT_SVC);
|
context_ = Context::create();
|
||||||
SVC_MGR.reg(&FILE_SVC);
|
if (!context_) {
|
||||||
|
E2D_LOG_ERROR("Failed to create context");
|
||||||
if (!SVC_MGR.initAll()) {
|
|
||||||
E2D_LOG_ERROR("Failed to initialize services");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WINDOW.create([&] {
|
// 初始化引擎
|
||||||
WindowCfg cfg;
|
if (!context_->init()) {
|
||||||
cfg.title = config_.title;
|
E2D_LOG_ERROR("Failed to initialize context");
|
||||||
cfg.width = config_.width;
|
|
||||||
cfg.height = config_.height;
|
|
||||||
cfg.fullscreen = config_.fullscreen;
|
|
||||||
cfg.resizable = config_.resizable;
|
|
||||||
cfg.vsync = config_.vsync;
|
|
||||||
cfg.glMajor = config_.glMajor;
|
|
||||||
cfg.glMinor = config_.glMinor;
|
|
||||||
return cfg;
|
|
||||||
}())) {
|
|
||||||
E2D_LOG_ERROR("Failed to create window");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DIRECTOR.init()) {
|
|
||||||
E2D_LOG_ERROR("Failed to initialize Director");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
WINDOW.setOnClose([this]() { quit(); });
|
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
running_ = true;
|
running_ = true;
|
||||||
|
|
||||||
|
|
@ -121,8 +103,9 @@ void Application::shutdown() {
|
||||||
|
|
||||||
E2D_LOG_INFO("Shutting down application...");
|
E2D_LOG_INFO("Shutting down application...");
|
||||||
|
|
||||||
DIRECTOR.shutdown();
|
// 关闭上下文(会自动关闭模块和插件)
|
||||||
SVC_MGR.shutdownAll();
|
context_.reset();
|
||||||
|
|
||||||
Sdl2::shutdown();
|
Sdl2::shutdown();
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
|
|
@ -144,7 +127,7 @@ void Application::run() {
|
||||||
|
|
||||||
lastFrameTime_ = getTimeSeconds();
|
lastFrameTime_ = getTimeSeconds();
|
||||||
|
|
||||||
while (running_ && !WINDOW.shouldClose()) {
|
while (running_) {
|
||||||
mainLoop();
|
mainLoop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,14 +135,11 @@ void Application::run() {
|
||||||
void Application::quit() {
|
void Application::quit() {
|
||||||
shouldQuit_ = true;
|
shouldQuit_ = true;
|
||||||
running_ = false;
|
running_ = false;
|
||||||
WINDOW.requestClose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::pause() {
|
void Application::pause() {
|
||||||
if (!paused_) {
|
if (!paused_) {
|
||||||
paused_ = true;
|
paused_ = true;
|
||||||
SVC_MGR.pauseAll();
|
|
||||||
DIRECTOR.pause();
|
|
||||||
events::OnPause::emit();
|
events::OnPause::emit();
|
||||||
E2D_LOG_INFO("Application paused");
|
E2D_LOG_INFO("Application paused");
|
||||||
}
|
}
|
||||||
|
|
@ -168,20 +148,13 @@ void Application::pause() {
|
||||||
void Application::resume() {
|
void Application::resume() {
|
||||||
if (paused_) {
|
if (paused_) {
|
||||||
paused_ = false;
|
paused_ = false;
|
||||||
SVC_MGR.resumeAll();
|
|
||||||
DIRECTOR.resume();
|
|
||||||
lastFrameTime_ = getTimeSeconds();
|
|
||||||
events::OnResume::emit();
|
events::OnResume::emit();
|
||||||
|
lastFrameTime_ = getTimeSeconds();
|
||||||
E2D_LOG_INFO("Application resumed");
|
E2D_LOG_INFO("Application resumed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mainLoop() {
|
void Application::mainLoop() {
|
||||||
if (!WINDOW.pollEvents()) {
|
|
||||||
running_ = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double currentTime = getTimeSeconds();
|
double currentTime = getTimeSeconds();
|
||||||
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
||||||
lastFrameTime_ = currentTime;
|
lastFrameTime_ = currentTime;
|
||||||
|
|
@ -200,8 +173,7 @@ void Application::mainLoop() {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
WINDOW.swapBuffers();
|
// 帧率限制
|
||||||
|
|
||||||
if (!config_.vsync && config_.fpsLimit > 0) {
|
if (!config_.vsync && config_.fpsLimit > 0) {
|
||||||
double frameEndTime = getTimeSeconds();
|
double frameEndTime = getTimeSeconds();
|
||||||
double frameTime = frameEndTime - currentTime;
|
double frameTime = frameEndTime - currentTime;
|
||||||
|
|
@ -214,9 +186,10 @@ void Application::mainLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::update() {
|
void Application::update() {
|
||||||
SVC_MGR.updateAll(deltaTime_);
|
// 通过上下文更新引擎
|
||||||
events::OnUpdate::emit(deltaTime_);
|
if (context_) {
|
||||||
DIRECTOR.mainLoop(deltaTime_);
|
context_->tick(deltaTime_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include <context/context.h>
|
||||||
|
#include <module/module_registry.h>
|
||||||
|
#include <module/timer_module.h>
|
||||||
|
#include <plugin/plugin_loader.h>
|
||||||
|
#include <event/events.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
Context::Context()
|
||||||
|
: moduleRegistry_(std::make_unique<ModuleRegistry>())
|
||||||
|
, pluginLoader_(std::make_unique<PluginLoader>())
|
||||||
|
, timerModule_(std::make_unique<TimerModule>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Context::~Context() {
|
||||||
|
if (inited_) {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context::Context(Context&&) noexcept = default;
|
||||||
|
Context& Context::operator=(Context&&) noexcept = default;
|
||||||
|
|
||||||
|
std::unique_ptr<Context> Context::create() {
|
||||||
|
return std::make_unique<Context>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Context::init() {
|
||||||
|
if (inited_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送引擎初始化事件
|
||||||
|
events::OnInit::emit();
|
||||||
|
|
||||||
|
// 注册核心模块
|
||||||
|
moduleRegistry_->registerModule(timerModule_.get());
|
||||||
|
|
||||||
|
// 初始化所有模块
|
||||||
|
if (!moduleRegistry_->initAll()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化所有插件
|
||||||
|
if (!pluginLoader_->initAll()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inited_ = true;
|
||||||
|
running_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::shutdown() {
|
||||||
|
if (!inited_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
running_ = false;
|
||||||
|
|
||||||
|
// 关闭所有插件
|
||||||
|
pluginLoader_->shutdownAll();
|
||||||
|
|
||||||
|
// 关闭所有模块
|
||||||
|
moduleRegistry_->shutdownAll();
|
||||||
|
|
||||||
|
// 发送引擎关闭事件
|
||||||
|
events::OnShutdown::emit();
|
||||||
|
|
||||||
|
inited_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::tick(float dt) {
|
||||||
|
if (!running_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新时间和帧数
|
||||||
|
totalTime_ += dt;
|
||||||
|
frameCount_++;
|
||||||
|
|
||||||
|
// 更新定时器模块
|
||||||
|
timerModule_->update(dt);
|
||||||
|
|
||||||
|
// 发送更新事件
|
||||||
|
events::OnUpdate::emit(dt);
|
||||||
|
|
||||||
|
// 发送延迟更新事件
|
||||||
|
events::OnLateUpdate::emit(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleRegistry& Context::modules() {
|
||||||
|
return *moduleRegistry_;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginLoader& Context::plugins() {
|
||||||
|
return *pluginLoader_;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerModule& Context::timer() {
|
||||||
|
return *timerModule_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
#include <core/director.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
Director& Director::inst() {
|
|
||||||
static Director instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Director::init() {
|
|
||||||
if (inited_) return true;
|
|
||||||
|
|
||||||
inited_ = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Director::shutdown() {
|
|
||||||
if (!inited_) return;
|
|
||||||
|
|
||||||
SVC_MGR.shutdownAll();
|
|
||||||
SCHED.unscheduleAll();
|
|
||||||
|
|
||||||
inited_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Director::mainLoop(float dt) {
|
|
||||||
if (paused_) return;
|
|
||||||
|
|
||||||
dt_ = dt;
|
|
||||||
totalTime_ += dt;
|
|
||||||
frameCount_++;
|
|
||||||
|
|
||||||
SVC_MGR.updateAll(dt);
|
|
||||||
|
|
||||||
fixedAccumulator_ += dt;
|
|
||||||
while (fixedAccumulator_ >= fixedDt_) {
|
|
||||||
SVC_MGR.fixedUpdateAll(fixedDt_);
|
|
||||||
fixedAccumulator_ -= fixedDt_;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCHED.update(dt);
|
|
||||||
|
|
||||||
SVC_MGR.lateUpdateAll(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Director::mainLoopParallel(float dt) {
|
|
||||||
if (paused_) return;
|
|
||||||
|
|
||||||
dt_ = dt;
|
|
||||||
totalTime_ += dt;
|
|
||||||
frameCount_++;
|
|
||||||
|
|
||||||
SVC_MGR.updateAll(dt);
|
|
||||||
|
|
||||||
fixedAccumulator_ += dt;
|
|
||||||
while (fixedAccumulator_ >= fixedDt_) {
|
|
||||||
SVC_MGR.fixedUpdateAll(fixedDt_);
|
|
||||||
fixedAccumulator_ -= fixedDt_;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCHED.updateParallel(dt);
|
|
||||||
|
|
||||||
SVC_MGR.lateUpdateAll(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Director::pause() {
|
|
||||||
paused_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Director::resume() {
|
|
||||||
paused_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Director::setTimeScale(float scale) {
|
|
||||||
SCHED.setTimeScale(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,322 +0,0 @@
|
||||||
#include <core/scheduler.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <thread>
|
|
||||||
#include <future>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class IntervalTimer : public Timer {
|
|
||||||
public:
|
|
||||||
IntervalTimer(Scheduler::Cb cb, float interval, uint32 repeat, float delay)
|
|
||||||
: cb_(std::move(cb)) {
|
|
||||||
interval_ = interval;
|
|
||||||
repeat_ = repeat;
|
|
||||||
delay_ = delay;
|
|
||||||
useDelay_ = delay > 0.0f;
|
|
||||||
runForever_ = repeat == 0;
|
|
||||||
elapsed_ = useDelay_ ? 0.0f : -interval_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(float dt) override {
|
|
||||||
if (paused_ || done_) return;
|
|
||||||
|
|
||||||
elapsed_ += dt;
|
|
||||||
|
|
||||||
if (useDelay_) {
|
|
||||||
if (elapsed_ < delay_) return;
|
|
||||||
elapsed_ -= delay_;
|
|
||||||
useDelay_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elapsed_ >= interval_) {
|
|
||||||
trigger();
|
|
||||||
elapsed_ -= interval_;
|
|
||||||
if (!runForever_) {
|
|
||||||
timesExecuted_++;
|
|
||||||
if (timesExecuted_ >= repeat_) {
|
|
||||||
done_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void trigger() override {
|
|
||||||
if (cb_) cb_(elapsed_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Scheduler::Cb cb_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OnceTimer : public Timer {
|
|
||||||
public:
|
|
||||||
OnceTimer(Scheduler::VoidCb cb, float delay)
|
|
||||||
: cb_(std::move(cb)) {
|
|
||||||
delay_ = delay;
|
|
||||||
elapsed_ = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(float dt) override {
|
|
||||||
if (paused_ || done_) return;
|
|
||||||
|
|
||||||
elapsed_ += dt;
|
|
||||||
if (elapsed_ >= delay_) {
|
|
||||||
trigger();
|
|
||||||
done_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void trigger() override {
|
|
||||||
if (cb_) cb_();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Scheduler::VoidCb cb_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// SafePriorityQueue 实现
|
|
||||||
void Scheduler::SafePriorityQueue::push(const UpdateEntry& entry) {
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
queue_.push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Scheduler::SafePriorityQueue::pop(UpdateEntry& entry) {
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
if (queue_.empty()) return false;
|
|
||||||
entry = queue_.top();
|
|
||||||
queue_.pop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Scheduler::SafePriorityQueue::empty() const {
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
return queue_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Scheduler::SafePriorityQueue::size() const {
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
return queue_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::SafePriorityQueue::clear() {
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
while (!queue_.empty()) queue_.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
Scheduler& Scheduler::inst() {
|
|
||||||
static Scheduler instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimerHdl Scheduler::scheduleUpdate(TimerTarget* target, int pri) {
|
|
||||||
if (!target) return INVALID_HDL;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(updateIndexMutex_);
|
|
||||||
UpdateEntry entry{target, pri, false, false};
|
|
||||||
updates_.push_back(entry);
|
|
||||||
updateIndex_[target] = updates_.size() - 1;
|
|
||||||
|
|
||||||
return genHdl();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::unscheduleUpdate(TimerTarget* target) {
|
|
||||||
if (!target) return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(updateIndexMutex_);
|
|
||||||
auto it = updateIndex_.find(target);
|
|
||||||
if (it != updateIndex_.end()) {
|
|
||||||
size_t idx = it->second;
|
|
||||||
if (idx < updates_.size()) {
|
|
||||||
updates_[idx].markedForDel = true;
|
|
||||||
}
|
|
||||||
updateIndex_.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TimerHdl Scheduler::schedule(Cb cb, float interval, uint32 repeat, float delay) {
|
|
||||||
if (!cb) return INVALID_HDL;
|
|
||||||
|
|
||||||
auto timer = makePtr<IntervalTimer>(std::move(cb), interval, repeat, delay);
|
|
||||||
TimerHdl hdl = genHdl();
|
|
||||||
timer->hdl_ = hdl;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
timers_[hdl] = timer;
|
|
||||||
}
|
|
||||||
return hdl;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimerHdl Scheduler::scheduleOnce(VoidCb cb, float delay) {
|
|
||||||
if (!cb) return INVALID_HDL;
|
|
||||||
|
|
||||||
auto timer = makePtr<OnceTimer>(std::move(cb), delay);
|
|
||||||
TimerHdl hdl = genHdl();
|
|
||||||
timer->hdl_ = hdl;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
timers_[hdl] = timer;
|
|
||||||
}
|
|
||||||
return hdl;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimerHdl Scheduler::scheduleForever(Cb cb, float interval) {
|
|
||||||
return schedule(std::move(cb), interval, 0, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::unschedule(TimerHdl hdl) {
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
timers_.erase(hdl);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::unscheduleAll() {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
timers_.clear();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(updateIndexMutex_);
|
|
||||||
updates_.clear();
|
|
||||||
updateIndex_.clear();
|
|
||||||
}
|
|
||||||
updateQueue_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::pause(TimerHdl hdl) {
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
auto it = timers_.find(hdl);
|
|
||||||
if (it != timers_.end()) {
|
|
||||||
it->second->pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::resume(TimerHdl hdl) {
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
auto it = timers_.find(hdl);
|
|
||||||
if (it != timers_.end()) {
|
|
||||||
it->second->resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::update(float dt) {
|
|
||||||
float scaledDt = dt * timeScale_.load();
|
|
||||||
|
|
||||||
locked_ = true;
|
|
||||||
|
|
||||||
// 更新所有 update 回调
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(updateIndexMutex_);
|
|
||||||
for (auto& entry : updates_) {
|
|
||||||
if (!entry.markedForDel && !entry.paused && entry.target) {
|
|
||||||
entry.target->update(scaledDt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updates_.erase(
|
|
||||||
std::remove_if(updates_.begin(), updates_.end(),
|
|
||||||
[](const UpdateEntry& e) { return e.markedForDel; }),
|
|
||||||
updates_.end()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新定时器
|
|
||||||
std::vector<TimerHdl> toRemove;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
for (auto it = timers_.begin(); it != timers_.end(); ++it) {
|
|
||||||
auto& timer = it->second;
|
|
||||||
timer->update(scaledDt);
|
|
||||||
if (timer->isDone()) {
|
|
||||||
toRemove.push_back(it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto hdl : toRemove) {
|
|
||||||
timers_.erase(hdl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
locked_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scheduler::updateParallel(float dt) {
|
|
||||||
float scaledDt = dt * timeScale_.load();
|
|
||||||
|
|
||||||
locked_ = true;
|
|
||||||
|
|
||||||
// 并行更新所有 update 回调(使用标准库线程)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(updateIndexMutex_);
|
|
||||||
size_t numThreads = std::thread::hardware_concurrency();
|
|
||||||
if (numThreads == 0) numThreads = 4;
|
|
||||||
|
|
||||||
size_t batchSize = updates_.size() / numThreads;
|
|
||||||
if (batchSize == 0) batchSize = 1;
|
|
||||||
|
|
||||||
std::vector<std::future<void>> futures;
|
|
||||||
|
|
||||||
for (size_t t = 0; t < numThreads && t * batchSize < updates_.size(); ++t) {
|
|
||||||
size_t start = t * batchSize;
|
|
||||||
size_t end = (t == numThreads - 1) ? updates_.size() : (t + 1) * batchSize;
|
|
||||||
|
|
||||||
futures.push_back(std::async(std::launch::async, [this, start, end, scaledDt]() {
|
|
||||||
for (size_t i = start; i < end; ++i) {
|
|
||||||
auto& entry = updates_[i];
|
|
||||||
if (!entry.markedForDel && !entry.paused && entry.target) {
|
|
||||||
entry.target->update(scaledDt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& f : futures) {
|
|
||||||
f.wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
updates_.erase(
|
|
||||||
std::remove_if(updates_.begin(), updates_.end(),
|
|
||||||
[](const UpdateEntry& e) { return e.markedForDel; }),
|
|
||||||
updates_.end()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新定时器
|
|
||||||
std::vector<TimerHdl> toRemove;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
for (auto it = timers_.begin(); it != timers_.end(); ++it) {
|
|
||||||
auto& timer = it->second;
|
|
||||||
timer->update(scaledDt);
|
|
||||||
if (timer->isDone()) {
|
|
||||||
toRemove.push_back(it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto hdl : toRemove) {
|
|
||||||
timers_.erase(hdl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
locked_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Scheduler::isScheduled(TimerHdl hdl) const {
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
return timers_.find(hdl) != timers_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Scheduler::count() const {
|
|
||||||
std::lock_guard<std::mutex> lock(timersMutex_);
|
|
||||||
return timers_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
TimerHdl Scheduler::genHdl() {
|
|
||||||
return nextHdl_.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
#include <core/service.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace extra2d {
|
|
||||||
|
|
||||||
SvcMgr& SvcMgr::inst() {
|
|
||||||
static SvcMgr instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::reg(IService* svc) {
|
|
||||||
if (!svc) return;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
svcMap_[svc->name()] = Ptr<IService>(svc);
|
|
||||||
sortSvcs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::unreg(const char* name) {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
svcMap_.erase(name);
|
|
||||||
sortSvcs();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<IService> SvcMgr::get(const char* name) {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
auto it = svcMap_.find(name);
|
|
||||||
if (it != svcMap_.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SvcMgr::initAll() {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
for (auto& svc : sortedSvcs_) {
|
|
||||||
if (svc && svc->state_ == SvcState::None) {
|
|
||||||
if (!svc->init()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
svc->state_ = SvcState::Inited;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::pauseAll() {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
for (auto& svc : sortedSvcs_) {
|
|
||||||
if (svc && svc->state_ == SvcState::Running) {
|
|
||||||
svc->pause();
|
|
||||||
svc->state_ = SvcState::Paused;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::resumeAll() {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
for (auto& svc : sortedSvcs_) {
|
|
||||||
if (svc && svc->state_ == SvcState::Paused) {
|
|
||||||
svc->resume();
|
|
||||||
svc->state_ = SvcState::Running;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::shutdownAll() {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
for (auto it = sortedSvcs_.rbegin(); it != sortedSvcs_.rend(); ++it) {
|
|
||||||
if (*it && (*it)->state_ >= SvcState::Inited) {
|
|
||||||
(*it)->shutdown();
|
|
||||||
(*it)->state_ = SvcState::Shutdown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::updateAll(float dt) {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
for (auto& svc : sortedSvcs_) {
|
|
||||||
if (svc && svc->state_ >= SvcState::Inited && svc->isEnabled()) {
|
|
||||||
if (svc->state_ == SvcState::Inited) {
|
|
||||||
svc->state_ = SvcState::Running;
|
|
||||||
}
|
|
||||||
svc->update(dt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::lateUpdateAll(float dt) {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
for (auto& svc : sortedSvcs_) {
|
|
||||||
if (svc && svc->state_ >= SvcState::Running && svc->isEnabled()) {
|
|
||||||
svc->lateUpdate(dt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::fixedUpdateAll(float dt) {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
for (auto& svc : sortedSvcs_) {
|
|
||||||
if (svc && svc->state_ >= SvcState::Running && svc->isEnabled()) {
|
|
||||||
svc->fixedUpdate(dt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SvcMgr::has(const char* name) const {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
return svcMap_.find(name) != svcMap_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SvcMgr::count() const {
|
|
||||||
std::lock_guard<std::mutex> lock(svcMapMutex_);
|
|
||||||
return svcMap_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvcMgr::sortSvcs() {
|
|
||||||
sortedSvcs_.clear();
|
|
||||||
for (auto it = svcMap_.begin(); it != svcMap_.end(); ++it) {
|
|
||||||
sortedSvcs_.push_back(it->second);
|
|
||||||
}
|
|
||||||
std::sort(sortedSvcs_.begin(), sortedSvcs_.end(),
|
|
||||||
[](const Ptr<IService>& a, const Ptr<IService>& b) {
|
|
||||||
return a->pri() < b->pri();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
#include <module/module_registry.h>
|
||||||
|
#include <event/event_bus.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
ModuleRegistry::ModuleRegistry() = default;
|
||||||
|
|
||||||
|
ModuleRegistry::~ModuleRegistry() {
|
||||||
|
if (inited_) {
|
||||||
|
shutdownAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleRegistry::ModuleRegistry(ModuleRegistry&&) noexcept = default;
|
||||||
|
ModuleRegistry& ModuleRegistry::operator=(ModuleRegistry&&) noexcept = default;
|
||||||
|
|
||||||
|
void ModuleRegistry::registerModule(IModule* module) {
|
||||||
|
if (!module) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = module->name();
|
||||||
|
if (!name || moduleMap_.find(name) != moduleMap_.end()) {
|
||||||
|
return; // 已存在或名称为空
|
||||||
|
}
|
||||||
|
|
||||||
|
modules_.push_back(module);
|
||||||
|
moduleMap_[name] = module;
|
||||||
|
sorted_ = false;
|
||||||
|
|
||||||
|
// 模块注册事件(暂不发送,避免依赖 events.h)
|
||||||
|
// event::broadcast<events::OnModuleRegistered>(name, module->type());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleRegistry::unregisterModule(const char* name) {
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = moduleMap_.find(name);
|
||||||
|
if (it == moduleMap_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IModule* module = it->second;
|
||||||
|
|
||||||
|
// 如果已初始化,先关闭
|
||||||
|
if (inited_ && module) {
|
||||||
|
module->shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从列表中移除
|
||||||
|
modules_.erase(
|
||||||
|
std::remove(modules_.begin(), modules_.end(), module),
|
||||||
|
modules_.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
moduleMap_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
IModule* ModuleRegistry::getModule(const char* name) const {
|
||||||
|
if (!name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = moduleMap_.find(name);
|
||||||
|
return it != moduleMap_.end() ? it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModuleRegistry::hasModule(const char* name) const {
|
||||||
|
if (!name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return moduleMap_.find(name) != moduleMap_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<IModule*> ModuleRegistry::getAllModules() const {
|
||||||
|
return modules_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<IModule*> ModuleRegistry::getModulesByType(ModuleType type) const {
|
||||||
|
std::vector<IModule*> result;
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && module->type() == type) {
|
||||||
|
result.push_back(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModuleRegistry::initAll() {
|
||||||
|
if (inited_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按优先级排序
|
||||||
|
sortModules();
|
||||||
|
|
||||||
|
// 初始化所有模块
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && !module->init()) {
|
||||||
|
// 初始化失败,关闭已初始化的模块
|
||||||
|
shutdownAll();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inited_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleRegistry::shutdownAll() {
|
||||||
|
if (!inited_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按优先级逆序关闭
|
||||||
|
for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) {
|
||||||
|
if (*it) {
|
||||||
|
(*it)->shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inited_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ModuleRegistry::getModuleCount() const {
|
||||||
|
return modules_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleRegistry::sortModules() {
|
||||||
|
if (sorted_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按优先级排序(数值小的优先)
|
||||||
|
std::sort(modules_.begin(), modules_.end(),
|
||||||
|
[](IModule* a, IModule* b) {
|
||||||
|
if (!a || !b) return a < b;
|
||||||
|
return a->priority() < b->priority();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
sorted_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,221 @@
|
||||||
|
#include <module/timer_module.h>
|
||||||
|
#include <event/events.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
TimerModule::TimerModule() = default;
|
||||||
|
|
||||||
|
TimerModule::~TimerModule() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerModule::TimerModule(TimerModule&& other) noexcept
|
||||||
|
: timers_(std::move(other.timers_))
|
||||||
|
, pendingRemove_(std::move(other.pendingRemove_))
|
||||||
|
, nextId_(other.nextId_)
|
||||||
|
, timeScale_(other.timeScale_)
|
||||||
|
, inUpdate_(other.inUpdate_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerModule& TimerModule::operator=(TimerModule&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
shutdown();
|
||||||
|
|
||||||
|
timers_ = std::move(other.timers_);
|
||||||
|
pendingRemove_ = std::move(other.pendingRemove_);
|
||||||
|
nextId_ = other.nextId_;
|
||||||
|
timeScale_ = other.timeScale_;
|
||||||
|
inUpdate_ = other.inUpdate_;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TimerModule::init() {
|
||||||
|
// 初始化完成,等待 OnUpdate 事件
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerModule::shutdown() {
|
||||||
|
cancelAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerId TimerModule::scheduleOnce(float delay, TimerCallback callback) {
|
||||||
|
if (delay < 0.0f) delay = 0.0f;
|
||||||
|
|
||||||
|
TimerId id = generateId();
|
||||||
|
auto info = std::make_unique<TimerInfo>();
|
||||||
|
info->id = id;
|
||||||
|
info->interval = delay;
|
||||||
|
info->elapsed = 0.0f;
|
||||||
|
info->repeat = 1;
|
||||||
|
info->executed = 0;
|
||||||
|
info->paused = false;
|
||||||
|
info->cancelled = false;
|
||||||
|
info->callback = std::move(callback);
|
||||||
|
|
||||||
|
timers_[id] = std::move(info);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerId TimerModule::scheduleRepeat(float interval, uint32 repeat, TimerCallback callback) {
|
||||||
|
if (interval < 0.0f) interval = 0.0f;
|
||||||
|
|
||||||
|
TimerId id = generateId();
|
||||||
|
auto info = std::make_unique<TimerInfo>();
|
||||||
|
info->id = id;
|
||||||
|
info->interval = interval;
|
||||||
|
info->elapsed = 0.0f;
|
||||||
|
info->repeat = repeat;
|
||||||
|
info->executed = 0;
|
||||||
|
info->paused = false;
|
||||||
|
info->cancelled = false;
|
||||||
|
info->callback = std::move(callback);
|
||||||
|
|
||||||
|
timers_[id] = std::move(info);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerId TimerModule::scheduleUpdate(TimerUpdateCallback callback) {
|
||||||
|
TimerId id = generateId();
|
||||||
|
auto info = std::make_unique<TimerInfo>();
|
||||||
|
info->id = id;
|
||||||
|
info->interval = 0.0f;
|
||||||
|
info->elapsed = 0.0f;
|
||||||
|
info->repeat = 0;
|
||||||
|
info->executed = 0;
|
||||||
|
info->paused = false;
|
||||||
|
info->cancelled = false;
|
||||||
|
info->updateCallback = std::move(callback);
|
||||||
|
|
||||||
|
timers_[id] = std::move(info);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerModule::cancel(TimerId id) {
|
||||||
|
if (id == INVALID_TIMER_ID) return;
|
||||||
|
|
||||||
|
auto it = timers_.find(id);
|
||||||
|
if (it != timers_.end()) {
|
||||||
|
if (inUpdate_) {
|
||||||
|
// 如果在更新中,标记为待删除
|
||||||
|
it->second->cancelled = true;
|
||||||
|
pendingRemove_.push_back(id);
|
||||||
|
} else {
|
||||||
|
// 直接删除
|
||||||
|
timers_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerModule::pause(TimerId id) {
|
||||||
|
if (id == INVALID_TIMER_ID) return;
|
||||||
|
|
||||||
|
auto it = timers_.find(id);
|
||||||
|
if (it != timers_.end()) {
|
||||||
|
it->second->paused = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerModule::resume(TimerId id) {
|
||||||
|
if (id == INVALID_TIMER_ID) return;
|
||||||
|
|
||||||
|
auto it = timers_.find(id);
|
||||||
|
if (it != timers_.end()) {
|
||||||
|
it->second->paused = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerModule::cancelAll() {
|
||||||
|
if (inUpdate_) {
|
||||||
|
// 如果在更新中,标记所有为待删除
|
||||||
|
for (auto& pair : timers_) {
|
||||||
|
pair.second->cancelled = true;
|
||||||
|
pendingRemove_.push_back(pair.first);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直接清空
|
||||||
|
timers_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TimerModule::getActiveCount() const {
|
||||||
|
size_t count = 0;
|
||||||
|
for (const auto& pair : timers_) {
|
||||||
|
if (!pair.second->cancelled) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerModule::update(float dt) {
|
||||||
|
if (timers_.empty()) return;
|
||||||
|
|
||||||
|
// 应用时间缩放
|
||||||
|
dt *= timeScale_;
|
||||||
|
|
||||||
|
inUpdate_ = true;
|
||||||
|
|
||||||
|
// 收集需要执行的定时器
|
||||||
|
std::vector<TimerInfo*> toExecute;
|
||||||
|
std::vector<TimerId> toRemove;
|
||||||
|
|
||||||
|
for (auto& pair : timers_) {
|
||||||
|
TimerInfo* info = pair.second.get();
|
||||||
|
|
||||||
|
if (info->cancelled || info->paused) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新任务(每帧执行)
|
||||||
|
if (info->updateCallback) {
|
||||||
|
toExecute.push_back(info);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定时任务
|
||||||
|
info->elapsed += dt;
|
||||||
|
|
||||||
|
if (info->elapsed >= info->interval) {
|
||||||
|
toExecute.push_back(info);
|
||||||
|
|
||||||
|
// 重置计时器
|
||||||
|
info->elapsed = 0.0f;
|
||||||
|
info->executed++;
|
||||||
|
|
||||||
|
// 检查是否需要移除
|
||||||
|
if (info->repeat > 0 && info->executed >= info->repeat) {
|
||||||
|
toRemove.push_back(info->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行回调
|
||||||
|
for (TimerInfo* info : toExecute) {
|
||||||
|
if (info->callback) {
|
||||||
|
info->callback();
|
||||||
|
}
|
||||||
|
if (info->updateCallback) {
|
||||||
|
info->updateCallback(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inUpdate_ = false;
|
||||||
|
|
||||||
|
// 移除已完成的定时器
|
||||||
|
for (TimerId id : toRemove) {
|
||||||
|
timers_.erase(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理待删除列表
|
||||||
|
for (TimerId id : pendingRemove_) {
|
||||||
|
timers_.erase(id);
|
||||||
|
}
|
||||||
|
pendingRemove_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerId TimerModule::generateId() {
|
||||||
|
return nextId_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -16,94 +16,92 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
FileSvc& FileSvc::inst() {
|
FileModule::FileModule() = default;
|
||||||
static FileSvc instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileSvc::init() {
|
FileModule::~FileModule() = default;
|
||||||
if (isInited()) return true;
|
|
||||||
|
FileModule::FileModule(FileModule&&) noexcept = default;
|
||||||
|
FileModule& FileModule::operator=(FileModule&&) noexcept = default;
|
||||||
|
|
||||||
|
bool FileModule::init() {
|
||||||
writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D");
|
writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D");
|
||||||
if (writableDir_.empty()) {
|
if (writableDir_.empty()) {
|
||||||
writableDir_ = "./";
|
writableDir_ = "./";
|
||||||
}
|
}
|
||||||
|
|
||||||
state_ = SvcState::Inited;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSvc::shutdown() {
|
void FileModule::shutdown() {
|
||||||
state_ = SvcState::Shutdown;
|
// 清理工作
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSvc::exists(const std::string& path) const {
|
bool FileModule::exists(const std::string& path) const {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
return stat(path.c_str(), &st) == 0;
|
return stat(path.c_str(), &st) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSvc::isDir(const std::string& path) const {
|
bool FileModule::isDir(const std::string& path) const {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path.c_str(), &st) != 0) return false;
|
if (stat(path.c_str(), &st) != 0) return false;
|
||||||
return (st.st_mode & S_IFDIR) != 0;
|
return (st.st_mode & S_IFDIR) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData FileSvc::read(const std::string& path) const {
|
FileData FileModule::read(const std::string& path) const {
|
||||||
FileData result;
|
FileData result;
|
||||||
|
|
||||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
result.error = "Cannot open file: " + path;
|
result.error = "Cannot open file: " + path;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::streamsize size = file.tellg();
|
std::streamsize size = file.tellg();
|
||||||
file.seekg(0, std::ios::beg);
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
result.data.resize(static_cast<size_t>(size));
|
result.data.resize(static_cast<size_t>(size));
|
||||||
if (!file.read(reinterpret_cast<char*>(result.data.data()), size)) {
|
if (!file.read(reinterpret_cast<char*>(result.data.data()), size)) {
|
||||||
result.error = "Failed to read file: " + path;
|
result.error = "Failed to read file: " + path;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.ok = true;
|
result.ok = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileSvc::readString(const std::string& path) const {
|
std::string FileModule::readString(const std::string& path) const {
|
||||||
std::ifstream file(path);
|
std::ifstream file(path);
|
||||||
if (!file.is_open()) return "";
|
if (!file.is_open()) return "";
|
||||||
|
|
||||||
std::stringstream buffer;
|
std::stringstream buffer;
|
||||||
buffer << file.rdbuf();
|
buffer << file.rdbuf();
|
||||||
return buffer.str();
|
return buffer.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSvc::write(const std::string& path, const void* data, size_t size) const {
|
bool FileModule::write(const std::string& path, const void* data, size_t size) const {
|
||||||
std::ofstream file(path, std::ios::binary);
|
std::ofstream file(path, std::ios::binary);
|
||||||
if (!file.is_open()) return false;
|
if (!file.is_open()) return false;
|
||||||
|
|
||||||
file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size));
|
file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size));
|
||||||
return file.good();
|
return file.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSvc::writeString(const std::string& path, const std::string& content) const {
|
bool FileModule::writeString(const std::string& path, const std::string& content) const {
|
||||||
return write(path, content.data(), content.size());
|
return write(path, content.data(), content.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSvc::append(const std::string& path, const void* data, size_t size) const {
|
bool FileModule::append(const std::string& path, const void* data, size_t size) const {
|
||||||
std::ofstream file(path, std::ios::binary | std::ios::app);
|
std::ofstream file(path, std::ios::binary | std::ios::app);
|
||||||
if (!file.is_open()) return false;
|
if (!file.is_open()) return false;
|
||||||
|
|
||||||
file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size));
|
file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size));
|
||||||
return file.good();
|
return file.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSvc::remove(const std::string& path) const {
|
bool FileModule::remove(const std::string& path) const {
|
||||||
return std::remove(path.c_str()) == 0;
|
return std::remove(path.c_str()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSvc::mkdir(const std::string& path) const {
|
bool FileModule::mkdir(const std::string& path) const {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
|
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
|
||||||
#else
|
#else
|
||||||
|
|
@ -111,41 +109,41 @@ bool FileSvc::mkdir(const std::string& path) const {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<FileInfo> FileSvc::listDir(const std::string& path) const {
|
std::vector<FileInfo> FileModule::listDir(const std::string& path) const {
|
||||||
std::vector<FileInfo> result;
|
std::vector<FileInfo> result;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WIN32_FIND_DATAA findData;
|
WIN32_FIND_DATAA findData;
|
||||||
std::string searchPath = path + "\\*";
|
std::string searchPath = path + "\\*";
|
||||||
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
|
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
|
||||||
|
|
||||||
if (hFind == INVALID_HANDLE_VALUE) return result;
|
if (hFind == INVALID_HANDLE_VALUE) return result;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
std::string name = findData.cFileName;
|
std::string name = findData.cFileName;
|
||||||
if (name == "." || name == "..") continue;
|
if (name == "." || name == "..") continue;
|
||||||
|
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
info.name = name;
|
info.name = name;
|
||||||
info.path = join(path, name);
|
info.path = join(path, name);
|
||||||
info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||||
if (!info.isDir) {
|
if (!info.isDir) {
|
||||||
info.size = static_cast<int64>(findData.nFileSizeLow) |
|
info.size = static_cast<int64>(findData.nFileSizeLow) |
|
||||||
(static_cast<int64>(findData.nFileSizeHigh) << 32);
|
(static_cast<int64>(findData.nFileSizeHigh) << 32);
|
||||||
}
|
}
|
||||||
result.push_back(info);
|
result.push_back(info);
|
||||||
} while (FindNextFileA(hFind, &findData));
|
} while (FindNextFileA(hFind, &findData));
|
||||||
|
|
||||||
FindClose(hFind);
|
FindClose(hFind);
|
||||||
#else
|
#else
|
||||||
DIR* dir = opendir(path.c_str());
|
DIR* dir = opendir(path.c_str());
|
||||||
if (!dir) return result;
|
if (!dir) return result;
|
||||||
|
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
while ((entry = readdir(dir)) != nullptr) {
|
while ((entry = readdir(dir)) != nullptr) {
|
||||||
std::string name = entry->d_name;
|
std::string name = entry->d_name;
|
||||||
if (name == "." || name == "..") continue;
|
if (name == "." || name == "..") continue;
|
||||||
|
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
info.name = name;
|
info.name = name;
|
||||||
info.path = join(path, name);
|
info.path = join(path, name);
|
||||||
|
|
@ -155,46 +153,46 @@ std::vector<FileInfo> FileSvc::listDir(const std::string& path) const {
|
||||||
}
|
}
|
||||||
result.push_back(info);
|
result.push_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64 FileSvc::fileSize(const std::string& path) const {
|
int64 FileModule::fileSize(const std::string& path) const {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(path.c_str(), &st) != 0) return -1;
|
if (stat(path.c_str(), &st) != 0) return -1;
|
||||||
return static_cast<int64>(st.st_size);
|
return static_cast<int64>(st.st_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileSvc::ext(const std::string& path) const {
|
std::string FileModule::ext(const std::string& path) const {
|
||||||
size_t pos = path.find_last_of('.');
|
size_t pos = path.find_last_of('.');
|
||||||
if (pos == std::string::npos || pos == 0) return "";
|
if (pos == std::string::npos || pos == 0) return "";
|
||||||
|
|
||||||
size_t lastSep = path.find_last_of("/\\");
|
size_t lastSep = path.find_last_of("/\\");
|
||||||
if (lastSep != std::string::npos && pos < lastSep) return "";
|
if (lastSep != std::string::npos && pos < lastSep) return "";
|
||||||
|
|
||||||
return path.substr(pos + 1);
|
return path.substr(pos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileSvc::fileName(const std::string& path) const {
|
std::string FileModule::fileName(const std::string& path) const {
|
||||||
size_t pos = path.find_last_of("/\\");
|
size_t pos = path.find_last_of("/\\");
|
||||||
if (pos == std::string::npos) return path;
|
if (pos == std::string::npos) return path;
|
||||||
return path.substr(pos + 1);
|
return path.substr(pos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileSvc::dirName(const std::string& path) const {
|
std::string FileModule::dirName(const std::string& path) const {
|
||||||
size_t pos = path.find_last_of("/\\");
|
size_t pos = path.find_last_of("/\\");
|
||||||
if (pos == std::string::npos) return ".";
|
if (pos == std::string::npos) return ".";
|
||||||
if (pos == 0) return "/";
|
if (pos == 0) return "/";
|
||||||
return path.substr(0, pos);
|
return path.substr(0, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileSvc::join(const std::string& a, const std::string& b) const {
|
std::string FileModule::join(const std::string& a, const std::string& b) const {
|
||||||
if (a.empty()) return b;
|
if (a.empty()) return b;
|
||||||
if (b.empty()) return a;
|
if (b.empty()) return a;
|
||||||
|
|
||||||
char last = a.back();
|
char last = a.back();
|
||||||
if (last == '/' || last == '\\') {
|
if (last == '/' || last == '\\') {
|
||||||
return a + b;
|
return a + b;
|
||||||
|
|
@ -202,11 +200,11 @@ std::string FileSvc::join(const std::string& a, const std::string& b) const {
|
||||||
return a + "/" + b;
|
return a + "/" + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileSvc::writableDir() const {
|
std::string FileModule::writableDir() const {
|
||||||
return writableDir_;
|
return writableDir_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FileSvc::assetPath(const std::string& relPath) const {
|
std::string FileModule::assetPath(const std::string& relPath) const {
|
||||||
if (assetRoot_.empty()) return relPath;
|
if (assetRoot_.empty()) return relPath;
|
||||||
return join(assetRoot_, relPath);
|
return join(assetRoot_, relPath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,76 +1,131 @@
|
||||||
#include <platform/input.h>
|
#include <platform/input.h>
|
||||||
#include <platform/window.h>
|
#include <event/events.h>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
InputSvc& InputSvc::inst() {
|
InputModule::InputModule() = default;
|
||||||
static InputSvc instance;
|
|
||||||
return instance;
|
InputModule::~InputModule() {
|
||||||
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::init() {
|
InputModule::InputModule(InputModule&& other) noexcept
|
||||||
if (isInited()) return true;
|
: mouseX_(other.mouseX_)
|
||||||
|
, mouseY_(other.mouseY_)
|
||||||
|
, mouseWheel_(other.mouseWheel_)
|
||||||
|
, activeTouches_(std::move(other.activeTouches_))
|
||||||
|
, endedTouches_(std::move(other.endedTouches_))
|
||||||
|
, onKeyDown_(std::move(other.onKeyDown_))
|
||||||
|
, onKeyUp_(std::move(other.onKeyUp_))
|
||||||
|
, onMouseDown_(std::move(other.onMouseDown_))
|
||||||
|
, onMouseUp_(std::move(other.onMouseUp_))
|
||||||
|
, onTouchBegan_(std::move(other.onTouchBegan_))
|
||||||
|
, onTouchMoved_(std::move(other.onTouchMoved_))
|
||||||
|
, onTouchEnded_(std::move(other.onTouchEnded_)) {
|
||||||
|
std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT);
|
||||||
|
std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT);
|
||||||
|
std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
|
||||||
|
std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast<size_t>(MouseBtn::Count));
|
||||||
|
std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_));
|
||||||
|
std::memcpy(padState_, other.padState_, sizeof(padState_));
|
||||||
|
std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_));
|
||||||
|
|
||||||
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
|
other.gamepads_[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InputModule& InputModule::operator=(InputModule&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
shutdown();
|
||||||
|
|
||||||
|
mouseX_ = other.mouseX_;
|
||||||
|
mouseY_ = other.mouseY_;
|
||||||
|
mouseWheel_ = other.mouseWheel_;
|
||||||
|
activeTouches_ = std::move(other.activeTouches_);
|
||||||
|
endedTouches_ = std::move(other.endedTouches_);
|
||||||
|
onKeyDown_ = std::move(other.onKeyDown_);
|
||||||
|
onKeyUp_ = std::move(other.onKeyUp_);
|
||||||
|
onMouseDown_ = std::move(other.onMouseDown_);
|
||||||
|
onMouseUp_ = std::move(other.onMouseUp_);
|
||||||
|
onTouchBegan_ = std::move(other.onTouchBegan_);
|
||||||
|
onTouchMoved_ = std::move(other.onTouchMoved_);
|
||||||
|
onTouchEnded_ = std::move(other.onTouchEnded_);
|
||||||
|
|
||||||
|
std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT);
|
||||||
|
std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT);
|
||||||
|
std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
|
||||||
|
std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast<size_t>(MouseBtn::Count));
|
||||||
|
std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_));
|
||||||
|
std::memcpy(padState_, other.padState_, sizeof(padState_));
|
||||||
|
std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_));
|
||||||
|
|
||||||
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
|
other.gamepads_[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputModule::init() {
|
||||||
std::memset(keyState_.data(), 0, KEY_COUNT);
|
std::memset(keyState_.data(), 0, KEY_COUNT);
|
||||||
std::memset(keyPrev_.data(), 0, KEY_COUNT);
|
std::memset(keyPrev_.data(), 0, KEY_COUNT);
|
||||||
std::memset(mouseState_.data(), 0, static_cast<size_t>(MouseBtn::Count));
|
std::memset(mouseState_.data(), 0, static_cast<size_t>(MouseBtn::Count));
|
||||||
std::memset(mousePrev_.data(), 0, static_cast<size_t>(MouseBtn::Count));
|
std::memset(mousePrev_.data(), 0, static_cast<size_t>(MouseBtn::Count));
|
||||||
|
|
||||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
gamepads_[i] = nullptr;
|
gamepads_[i] = nullptr;
|
||||||
std::memset(padState_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count));
|
std::memset(padState_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count));
|
||||||
std::memset(padPrev_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count));
|
std::memset(padPrev_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count));
|
||||||
}
|
}
|
||||||
|
|
||||||
activeTouches_.clear();
|
activeTouches_.clear();
|
||||||
activeTouches_.reserve(MAX_TOUCHES);
|
activeTouches_.reserve(MAX_TOUCHES);
|
||||||
endedTouches_.clear();
|
endedTouches_.clear();
|
||||||
|
|
||||||
int32 numJoysticks = SDL_NumJoysticks();
|
int32 numJoysticks = SDL_NumJoysticks();
|
||||||
for (int32 i = 0; i < numJoysticks && i < MAX_GAMEPADS; ++i) {
|
for (int32 i = 0; i < numJoysticks && i < MAX_GAMEPADS; ++i) {
|
||||||
if (SDL_IsGameController(i)) {
|
if (SDL_IsGameController(i)) {
|
||||||
openGamepad(i);
|
openGamepad(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state_ = SvcState::Inited;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::shutdown() {
|
void InputModule::shutdown() {
|
||||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
closeGamepad(i);
|
closeGamepad(i);
|
||||||
}
|
}
|
||||||
activeTouches_.clear();
|
activeTouches_.clear();
|
||||||
endedTouches_.clear();
|
endedTouches_.clear();
|
||||||
state_ = SvcState::Shutdown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::update(float dt) {
|
void InputModule::update() {
|
||||||
std::memcpy(keyPrev_.data(), keyState_.data(), KEY_COUNT);
|
std::memcpy(keyPrev_.data(), keyState_.data(), KEY_COUNT);
|
||||||
std::memcpy(mousePrev_.data(), mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
|
std::memcpy(mousePrev_.data(), mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
|
||||||
|
|
||||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
if (gamepads_[i]) {
|
if (gamepads_[i]) {
|
||||||
std::memcpy(padPrev_[i].data(), padState_[i].data(), static_cast<size_t>(GamepadBtn::Count));
|
std::memcpy(padPrev_[i].data(), padState_[i].data(), static_cast<size_t>(GamepadBtn::Count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mouseWheel_ = 0;
|
mouseWheel_ = 0;
|
||||||
|
|
||||||
const uint8* state = SDL_GetKeyboardState(nullptr);
|
const uint8* state = SDL_GetKeyboardState(nullptr);
|
||||||
std::memcpy(keyState_.data(), state, KEY_COUNT);
|
std::memcpy(keyState_.data(), state, KEY_COUNT);
|
||||||
|
|
||||||
uint32 btnState = SDL_GetMouseState(&mouseX_, &mouseY_);
|
uint32 btnState = SDL_GetMouseState(&mouseX_, &mouseY_);
|
||||||
mouseState_[static_cast<size_t>(MouseBtn::Left)] = (btnState & SDL_BUTTON_LMASK) ? 1 : 0;
|
mouseState_[static_cast<size_t>(MouseBtn::Left)] = (btnState & SDL_BUTTON_LMASK) ? 1 : 0;
|
||||||
mouseState_[static_cast<size_t>(MouseBtn::Middle)] = (btnState & SDL_BUTTON_MMASK) ? 1 : 0;
|
mouseState_[static_cast<size_t>(MouseBtn::Middle)] = (btnState & SDL_BUTTON_MMASK) ? 1 : 0;
|
||||||
mouseState_[static_cast<size_t>(MouseBtn::Right)] = (btnState & SDL_BUTTON_RMASK) ? 1 : 0;
|
mouseState_[static_cast<size_t>(MouseBtn::Right)] = (btnState & SDL_BUTTON_RMASK) ? 1 : 0;
|
||||||
mouseState_[static_cast<size_t>(MouseBtn::X1)] = (btnState & SDL_BUTTON_X1MASK) ? 1 : 0;
|
mouseState_[static_cast<size_t>(MouseBtn::X1)] = (btnState & SDL_BUTTON_X1MASK) ? 1 : 0;
|
||||||
mouseState_[static_cast<size_t>(MouseBtn::X2)] = (btnState & SDL_BUTTON_X2MASK) ? 1 : 0;
|
mouseState_[static_cast<size_t>(MouseBtn::X2)] = (btnState & SDL_BUTTON_X2MASK) ? 1 : 0;
|
||||||
|
|
||||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
if (gamepads_[i]) {
|
if (gamepads_[i]) {
|
||||||
SDL_GameController* gc = gamepads_[i];
|
SDL_GameController* gc = gamepads_[i];
|
||||||
|
|
@ -91,22 +146,22 @@ void InputSvc::update(float dt) {
|
||||||
padState_[i][static_cast<size_t>(GamepadBtn::DPadRight)] = SDL_GameControllerGetButton(gc, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
|
padState_[i][static_cast<size_t>(GamepadBtn::DPadRight)] = SDL_GameControllerGetButton(gc, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
endedTouches_.clear();
|
endedTouches_.clear();
|
||||||
|
|
||||||
for (auto& touch : activeTouches_) {
|
for (auto& touch : activeTouches_) {
|
||||||
if (touch.state == TouchState::Ended || touch.state == TouchState::Cancelled) {
|
if (touch.state == TouchState::Ended || touch.state == TouchState::Cancelled) {
|
||||||
endedTouches_.push_back(touch);
|
endedTouches_.push_back(touch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activeTouches_.erase(
|
activeTouches_.erase(
|
||||||
std::remove_if(activeTouches_.begin(), activeTouches_.end(),
|
std::remove_if(activeTouches_.begin(), activeTouches_.end(),
|
||||||
[](const TouchPoint& t) {
|
[](const TouchPoint& t) {
|
||||||
return t.state == TouchState::Ended || t.state == TouchState::Cancelled;
|
return t.state == TouchState::Ended || t.state == TouchState::Cancelled;
|
||||||
}),
|
}),
|
||||||
activeTouches_.end());
|
activeTouches_.end());
|
||||||
|
|
||||||
for (auto& touch : activeTouches_) {
|
for (auto& touch : activeTouches_) {
|
||||||
touch.prevX = touch.x;
|
touch.prevX = touch.x;
|
||||||
touch.prevY = touch.y;
|
touch.prevY = touch.y;
|
||||||
|
|
@ -118,38 +173,65 @@ void InputSvc::update(float dt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::processEvent(const SDL_Event& evt) {
|
void InputModule::processEvent(const SDL_Event& evt) {
|
||||||
switch (evt.type) {
|
switch (evt.type) {
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
if (evt.key.repeat == 0 && onKeyDown_) {
|
if (evt.key.repeat == 0) {
|
||||||
onKeyDown_(static_cast<Key>(evt.key.keysym.scancode));
|
Key key = static_cast<Key>(evt.key.keysym.scancode);
|
||||||
|
// 发送按键按下事件
|
||||||
|
events::OnKeyDown::emit(static_cast<int32>(key));
|
||||||
|
if (onKeyDown_) {
|
||||||
|
onKeyDown_(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_KEYUP:
|
case SDL_KEYUP: {
|
||||||
|
Key key = static_cast<Key>(evt.key.keysym.scancode);
|
||||||
|
// 发送按键释放事件
|
||||||
|
events::OnKeyUp::emit(static_cast<int32>(key));
|
||||||
if (onKeyUp_) {
|
if (onKeyUp_) {
|
||||||
onKeyUp_(static_cast<Key>(evt.key.keysym.scancode));
|
onKeyUp_(key);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
}
|
||||||
|
case SDL_MOUSEBUTTONDOWN: {
|
||||||
|
MouseBtn btn = static_cast<MouseBtn>(evt.button.button - 1);
|
||||||
|
// 发送鼠标按下事件
|
||||||
|
events::OnMouseDown::emit(static_cast<int32>(btn), evt.button.x, evt.button.y);
|
||||||
if (onMouseDown_) {
|
if (onMouseDown_) {
|
||||||
onMouseDown_(static_cast<MouseBtn>(evt.button.button - 1), evt.button.x, evt.button.y);
|
onMouseDown_(btn, evt.button.x, evt.button.y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEBUTTONUP:
|
}
|
||||||
|
case SDL_MOUSEBUTTONUP: {
|
||||||
|
MouseBtn btn = static_cast<MouseBtn>(evt.button.button - 1);
|
||||||
|
// 发送鼠标释放事件
|
||||||
|
events::OnMouseUp::emit(static_cast<int32>(btn), evt.button.x, evt.button.y);
|
||||||
if (onMouseUp_) {
|
if (onMouseUp_) {
|
||||||
onMouseUp_(static_cast<MouseBtn>(evt.button.button - 1), evt.button.x, evt.button.y);
|
onMouseUp_(btn, evt.button.x, evt.button.y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case SDL_MOUSEMOTION:
|
||||||
|
// 发送鼠标移动事件
|
||||||
|
events::OnMouseMove::emit(evt.motion.x, evt.motion.y);
|
||||||
|
break;
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_MOUSEWHEEL:
|
||||||
mouseWheel_ = evt.wheel.y;
|
mouseWheel_ = evt.wheel.y;
|
||||||
|
// 发送鼠标滚轮事件
|
||||||
|
events::OnMouseWheel::emit(evt.wheel.y);
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
openGamepad(evt.cdevice.which);
|
openGamepad(evt.cdevice.which);
|
||||||
|
// 发送手柄连接事件
|
||||||
|
events::OnGamepadConnect::emit(evt.cdevice.which);
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
if (gamepads_[i] && SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepads_[i])) == evt.cdevice.which) {
|
if (gamepads_[i] && SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepads_[i])) == evt.cdevice.which) {
|
||||||
closeGamepad(i);
|
closeGamepad(i);
|
||||||
|
// 发送手柄断开事件
|
||||||
|
events::OnGamepadDisconnect::emit(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -166,12 +248,12 @@ void InputSvc::processEvent(const SDL_Event& evt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::processTouchDown(const SDL_TouchFingerEvent& evt) {
|
void InputModule::processTouchDown(const SDL_TouchFingerEvent& evt) {
|
||||||
if (static_cast<int32>(activeTouches_.size()) >= MAX_TOUCHES) return;
|
if (static_cast<int32>(activeTouches_.size()) >= MAX_TOUCHES) return;
|
||||||
|
|
||||||
int32 winW = 0, winH = 0;
|
int32 winW = 0, winH = 0;
|
||||||
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
|
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
|
||||||
|
|
||||||
TouchPoint touch;
|
TouchPoint touch;
|
||||||
touch.id = static_cast<int64>(evt.fingerId);
|
touch.id = static_cast<int64>(evt.fingerId);
|
||||||
touch.x = evt.x * static_cast<float>(winW);
|
touch.x = evt.x * static_cast<float>(winW);
|
||||||
|
|
@ -182,29 +264,35 @@ void InputSvc::processTouchDown(const SDL_TouchFingerEvent& evt) {
|
||||||
touch.deltaY = 0.0f;
|
touch.deltaY = 0.0f;
|
||||||
touch.state = TouchState::Began;
|
touch.state = TouchState::Began;
|
||||||
touch.pressure = evt.pressure;
|
touch.pressure = evt.pressure;
|
||||||
|
|
||||||
activeTouches_.push_back(touch);
|
activeTouches_.push_back(touch);
|
||||||
|
|
||||||
|
// 发送触摸开始事件
|
||||||
|
events::OnTouchBegin::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
|
||||||
|
|
||||||
if (onTouchBegan_) {
|
if (onTouchBegan_) {
|
||||||
onTouchBegan_(touch);
|
onTouchBegan_(touch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) {
|
void InputModule::processTouchUp(const SDL_TouchFingerEvent& evt) {
|
||||||
int64 fingerId = static_cast<int64>(evt.fingerId);
|
int64 fingerId = static_cast<int64>(evt.fingerId);
|
||||||
|
|
||||||
for (auto& touch : activeTouches_) {
|
for (auto& touch : activeTouches_) {
|
||||||
if (touch.id == fingerId) {
|
if (touch.id == fingerId) {
|
||||||
int32 winW = 0, winH = 0;
|
int32 winW = 0, winH = 0;
|
||||||
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
|
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
|
||||||
|
|
||||||
touch.x = evt.x * static_cast<float>(winW);
|
touch.x = evt.x * static_cast<float>(winW);
|
||||||
touch.y = evt.y * static_cast<float>(winH);
|
touch.y = evt.y * static_cast<float>(winH);
|
||||||
touch.deltaX = touch.x - touch.prevX;
|
touch.deltaX = touch.x - touch.prevX;
|
||||||
touch.deltaY = touch.y - touch.prevY;
|
touch.deltaY = touch.y - touch.prevY;
|
||||||
touch.state = TouchState::Ended;
|
touch.state = TouchState::Ended;
|
||||||
touch.pressure = evt.pressure;
|
touch.pressure = evt.pressure;
|
||||||
|
|
||||||
|
// 发送触摸结束事件
|
||||||
|
events::OnTouchEnd::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
|
||||||
|
|
||||||
if (onTouchEnded_) {
|
if (onTouchEnded_) {
|
||||||
onTouchEnded_(touch);
|
onTouchEnded_(touch);
|
||||||
}
|
}
|
||||||
|
|
@ -213,21 +301,24 @@ void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::processTouchMotion(const SDL_TouchFingerEvent& evt) {
|
void InputModule::processTouchMotion(const SDL_TouchFingerEvent& evt) {
|
||||||
int64 fingerId = static_cast<int64>(evt.fingerId);
|
int64 fingerId = static_cast<int64>(evt.fingerId);
|
||||||
|
|
||||||
for (auto& touch : activeTouches_) {
|
for (auto& touch : activeTouches_) {
|
||||||
if (touch.id == fingerId) {
|
if (touch.id == fingerId) {
|
||||||
int32 winW = 0, winH = 0;
|
int32 winW = 0, winH = 0;
|
||||||
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
|
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
|
||||||
|
|
||||||
touch.x = evt.x * static_cast<float>(winW);
|
touch.x = evt.x * static_cast<float>(winW);
|
||||||
touch.y = evt.y * static_cast<float>(winH);
|
touch.y = evt.y * static_cast<float>(winH);
|
||||||
touch.deltaX = touch.x - touch.prevX;
|
touch.deltaX = touch.x - touch.prevX;
|
||||||
touch.deltaY = touch.y - touch.prevY;
|
touch.deltaY = touch.y - touch.prevY;
|
||||||
touch.state = TouchState::Moved;
|
touch.state = TouchState::Moved;
|
||||||
touch.pressure = evt.pressure;
|
touch.pressure = evt.pressure;
|
||||||
|
|
||||||
|
// 发送触摸移动事件
|
||||||
|
events::OnTouchMove::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
|
||||||
|
|
||||||
if (onTouchMoved_) {
|
if (onTouchMoved_) {
|
||||||
onTouchMoved_(touch);
|
onTouchMoved_(touch);
|
||||||
}
|
}
|
||||||
|
|
@ -236,10 +327,10 @@ void InputSvc::processTouchMotion(const SDL_TouchFingerEvent& evt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::openGamepad(int32 idx) {
|
void InputModule::openGamepad(int32 idx) {
|
||||||
if (idx < 0 || idx >= MAX_GAMEPADS) return;
|
if (idx < 0 || idx >= MAX_GAMEPADS) return;
|
||||||
if (gamepads_[idx]) return;
|
if (gamepads_[idx]) return;
|
||||||
|
|
||||||
SDL_GameController* gc = SDL_GameControllerOpen(idx);
|
SDL_GameController* gc = SDL_GameControllerOpen(idx);
|
||||||
if (gc) {
|
if (gc) {
|
||||||
gamepads_[idx] = gc;
|
gamepads_[idx] = gc;
|
||||||
|
|
@ -248,7 +339,7 @@ void InputSvc::openGamepad(int32 idx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::closeGamepad(int32 idx) {
|
void InputModule::closeGamepad(int32 idx) {
|
||||||
if (idx < 0 || idx >= MAX_GAMEPADS) return;
|
if (idx < 0 || idx >= MAX_GAMEPADS) return;
|
||||||
if (gamepads_[idx]) {
|
if (gamepads_[idx]) {
|
||||||
SDL_GameControllerClose(gamepads_[idx]);
|
SDL_GameControllerClose(gamepads_[idx]);
|
||||||
|
|
@ -256,70 +347,70 @@ void InputSvc::closeGamepad(int32 idx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isKeyDown(Key key) const {
|
bool InputModule::isKeyDown(Key key) const {
|
||||||
if (key < 0 || key >= KEY_COUNT) return false;
|
if (key < 0 || key >= KEY_COUNT) return false;
|
||||||
return keyState_[key] != 0;
|
return keyState_[key] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isKeyPressed(Key key) const {
|
bool InputModule::isKeyPressed(Key key) const {
|
||||||
if (key < 0 || key >= KEY_COUNT) return false;
|
if (key < 0 || key >= KEY_COUNT) return false;
|
||||||
return keyState_[key] != 0 && keyPrev_[key] == 0;
|
return keyState_[key] != 0 && keyPrev_[key] == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isKeyReleased(Key key) const {
|
bool InputModule::isKeyReleased(Key key) const {
|
||||||
if (key < 0 || key >= KEY_COUNT) return false;
|
if (key < 0 || key >= KEY_COUNT) return false;
|
||||||
return keyState_[key] == 0 && keyPrev_[key] != 0;
|
return keyState_[key] == 0 && keyPrev_[key] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::getMousePos(int32& x, int32& y) const {
|
void InputModule::getMousePos(int32& x, int32& y) const {
|
||||||
x = mouseX_;
|
x = mouseX_;
|
||||||
y = mouseY_;
|
y = mouseY_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSvc::getMousePos(float& x, float& y) const {
|
void InputModule::getMousePos(float& x, float& y) const {
|
||||||
x = static_cast<float>(mouseX_);
|
x = static_cast<float>(mouseX_);
|
||||||
y = static_cast<float>(mouseY_);
|
y = static_cast<float>(mouseY_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isMouseBtnDown(MouseBtn btn) const {
|
bool InputModule::isMouseBtnDown(MouseBtn btn) const {
|
||||||
size_t idx = static_cast<size_t>(btn);
|
size_t idx = static_cast<size_t>(btn);
|
||||||
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
||||||
return mouseState_[idx] != 0;
|
return mouseState_[idx] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isMouseBtnPressed(MouseBtn btn) const {
|
bool InputModule::isMouseBtnPressed(MouseBtn btn) const {
|
||||||
size_t idx = static_cast<size_t>(btn);
|
size_t idx = static_cast<size_t>(btn);
|
||||||
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
||||||
return mouseState_[idx] != 0 && mousePrev_[idx] == 0;
|
return mouseState_[idx] != 0 && mousePrev_[idx] == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isMouseBtnReleased(MouseBtn btn) const {
|
bool InputModule::isMouseBtnReleased(MouseBtn btn) const {
|
||||||
size_t idx = static_cast<size_t>(btn);
|
size_t idx = static_cast<size_t>(btn);
|
||||||
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
||||||
return mouseState_[idx] == 0 && mousePrev_[idx] != 0;
|
return mouseState_[idx] == 0 && mousePrev_[idx] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 InputSvc::getMouseWheel() const {
|
int32 InputModule::getMouseWheel() const {
|
||||||
return mouseWheel_;
|
return mouseWheel_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 InputSvc::touchCount() const {
|
int32 InputModule::touchCount() const {
|
||||||
return static_cast<int32>(activeTouches_.size());
|
return static_cast<int32>(activeTouches_.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
const TouchPoint* InputSvc::getTouch(int32 idx) const {
|
const TouchPoint* InputModule::getTouch(int32 idx) const {
|
||||||
if (idx < 0 || idx >= static_cast<int32>(activeTouches_.size())) return nullptr;
|
if (idx < 0 || idx >= static_cast<int32>(activeTouches_.size())) return nullptr;
|
||||||
return &activeTouches_[idx];
|
return &activeTouches_[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
const TouchPoint* InputSvc::getTouchById(int64 id) const {
|
const TouchPoint* InputModule::getTouchById(int64 id) const {
|
||||||
for (const auto& touch : activeTouches_) {
|
for (const auto& touch : activeTouches_) {
|
||||||
if (touch.id == id) return &touch;
|
if (touch.id == id) return &touch;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 InputSvc::gamepadCount() const {
|
int32 InputModule::gamepadCount() const {
|
||||||
int32 count = 0;
|
int32 count = 0;
|
||||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||||
if (gamepads_[i]) ++count;
|
if (gamepads_[i]) ++count;
|
||||||
|
|
@ -327,7 +418,7 @@ int32 InputSvc::gamepadCount() const {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isGamepadBtnDown(int32 idx, GamepadBtn btn) const {
|
bool InputModule::isGamepadBtnDown(int32 idx, GamepadBtn btn) const {
|
||||||
if (idx < 0 || idx >= MAX_GAMEPADS) return false;
|
if (idx < 0 || idx >= MAX_GAMEPADS) return false;
|
||||||
if (!gamepads_[idx]) return false;
|
if (!gamepads_[idx]) return false;
|
||||||
size_t b = static_cast<size_t>(btn);
|
size_t b = static_cast<size_t>(btn);
|
||||||
|
|
@ -335,7 +426,7 @@ bool InputSvc::isGamepadBtnDown(int32 idx, GamepadBtn btn) const {
|
||||||
return padState_[idx][b] != 0;
|
return padState_[idx][b] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputSvc::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const {
|
bool InputModule::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const {
|
||||||
if (idx < 0 || idx >= MAX_GAMEPADS) return false;
|
if (idx < 0 || idx >= MAX_GAMEPADS) return false;
|
||||||
if (!gamepads_[idx]) return false;
|
if (!gamepads_[idx]) return false;
|
||||||
size_t b = static_cast<size_t>(btn);
|
size_t b = static_cast<size_t>(btn);
|
||||||
|
|
@ -343,10 +434,10 @@ bool InputSvc::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const {
|
||||||
return padState_[idx][b] != 0 && padPrev_[idx][b] == 0;
|
return padState_[idx][b] != 0 && padPrev_[idx][b] == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float InputSvc::getGamepadAxis(int32 idx, GamepadAxis axis) const {
|
float InputModule::getGamepadAxis(int32 idx, GamepadAxis axis) const {
|
||||||
if (idx < 0 || idx >= MAX_GAMEPADS) return 0.0f;
|
if (idx < 0 || idx >= MAX_GAMEPADS) return 0.0f;
|
||||||
if (!gamepads_[idx]) return 0.0f;
|
if (!gamepads_[idx]) return 0.0f;
|
||||||
|
|
||||||
SDL_GameControllerAxis sdlAxis = SDL_CONTROLLER_AXIS_INVALID;
|
SDL_GameControllerAxis sdlAxis = SDL_CONTROLLER_AXIS_INVALID;
|
||||||
switch (axis) {
|
switch (axis) {
|
||||||
case GamepadAxis::LeftX: sdlAxis = SDL_CONTROLLER_AXIS_LEFTX; break;
|
case GamepadAxis::LeftX: sdlAxis = SDL_CONTROLLER_AXIS_LEFTX; break;
|
||||||
|
|
@ -357,7 +448,7 @@ float InputSvc::getGamepadAxis(int32 idx, GamepadAxis axis) const {
|
||||||
case GamepadAxis::TriggerRight: sdlAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; break;
|
case GamepadAxis::TriggerRight: sdlAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; break;
|
||||||
default: return 0.0f;
|
default: return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int16 value = SDL_GameControllerGetAxis(gamepads_[idx], sdlAxis);
|
int16 value = SDL_GameControllerGetAxis(gamepads_[idx], sdlAxis);
|
||||||
return static_cast<float>(value) / 32767.0f;
|
return static_cast<float>(value) / 32767.0f;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,178 +1,241 @@
|
||||||
#include <SDL.h>
|
|
||||||
#include <platform/input.h>
|
|
||||||
#include <platform/sdl2.h>
|
|
||||||
#include <platform/window.h>
|
#include <platform/window.h>
|
||||||
|
#include <platform/sdl2.h>
|
||||||
|
#include <event/events.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
WindowSvc &WindowSvc::inst() {
|
WindowModule::WindowModule() = default;
|
||||||
static WindowSvc instance;
|
|
||||||
return instance;
|
WindowModule::~WindowModule() {
|
||||||
|
shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowSvc::init() {
|
WindowModule::WindowModule(WindowModule&& other) noexcept
|
||||||
if (isInited())
|
: window_(other.window_)
|
||||||
|
, glCtx_(other.glCtx_)
|
||||||
|
, shouldClose_(other.shouldClose_)
|
||||||
|
, vsync_(other.vsync_)
|
||||||
|
, onClose_(std::move(other.onClose_))
|
||||||
|
, onResize_(std::move(other.onResize_)) {
|
||||||
|
other.window_ = nullptr;
|
||||||
|
other.glCtx_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowModule& WindowModule::operator=(WindowModule&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
shutdown();
|
||||||
|
window_ = other.window_;
|
||||||
|
glCtx_ = other.glCtx_;
|
||||||
|
shouldClose_ = other.shouldClose_;
|
||||||
|
vsync_ = other.vsync_;
|
||||||
|
onClose_ = std::move(other.onClose_);
|
||||||
|
onResize_ = std::move(other.onResize_);
|
||||||
|
other.window_ = nullptr;
|
||||||
|
other.glCtx_ = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowModule::init() {
|
||||||
|
if (!Sdl2::initVideo()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!Sdl2::initVideo()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
||||||
|
|
||||||
state_ = SvcState::Inited;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSvc::shutdown() {
|
void WindowModule::shutdown() {
|
||||||
if (glCtx_) {
|
if (glCtx_) {
|
||||||
SDL_GL_DeleteContext(glCtx_);
|
SDL_GL_DeleteContext(glCtx_);
|
||||||
glCtx_ = nullptr;
|
glCtx_ = nullptr;
|
||||||
}
|
}
|
||||||
if (window_) {
|
if (window_) {
|
||||||
SDL_DestroyWindow(window_);
|
SDL_DestroyWindow(window_);
|
||||||
window_ = nullptr;
|
window_ = nullptr;
|
||||||
}
|
}
|
||||||
state_ = SvcState::Shutdown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowSvc::create(const WindowCfg &cfg) {
|
bool WindowModule::create(const WindowCfg& cfg) {
|
||||||
if (window_)
|
if (window_)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
||||||
|
if (cfg.resizable) {
|
||||||
|
flags |= SDL_WINDOW_RESIZABLE;
|
||||||
|
}
|
||||||
|
if (cfg.fullscreen) {
|
||||||
|
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor);
|
||||||
|
|
||||||
|
window_ = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
|
||||||
|
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
|
||||||
|
|
||||||
|
if (!window_) {
|
||||||
|
E2D_LOG_ERROR("Failed to create window: {}", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glCtx_ = SDL_GL_CreateContext(window_);
|
||||||
|
if (!glCtx_) {
|
||||||
|
E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError());
|
||||||
|
SDL_DestroyWindow(window_);
|
||||||
|
window_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVsync(cfg.vsync);
|
||||||
|
vsync_ = cfg.vsync;
|
||||||
|
|
||||||
|
// 发送窗口显示事件
|
||||||
|
events::OnShow::emit();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
|
|
||||||
if (cfg.resizable) {
|
|
||||||
flags |= SDL_WINDOW_RESIZABLE;
|
|
||||||
}
|
|
||||||
if (cfg.fullscreen) {
|
|
||||||
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor);
|
|
||||||
|
|
||||||
window_ =
|
|
||||||
SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
|
|
||||||
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
|
|
||||||
|
|
||||||
if (!window_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
glCtx_ = SDL_GL_CreateContext(window_);
|
|
||||||
if (!glCtx_) {
|
|
||||||
SDL_DestroyWindow(window_);
|
|
||||||
window_ = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setVsync(cfg.vsync);
|
|
||||||
vsync_ = cfg.vsync;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowSvc::pollEvents() {
|
bool WindowModule::pollEvents() {
|
||||||
SDL_Event evt;
|
SDL_Event evt;
|
||||||
while (SDL_PollEvent(&evt)) {
|
while (SDL_PollEvent(&evt)) {
|
||||||
switch (evt.type) {
|
switch (evt.type) {
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
shouldClose_ = true;
|
shouldClose_ = true;
|
||||||
if (onClose_)
|
// 发送窗口关闭事件
|
||||||
onClose_();
|
events::OnClose::emit();
|
||||||
break;
|
if (onClose_)
|
||||||
case SDL_WINDOWEVENT:
|
onClose_();
|
||||||
if (evt.window.event == SDL_WINDOWEVENT_RESIZED) {
|
break;
|
||||||
if (onResize_) {
|
case SDL_WINDOWEVENT:
|
||||||
onResize_(evt.window.data1, evt.window.data2);
|
handleWindowEvent(evt.window);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 输入事件由 InputModule 处理
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (INPUT_SVC.isInited()) {
|
|
||||||
INPUT_SVC.processEvent(evt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
return !shouldClose_;
|
||||||
return !shouldClose_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSvc::swapBuffers() {
|
void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) {
|
||||||
if (window_ && glCtx_) {
|
switch (evt.event) {
|
||||||
SDL_GL_SwapWindow(window_);
|
case SDL_WINDOWEVENT_RESIZED:
|
||||||
}
|
// 发送窗口大小改变事件
|
||||||
}
|
events::OnResize::emit(evt.data1, evt.data2);
|
||||||
|
if (onResize_) {
|
||||||
Size WindowSvc::getSize() const {
|
onResize_(evt.data1, evt.data2);
|
||||||
if (!window_)
|
}
|
||||||
return Size(0, 0);
|
break;
|
||||||
int w, h;
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||||
SDL_GetWindowSize(window_, &w, &h);
|
// 发送窗口获得焦点事件
|
||||||
return Size(w, h);
|
events::OnFocus::emit();
|
||||||
}
|
break;
|
||||||
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||||
Vec2 WindowSvc::getPosition() const {
|
// 发送窗口失去焦点事件
|
||||||
if (!window_)
|
events::OnBlur::emit();
|
||||||
return Vec2(0, 0);
|
break;
|
||||||
int x, y;
|
case SDL_WINDOWEVENT_SHOWN:
|
||||||
SDL_GetWindowPosition(window_, &x, &y);
|
// 发送窗口显示事件
|
||||||
return Vec2(static_cast<float>(x), static_cast<float>(y));
|
events::OnShow::emit();
|
||||||
}
|
break;
|
||||||
|
case SDL_WINDOWEVENT_HIDDEN:
|
||||||
void WindowSvc::setSize(int32 w, int32 h) {
|
// 发送窗口隐藏事件
|
||||||
if (window_) {
|
events::OnHide::emit();
|
||||||
SDL_SetWindowSize(window_, w, h);
|
break;
|
||||||
}
|
case SDL_WINDOWEVENT_CLOSE:
|
||||||
}
|
shouldClose_ = true;
|
||||||
|
// 发送窗口关闭事件
|
||||||
void WindowSvc::setTitle(const std::string &title) {
|
events::OnClose::emit();
|
||||||
if (window_) {
|
if (onClose_)
|
||||||
SDL_SetWindowTitle(window_, title.c_str());
|
onClose_();
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
void WindowSvc::setFullscreen(bool fullscreen) {
|
|
||||||
if (window_) {
|
|
||||||
SDL_SetWindowFullscreen(window_,
|
|
||||||
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WindowSvc::isFullscreen() const {
|
|
||||||
if (!window_)
|
|
||||||
return false;
|
|
||||||
uint32 flags = SDL_GetWindowFlags(window_);
|
|
||||||
return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowSvc::setVsync(bool vsync) {
|
|
||||||
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
|
||||||
vsync_ = vsync;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WindowSvc::isVsync() const { return vsync_; }
|
|
||||||
|
|
||||||
void WindowSvc::setVisible(bool visible) {
|
|
||||||
if (window_) {
|
|
||||||
if (visible) {
|
|
||||||
SDL_ShowWindow(window_);
|
|
||||||
} else {
|
|
||||||
SDL_HideWindow(window_);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowSvc::isVisible() const {
|
void WindowModule::swapBuffers() {
|
||||||
if (!window_)
|
if (window_ && glCtx_) {
|
||||||
return false;
|
SDL_GL_SwapWindow(window_);
|
||||||
uint32 flags = SDL_GetWindowFlags(window_);
|
}
|
||||||
return (flags & SDL_WINDOW_SHOWN) != 0;
|
}
|
||||||
|
|
||||||
|
Size WindowModule::getSize() const {
|
||||||
|
if (!window_)
|
||||||
|
return Size(0, 0);
|
||||||
|
int w, h;
|
||||||
|
SDL_GetWindowSize(window_, &w, &h);
|
||||||
|
return Size(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 WindowModule::getPosition() const {
|
||||||
|
if (!window_)
|
||||||
|
return Vec2(0, 0);
|
||||||
|
int x, y;
|
||||||
|
SDL_GetWindowPosition(window_, &x, &y);
|
||||||
|
return Vec2(static_cast<float>(x), static_cast<float>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowModule::setSize(int32 w, int32 h) {
|
||||||
|
if (window_) {
|
||||||
|
SDL_SetWindowSize(window_, w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowModule::setTitle(const std::string& title) {
|
||||||
|
if (window_) {
|
||||||
|
SDL_SetWindowTitle(window_, title.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowModule::setFullscreen(bool fullscreen) {
|
||||||
|
if (window_) {
|
||||||
|
SDL_SetWindowFullscreen(window_,
|
||||||
|
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowModule::isFullscreen() const {
|
||||||
|
if (!window_)
|
||||||
|
return false;
|
||||||
|
uint32 flags = SDL_GetWindowFlags(window_);
|
||||||
|
return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowModule::setVsync(bool vsync) {
|
||||||
|
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
|
||||||
|
vsync_ = vsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowModule::isVsync() const {
|
||||||
|
return vsync_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowModule::setVisible(bool visible) {
|
||||||
|
if (window_) {
|
||||||
|
if (visible) {
|
||||||
|
SDL_ShowWindow(window_);
|
||||||
|
// 发送窗口显示事件
|
||||||
|
events::OnShow::emit();
|
||||||
|
} else {
|
||||||
|
SDL_HideWindow(window_);
|
||||||
|
// 发送窗口隐藏事件
|
||||||
|
events::OnHide::emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowModule::isVisible() const {
|
||||||
|
if (!window_)
|
||||||
|
return false;
|
||||||
|
uint32 flags = SDL_GetWindowFlags(window_);
|
||||||
|
return (flags & SDL_WINDOW_SHOWN) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,337 @@
|
||||||
|
#include <plugin/plugin_loader.h>
|
||||||
|
#include <event/event_bus.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
PluginLoader::PluginLoader() = default;
|
||||||
|
|
||||||
|
PluginLoader::~PluginLoader() {
|
||||||
|
if (inited_) {
|
||||||
|
shutdownAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卸载所有动态库插件
|
||||||
|
for (auto& pair : plugins_) {
|
||||||
|
if (pair.second.isDynamic && pair.second.handle) {
|
||||||
|
if (pair.second.plugin) {
|
||||||
|
pair.second.plugin->unload();
|
||||||
|
if (pair.second.owned) {
|
||||||
|
delete pair.second.plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unloadDynamicLibrary(pair.second.handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginLoader::PluginLoader(PluginLoader&&) noexcept = default;
|
||||||
|
PluginLoader& PluginLoader::operator=(PluginLoader&&) noexcept = default;
|
||||||
|
|
||||||
|
bool PluginLoader::loadFromLibrary(const char* path) {
|
||||||
|
if (!path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载动态库
|
||||||
|
void* handle = loadDynamicLibrary(path);
|
||||||
|
if (!handle) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取创建函数
|
||||||
|
using CreateFunc = IPlugin* (*)();
|
||||||
|
CreateFunc createFunc = reinterpret_cast<CreateFunc>(
|
||||||
|
getSymbol(handle, "extra2d_create_plugin")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!createFunc) {
|
||||||
|
unloadDynamicLibrary(handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建插件实例
|
||||||
|
IPlugin* plugin = createFunc();
|
||||||
|
if (!plugin) {
|
||||||
|
unloadDynamicLibrary(handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已存在
|
||||||
|
const char* name = plugin->getInfo().name.c_str();
|
||||||
|
if (hasPlugin(name)) {
|
||||||
|
delete plugin;
|
||||||
|
unloadDynamicLibrary(handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到插件列表
|
||||||
|
PluginEntry entry;
|
||||||
|
entry.plugin = plugin;
|
||||||
|
entry.handle = handle;
|
||||||
|
entry.isDynamic = true;
|
||||||
|
entry.owned = true;
|
||||||
|
|
||||||
|
plugins_[name] = entry;
|
||||||
|
sortedPlugins_.clear();
|
||||||
|
|
||||||
|
// 插件加载事件(暂不发送,避免依赖 events.h)
|
||||||
|
// event::broadcast<events::OnPluginLoaded>(name, plugin->getInfo().version.c_str());
|
||||||
|
|
||||||
|
// 如果已经初始化,立即加载插件
|
||||||
|
if (inited_) {
|
||||||
|
if (!plugin->load()) {
|
||||||
|
unloadPlugin(name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginLoader::registerPlugin(IPlugin* plugin) {
|
||||||
|
if (!plugin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = plugin->getInfo().name.c_str();
|
||||||
|
if (!name || hasPlugin(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginEntry entry;
|
||||||
|
entry.plugin = plugin;
|
||||||
|
entry.handle = nullptr;
|
||||||
|
entry.isDynamic = false;
|
||||||
|
entry.owned = false;
|
||||||
|
|
||||||
|
plugins_[name] = entry;
|
||||||
|
sortedPlugins_.clear();
|
||||||
|
|
||||||
|
// 插件加载事件(暂不发送,避免依赖 events.h)
|
||||||
|
// event::broadcast<events::OnPluginLoaded>(name, plugin->getInfo().version.c_str());
|
||||||
|
|
||||||
|
// 如果已经初始化,立即加载插件
|
||||||
|
if (inited_) {
|
||||||
|
plugin->load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginLoader::unloadPlugin(const char* name) {
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = plugins_.find(name);
|
||||||
|
if (it == plugins_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginEntry& entry = it->second;
|
||||||
|
|
||||||
|
// 如果已初始化,先卸载
|
||||||
|
if (inited_ && entry.plugin) {
|
||||||
|
entry.plugin->unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是动态加载且由加载器管理,删除实例
|
||||||
|
if (entry.isDynamic && entry.owned && entry.plugin) {
|
||||||
|
delete entry.plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卸载动态库
|
||||||
|
if (entry.handle) {
|
||||||
|
unloadDynamicLibrary(entry.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins_.erase(it);
|
||||||
|
sortedPlugins_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPlugin* PluginLoader::getPlugin(const char* name) const {
|
||||||
|
if (!name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = plugins_.find(name);
|
||||||
|
return it != plugins_.end() ? it->second.plugin : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginLoader::hasPlugin(const char* name) const {
|
||||||
|
if (!name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return plugins_.find(name) != plugins_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<IPlugin*> PluginLoader::getAllPlugins() const {
|
||||||
|
std::vector<IPlugin*> result;
|
||||||
|
for (const auto& pair : plugins_) {
|
||||||
|
if (pair.second.plugin) {
|
||||||
|
result.push_back(pair.second.plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginLoader::initAll() {
|
||||||
|
if (inited_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按依赖顺序排序
|
||||||
|
sortPluginsByDependencies();
|
||||||
|
|
||||||
|
// 加载所有插件
|
||||||
|
for (auto* plugin : sortedPlugins_) {
|
||||||
|
if (plugin && !plugin->load()) {
|
||||||
|
// 加载失败,卸载已加载的插件
|
||||||
|
shutdownAll();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inited_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginLoader::shutdownAll() {
|
||||||
|
if (!inited_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按依赖逆序卸载
|
||||||
|
for (auto it = sortedPlugins_.rbegin(); it != sortedPlugins_.rend(); ++it) {
|
||||||
|
if (*it) {
|
||||||
|
(*it)->unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inited_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PluginLoader::getPluginCount() const {
|
||||||
|
return plugins_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginLoader::addSearchPath(const char* path) {
|
||||||
|
if (path) {
|
||||||
|
searchPaths_.push_back(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PluginLoader::loadPluginsFromDirectory(const char* directory) {
|
||||||
|
// TODO: 实现目录扫描加载
|
||||||
|
// 需要平台相关的文件系统操作
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginLoader::resolveDependencies(IPlugin* plugin) {
|
||||||
|
if (!plugin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkDependencies(plugin->getDependencies());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginLoader::checkDependencies(const std::vector<std::string>& dependencies) {
|
||||||
|
for (const auto& dep : dependencies) {
|
||||||
|
if (!hasPlugin(dep.c_str())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginLoader::sortPluginsByDependencies() {
|
||||||
|
if (!sortedPlugins_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拓扑排序处理依赖关系
|
||||||
|
std::unordered_map<std::string, int> inDegree;
|
||||||
|
std::unordered_map<std::string, std::vector<std::string>> graph;
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
for (const auto& pair : plugins_) {
|
||||||
|
inDegree[pair.first] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建图
|
||||||
|
for (const auto& pair : plugins_) {
|
||||||
|
if (!pair.second.plugin) continue;
|
||||||
|
|
||||||
|
for (const auto& dep : pair.second.plugin->getDependencies()) {
|
||||||
|
graph[dep].push_back(pair.first);
|
||||||
|
inDegree[pair.first]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拓扑排序
|
||||||
|
std::queue<std::string> q;
|
||||||
|
for (const auto& pair : inDegree) {
|
||||||
|
if (pair.second == 0) {
|
||||||
|
q.push(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> sortedNames;
|
||||||
|
while (!q.empty()) {
|
||||||
|
std::string name = q.front();
|
||||||
|
q.pop();
|
||||||
|
sortedNames.push_back(name);
|
||||||
|
|
||||||
|
for (const auto& next : graph[name]) {
|
||||||
|
inDegree[next]--;
|
||||||
|
if (inDegree[next] == 0) {
|
||||||
|
q.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建排序后的插件列表
|
||||||
|
sortedPlugins_.clear();
|
||||||
|
for (const auto& name : sortedNames) {
|
||||||
|
auto it = plugins_.find(name);
|
||||||
|
if (it != plugins_.end() && it->second.plugin) {
|
||||||
|
sortedPlugins_.push_back(it->second.plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* PluginLoader::loadDynamicLibrary(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return LoadLibraryA(path);
|
||||||
|
#else
|
||||||
|
return dlopen(path, RTLD_LAZY);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginLoader::unloadDynamicLibrary(void* handle) {
|
||||||
|
if (!handle) return;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
FreeLibrary(static_cast<HMODULE>(handle));
|
||||||
|
#else
|
||||||
|
dlclose(handle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void* PluginLoader::getSymbol(void* handle, const char* name) {
|
||||||
|
if (!handle || !name) return nullptr;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name));
|
||||||
|
#else
|
||||||
|
return dlsym(handle, name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -3,66 +3,110 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Random 类实现
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
Random::Random() : floatDist_(0.0f, 1.0f) {
|
Random::Random() : floatDist_(0.0f, 1.0f) {
|
||||||
// 使用当前时间作为默认种子
|
// 使用当前时间作为默认种子
|
||||||
randomize();
|
randomize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Random &Random::getInstance() {
|
Random::Random(uint32 seed) : floatDist_(0.0f, 1.0f) {
|
||||||
static Random instance;
|
setSeed(seed);
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Random::setSeed(uint32 seed) { generator_.seed(seed); }
|
void Random::setSeed(uint32 seed) {
|
||||||
|
generator_.seed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
void Random::randomize() {
|
void Random::randomize() {
|
||||||
auto now = std::chrono::high_resolution_clock::now();
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
auto time = now.time_since_epoch().count();
|
auto time = now.time_since_epoch().count();
|
||||||
generator_.seed(static_cast<uint32>(time));
|
generator_.seed(static_cast<uint32>(time));
|
||||||
}
|
}
|
||||||
|
|
||||||
float Random::getFloat() { return floatDist_(generator_); }
|
float Random::getFloat() {
|
||||||
|
return floatDist_(generator_);
|
||||||
|
}
|
||||||
|
|
||||||
float Random::getFloat(float min, float max) {
|
float Random::getFloat(float min, float max) {
|
||||||
if (min >= max) {
|
if (min >= max) {
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
return min + floatDist_(generator_) * (max - min);
|
return min + floatDist_(generator_) * (max - min);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Random::getInt(int max) {
|
int Random::getInt(int max) {
|
||||||
if (max <= 0) {
|
if (max <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
std::uniform_int_distribution<int> dist(0, max);
|
std::uniform_int_distribution<int> dist(0, max);
|
||||||
return dist(generator_);
|
return dist(generator_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Random::getInt(int min, int max) {
|
int Random::getInt(int min, int max) {
|
||||||
if (min >= max) {
|
if (min >= max) {
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
std::uniform_int_distribution<int> dist(min, max);
|
std::uniform_int_distribution<int> dist(min, max);
|
||||||
return dist(generator_);
|
return dist(generator_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Random::getBool() { return floatDist_(generator_) >= 0.5f; }
|
bool Random::getBool() {
|
||||||
|
return floatDist_(generator_) >= 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
bool Random::getBool(float probability) {
|
bool Random::getBool(float probability) {
|
||||||
if (probability <= 0.0f) {
|
if (probability <= 0.0f) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (probability >= 1.0f) {
|
if (probability >= 1.0f) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return floatDist_(generator_) < probability;
|
return floatDist_(generator_) < probability;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Random::getAngle() {
|
float Random::getAngle() {
|
||||||
static const float TWO_PI = 6.28318530718f;
|
static const float TWO_PI = 6.28318530718f;
|
||||||
return floatDist_(generator_) * TWO_PI;
|
return floatDist_(generator_) * TWO_PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Random::getSigned() { return floatDist_(generator_) * 2.0f - 1.0f; }
|
float Random::getSigned() {
|
||||||
|
return floatDist_(generator_) * 2.0f - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 全局便捷函数 - 使用静态的默认随机数生成器
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static Random& getDefaultRandom() {
|
||||||
|
static Random defaultRandom;
|
||||||
|
return defaultRandom;
|
||||||
|
}
|
||||||
|
|
||||||
|
float randomFloat() {
|
||||||
|
return getDefaultRandom().getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
float randomFloat(float min, float max) {
|
||||||
|
return getDefaultRandom().getFloat(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
int randomInt(int max) {
|
||||||
|
return getDefaultRandom().getInt(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
int randomInt(int min, int max) {
|
||||||
|
return getDefaultRandom().getInt(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool randomBool() {
|
||||||
|
return getDefaultRandom().getBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool randomBool(float probability) {
|
||||||
|
return getDefaultRandom().getBool(probability);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ function define_extra2d_engine()
|
||||||
target("extra2d")
|
target("extra2d")
|
||||||
set_kind("static")
|
set_kind("static")
|
||||||
|
|
||||||
add_files("src/**.cpp")
|
add_files("src/**.cpp|core/*.cpp|module/module_manager.cpp|plugin/plugin_manager.cpp")
|
||||||
add_files("third_party/glad/src/glad.c")
|
add_files("third_party/glad/src/glad.c")
|
||||||
|
|
||||||
add_includedirs("include", {public = true})
|
add_includedirs("include", {public = true})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue