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 <types/base/types.h>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class Context;
|
||||
|
||||
/**
|
||||
* @brief 应用程序配置
|
||||
*/
|
||||
|
|
@ -26,17 +30,23 @@ struct AppConfig {
|
|||
* @brief 应用程序主控类
|
||||
*
|
||||
* 管理应用程序生命周期、窗口和主循环
|
||||
* 基于新的 Context 架构,支持多实例
|
||||
*/
|
||||
class Application {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @brief 创建应用程序实例
|
||||
*/
|
||||
static Application& instance();
|
||||
static std::unique_ptr<Application> create();
|
||||
|
||||
Application(const Application&) = delete;
|
||||
Application& operator=(const Application&) = delete;
|
||||
|
||||
Application(Application&&) noexcept;
|
||||
Application& operator=(Application&&) noexcept;
|
||||
|
||||
~Application();
|
||||
|
||||
/**
|
||||
* @brief 初始化应用程序
|
||||
*/
|
||||
|
|
@ -97,13 +107,19 @@ public:
|
|||
*/
|
||||
const AppConfig& getConfig() const { return config_; }
|
||||
|
||||
/**
|
||||
* @brief 获取引擎上下文
|
||||
*/
|
||||
Context* getContext() const { return context_.get(); }
|
||||
|
||||
private:
|
||||
Application() = default;
|
||||
~Application();
|
||||
Application();
|
||||
|
||||
void mainLoop();
|
||||
void update();
|
||||
|
||||
std::unique_ptr<Context> context_;
|
||||
|
||||
AppConfig config_;
|
||||
|
||||
bool initialized_ = false;
|
||||
|
|
@ -119,6 +135,4 @@ private:
|
|||
int32 currentFps_ = 0;
|
||||
};
|
||||
|
||||
#define APP extra2d::Application::instance()
|
||||
|
||||
} // 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 触摸开始事件
|
||||
* @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 触摸移动事件
|
||||
* @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 触摸结束事件
|
||||
* @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 触摸取消事件
|
||||
* @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/vec3.h>
|
||||
|
||||
// Core
|
||||
#include <core/director.h>
|
||||
#include <core/scheduler.h>
|
||||
#include <core/service.h>
|
||||
// Context (核心上下文)
|
||||
#include <context/context.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
|
||||
#include <platform/file.h>
|
||||
#include <platform/input.h>
|
||||
#include <platform/sdl2.h>
|
||||
#include <platform/window.h>
|
||||
|
||||
|
||||
// Event
|
||||
#include <event/event_bus.h>
|
||||
#include <event/events.h>
|
||||
|
||||
// Utils
|
||||
#include <utils/logger.h>
|
||||
|
|
@ -40,6 +43,13 @@
|
|||
// Application
|
||||
#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__
|
||||
#include <switch.h>
|
||||
#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
|
||||
|
||||
#include <module/imodule.h>
|
||||
#include <types/base/types.h>
|
||||
#include <core/service.h>
|
||||
#include <types/const/priority.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -32,20 +32,28 @@ struct FileData {
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief 文件服务
|
||||
* @brief 文件模块
|
||||
*
|
||||
* 提供跨平台文件系统操作
|
||||
* 非单例设计,通过 Context 管理生命周期
|
||||
*/
|
||||
class FileSvc : public IService {
|
||||
class FileModule : public IModule {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static FileSvc& inst();
|
||||
FileModule();
|
||||
~FileModule() override;
|
||||
|
||||
const char* name() const override { return "FileSvc"; }
|
||||
int pri() const override { return Pri::System; }
|
||||
// 禁止拷贝
|
||||
FileModule(const FileModule&) = delete;
|
||||
FileModule& operator=(const FileModule&) = delete;
|
||||
|
||||
// 允许移动
|
||||
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;
|
||||
void shutdown() override;
|
||||
|
||||
|
|
@ -145,12 +153,8 @@ public:
|
|||
std::string assetPath(const std::string& relPath) const;
|
||||
|
||||
private:
|
||||
FileSvc() = default;
|
||||
|
||||
std::string assetRoot_;
|
||||
std::string writableDir_;
|
||||
};
|
||||
|
||||
#define FILE_SVC extra2d::FileSvc::inst()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
#include <SDL.h>
|
||||
#include <array>
|
||||
#include <core/service.h>
|
||||
#include <functional>
|
||||
#include <module/imodule.h>
|
||||
#include <types/base/types.h>
|
||||
#include <types/ptr/ref_counted.h>
|
||||
#include <types/const/priority.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -94,47 +95,47 @@ constexpr Key RightAlt = SDL_SCANCODE_RALT;
|
|||
* @brief 鼠标按键
|
||||
*/
|
||||
enum class MouseBtn : uint8 {
|
||||
Left = 0,
|
||||
Middle = 1,
|
||||
Right = 2,
|
||||
X1 = 3,
|
||||
X2 = 4,
|
||||
Count = 5
|
||||
Left = 0,
|
||||
Middle = 1,
|
||||
Right = 2,
|
||||
X1 = 3,
|
||||
X2 = 4,
|
||||
Count = 5
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 游戏手柄按键
|
||||
*/
|
||||
enum class GamepadBtn : uint8 {
|
||||
A = 0,
|
||||
B = 1,
|
||||
X = 2,
|
||||
Y = 3,
|
||||
Back = 4,
|
||||
Guide = 5,
|
||||
Start = 6,
|
||||
LeftStick = 7,
|
||||
RightStick = 8,
|
||||
LeftShoulder = 9,
|
||||
RightShoulder = 10,
|
||||
DPadUp = 11,
|
||||
DPadDown = 12,
|
||||
DPadLeft = 13,
|
||||
DPadRight = 14,
|
||||
Count = 15
|
||||
A = 0,
|
||||
B = 1,
|
||||
X = 2,
|
||||
Y = 3,
|
||||
Back = 4,
|
||||
Guide = 5,
|
||||
Start = 6,
|
||||
LeftStick = 7,
|
||||
RightStick = 8,
|
||||
LeftShoulder = 9,
|
||||
RightShoulder = 10,
|
||||
DPadUp = 11,
|
||||
DPadDown = 12,
|
||||
DPadLeft = 13,
|
||||
DPadRight = 14,
|
||||
Count = 15
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 游戏手柄轴
|
||||
*/
|
||||
enum class GamepadAxis : uint8 {
|
||||
LeftX = 0,
|
||||
LeftY = 1,
|
||||
RightX = 2,
|
||||
RightY = 3,
|
||||
TriggerLeft = 4,
|
||||
TriggerRight = 5,
|
||||
Count = 6
|
||||
LeftX = 0,
|
||||
LeftY = 1,
|
||||
RightX = 2,
|
||||
RightY = 3,
|
||||
TriggerLeft = 4,
|
||||
TriggerRight = 5,
|
||||
Count = 6
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -146,224 +147,232 @@ enum class TouchState : uint8 { None = 0, Began, Moved, Ended, Cancelled };
|
|||
* @brief 触摸点信息
|
||||
*/
|
||||
struct TouchPoint {
|
||||
int64 id = 0;
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float prevX = 0.0f;
|
||||
float prevY = 0.0f;
|
||||
float deltaX = 0.0f;
|
||||
float deltaY = 0.0f;
|
||||
TouchState state = TouchState::None;
|
||||
float pressure = 1.0f;
|
||||
int64 id = 0;
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float prevX = 0.0f;
|
||||
float prevY = 0.0f;
|
||||
float deltaX = 0.0f;
|
||||
float deltaY = 0.0f;
|
||||
TouchState state = TouchState::None;
|
||||
float pressure = 1.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 按键回调类型
|
||||
*/
|
||||
using KeyCb = Fn<void(Key)>;
|
||||
using MouseBtnCb = Fn<void(MouseBtn, int32 x, int32 y)>;
|
||||
using TouchCb = Fn<void(const TouchPoint &)>;
|
||||
using KeyCb = std::function<void(Key)>;
|
||||
using MouseBtnCb = std::function<void(MouseBtn, int32 x, int32 y)>;
|
||||
using TouchCb = std::function<void(const TouchPoint&)>;
|
||||
|
||||
/**
|
||||
* @brief 输入服务
|
||||
* @brief 输入模块
|
||||
*
|
||||
* 管理键盘、鼠标、触摸、游戏手柄输入
|
||||
* 非单例设计,通过 Context 管理生命周期
|
||||
*/
|
||||
class InputSvc : public IService {
|
||||
class InputModule : public IModule {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static InputSvc &inst();
|
||||
InputModule();
|
||||
~InputModule() override;
|
||||
|
||||
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;
|
||||
void update(float dt) override;
|
||||
// 允许移动
|
||||
InputModule(InputModule&&) noexcept;
|
||||
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 检查按键是否按下
|
||||
*/
|
||||
bool isKeyDown(Key key) const;
|
||||
/**
|
||||
* @brief 处理输入事件
|
||||
*/
|
||||
void processEvent(const SDL_Event& evt);
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否刚按下
|
||||
*/
|
||||
bool isKeyPressed(Key key) const;
|
||||
/**
|
||||
* @brief 更新输入状态(每帧调用)
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否刚释放
|
||||
*/
|
||||
bool isKeyReleased(Key key) const;
|
||||
// ========== 键盘 ==========
|
||||
|
||||
// ========== 鼠标 ==========
|
||||
/**
|
||||
* @brief 检查按键是否按下
|
||||
*/
|
||||
bool isKeyDown(Key key) const;
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标位置
|
||||
*/
|
||||
void getMousePos(int32 &x, int32 &y) const;
|
||||
/**
|
||||
* @brief 检查按键是否刚按下
|
||||
*/
|
||||
bool isKeyPressed(Key key) const;
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标位置(浮点)
|
||||
*/
|
||||
void getMousePos(float &x, float &y) const;
|
||||
/**
|
||||
* @brief 检查按键是否刚释放
|
||||
*/
|
||||
bool isKeyReleased(Key key) const;
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按键是否按下
|
||||
*/
|
||||
bool isMouseBtnDown(MouseBtn btn) const;
|
||||
// ========== 鼠标 ==========
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按键是否刚按下
|
||||
*/
|
||||
bool isMouseBtnPressed(MouseBtn btn) const;
|
||||
/**
|
||||
* @brief 获取鼠标位置
|
||||
*/
|
||||
void getMousePos(int32& x, int32& y) const;
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按键是否刚释放
|
||||
*/
|
||||
bool isMouseBtnReleased(MouseBtn btn) const;
|
||||
/**
|
||||
* @brief 获取鼠标位置(浮点)
|
||||
*/
|
||||
void getMousePos(float& x, float& y) const;
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标滚轮
|
||||
*/
|
||||
int32 getMouseWheel() const;
|
||||
/**
|
||||
* @brief 检查鼠标按键是否按下
|
||||
*/
|
||||
bool isMouseBtnDown(MouseBtn btn) const;
|
||||
|
||||
// ========== 触摸 ==========
|
||||
/**
|
||||
* @brief 检查鼠标按键是否刚按下
|
||||
*/
|
||||
bool isMouseBtnPressed(MouseBtn btn) const;
|
||||
|
||||
/**
|
||||
* @brief 获取触摸点数量
|
||||
*/
|
||||
int32 touchCount() const;
|
||||
/**
|
||||
* @brief 检查鼠标按键是否刚释放
|
||||
*/
|
||||
bool isMouseBtnReleased(MouseBtn btn) const;
|
||||
|
||||
/**
|
||||
* @brief 获取触摸点
|
||||
* @param idx 触摸点索引
|
||||
* @return 触摸点信息,无效索引返回 nullptr
|
||||
*/
|
||||
const TouchPoint *getTouch(int32 idx) const;
|
||||
/**
|
||||
* @brief 获取鼠标滚轮
|
||||
*/
|
||||
int32 getMouseWheel() const;
|
||||
|
||||
/**
|
||||
* @brief 根据 ID 获取触摸点
|
||||
*/
|
||||
const TouchPoint *getTouchById(int64 id) const;
|
||||
// ========== 触摸 ==========
|
||||
|
||||
/**
|
||||
* @brief 检查是否有触摸
|
||||
*/
|
||||
bool hasTouch() const { return touchCount() > 0; }
|
||||
/**
|
||||
* @brief 获取触摸点数量
|
||||
*/
|
||||
int32 touchCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取所有活跃触摸点
|
||||
*/
|
||||
const std::vector<TouchPoint> &getTouches() const { return activeTouches_; }
|
||||
/**
|
||||
* @brief 获取触摸点
|
||||
* @param idx 触摸点索引
|
||||
* @return 触摸点信息,无效索引返回 nullptr
|
||||
*/
|
||||
const TouchPoint* getTouch(int32 idx) const;
|
||||
|
||||
// ========== 游戏手柄 ==========
|
||||
/**
|
||||
* @brief 根据 ID 获取触摸点
|
||||
*/
|
||||
const TouchPoint* getTouchById(int64 id) const;
|
||||
|
||||
/**
|
||||
* @brief 连接的游戏手柄数量
|
||||
*/
|
||||
int32 gamepadCount() const;
|
||||
/**
|
||||
* @brief 检查是否有触摸
|
||||
*/
|
||||
bool hasTouch() const { return touchCount() > 0; }
|
||||
|
||||
/**
|
||||
* @brief 检查手柄按键是否按下
|
||||
*/
|
||||
bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const;
|
||||
/**
|
||||
* @brief 获取所有活跃触摸点
|
||||
*/
|
||||
const std::vector<TouchPoint>& getTouches() const { return activeTouches_; }
|
||||
|
||||
/**
|
||||
* @brief 检查手柄按键是否刚按下
|
||||
*/
|
||||
bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const;
|
||||
// ========== 游戏手柄 ==========
|
||||
|
||||
/**
|
||||
* @brief 获取手柄轴值 (-1.0 到 1.0)
|
||||
*/
|
||||
float getGamepadAxis(int32 idx, GamepadAxis axis) const;
|
||||
/**
|
||||
* @brief 连接的游戏手柄数量
|
||||
*/
|
||||
int32 gamepadCount() const;
|
||||
|
||||
// ========== 回调设置 ==========
|
||||
/**
|
||||
* @brief 检查手柄按键是否按下
|
||||
*/
|
||||
bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const;
|
||||
|
||||
/**
|
||||
* @brief 设置按键按下回调
|
||||
*/
|
||||
void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 检查手柄按键是否刚按下
|
||||
*/
|
||||
bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const;
|
||||
|
||||
/**
|
||||
* @brief 设置按键释放回调
|
||||
*/
|
||||
void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 获取手柄轴值 (-1.0 到 1.0)
|
||||
*/
|
||||
float getGamepadAxis(int32 idx, GamepadAxis axis) const;
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标按下回调
|
||||
*/
|
||||
void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); }
|
||||
// ========== 回调设置 ==========
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标释放回调
|
||||
*/
|
||||
void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 设置按键按下回调
|
||||
*/
|
||||
void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); }
|
||||
|
||||
/**
|
||||
* @brief 设置触摸开始回调
|
||||
*/
|
||||
void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 设置按键释放回调
|
||||
*/
|
||||
void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); }
|
||||
|
||||
/**
|
||||
* @brief 设置触摸移动回调
|
||||
*/
|
||||
void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 设置鼠标按下回调
|
||||
*/
|
||||
void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); }
|
||||
|
||||
/**
|
||||
* @brief 设置触摸结束回调
|
||||
*/
|
||||
void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 设置鼠标释放回调
|
||||
*/
|
||||
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:
|
||||
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;
|
||||
static constexpr int32 MAX_GAMEPADS = 4;
|
||||
static constexpr int32 MAX_TOUCHES = 10;
|
||||
std::array<uint8, KEY_COUNT> keyState_{};
|
||||
std::array<uint8, KEY_COUNT> keyPrev_{};
|
||||
|
||||
std::array<uint8, KEY_COUNT> keyState_{};
|
||||
std::array<uint8, KEY_COUNT> keyPrev_{};
|
||||
int32 mouseX_ = 0;
|
||||
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;
|
||||
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_{};
|
||||
std::vector<TouchPoint> activeTouches_;
|
||||
std::vector<TouchPoint> endedTouches_;
|
||||
|
||||
std::vector<TouchPoint> activeTouches_;
|
||||
std::vector<TouchPoint> endedTouches_;
|
||||
SDL_GameController* gamepads_[MAX_GAMEPADS] = {};
|
||||
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] = {};
|
||||
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)>
|
||||
padState_[MAX_GAMEPADS];
|
||||
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)>
|
||||
padPrev_[MAX_GAMEPADS];
|
||||
KeyCb onKeyDown_;
|
||||
KeyCb onKeyUp_;
|
||||
MouseBtnCb onMouseDown_;
|
||||
MouseBtnCb onMouseUp_;
|
||||
TouchCb onTouchBegan_;
|
||||
TouchCb onTouchMoved_;
|
||||
TouchCb onTouchEnded_;
|
||||
|
||||
KeyCb onKeyDown_;
|
||||
KeyCb onKeyUp_;
|
||||
MouseBtnCb onMouseDown_;
|
||||
MouseBtnCb onMouseUp_;
|
||||
TouchCb onTouchBegan_;
|
||||
TouchCb onTouchMoved_;
|
||||
TouchCb onTouchEnded_;
|
||||
void openGamepad(int32 idx);
|
||||
void closeGamepad(int32 idx);
|
||||
|
||||
void processEvent(const SDL_Event &evt);
|
||||
void openGamepad(int32 idx);
|
||||
void closeGamepad(int32 idx);
|
||||
|
||||
void processTouchDown(const SDL_TouchFingerEvent &evt);
|
||||
void processTouchUp(const SDL_TouchFingerEvent &evt);
|
||||
void processTouchMotion(const SDL_TouchFingerEvent &evt);
|
||||
|
||||
friend class WindowSvc;
|
||||
void processTouchDown(const SDL_TouchFingerEvent& evt);
|
||||
void processTouchUp(const SDL_TouchFingerEvent& evt);
|
||||
void processTouchMotion(const SDL_TouchFingerEvent& evt);
|
||||
};
|
||||
|
||||
#define INPUT_SVC extra2d::InputSvc::inst()
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <core/service.h>
|
||||
#include <functional>
|
||||
#include <module/imodule.h>
|
||||
#include <string>
|
||||
#include <types/base/types.h>
|
||||
#include <types/const/priority.h>
|
||||
#include <types/math/size.h>
|
||||
#include <types/math/vec2.h>
|
||||
#include <types/ptr/ref_counted.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -14,148 +15,154 @@ namespace extra2d {
|
|||
* @brief 窗口配置
|
||||
*/
|
||||
struct WindowCfg {
|
||||
std::string title = "Extra2D";
|
||||
int32 width = 1280;
|
||||
int32 height = 720;
|
||||
bool fullscreen = false;
|
||||
bool resizable = true;
|
||||
bool vsync = true;
|
||||
int32 glMajor = 3;
|
||||
int32 glMinor = 3;
|
||||
std::string title = "Extra2D";
|
||||
int32 width = 1280;
|
||||
int32 height = 720;
|
||||
bool fullscreen = false;
|
||||
bool resizable = true;
|
||||
bool vsync = true;
|
||||
int32 glMajor = 3;
|
||||
int32 glMinor = 3;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口事件回调
|
||||
*/
|
||||
using ResizeCb = Fn<void(int32 w, int32 h)>;
|
||||
using CloseCb = Fn<void()>;
|
||||
using ResizeCb = std::function<void(int32 w, int32 h)>;
|
||||
using CloseCb = std::function<void()>;
|
||||
|
||||
/**
|
||||
* @brief 窗口服务
|
||||
* @brief 窗口模块
|
||||
*
|
||||
* 管理 SDL2 窗口和 OpenGL 上下文
|
||||
* 非单例设计,通过 Context 管理生命周期
|
||||
*/
|
||||
class WindowSvc : public IService {
|
||||
class WindowModule : public IModule {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*/
|
||||
static WindowSvc &inst();
|
||||
WindowModule();
|
||||
~WindowModule() override;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief 使用配置创建窗口
|
||||
*/
|
||||
bool create(const WindowCfg &cfg);
|
||||
// IModule 接口实现
|
||||
const char* name() const override { return "Window"; }
|
||||
ModuleType type() const override { return ModuleType::System; }
|
||||
int priority() const override { return Pri::Window; }
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
|
||||
/**
|
||||
* @brief 处理窗口事件
|
||||
* @return true 继续运行,false 应退出
|
||||
*/
|
||||
bool pollEvents();
|
||||
/**
|
||||
* @brief 使用配置创建窗口
|
||||
*/
|
||||
bool create(const WindowCfg& cfg);
|
||||
|
||||
/**
|
||||
* @brief 交换缓冲区
|
||||
*/
|
||||
void swapBuffers();
|
||||
/**
|
||||
* @brief 处理窗口事件
|
||||
* @return true 继续运行,false 应退出
|
||||
*/
|
||||
bool pollEvents();
|
||||
|
||||
/**
|
||||
* @brief 获取 SDL 窗口句柄
|
||||
*/
|
||||
SDL_Window *handle() const { return window_; }
|
||||
/**
|
||||
* @brief 交换缓冲区
|
||||
*/
|
||||
void swapBuffers();
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 上下文
|
||||
*/
|
||||
SDL_GLContext glContext() const { return glCtx_; }
|
||||
/**
|
||||
* @brief 获取 SDL 窗口句柄
|
||||
*/
|
||||
SDL_Window* handle() const { return window_; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口尺寸
|
||||
*/
|
||||
Size getSize() const;
|
||||
/**
|
||||
* @brief 获取 OpenGL 上下文
|
||||
*/
|
||||
SDL_GLContext glContext() const { return glCtx_; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
*/
|
||||
Vec2 getPosition() const;
|
||||
/**
|
||||
* @brief 获取窗口尺寸
|
||||
*/
|
||||
Size getSize() const;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口尺寸
|
||||
*/
|
||||
void setSize(int32 w, int32 h);
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
*/
|
||||
Vec2 getPosition() const;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口标题
|
||||
*/
|
||||
void setTitle(const std::string &title);
|
||||
/**
|
||||
* @brief 设置窗口尺寸
|
||||
*/
|
||||
void setSize(int32 w, int32 h);
|
||||
|
||||
/**
|
||||
* @brief 设置全屏模式
|
||||
*/
|
||||
void setFullscreen(bool fullscreen);
|
||||
/**
|
||||
* @brief 设置窗口标题
|
||||
*/
|
||||
void setTitle(const std::string& title);
|
||||
|
||||
/**
|
||||
* @brief 检查是否全屏
|
||||
*/
|
||||
bool isFullscreen() const;
|
||||
/**
|
||||
* @brief 设置全屏模式
|
||||
*/
|
||||
void setFullscreen(bool fullscreen);
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*/
|
||||
void setVsync(bool vsync);
|
||||
/**
|
||||
* @brief 检查是否全屏
|
||||
*/
|
||||
bool isFullscreen() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否垂直同步
|
||||
*/
|
||||
bool isVsync() const;
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*/
|
||||
void setVsync(bool vsync);
|
||||
|
||||
/**
|
||||
* @brief 显示/隐藏窗口
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
/**
|
||||
* @brief 检查是否垂直同步
|
||||
*/
|
||||
bool isVsync() const;
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否可见
|
||||
*/
|
||||
bool isVisible() const;
|
||||
/**
|
||||
* @brief 显示/隐藏窗口
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口关闭回调
|
||||
*/
|
||||
void setOnClose(CloseCb cb) { onClose_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 检查窗口是否可见
|
||||
*/
|
||||
bool isVisible() const;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口大小改变回调
|
||||
*/
|
||||
void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); }
|
||||
/**
|
||||
* @brief 设置窗口关闭回调
|
||||
*/
|
||||
void setOnClose(CloseCb cb) { onClose_ = std::move(cb); }
|
||||
|
||||
/**
|
||||
* @brief 请求关闭窗口
|
||||
*/
|
||||
void requestClose() { shouldClose_ = true; }
|
||||
/**
|
||||
* @brief 设置窗口大小改变回调
|
||||
*/
|
||||
void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); }
|
||||
|
||||
/**
|
||||
* @brief 检查是否应该关闭
|
||||
*/
|
||||
bool shouldClose() const { return shouldClose_; }
|
||||
/**
|
||||
* @brief 请求关闭窗口
|
||||
*/
|
||||
void requestClose() { shouldClose_ = true; }
|
||||
|
||||
/**
|
||||
* @brief 检查是否应该关闭
|
||||
*/
|
||||
bool shouldClose() const { return shouldClose_; }
|
||||
|
||||
private:
|
||||
WindowSvc() = default;
|
||||
void handleWindowEvent(const SDL_WindowEvent& evt);
|
||||
|
||||
SDL_Window *window_ = nullptr;
|
||||
SDL_GLContext glCtx_ = nullptr;
|
||||
bool shouldClose_ = false;
|
||||
bool vsync_ = true;
|
||||
SDL_Window* window_ = nullptr;
|
||||
SDL_GLContext glCtx_ = nullptr;
|
||||
bool shouldClose_ = false;
|
||||
bool vsync_ = true;
|
||||
|
||||
CloseCb onClose_;
|
||||
ResizeCb onResize_;
|
||||
CloseCb onClose_;
|
||||
ResizeCb onResize_;
|
||||
};
|
||||
|
||||
#define WINDOW extra2d::WindowSvc::inst()
|
||||
|
||||
} // 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 {
|
||||
|
||||
/**
|
||||
* @brief 模块优先级常量
|
||||
*
|
||||
* 数值越小优先级越高,越早初始化,越晚关闭
|
||||
*/
|
||||
namespace Pri {
|
||||
constexpr int32 Min = INT32_MIN;
|
||||
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 Logic = 100;
|
||||
constexpr int32 Anim = 200;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public:
|
|||
|
||||
Ptr<T>& operator=(T* p) { reset(p); return *this; }
|
||||
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; }
|
||||
|
||||
bool operator==(std::nullptr_t) const { return p_ == nullptr; }
|
||||
|
|
|
|||
|
|
@ -3,84 +3,75 @@
|
|||
#include <random>
|
||||
#include <types/base/types.h>
|
||||
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Random 类 - 随机数生成器
|
||||
// 非单例设计,可以创建多个独立的随机数生成器实例
|
||||
// ============================================================================
|
||||
class Random {
|
||||
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] 范围内的随机浮点数
|
||||
float getFloat(float min, float max);
|
||||
/// 获取 [0, 1) 范围内的随机浮点数
|
||||
float getFloat();
|
||||
|
||||
/// 获取 [0, max] 范围内的随机整数
|
||||
int getInt(int max);
|
||||
/// 获取 [min, max] 范围内的随机浮点数
|
||||
float getFloat(float min, float max);
|
||||
|
||||
/// 获取 [min, max] 范围内的随机整数
|
||||
int getInt(int min, int max);
|
||||
/// 获取 [0, max] 范围内的随机整数
|
||||
int getInt(int max);
|
||||
|
||||
/// 获取随机布尔值
|
||||
bool getBool();
|
||||
/// 获取 [min, max] 范围内的随机整数
|
||||
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:
|
||||
Random();
|
||||
~Random() = default;
|
||||
|
||||
Random(const Random &) = delete;
|
||||
Random &operator=(const Random &) = delete;
|
||||
|
||||
std::mt19937 generator_;
|
||||
std::uniform_real_distribution<float> floatDist_;
|
||||
std::mt19937 generator_;
|
||||
std::uniform_real_distribution<float> floatDist_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 便捷函数
|
||||
// 便捷函数 - 使用全局默认随机数生成器
|
||||
// ============================================================================
|
||||
|
||||
/// 获取 [0, 1) 范围内的随机浮点数
|
||||
inline float randomFloat() { return Random::getInstance().getFloat(); }
|
||||
float randomFloat();
|
||||
|
||||
/// 获取 [min, max] 范围内的随机浮点数
|
||||
inline float randomFloat(float min, float max) {
|
||||
return Random::getInstance().getFloat(min, max);
|
||||
}
|
||||
float randomFloat(float min, float max);
|
||||
|
||||
/// 获取 [0, max] 范围内的随机整数
|
||||
inline int randomInt(int max) { return Random::getInstance().getInt(max); }
|
||||
int randomInt(int max);
|
||||
|
||||
/// 获取 [min, max] 范围内的随机整数
|
||||
inline int randomInt(int min, int max) {
|
||||
return Random::getInstance().getInt(min, max);
|
||||
}
|
||||
int randomInt(int min, int max);
|
||||
|
||||
/// 获取随机布尔值
|
||||
inline bool randomBool() { return Random::getInstance().getBool(); }
|
||||
bool randomBool();
|
||||
|
||||
/// 获取随机布尔值(带概率)
|
||||
inline bool randomBool(float probability) {
|
||||
return Random::getInstance().getBool(probability);
|
||||
}
|
||||
bool randomBool(float probability);
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
#include <app/application.h>
|
||||
#include <core/director.h>
|
||||
#include <core/service.h>
|
||||
#include <context/context.h>
|
||||
#include <event/events.h>
|
||||
#include <platform/file.h>
|
||||
#include <platform/input.h>
|
||||
#include <platform/sdl2.h>
|
||||
#include <platform/window.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
|
|
@ -35,12 +30,18 @@ static double getTimeSeconds() {
|
|||
#endif
|
||||
}
|
||||
|
||||
Application &Application::instance() {
|
||||
static Application instance;
|
||||
return instance;
|
||||
std::unique_ptr<Application> Application::create() {
|
||||
return std::unique_ptr<Application>(new Application());
|
||||
}
|
||||
|
||||
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) {
|
||||
if (initialized_) {
|
||||
|
|
@ -70,38 +71,19 @@ bool Application::init(const AppConfig &config) {
|
|||
return false;
|
||||
}
|
||||
|
||||
SVC_MGR.reg(&WINDOW);
|
||||
SVC_MGR.reg(&INPUT_SVC);
|
||||
SVC_MGR.reg(&FILE_SVC);
|
||||
|
||||
if (!SVC_MGR.initAll()) {
|
||||
E2D_LOG_ERROR("Failed to initialize services");
|
||||
// 创建引擎上下文
|
||||
context_ = Context::create();
|
||||
if (!context_) {
|
||||
E2D_LOG_ERROR("Failed to create context");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WINDOW.create([&] {
|
||||
WindowCfg cfg;
|
||||
cfg.title = config_.title;
|
||||
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");
|
||||
// 初始化引擎
|
||||
if (!context_->init()) {
|
||||
E2D_LOG_ERROR("Failed to initialize context");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DIRECTOR.init()) {
|
||||
E2D_LOG_ERROR("Failed to initialize Director");
|
||||
return false;
|
||||
}
|
||||
|
||||
WINDOW.setOnClose([this]() { quit(); });
|
||||
|
||||
initialized_ = true;
|
||||
running_ = true;
|
||||
|
||||
|
|
@ -121,8 +103,9 @@ void Application::shutdown() {
|
|||
|
||||
E2D_LOG_INFO("Shutting down application...");
|
||||
|
||||
DIRECTOR.shutdown();
|
||||
SVC_MGR.shutdownAll();
|
||||
// 关闭上下文(会自动关闭模块和插件)
|
||||
context_.reset();
|
||||
|
||||
Sdl2::shutdown();
|
||||
|
||||
#ifdef __SWITCH__
|
||||
|
|
@ -144,7 +127,7 @@ void Application::run() {
|
|||
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
|
||||
while (running_ && !WINDOW.shouldClose()) {
|
||||
while (running_) {
|
||||
mainLoop();
|
||||
}
|
||||
}
|
||||
|
|
@ -152,14 +135,11 @@ void Application::run() {
|
|||
void Application::quit() {
|
||||
shouldQuit_ = true;
|
||||
running_ = false;
|
||||
WINDOW.requestClose();
|
||||
}
|
||||
|
||||
void Application::pause() {
|
||||
if (!paused_) {
|
||||
paused_ = true;
|
||||
SVC_MGR.pauseAll();
|
||||
DIRECTOR.pause();
|
||||
events::OnPause::emit();
|
||||
E2D_LOG_INFO("Application paused");
|
||||
}
|
||||
|
|
@ -168,20 +148,13 @@ void Application::pause() {
|
|||
void Application::resume() {
|
||||
if (paused_) {
|
||||
paused_ = false;
|
||||
SVC_MGR.resumeAll();
|
||||
DIRECTOR.resume();
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
events::OnResume::emit();
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
E2D_LOG_INFO("Application resumed");
|
||||
}
|
||||
}
|
||||
|
||||
void Application::mainLoop() {
|
||||
if (!WINDOW.pollEvents()) {
|
||||
running_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
double currentTime = getTimeSeconds();
|
||||
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
||||
lastFrameTime_ = currentTime;
|
||||
|
|
@ -200,8 +173,7 @@ void Application::mainLoop() {
|
|||
update();
|
||||
}
|
||||
|
||||
WINDOW.swapBuffers();
|
||||
|
||||
// 帧率限制
|
||||
if (!config_.vsync && config_.fpsLimit > 0) {
|
||||
double frameEndTime = getTimeSeconds();
|
||||
double frameTime = frameEndTime - currentTime;
|
||||
|
|
@ -214,9 +186,10 @@ void Application::mainLoop() {
|
|||
}
|
||||
|
||||
void Application::update() {
|
||||
SVC_MGR.updateAll(deltaTime_);
|
||||
events::OnUpdate::emit(deltaTime_);
|
||||
DIRECTOR.mainLoop(deltaTime_);
|
||||
// 通过上下文更新引擎
|
||||
if (context_) {
|
||||
context_->tick(deltaTime_);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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,39 +16,37 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
FileSvc& FileSvc::inst() {
|
||||
static FileSvc instance;
|
||||
return instance;
|
||||
}
|
||||
FileModule::FileModule() = default;
|
||||
|
||||
bool FileSvc::init() {
|
||||
if (isInited()) return true;
|
||||
FileModule::~FileModule() = default;
|
||||
|
||||
FileModule::FileModule(FileModule&&) noexcept = default;
|
||||
FileModule& FileModule::operator=(FileModule&&) noexcept = default;
|
||||
|
||||
bool FileModule::init() {
|
||||
writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D");
|
||||
if (writableDir_.empty()) {
|
||||
writableDir_ = "./";
|
||||
}
|
||||
|
||||
state_ = SvcState::Inited;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSvc::shutdown() {
|
||||
state_ = SvcState::Shutdown;
|
||||
void FileModule::shutdown() {
|
||||
// 清理工作
|
||||
}
|
||||
|
||||
bool FileSvc::exists(const std::string& path) const {
|
||||
bool FileModule::exists(const std::string& path) const {
|
||||
struct stat st;
|
||||
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;
|
||||
if (stat(path.c_str(), &st) != 0) return false;
|
||||
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;
|
||||
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
|
|
@ -70,7 +68,7 @@ FileData FileSvc::read(const std::string& path) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::string FileSvc::readString(const std::string& path) const {
|
||||
std::string FileModule::readString(const std::string& path) const {
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open()) return "";
|
||||
|
||||
|
|
@ -79,7 +77,7 @@ std::string FileSvc::readString(const std::string& path) const {
|
|||
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);
|
||||
if (!file.is_open()) return false;
|
||||
|
||||
|
|
@ -87,11 +85,11 @@ bool FileSvc::write(const std::string& path, const void* data, size_t size) cons
|
|||
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());
|
||||
}
|
||||
|
||||
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);
|
||||
if (!file.is_open()) return false;
|
||||
|
||||
|
|
@ -99,11 +97,11 @@ bool FileSvc::append(const std::string& path, const void* data, size_t size) con
|
|||
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;
|
||||
}
|
||||
|
||||
bool FileSvc::mkdir(const std::string& path) const {
|
||||
bool FileModule::mkdir(const std::string& path) const {
|
||||
#ifdef _WIN32
|
||||
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
|
||||
#else
|
||||
|
|
@ -111,7 +109,7 @@ bool FileSvc::mkdir(const std::string& path) const {
|
|||
#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;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -162,13 +160,13 @@ std::vector<FileInfo> FileSvc::listDir(const std::string& path) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
int64 FileSvc::fileSize(const std::string& path) const {
|
||||
int64 FileModule::fileSize(const std::string& path) const {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) != 0) return -1;
|
||||
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('.');
|
||||
if (pos == std::string::npos || pos == 0) return "";
|
||||
|
||||
|
|
@ -178,20 +176,20 @@ std::string FileSvc::ext(const std::string& path) const {
|
|||
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("/\\");
|
||||
if (pos == std::string::npos) return path;
|
||||
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("/\\");
|
||||
if (pos == std::string::npos) return ".";
|
||||
if (pos == 0) return "/";
|
||||
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 (b.empty()) return a;
|
||||
|
||||
|
|
@ -202,11 +200,11 @@ std::string FileSvc::join(const std::string& a, const std::string& b) const {
|
|||
return a + "/" + b;
|
||||
}
|
||||
|
||||
std::string FileSvc::writableDir() const {
|
||||
std::string FileModule::writableDir() const {
|
||||
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;
|
||||
return join(assetRoot_, relPath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,76 @@
|
|||
#include <platform/input.h>
|
||||
#include <platform/window.h>
|
||||
#include <event/events.h>
|
||||
#include <SDL.h>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
InputSvc& InputSvc::inst() {
|
||||
static InputSvc instance;
|
||||
return instance;
|
||||
InputModule::InputModule() = default;
|
||||
|
||||
InputModule::~InputModule() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool InputSvc::init() {
|
||||
if (isInited()) return true;
|
||||
InputModule::InputModule(InputModule&& other) noexcept
|
||||
: 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(keyPrev_.data(), 0, KEY_COUNT);
|
||||
std::memset(mouseState_.data(), 0, static_cast<size_t>(MouseBtn::Count));
|
||||
|
|
@ -36,20 +93,18 @@ bool InputSvc::init() {
|
|||
}
|
||||
}
|
||||
|
||||
state_ = SvcState::Inited;
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputSvc::shutdown() {
|
||||
void InputModule::shutdown() {
|
||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
closeGamepad(i);
|
||||
}
|
||||
activeTouches_.clear();
|
||||
endedTouches_.clear();
|
||||
state_ = SvcState::Shutdown;
|
||||
}
|
||||
|
||||
void InputSvc::update(float dt) {
|
||||
void InputModule::update() {
|
||||
std::memcpy(keyPrev_.data(), keyState_.data(), KEY_COUNT);
|
||||
std::memcpy(mousePrev_.data(), mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
|
||||
|
||||
|
|
@ -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) {
|
||||
case SDL_KEYDOWN:
|
||||
if (evt.key.repeat == 0 && onKeyDown_) {
|
||||
onKeyDown_(static_cast<Key>(evt.key.keysym.scancode));
|
||||
if (evt.key.repeat == 0) {
|
||||
Key key = static_cast<Key>(evt.key.keysym.scancode);
|
||||
// 发送按键按下事件
|
||||
events::OnKeyDown::emit(static_cast<int32>(key));
|
||||
if (onKeyDown_) {
|
||||
onKeyDown_(key);
|
||||
}
|
||||
}
|
||||
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_) {
|
||||
onKeyUp_(static_cast<Key>(evt.key.keysym.scancode));
|
||||
onKeyUp_(key);
|
||||
}
|
||||
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_) {
|
||||
onMouseDown_(static_cast<MouseBtn>(evt.button.button - 1), evt.button.x, evt.button.y);
|
||||
onMouseDown_(btn, evt.button.x, evt.button.y);
|
||||
}
|
||||
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_) {
|
||||
onMouseUp_(static_cast<MouseBtn>(evt.button.button - 1), evt.button.x, evt.button.y);
|
||||
onMouseUp_(btn, evt.button.x, evt.button.y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEMOTION:
|
||||
// 发送鼠标移动事件
|
||||
events::OnMouseMove::emit(evt.motion.x, evt.motion.y);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
mouseWheel_ = evt.wheel.y;
|
||||
// 发送鼠标滚轮事件
|
||||
events::OnMouseWheel::emit(evt.wheel.y);
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
openGamepad(evt.cdevice.which);
|
||||
// 发送手柄连接事件
|
||||
events::OnGamepadConnect::emit(evt.cdevice.which);
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
if (gamepads_[i] && SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepads_[i])) == evt.cdevice.which) {
|
||||
closeGamepad(i);
|
||||
// 发送手柄断开事件
|
||||
events::OnGamepadDisconnect::emit(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +248,7 @@ 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;
|
||||
|
||||
int32 winW = 0, winH = 0;
|
||||
|
|
@ -185,12 +267,15 @@ void InputSvc::processTouchDown(const SDL_TouchFingerEvent& evt) {
|
|||
|
||||
activeTouches_.push_back(touch);
|
||||
|
||||
// 发送触摸开始事件
|
||||
events::OnTouchBegin::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
|
||||
|
||||
if (onTouchBegan_) {
|
||||
onTouchBegan_(touch);
|
||||
}
|
||||
}
|
||||
|
||||
void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) {
|
||||
void InputModule::processTouchUp(const SDL_TouchFingerEvent& evt) {
|
||||
int64 fingerId = static_cast<int64>(evt.fingerId);
|
||||
|
||||
for (auto& touch : activeTouches_) {
|
||||
|
|
@ -205,6 +290,9 @@ void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) {
|
|||
touch.state = TouchState::Ended;
|
||||
touch.pressure = evt.pressure;
|
||||
|
||||
// 发送触摸结束事件
|
||||
events::OnTouchEnd::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
|
||||
|
||||
if (onTouchEnded_) {
|
||||
onTouchEnded_(touch);
|
||||
}
|
||||
|
|
@ -213,7 +301,7 @@ 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);
|
||||
|
||||
for (auto& touch : activeTouches_) {
|
||||
|
|
@ -228,6 +316,9 @@ void InputSvc::processTouchMotion(const SDL_TouchFingerEvent& evt) {
|
|||
touch.state = TouchState::Moved;
|
||||
touch.pressure = evt.pressure;
|
||||
|
||||
// 发送触摸移动事件
|
||||
events::OnTouchMove::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
|
||||
|
||||
if (onTouchMoved_) {
|
||||
onTouchMoved_(touch);
|
||||
}
|
||||
|
|
@ -236,7 +327,7 @@ 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 (gamepads_[idx]) return;
|
||||
|
||||
|
|
@ -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 (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;
|
||||
return keyState_[key] != 0;
|
||||
}
|
||||
|
||||
bool InputSvc::isKeyPressed(Key key) const {
|
||||
bool InputModule::isKeyPressed(Key key) const {
|
||||
if (key < 0 || key >= KEY_COUNT) return false;
|
||||
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;
|
||||
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_;
|
||||
y = mouseY_;
|
||||
}
|
||||
|
||||
void InputSvc::getMousePos(float& x, float& y) const {
|
||||
void InputModule::getMousePos(float& x, float& y) const {
|
||||
x = static_cast<float>(mouseX_);
|
||||
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);
|
||||
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
||||
return mouseState_[idx] != 0;
|
||||
}
|
||||
|
||||
bool InputSvc::isMouseBtnPressed(MouseBtn btn) const {
|
||||
bool InputModule::isMouseBtnPressed(MouseBtn btn) const {
|
||||
size_t idx = static_cast<size_t>(btn);
|
||||
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
||||
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);
|
||||
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
|
||||
return mouseState_[idx] == 0 && mousePrev_[idx] != 0;
|
||||
}
|
||||
|
||||
int32 InputSvc::getMouseWheel() const {
|
||||
int32 InputModule::getMouseWheel() const {
|
||||
return mouseWheel_;
|
||||
}
|
||||
|
||||
int32 InputSvc::touchCount() const {
|
||||
int32 InputModule::touchCount() const {
|
||||
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;
|
||||
return &activeTouches_[idx];
|
||||
}
|
||||
|
||||
const TouchPoint* InputSvc::getTouchById(int64 id) const {
|
||||
const TouchPoint* InputModule::getTouchById(int64 id) const {
|
||||
for (const auto& touch : activeTouches_) {
|
||||
if (touch.id == id) return &touch;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32 InputSvc::gamepadCount() const {
|
||||
int32 InputModule::gamepadCount() const {
|
||||
int32 count = 0;
|
||||
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
|
||||
if (gamepads_[i]) ++count;
|
||||
|
|
@ -327,7 +418,7 @@ int32 InputSvc::gamepadCount() const {
|
|||
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 (!gamepads_[idx]) return false;
|
||||
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;
|
||||
}
|
||||
|
||||
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 (!gamepads_[idx]) return false;
|
||||
size_t b = static_cast<size_t>(btn);
|
||||
|
|
@ -343,7 +434,7 @@ bool InputSvc::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const {
|
|||
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 (!gamepads_[idx]) return 0.0f;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,178 +1,241 @@
|
|||
#include <SDL.h>
|
||||
#include <platform/input.h>
|
||||
#include <platform/sdl2.h>
|
||||
#include <platform/window.h>
|
||||
#include <platform/sdl2.h>
|
||||
#include <event/events.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
WindowSvc &WindowSvc::inst() {
|
||||
static WindowSvc instance;
|
||||
return instance;
|
||||
WindowModule::WindowModule() = default;
|
||||
|
||||
WindowModule::~WindowModule() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool WindowSvc::init() {
|
||||
if (isInited())
|
||||
WindowModule::WindowModule(WindowModule&& other) noexcept
|
||||
: 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;
|
||||
|
||||
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() {
|
||||
if (glCtx_) {
|
||||
SDL_GL_DeleteContext(glCtx_);
|
||||
glCtx_ = nullptr;
|
||||
}
|
||||
if (window_) {
|
||||
SDL_DestroyWindow(window_);
|
||||
window_ = nullptr;
|
||||
}
|
||||
state_ = SvcState::Shutdown;
|
||||
void WindowModule::shutdown() {
|
||||
if (glCtx_) {
|
||||
SDL_GL_DeleteContext(glCtx_);
|
||||
glCtx_ = nullptr;
|
||||
}
|
||||
if (window_) {
|
||||
SDL_DestroyWindow(window_);
|
||||
window_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSvc::create(const WindowCfg &cfg) {
|
||||
if (window_)
|
||||
bool WindowModule::create(const WindowCfg& cfg) {
|
||||
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;
|
||||
|
||||
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() {
|
||||
SDL_Event evt;
|
||||
while (SDL_PollEvent(&evt)) {
|
||||
switch (evt.type) {
|
||||
case SDL_QUIT:
|
||||
shouldClose_ = true;
|
||||
if (onClose_)
|
||||
onClose_();
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
if (evt.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
if (onResize_) {
|
||||
onResize_(evt.window.data1, evt.window.data2);
|
||||
bool WindowModule::pollEvents() {
|
||||
SDL_Event evt;
|
||||
while (SDL_PollEvent(&evt)) {
|
||||
switch (evt.type) {
|
||||
case SDL_QUIT:
|
||||
shouldClose_ = true;
|
||||
// 发送窗口关闭事件
|
||||
events::OnClose::emit();
|
||||
if (onClose_)
|
||||
onClose_();
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
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() {
|
||||
if (window_ && glCtx_) {
|
||||
SDL_GL_SwapWindow(window_);
|
||||
}
|
||||
}
|
||||
|
||||
Size WindowSvc::getSize() const {
|
||||
if (!window_)
|
||||
return Size(0, 0);
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window_, &w, &h);
|
||||
return Size(w, h);
|
||||
}
|
||||
|
||||
Vec2 WindowSvc::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 WindowSvc::setSize(int32 w, int32 h) {
|
||||
if (window_) {
|
||||
SDL_SetWindowSize(window_, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSvc::setTitle(const std::string &title) {
|
||||
if (window_) {
|
||||
SDL_SetWindowTitle(window_, title.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
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_);
|
||||
void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) {
|
||||
switch (evt.event) {
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
// 发送窗口大小改变事件
|
||||
events::OnResize::emit(evt.data1, evt.data2);
|
||||
if (onResize_) {
|
||||
onResize_(evt.data1, evt.data2);
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
// 发送窗口获得焦点事件
|
||||
events::OnFocus::emit();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
// 发送窗口失去焦点事件
|
||||
events::OnBlur::emit();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_SHOWN:
|
||||
// 发送窗口显示事件
|
||||
events::OnShow::emit();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_HIDDEN:
|
||||
// 发送窗口隐藏事件
|
||||
events::OnHide::emit();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
shouldClose_ = true;
|
||||
// 发送窗口关闭事件
|
||||
events::OnClose::emit();
|
||||
if (onClose_)
|
||||
onClose_();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSvc::isVisible() const {
|
||||
if (!window_)
|
||||
return false;
|
||||
uint32 flags = SDL_GetWindowFlags(window_);
|
||||
return (flags & SDL_WINDOW_SHOWN) != 0;
|
||||
void WindowModule::swapBuffers() {
|
||||
if (window_ && glCtx_) {
|
||||
SDL_GL_SwapWindow(window_);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
// ============================================================================
|
||||
// Random 类实现
|
||||
// ============================================================================
|
||||
|
||||
Random::Random() : floatDist_(0.0f, 1.0f) {
|
||||
// 使用当前时间作为默认种子
|
||||
randomize();
|
||||
// 使用当前时间作为默认种子
|
||||
randomize();
|
||||
}
|
||||
|
||||
Random &Random::getInstance() {
|
||||
static Random instance;
|
||||
return instance;
|
||||
Random::Random(uint32 seed) : floatDist_(0.0f, 1.0f) {
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
void Random::setSeed(uint32 seed) { generator_.seed(seed); }
|
||||
void Random::setSeed(uint32 seed) {
|
||||
generator_.seed(seed);
|
||||
}
|
||||
|
||||
void Random::randomize() {
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto time = now.time_since_epoch().count();
|
||||
generator_.seed(static_cast<uint32>(time));
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto time = now.time_since_epoch().count();
|
||||
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) {
|
||||
if (min >= max) {
|
||||
return min;
|
||||
}
|
||||
return min + floatDist_(generator_) * (max - min);
|
||||
if (min >= max) {
|
||||
return min;
|
||||
}
|
||||
return min + floatDist_(generator_) * (max - min);
|
||||
}
|
||||
|
||||
int Random::getInt(int max) {
|
||||
if (max <= 0) {
|
||||
return 0;
|
||||
}
|
||||
std::uniform_int_distribution<int> dist(0, max);
|
||||
return dist(generator_);
|
||||
if (max <= 0) {
|
||||
return 0;
|
||||
}
|
||||
std::uniform_int_distribution<int> dist(0, max);
|
||||
return dist(generator_);
|
||||
}
|
||||
|
||||
int Random::getInt(int min, int max) {
|
||||
if (min >= max) {
|
||||
return min;
|
||||
}
|
||||
std::uniform_int_distribution<int> dist(min, max);
|
||||
return dist(generator_);
|
||||
if (min >= max) {
|
||||
return min;
|
||||
}
|
||||
std::uniform_int_distribution<int> dist(min, max);
|
||||
return dist(generator_);
|
||||
}
|
||||
|
||||
bool Random::getBool() { return floatDist_(generator_) >= 0.5f; }
|
||||
bool Random::getBool() {
|
||||
return floatDist_(generator_) >= 0.5f;
|
||||
}
|
||||
|
||||
bool Random::getBool(float probability) {
|
||||
if (probability <= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
if (probability >= 1.0f) {
|
||||
return true;
|
||||
}
|
||||
return floatDist_(generator_) < probability;
|
||||
if (probability <= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
if (probability >= 1.0f) {
|
||||
return true;
|
||||
}
|
||||
return floatDist_(generator_) < probability;
|
||||
}
|
||||
|
||||
float Random::getAngle() {
|
||||
static const float TWO_PI = 6.28318530718f;
|
||||
return floatDist_(generator_) * TWO_PI;
|
||||
static const float TWO_PI = 6.28318530718f;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function define_extra2d_engine()
|
|||
target("extra2d")
|
||||
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_includedirs("include", {public = true})
|
||||
|
|
|
|||
Loading…
Reference in New Issue