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:
ChestnutYueyue 2026-02-28 20:56:11 +08:00
parent 5a3d0cd9de
commit e68ce87638
31 changed files with 2490 additions and 1604 deletions

View File

@ -2,9 +2,13 @@
#include <string> #include <string>
#include <types/base/types.h> #include <types/base/types.h>
#include <memory>
namespace extra2d { namespace extra2d {
// 前向声明
class Context;
/** /**
* @brief * @brief
*/ */
@ -24,34 +28,40 @@ struct AppConfig {
/** /**
* @brief * @brief
* *
* *
* Context
*/ */
class Application { class Application {
public: public:
/** /**
* @brief * @brief
*/ */
static Application& instance(); static std::unique_ptr<Application> create();
Application(const Application&) = delete; Application(const Application&) = delete;
Application& operator=(const Application&) = delete; Application& operator=(const Application&) = delete;
Application(Application&&) noexcept;
Application& operator=(Application&&) noexcept;
~Application();
/** /**
* @brief * @brief
*/ */
bool init(const AppConfig& config); bool init(const AppConfig& config);
/** /**
* @brief * @brief
*/ */
void shutdown(); void shutdown();
/** /**
* @brief * @brief
*/ */
void run(); void run();
/** /**
* @brief 退 * @brief 退
*/ */
@ -61,17 +71,17 @@ public:
* @brief * @brief
*/ */
void pause(); void pause();
/** /**
* @brief * @brief
*/ */
void resume(); void resume();
/** /**
* @brief * @brief
*/ */
bool isPaused() const { return paused_; } bool isPaused() const { return paused_; }
/** /**
* @brief * @brief
*/ */
@ -81,12 +91,12 @@ public:
* @brief * @brief
*/ */
float deltaTime() const { return deltaTime_; } float deltaTime() const { return deltaTime_; }
/** /**
* @brief * @brief
*/ */
float totalTime() const { return totalTime_; } float totalTime() const { return totalTime_; }
/** /**
* @brief FPS * @brief FPS
*/ */
@ -97,13 +107,19 @@ public:
*/ */
const AppConfig& getConfig() const { return config_; } const AppConfig& getConfig() const { return config_; }
/**
* @brief
*/
Context* getContext() const { return context_.get(); }
private: private:
Application() = default; Application();
~Application();
void mainLoop(); void mainLoop();
void update(); void update();
std::unique_ptr<Context> context_;
AppConfig config_; AppConfig config_;
bool initialized_ = false; bool initialized_ = false;
@ -119,6 +135,4 @@ private:
int32 currentFps_ = 0; int32 currentFps_ = 0;
}; };
#define APP extra2d::Application::instance()
} // namespace extra2d } // namespace extra2d

100
include/context/context.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -137,27 +137,47 @@ DECLARE_EVENT_1(OnMouseWheel, Engine, int32)
/** /**
* @brief * @brief
* @param touch * @param touchId ID
* @param x X
* @param y Y
*/ */
DECLARE_EVENT_1(OnTouchBegan, Engine, const TouchPoint &) DECLARE_EVENT_3(OnTouchBegin, Engine, int32, int32, int32)
/** /**
* @brief * @brief
* @param touch * @param touchId ID
* @param x X
* @param y Y
*/ */
DECLARE_EVENT_1(OnTouchMoved, Engine, const TouchPoint &) DECLARE_EVENT_3(OnTouchMove, Engine, int32, int32, int32)
/** /**
* @brief * @brief
* @param touch * @param touchId ID
* @param x X
* @param y Y
*/ */
DECLARE_EVENT_1(OnTouchEnded, Engine, const TouchPoint &) DECLARE_EVENT_3(OnTouchEnd, Engine, int32, int32, int32)
/** /**
* @brief * @brief
* @param touch * @param touchId ID
* @param x X
* @param y Y
*/ */
DECLARE_EVENT_1(OnTouchCancelled, Engine, const TouchPoint &) DECLARE_EVENT_3(OnTouchCancel, Engine, int32, int32, int32)
/**
* @brief
* @param deviceId ID
*/
DECLARE_EVENT_1(OnGamepadConnect, Engine, int32)
/**
* @brief
* @param deviceId ID
*/
DECLARE_EVENT_1(OnGamepadDisconnect, Engine, int32)
// ============================================================================ // ============================================================================
// 场景事件 // 场景事件

View File

@ -17,21 +17,24 @@
#include <types/math/vec2.h> #include <types/math/vec2.h>
#include <types/math/vec3.h> #include <types/math/vec3.h>
// Core // Context (核心上下文)
#include <core/director.h> #include <context/context.h>
#include <core/scheduler.h>
#include <core/service.h> // Module System (新架构 - 基于事件总线)
#include <module/imodule.h>
#include <module/module_registry.h>
// Plugin System (新架构 - 基于事件总线)
#include <plugin/iplugin.h>
#include <plugin/plugin_loader.h>
// Event System
#include <event/event_bus.h>
#include <event/event_bus_macros.h>
#include <event/events.h>
// Platform // Platform
#include <platform/file.h>
#include <platform/input.h>
#include <platform/sdl2.h> #include <platform/sdl2.h>
#include <platform/window.h>
// Event
#include <event/event_bus.h>
#include <event/events.h>
// Utils // Utils
#include <utils/logger.h> #include <utils/logger.h>
@ -40,6 +43,13 @@
// Application // Application
#include <app/application.h> #include <app/application.h>
// 兼容层 - 旧的头文件(将在后续版本中移除)
// #include <core/director.h> // 已废弃,使用 Context
// #include <module/module.h> // 已废弃,使用 imodule.h
// #include <module/module_manager.h> // 已废弃,使用 module_registry.h
// #include <plugin/plugin.h> // 已废弃,使用 iplugin.h
// #include <plugin/plugin_manager.h> // 已废弃,使用 plugin_loader.h
#ifdef __SWITCH__ #ifdef __SWITCH__
#include <switch.h> #include <switch.h>
#endif #endif

69
include/module/imodule.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <module/imodule.h>
#include <types/base/types.h> #include <types/base/types.h>
#include <core/service.h> #include <types/const/priority.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <functional>
namespace extra2d { namespace extra2d {
@ -25,132 +25,136 @@ struct FileData {
bool ok = false; bool ok = false;
std::vector<uint8> data; std::vector<uint8> data;
std::string error; std::string error;
operator bool() const { return ok; } operator bool() const { return ok; }
const uint8* ptr() const { return data.data(); } const uint8* ptr() const { return data.data(); }
size_t size() const { return data.size(); } size_t size() const { return data.size(); }
}; };
/** /**
* @brief * @brief
* *
* *
* Context
*/ */
class FileSvc : public IService { class FileModule : public IModule {
public: public:
/** FileModule();
* @brief ~FileModule() override;
*/
static FileSvc& inst(); // 禁止拷贝
FileModule(const FileModule&) = delete;
const char* name() const override { return "FileSvc"; } FileModule& operator=(const FileModule&) = delete;
int pri() const override { return Pri::System; }
// 允许移动
FileModule(FileModule&&) noexcept;
FileModule& operator=(FileModule&&) noexcept;
// IModule 接口实现
const char* name() const override { return "File"; }
ModuleType type() const override { return ModuleType::System; }
int priority() const override { return Pri::File; }
bool init() override; bool init() override;
void shutdown() override; void shutdown() override;
/** /**
* @brief * @brief
*/ */
bool exists(const std::string& path) const; bool exists(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
bool isDir(const std::string& path) const; bool isDir(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
FileData read(const std::string& path) const; FileData read(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
std::string readString(const std::string& path) const; std::string readString(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
bool write(const std::string& path, const void* data, size_t size) const; bool write(const std::string& path, const void* data, size_t size) const;
/** /**
* @brief * @brief
*/ */
bool writeString(const std::string& path, const std::string& content) const; bool writeString(const std::string& path, const std::string& content) const;
/** /**
* @brief * @brief
*/ */
bool append(const std::string& path, const void* data, size_t size) const; bool append(const std::string& path, const void* data, size_t size) const;
/** /**
* @brief * @brief
*/ */
bool remove(const std::string& path) const; bool remove(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
bool mkdir(const std::string& path) const; bool mkdir(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
std::vector<FileInfo> listDir(const std::string& path) const; std::vector<FileInfo> listDir(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
int64 fileSize(const std::string& path) const; int64 fileSize(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
std::string ext(const std::string& path) const; std::string ext(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
std::string fileName(const std::string& path) const; std::string fileName(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
std::string dirName(const std::string& path) const; std::string dirName(const std::string& path) const;
/** /**
* @brief * @brief
*/ */
std::string join(const std::string& a, const std::string& b) const; std::string join(const std::string& a, const std::string& b) const;
/** /**
* @brief * @brief
*/ */
std::string writableDir() const; std::string writableDir() const;
/** /**
* @brief * @brief
*/ */
void setAssetRoot(const std::string& root) { assetRoot_ = root; } void setAssetRoot(const std::string& root) { assetRoot_ = root; }
/** /**
* @brief * @brief
*/ */
const std::string& assetRoot() const { return assetRoot_; } const std::string& assetRoot() const { return assetRoot_; }
/** /**
* @brief * @brief
*/ */
std::string assetPath(const std::string& relPath) const; std::string assetPath(const std::string& relPath) const;
private: private:
FileSvc() = default;
std::string assetRoot_; std::string assetRoot_;
std::string writableDir_; std::string writableDir_;
}; };
#define FILE_SVC extra2d::FileSvc::inst()
} // namespace extra2d } // namespace extra2d

View File

@ -2,9 +2,10 @@
#include <SDL.h> #include <SDL.h>
#include <array> #include <array>
#include <core/service.h> #include <functional>
#include <module/imodule.h>
#include <types/base/types.h> #include <types/base/types.h>
#include <types/ptr/ref_counted.h> #include <types/const/priority.h>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
@ -94,47 +95,47 @@ constexpr Key RightAlt = SDL_SCANCODE_RALT;
* @brief * @brief
*/ */
enum class MouseBtn : uint8 { enum class MouseBtn : uint8 {
Left = 0, Left = 0,
Middle = 1, Middle = 1,
Right = 2, Right = 2,
X1 = 3, X1 = 3,
X2 = 4, X2 = 4,
Count = 5 Count = 5
}; };
/** /**
* @brief * @brief
*/ */
enum class GamepadBtn : uint8 { enum class GamepadBtn : uint8 {
A = 0, A = 0,
B = 1, B = 1,
X = 2, X = 2,
Y = 3, Y = 3,
Back = 4, Back = 4,
Guide = 5, Guide = 5,
Start = 6, Start = 6,
LeftStick = 7, LeftStick = 7,
RightStick = 8, RightStick = 8,
LeftShoulder = 9, LeftShoulder = 9,
RightShoulder = 10, RightShoulder = 10,
DPadUp = 11, DPadUp = 11,
DPadDown = 12, DPadDown = 12,
DPadLeft = 13, DPadLeft = 13,
DPadRight = 14, DPadRight = 14,
Count = 15 Count = 15
}; };
/** /**
* @brief * @brief
*/ */
enum class GamepadAxis : uint8 { enum class GamepadAxis : uint8 {
LeftX = 0, LeftX = 0,
LeftY = 1, LeftY = 1,
RightX = 2, RightX = 2,
RightY = 3, RightY = 3,
TriggerLeft = 4, TriggerLeft = 4,
TriggerRight = 5, TriggerRight = 5,
Count = 6 Count = 6
}; };
/** /**
@ -146,224 +147,232 @@ enum class TouchState : uint8 { None = 0, Began, Moved, Ended, Cancelled };
* @brief * @brief
*/ */
struct TouchPoint { struct TouchPoint {
int64 id = 0; int64 id = 0;
float x = 0.0f; float x = 0.0f;
float y = 0.0f; float y = 0.0f;
float prevX = 0.0f; float prevX = 0.0f;
float prevY = 0.0f; float prevY = 0.0f;
float deltaX = 0.0f; float deltaX = 0.0f;
float deltaY = 0.0f; float deltaY = 0.0f;
TouchState state = TouchState::None; TouchState state = TouchState::None;
float pressure = 1.0f; float pressure = 1.0f;
}; };
/** /**
* @brief * @brief
*/ */
using KeyCb = Fn<void(Key)>; using KeyCb = std::function<void(Key)>;
using MouseBtnCb = Fn<void(MouseBtn, int32 x, int32 y)>; using MouseBtnCb = std::function<void(MouseBtn, int32 x, int32 y)>;
using TouchCb = Fn<void(const TouchPoint &)>; using TouchCb = std::function<void(const TouchPoint&)>;
/** /**
* @brief * @brief
* *
* *
* Context
*/ */
class InputSvc : public IService { class InputModule : public IModule {
public: public:
/** InputModule();
* @brief ~InputModule() override;
*/
static InputSvc &inst();
const char *name() const override { return "InputSvc"; } // 禁止拷贝
int pri() const override { return Pri::Input; } InputModule(const InputModule&) = delete;
InputModule& operator=(const InputModule&) = delete;
bool init() override; // 允许移动
void shutdown() override; InputModule(InputModule&&) noexcept;
void update(float dt) override; InputModule& operator=(InputModule&&) noexcept;
// ========== 键盘 ========== // IModule 接口实现
const char* name() const override { return "Input"; }
ModuleType type() const override { return ModuleType::System; }
int priority() const override { return Pri::Input; }
bool init() override;
void shutdown() override;
/** /**
* @brief * @brief
*/ */
bool isKeyDown(Key key) const; void processEvent(const SDL_Event& evt);
/** /**
* @brief * @brief
*/ */
bool isKeyPressed(Key key) const; void update();
/** // ========== 键盘 ==========
* @brief
*/
bool isKeyReleased(Key key) const;
// ========== 鼠标 ========== /**
* @brief
*/
bool isKeyDown(Key key) const;
/** /**
* @brief * @brief
*/ */
void getMousePos(int32 &x, int32 &y) const; bool isKeyPressed(Key key) const;
/** /**
* @brief * @brief
*/ */
void getMousePos(float &x, float &y) const; bool isKeyReleased(Key key) const;
/** // ========== 鼠标 ==========
* @brief
*/
bool isMouseBtnDown(MouseBtn btn) const;
/** /**
* @brief * @brief
*/ */
bool isMouseBtnPressed(MouseBtn btn) const; void getMousePos(int32& x, int32& y) const;
/** /**
* @brief * @brief
*/ */
bool isMouseBtnReleased(MouseBtn btn) const; void getMousePos(float& x, float& y) const;
/** /**
* @brief * @brief
*/ */
int32 getMouseWheel() const; bool isMouseBtnDown(MouseBtn btn) const;
// ========== 触摸 ========== /**
* @brief
*/
bool isMouseBtnPressed(MouseBtn btn) const;
/** /**
* @brief * @brief
*/ */
int32 touchCount() const; bool isMouseBtnReleased(MouseBtn btn) const;
/** /**
* @brief * @brief
* @param idx */
* @return nullptr int32 getMouseWheel() const;
*/
const TouchPoint *getTouch(int32 idx) const;
/** // ========== 触摸 ==========
* @brief ID
*/
const TouchPoint *getTouchById(int64 id) const;
/** /**
* @brief * @brief
*/ */
bool hasTouch() const { return touchCount() > 0; } int32 touchCount() const;
/** /**
* @brief * @brief
*/ * @param idx
const std::vector<TouchPoint> &getTouches() const { return activeTouches_; } * @return nullptr
*/
const TouchPoint* getTouch(int32 idx) const;
// ========== 游戏手柄 ========== /**
* @brief ID
*/
const TouchPoint* getTouchById(int64 id) const;
/** /**
* @brief * @brief
*/ */
int32 gamepadCount() const; bool hasTouch() const { return touchCount() > 0; }
/** /**
* @brief * @brief
*/ */
bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const; const std::vector<TouchPoint>& getTouches() const { return activeTouches_; }
/** // ========== 游戏手柄 ==========
* @brief
*/
bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const;
/** /**
* @brief (-1.0 1.0) * @brief
*/ */
float getGamepadAxis(int32 idx, GamepadAxis axis) const; int32 gamepadCount() const;
// ========== 回调设置 ========== /**
* @brief
*/
bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const;
/** /**
* @brief * @brief
*/ */
void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); } bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const;
/** /**
* @brief * @brief (-1.0 1.0)
*/ */
void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); } float getGamepadAxis(int32 idx, GamepadAxis axis) const;
/** // ========== 回调设置 ==========
* @brief
*/
void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); }
/** /**
* @brief * @brief
*/ */
void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); } void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); }
/** /**
* @brief * @brief
*/ */
void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); } void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); }
/** /**
* @brief * @brief
*/ */
void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); } void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); }
/** /**
* @brief * @brief
*/ */
void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); } void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); }
/**
* @brief
*/
void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); }
/**
* @brief
*/
void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); }
/**
* @brief
*/
void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); }
private: private:
InputSvc() = default; static constexpr int32 KEY_COUNT = SDL_NUM_SCANCODES;
static constexpr int32 MAX_GAMEPADS = 4;
static constexpr int32 MAX_TOUCHES = 10;
static constexpr int32 KEY_COUNT = SDL_NUM_SCANCODES; std::array<uint8, KEY_COUNT> keyState_{};
static constexpr int32 MAX_GAMEPADS = 4; std::array<uint8, KEY_COUNT> keyPrev_{};
static constexpr int32 MAX_TOUCHES = 10;
std::array<uint8, KEY_COUNT> keyState_{}; int32 mouseX_ = 0;
std::array<uint8, KEY_COUNT> keyPrev_{}; int32 mouseY_ = 0;
int32 mouseWheel_ = 0;
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mouseState_{};
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mousePrev_{};
int32 mouseX_ = 0; std::vector<TouchPoint> activeTouches_;
int32 mouseY_ = 0; std::vector<TouchPoint> endedTouches_;
int32 mouseWheel_ = 0;
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mouseState_{};
std::array<uint8, static_cast<size_t>(MouseBtn::Count)> mousePrev_{};
std::vector<TouchPoint> activeTouches_; SDL_GameController* gamepads_[MAX_GAMEPADS] = {};
std::vector<TouchPoint> endedTouches_; std::array<uint8, static_cast<size_t>(GamepadBtn::Count)> padState_[MAX_GAMEPADS];
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)> padPrev_[MAX_GAMEPADS];
SDL_GameController *gamepads_[MAX_GAMEPADS] = {}; KeyCb onKeyDown_;
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)> KeyCb onKeyUp_;
padState_[MAX_GAMEPADS]; MouseBtnCb onMouseDown_;
std::array<uint8, static_cast<size_t>(GamepadBtn::Count)> MouseBtnCb onMouseUp_;
padPrev_[MAX_GAMEPADS]; TouchCb onTouchBegan_;
TouchCb onTouchMoved_;
TouchCb onTouchEnded_;
KeyCb onKeyDown_; void openGamepad(int32 idx);
KeyCb onKeyUp_; void closeGamepad(int32 idx);
MouseBtnCb onMouseDown_;
MouseBtnCb onMouseUp_;
TouchCb onTouchBegan_;
TouchCb onTouchMoved_;
TouchCb onTouchEnded_;
void processEvent(const SDL_Event &evt); void processTouchDown(const SDL_TouchFingerEvent& evt);
void openGamepad(int32 idx); void processTouchUp(const SDL_TouchFingerEvent& evt);
void closeGamepad(int32 idx); void processTouchMotion(const SDL_TouchFingerEvent& evt);
void processTouchDown(const SDL_TouchFingerEvent &evt);
void processTouchUp(const SDL_TouchFingerEvent &evt);
void processTouchMotion(const SDL_TouchFingerEvent &evt);
friend class WindowSvc;
}; };
#define INPUT_SVC extra2d::InputSvc::inst()
} // namespace extra2d } // namespace extra2d

View File

@ -1,12 +1,13 @@
#pragma once #pragma once
#include <SDL.h> #include <SDL.h>
#include <core/service.h> #include <functional>
#include <module/imodule.h>
#include <string> #include <string>
#include <types/base/types.h> #include <types/base/types.h>
#include <types/const/priority.h>
#include <types/math/size.h> #include <types/math/size.h>
#include <types/math/vec2.h> #include <types/math/vec2.h>
#include <types/ptr/ref_counted.h>
namespace extra2d { namespace extra2d {
@ -14,148 +15,154 @@ namespace extra2d {
* @brief * @brief
*/ */
struct WindowCfg { struct WindowCfg {
std::string title = "Extra2D"; std::string title = "Extra2D";
int32 width = 1280; int32 width = 1280;
int32 height = 720; int32 height = 720;
bool fullscreen = false; bool fullscreen = false;
bool resizable = true; bool resizable = true;
bool vsync = true; bool vsync = true;
int32 glMajor = 3; int32 glMajor = 3;
int32 glMinor = 3; int32 glMinor = 3;
}; };
/** /**
* @brief * @brief
*/ */
using ResizeCb = Fn<void(int32 w, int32 h)>; using ResizeCb = std::function<void(int32 w, int32 h)>;
using CloseCb = Fn<void()>; using CloseCb = std::function<void()>;
/** /**
* @brief * @brief
* *
* SDL2 OpenGL * SDL2 OpenGL
* Context
*/ */
class WindowSvc : public IService { class WindowModule : public IModule {
public: public:
/** WindowModule();
* @brief ~WindowModule() override;
*/
static WindowSvc &inst();
const char *name() const override { return "WindowSvc"; } // 禁止拷贝
int pri() const override { return Pri::System; } WindowModule(const WindowModule&) = delete;
WindowModule& operator=(const WindowModule&) = delete;
bool init() override; // 允许移动
void shutdown() override; WindowModule(WindowModule&&) noexcept;
WindowModule& operator=(WindowModule&&) noexcept;
/** // IModule 接口实现
* @brief 使 const char* name() const override { return "Window"; }
*/ ModuleType type() const override { return ModuleType::System; }
bool create(const WindowCfg &cfg); int priority() const override { return Pri::Window; }
bool init() override;
void shutdown() override;
/** /**
* @brief * @brief 使
* @return true false 退 */
*/ bool create(const WindowCfg& cfg);
bool pollEvents();
/** /**
* @brief * @brief
*/ * @return true false 退
void swapBuffers(); */
bool pollEvents();
/** /**
* @brief SDL * @brief
*/ */
SDL_Window *handle() const { return window_; } void swapBuffers();
/** /**
* @brief OpenGL * @brief SDL
*/ */
SDL_GLContext glContext() const { return glCtx_; } SDL_Window* handle() const { return window_; }
/** /**
* @brief * @brief OpenGL
*/ */
Size getSize() const; SDL_GLContext glContext() const { return glCtx_; }
/** /**
* @brief * @brief
*/ */
Vec2 getPosition() const; Size getSize() const;
/** /**
* @brief * @brief
*/ */
void setSize(int32 w, int32 h); Vec2 getPosition() const;
/** /**
* @brief * @brief
*/ */
void setTitle(const std::string &title); void setSize(int32 w, int32 h);
/** /**
* @brief * @brief
*/ */
void setFullscreen(bool fullscreen); void setTitle(const std::string& title);
/** /**
* @brief * @brief
*/ */
bool isFullscreen() const; void setFullscreen(bool fullscreen);
/** /**
* @brief * @brief
*/ */
void setVsync(bool vsync); bool isFullscreen() const;
/** /**
* @brief * @brief
*/ */
bool isVsync() const; void setVsync(bool vsync);
/** /**
* @brief / * @brief
*/ */
void setVisible(bool visible); bool isVsync() const;
/** /**
* @brief * @brief /
*/ */
bool isVisible() const; void setVisible(bool visible);
/** /**
* @brief * @brief
*/ */
void setOnClose(CloseCb cb) { onClose_ = std::move(cb); } bool isVisible() const;
/** /**
* @brief * @brief
*/ */
void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); } void setOnClose(CloseCb cb) { onClose_ = std::move(cb); }
/** /**
* @brief * @brief
*/ */
void requestClose() { shouldClose_ = true; } void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); }
/** /**
* @brief * @brief
*/ */
bool shouldClose() const { return shouldClose_; } void requestClose() { shouldClose_ = true; }
/**
* @brief
*/
bool shouldClose() const { return shouldClose_; }
private: private:
WindowSvc() = default; void handleWindowEvent(const SDL_WindowEvent& evt);
SDL_Window *window_ = nullptr; SDL_Window* window_ = nullptr;
SDL_GLContext glCtx_ = nullptr; SDL_GLContext glCtx_ = nullptr;
bool shouldClose_ = false; bool shouldClose_ = false;
bool vsync_ = true; bool vsync_ = true;
CloseCb onClose_; CloseCb onClose_;
ResizeCb onResize_; ResizeCb onResize_;
}; };
#define WINDOW extra2d::WindowSvc::inst()
} // namespace extra2d } // namespace extra2d

92
include/plugin/iplugin.h Normal file
View File

@ -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; \
}

View File

@ -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

View File

@ -5,10 +5,17 @@
namespace extra2d { namespace extra2d {
/**
* @brief
*
*
*/
namespace Pri { namespace Pri {
constexpr int32 Min = INT32_MIN; constexpr int32 Min = INT32_MIN;
constexpr int32 System = -1000; constexpr int32 System = -1000;
constexpr int32 Input = -100; constexpr int32 Window = -900; // 窗口模块(最早初始化)
constexpr int32 Input = -800; // 输入模块
constexpr int32 File = -700; // 文件模块
constexpr int32 Default = 0; constexpr int32 Default = 0;
constexpr int32 Logic = 100; constexpr int32 Logic = 100;
constexpr int32 Anim = 200; constexpr int32 Anim = 200;

View File

@ -25,6 +25,7 @@ public:
Ptr<T>& operator=(T* p) { reset(p); return *this; } Ptr<T>& operator=(T* p) { reset(p); return *this; }
Ptr<T>& operator=(const Ptr<T>& r) { return *this = r.p_; } Ptr<T>& operator=(const Ptr<T>& r) { return *this = r.p_; }
template <typename U> Ptr<T>& operator=(const Ptr<U>& r) { return *this = r.get(); }
Ptr<T>& operator=(Ptr<T>&& r) noexcept { Ptr<T>(std::move(r)).swap(*this); return *this; } Ptr<T>& operator=(Ptr<T>&& r) noexcept { Ptr<T>(std::move(r)).swap(*this); return *this; }
bool operator==(std::nullptr_t) const { return p_ == nullptr; } bool operator==(std::nullptr_t) const { return p_ == nullptr; }

View File

@ -3,84 +3,75 @@
#include <random> #include <random>
#include <types/base/types.h> #include <types/base/types.h>
namespace extra2d { namespace extra2d {
// ============================================================================ // ============================================================================
// Random 类 - 随机数生成器 // Random 类 - 随机数生成器
// 非单例设计,可以创建多个独立的随机数生成器实例
// ============================================================================ // ============================================================================
class Random { class Random {
public: public:
/// 获取单例实例 /// 默认构造函数(使用随机种子)
static Random &getInstance(); Random();
/// 设置随机种子 /// 使用指定种子构造
void setSeed(uint32 seed); explicit Random(uint32 seed);
/// 使用当前时间作为种子 /// 设置随机种子
void randomize(); void setSeed(uint32 seed);
/// 获取 [0, 1) 范围内的随机浮点数 /// 使用当前时间作为种子
float getFloat(); void randomize();
/// 获取 [min, max] 范围内的随机浮点数 /// 获取 [0, 1) 范围内的随机浮点数
float getFloat(float min, float max); float getFloat();
/// 获取 [0, max] 范围内的随机整 /// 获取 [min, max] 范围内的随机浮点
int getInt(int max); float getFloat(float min, float max);
/// 获取 [min, max] 范围内的随机整数 /// 获取 [0, max] 范围内的随机整数
int getInt(int min, int max); int getInt(int max);
/// 获取随机布尔值 /// 获取 [min, max] 范围内的随机整数
bool getBool(); int getInt(int min, int max);
/// 获取随机布尔值(带概率) /// 获取随机布尔值
bool getBool(float probability); bool getBool();
/// 获取指定范围内的随机角度(弧度 /// 获取随机布尔值(带概率
float getAngle(); bool getBool(float probability);
/// 获取 [-1, 1] 范围内的随机数(用于方向) /// 获取指定范围内的随机角度(弧度)
float getSigned(); float getAngle();
/// 获取 [-1, 1] 范围内的随机数(用于方向)
float getSigned();
private: private:
Random(); std::mt19937 generator_;
~Random() = default; std::uniform_real_distribution<float> floatDist_;
Random(const Random &) = delete;
Random &operator=(const Random &) = delete;
std::mt19937 generator_;
std::uniform_real_distribution<float> floatDist_;
}; };
// ============================================================================ // ============================================================================
// 便捷函数 // 便捷函数 - 使用全局默认随机数生成器
// ============================================================================ // ============================================================================
/// 获取 [0, 1) 范围内的随机浮点数 /// 获取 [0, 1) 范围内的随机浮点数
inline float randomFloat() { return Random::getInstance().getFloat(); } float randomFloat();
/// 获取 [min, max] 范围内的随机浮点数 /// 获取 [min, max] 范围内的随机浮点数
inline float randomFloat(float min, float max) { float randomFloat(float min, float max);
return Random::getInstance().getFloat(min, max);
}
/// 获取 [0, max] 范围内的随机整数 /// 获取 [0, max] 范围内的随机整数
inline int randomInt(int max) { return Random::getInstance().getInt(max); } int randomInt(int max);
/// 获取 [min, max] 范围内的随机整数 /// 获取 [min, max] 范围内的随机整数
inline int randomInt(int min, int max) { int randomInt(int min, int max);
return Random::getInstance().getInt(min, max);
}
/// 获取随机布尔值 /// 获取随机布尔值
inline bool randomBool() { return Random::getInstance().getBool(); } bool randomBool();
/// 获取随机布尔值(带概率) /// 获取随机布尔值(带概率)
inline bool randomBool(float probability) { bool randomBool(float probability);
return Random::getInstance().getBool(probability);
}
} // namespace extra2d } // namespace extra2d

View File

@ -1,14 +1,9 @@
#include <app/application.h> #include <app/application.h>
#include <core/director.h> #include <context/context.h>
#include <core/service.h>
#include <event/events.h> #include <event/events.h>
#include <platform/file.h>
#include <platform/input.h>
#include <platform/sdl2.h> #include <platform/sdl2.h>
#include <platform/window.h>
#include <utils/logger.h> #include <utils/logger.h>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
@ -35,12 +30,18 @@ static double getTimeSeconds() {
#endif #endif
} }
Application &Application::instance() { std::unique_ptr<Application> Application::create() {
static Application instance; return std::unique_ptr<Application>(new Application());
return instance;
} }
Application::~Application() { shutdown(); } Application::Application() = default;
Application::~Application() {
shutdown();
}
Application::Application(Application&&) noexcept = default;
Application& Application::operator=(Application&&) noexcept = default;
bool Application::init(const AppConfig &config) { bool Application::init(const AppConfig &config) {
if (initialized_) { if (initialized_) {
@ -70,38 +71,19 @@ bool Application::init(const AppConfig &config) {
return false; return false;
} }
SVC_MGR.reg(&WINDOW); // 创建引擎上下文
SVC_MGR.reg(&INPUT_SVC); context_ = Context::create();
SVC_MGR.reg(&FILE_SVC); if (!context_) {
E2D_LOG_ERROR("Failed to create context");
if (!SVC_MGR.initAll()) {
E2D_LOG_ERROR("Failed to initialize services");
return false; return false;
} }
if (!WINDOW.create([&] { // 初始化引擎
WindowCfg cfg; if (!context_->init()) {
cfg.title = config_.title; E2D_LOG_ERROR("Failed to initialize context");
cfg.width = config_.width;
cfg.height = config_.height;
cfg.fullscreen = config_.fullscreen;
cfg.resizable = config_.resizable;
cfg.vsync = config_.vsync;
cfg.glMajor = config_.glMajor;
cfg.glMinor = config_.glMinor;
return cfg;
}())) {
E2D_LOG_ERROR("Failed to create window");
return false; return false;
} }
if (!DIRECTOR.init()) {
E2D_LOG_ERROR("Failed to initialize Director");
return false;
}
WINDOW.setOnClose([this]() { quit(); });
initialized_ = true; initialized_ = true;
running_ = true; running_ = true;
@ -121,8 +103,9 @@ void Application::shutdown() {
E2D_LOG_INFO("Shutting down application..."); E2D_LOG_INFO("Shutting down application...");
DIRECTOR.shutdown(); // 关闭上下文(会自动关闭模块和插件)
SVC_MGR.shutdownAll(); context_.reset();
Sdl2::shutdown(); Sdl2::shutdown();
#ifdef __SWITCH__ #ifdef __SWITCH__
@ -144,7 +127,7 @@ void Application::run() {
lastFrameTime_ = getTimeSeconds(); lastFrameTime_ = getTimeSeconds();
while (running_ && !WINDOW.shouldClose()) { while (running_) {
mainLoop(); mainLoop();
} }
} }
@ -152,14 +135,11 @@ void Application::run() {
void Application::quit() { void Application::quit() {
shouldQuit_ = true; shouldQuit_ = true;
running_ = false; running_ = false;
WINDOW.requestClose();
} }
void Application::pause() { void Application::pause() {
if (!paused_) { if (!paused_) {
paused_ = true; paused_ = true;
SVC_MGR.pauseAll();
DIRECTOR.pause();
events::OnPause::emit(); events::OnPause::emit();
E2D_LOG_INFO("Application paused"); E2D_LOG_INFO("Application paused");
} }
@ -168,20 +148,13 @@ void Application::pause() {
void Application::resume() { void Application::resume() {
if (paused_) { if (paused_) {
paused_ = false; paused_ = false;
SVC_MGR.resumeAll();
DIRECTOR.resume();
lastFrameTime_ = getTimeSeconds();
events::OnResume::emit(); events::OnResume::emit();
lastFrameTime_ = getTimeSeconds();
E2D_LOG_INFO("Application resumed"); E2D_LOG_INFO("Application resumed");
} }
} }
void Application::mainLoop() { void Application::mainLoop() {
if (!WINDOW.pollEvents()) {
running_ = false;
return;
}
double currentTime = getTimeSeconds(); double currentTime = getTimeSeconds();
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_); deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
lastFrameTime_ = currentTime; lastFrameTime_ = currentTime;
@ -200,8 +173,7 @@ void Application::mainLoop() {
update(); update();
} }
WINDOW.swapBuffers(); // 帧率限制
if (!config_.vsync && config_.fpsLimit > 0) { if (!config_.vsync && config_.fpsLimit > 0) {
double frameEndTime = getTimeSeconds(); double frameEndTime = getTimeSeconds();
double frameTime = frameEndTime - currentTime; double frameTime = frameEndTime - currentTime;
@ -214,9 +186,10 @@ void Application::mainLoop() {
} }
void Application::update() { void Application::update() {
SVC_MGR.updateAll(deltaTime_); // 通过上下文更新引擎
events::OnUpdate::emit(deltaTime_); if (context_) {
DIRECTOR.mainLoop(deltaTime_); context_->tick(deltaTime_);
}
} }
} // namespace extra2d } // namespace extra2d

106
src/context/context.cpp Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

221
src/module/timer_module.cpp Normal file
View File

@ -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

View File

@ -16,94 +16,92 @@
namespace extra2d { namespace extra2d {
FileSvc& FileSvc::inst() { FileModule::FileModule() = default;
static FileSvc instance;
return instance;
}
bool FileSvc::init() { FileModule::~FileModule() = default;
if (isInited()) return true;
FileModule::FileModule(FileModule&&) noexcept = default;
FileModule& FileModule::operator=(FileModule&&) noexcept = default;
bool FileModule::init() {
writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D"); writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D");
if (writableDir_.empty()) { if (writableDir_.empty()) {
writableDir_ = "./"; writableDir_ = "./";
} }
state_ = SvcState::Inited;
return true; return true;
} }
void FileSvc::shutdown() { void FileModule::shutdown() {
state_ = SvcState::Shutdown; // 清理工作
} }
bool FileSvc::exists(const std::string& path) const { bool FileModule::exists(const std::string& path) const {
struct stat st; struct stat st;
return stat(path.c_str(), &st) == 0; return stat(path.c_str(), &st) == 0;
} }
bool FileSvc::isDir(const std::string& path) const { bool FileModule::isDir(const std::string& path) const {
struct stat st; struct stat st;
if (stat(path.c_str(), &st) != 0) return false; if (stat(path.c_str(), &st) != 0) return false;
return (st.st_mode & S_IFDIR) != 0; return (st.st_mode & S_IFDIR) != 0;
} }
FileData FileSvc::read(const std::string& path) const { FileData FileModule::read(const std::string& path) const {
FileData result; FileData result;
std::ifstream file(path, std::ios::binary | std::ios::ate); std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open()) { if (!file.is_open()) {
result.error = "Cannot open file: " + path; result.error = "Cannot open file: " + path;
return result; return result;
} }
std::streamsize size = file.tellg(); std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg); file.seekg(0, std::ios::beg);
result.data.resize(static_cast<size_t>(size)); result.data.resize(static_cast<size_t>(size));
if (!file.read(reinterpret_cast<char*>(result.data.data()), size)) { if (!file.read(reinterpret_cast<char*>(result.data.data()), size)) {
result.error = "Failed to read file: " + path; result.error = "Failed to read file: " + path;
return result; return result;
} }
result.ok = true; result.ok = true;
return result; return result;
} }
std::string FileSvc::readString(const std::string& path) const { std::string FileModule::readString(const std::string& path) const {
std::ifstream file(path); std::ifstream file(path);
if (!file.is_open()) return ""; if (!file.is_open()) return "";
std::stringstream buffer; std::stringstream buffer;
buffer << file.rdbuf(); buffer << file.rdbuf();
return buffer.str(); return buffer.str();
} }
bool FileSvc::write(const std::string& path, const void* data, size_t size) const { bool FileModule::write(const std::string& path, const void* data, size_t size) const {
std::ofstream file(path, std::ios::binary); std::ofstream file(path, std::ios::binary);
if (!file.is_open()) return false; if (!file.is_open()) return false;
file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size)); file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size));
return file.good(); return file.good();
} }
bool FileSvc::writeString(const std::string& path, const std::string& content) const { bool FileModule::writeString(const std::string& path, const std::string& content) const {
return write(path, content.data(), content.size()); return write(path, content.data(), content.size());
} }
bool FileSvc::append(const std::string& path, const void* data, size_t size) const { bool FileModule::append(const std::string& path, const void* data, size_t size) const {
std::ofstream file(path, std::ios::binary | std::ios::app); std::ofstream file(path, std::ios::binary | std::ios::app);
if (!file.is_open()) return false; if (!file.is_open()) return false;
file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size)); file.write(static_cast<const char*>(data), static_cast<std::streamsize>(size));
return file.good(); return file.good();
} }
bool FileSvc::remove(const std::string& path) const { bool FileModule::remove(const std::string& path) const {
return std::remove(path.c_str()) == 0; return std::remove(path.c_str()) == 0;
} }
bool FileSvc::mkdir(const std::string& path) const { bool FileModule::mkdir(const std::string& path) const {
#ifdef _WIN32 #ifdef _WIN32
return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST; return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST;
#else #else
@ -111,41 +109,41 @@ bool FileSvc::mkdir(const std::string& path) const {
#endif #endif
} }
std::vector<FileInfo> FileSvc::listDir(const std::string& path) const { std::vector<FileInfo> FileModule::listDir(const std::string& path) const {
std::vector<FileInfo> result; std::vector<FileInfo> result;
#ifdef _WIN32 #ifdef _WIN32
WIN32_FIND_DATAA findData; WIN32_FIND_DATAA findData;
std::string searchPath = path + "\\*"; std::string searchPath = path + "\\*";
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData); HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) return result; if (hFind == INVALID_HANDLE_VALUE) return result;
do { do {
std::string name = findData.cFileName; std::string name = findData.cFileName;
if (name == "." || name == "..") continue; if (name == "." || name == "..") continue;
FileInfo info; FileInfo info;
info.name = name; info.name = name;
info.path = join(path, name); info.path = join(path, name);
info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
if (!info.isDir) { if (!info.isDir) {
info.size = static_cast<int64>(findData.nFileSizeLow) | info.size = static_cast<int64>(findData.nFileSizeLow) |
(static_cast<int64>(findData.nFileSizeHigh) << 32); (static_cast<int64>(findData.nFileSizeHigh) << 32);
} }
result.push_back(info); result.push_back(info);
} while (FindNextFileA(hFind, &findData)); } while (FindNextFileA(hFind, &findData));
FindClose(hFind); FindClose(hFind);
#else #else
DIR* dir = opendir(path.c_str()); DIR* dir = opendir(path.c_str());
if (!dir) return result; if (!dir) return result;
struct dirent* entry; struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) { while ((entry = readdir(dir)) != nullptr) {
std::string name = entry->d_name; std::string name = entry->d_name;
if (name == "." || name == "..") continue; if (name == "." || name == "..") continue;
FileInfo info; FileInfo info;
info.name = name; info.name = name;
info.path = join(path, name); info.path = join(path, name);
@ -155,46 +153,46 @@ std::vector<FileInfo> FileSvc::listDir(const std::string& path) const {
} }
result.push_back(info); result.push_back(info);
} }
closedir(dir); closedir(dir);
#endif #endif
return result; return result;
} }
int64 FileSvc::fileSize(const std::string& path) const { int64 FileModule::fileSize(const std::string& path) const {
struct stat st; struct stat st;
if (stat(path.c_str(), &st) != 0) return -1; if (stat(path.c_str(), &st) != 0) return -1;
return static_cast<int64>(st.st_size); return static_cast<int64>(st.st_size);
} }
std::string FileSvc::ext(const std::string& path) const { std::string FileModule::ext(const std::string& path) const {
size_t pos = path.find_last_of('.'); size_t pos = path.find_last_of('.');
if (pos == std::string::npos || pos == 0) return ""; if (pos == std::string::npos || pos == 0) return "";
size_t lastSep = path.find_last_of("/\\"); size_t lastSep = path.find_last_of("/\\");
if (lastSep != std::string::npos && pos < lastSep) return ""; if (lastSep != std::string::npos && pos < lastSep) return "";
return path.substr(pos + 1); return path.substr(pos + 1);
} }
std::string FileSvc::fileName(const std::string& path) const { std::string FileModule::fileName(const std::string& path) const {
size_t pos = path.find_last_of("/\\"); size_t pos = path.find_last_of("/\\");
if (pos == std::string::npos) return path; if (pos == std::string::npos) return path;
return path.substr(pos + 1); return path.substr(pos + 1);
} }
std::string FileSvc::dirName(const std::string& path) const { std::string FileModule::dirName(const std::string& path) const {
size_t pos = path.find_last_of("/\\"); size_t pos = path.find_last_of("/\\");
if (pos == std::string::npos) return "."; if (pos == std::string::npos) return ".";
if (pos == 0) return "/"; if (pos == 0) return "/";
return path.substr(0, pos); return path.substr(0, pos);
} }
std::string FileSvc::join(const std::string& a, const std::string& b) const { std::string FileModule::join(const std::string& a, const std::string& b) const {
if (a.empty()) return b; if (a.empty()) return b;
if (b.empty()) return a; if (b.empty()) return a;
char last = a.back(); char last = a.back();
if (last == '/' || last == '\\') { if (last == '/' || last == '\\') {
return a + b; return a + b;
@ -202,11 +200,11 @@ std::string FileSvc::join(const std::string& a, const std::string& b) const {
return a + "/" + b; return a + "/" + b;
} }
std::string FileSvc::writableDir() const { std::string FileModule::writableDir() const {
return writableDir_; return writableDir_;
} }
std::string FileSvc::assetPath(const std::string& relPath) const { std::string FileModule::assetPath(const std::string& relPath) const {
if (assetRoot_.empty()) return relPath; if (assetRoot_.empty()) return relPath;
return join(assetRoot_, relPath); return join(assetRoot_, relPath);
} }

View File

@ -1,76 +1,131 @@
#include <platform/input.h> #include <platform/input.h>
#include <platform/window.h> #include <event/events.h>
#include <SDL.h> #include <SDL.h>
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
namespace extra2d { namespace extra2d {
InputSvc& InputSvc::inst() { InputModule::InputModule() = default;
static InputSvc instance;
return instance; InputModule::~InputModule() {
shutdown();
} }
bool InputSvc::init() { InputModule::InputModule(InputModule&& other) noexcept
if (isInited()) return true; : mouseX_(other.mouseX_)
, mouseY_(other.mouseY_)
, mouseWheel_(other.mouseWheel_)
, activeTouches_(std::move(other.activeTouches_))
, endedTouches_(std::move(other.endedTouches_))
, onKeyDown_(std::move(other.onKeyDown_))
, onKeyUp_(std::move(other.onKeyUp_))
, onMouseDown_(std::move(other.onMouseDown_))
, onMouseUp_(std::move(other.onMouseUp_))
, onTouchBegan_(std::move(other.onTouchBegan_))
, onTouchMoved_(std::move(other.onTouchMoved_))
, onTouchEnded_(std::move(other.onTouchEnded_)) {
std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT);
std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT);
std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_));
std::memcpy(padState_, other.padState_, sizeof(padState_));
std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_));
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
other.gamepads_[i] = nullptr;
}
}
InputModule& InputModule::operator=(InputModule&& other) noexcept {
if (this != &other) {
shutdown();
mouseX_ = other.mouseX_;
mouseY_ = other.mouseY_;
mouseWheel_ = other.mouseWheel_;
activeTouches_ = std::move(other.activeTouches_);
endedTouches_ = std::move(other.endedTouches_);
onKeyDown_ = std::move(other.onKeyDown_);
onKeyUp_ = std::move(other.onKeyUp_);
onMouseDown_ = std::move(other.onMouseDown_);
onMouseUp_ = std::move(other.onMouseUp_);
onTouchBegan_ = std::move(other.onTouchBegan_);
onTouchMoved_ = std::move(other.onTouchMoved_);
onTouchEnded_ = std::move(other.onTouchEnded_);
std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT);
std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT);
std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast<size_t>(MouseBtn::Count));
std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_));
std::memcpy(padState_, other.padState_, sizeof(padState_));
std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_));
for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
other.gamepads_[i] = nullptr;
}
}
return *this;
}
bool InputModule::init() {
std::memset(keyState_.data(), 0, KEY_COUNT); std::memset(keyState_.data(), 0, KEY_COUNT);
std::memset(keyPrev_.data(), 0, KEY_COUNT); std::memset(keyPrev_.data(), 0, KEY_COUNT);
std::memset(mouseState_.data(), 0, static_cast<size_t>(MouseBtn::Count)); std::memset(mouseState_.data(), 0, static_cast<size_t>(MouseBtn::Count));
std::memset(mousePrev_.data(), 0, static_cast<size_t>(MouseBtn::Count)); std::memset(mousePrev_.data(), 0, static_cast<size_t>(MouseBtn::Count));
for (int32 i = 0; i < MAX_GAMEPADS; ++i) { for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
gamepads_[i] = nullptr; gamepads_[i] = nullptr;
std::memset(padState_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count)); std::memset(padState_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count));
std::memset(padPrev_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count)); std::memset(padPrev_[i].data(), 0, static_cast<size_t>(GamepadBtn::Count));
} }
activeTouches_.clear(); activeTouches_.clear();
activeTouches_.reserve(MAX_TOUCHES); activeTouches_.reserve(MAX_TOUCHES);
endedTouches_.clear(); endedTouches_.clear();
int32 numJoysticks = SDL_NumJoysticks(); int32 numJoysticks = SDL_NumJoysticks();
for (int32 i = 0; i < numJoysticks && i < MAX_GAMEPADS; ++i) { for (int32 i = 0; i < numJoysticks && i < MAX_GAMEPADS; ++i) {
if (SDL_IsGameController(i)) { if (SDL_IsGameController(i)) {
openGamepad(i); openGamepad(i);
} }
} }
state_ = SvcState::Inited;
return true; return true;
} }
void InputSvc::shutdown() { void InputModule::shutdown() {
for (int32 i = 0; i < MAX_GAMEPADS; ++i) { for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
closeGamepad(i); closeGamepad(i);
} }
activeTouches_.clear(); activeTouches_.clear();
endedTouches_.clear(); endedTouches_.clear();
state_ = SvcState::Shutdown;
} }
void InputSvc::update(float dt) { void InputModule::update() {
std::memcpy(keyPrev_.data(), keyState_.data(), KEY_COUNT); std::memcpy(keyPrev_.data(), keyState_.data(), KEY_COUNT);
std::memcpy(mousePrev_.data(), mouseState_.data(), static_cast<size_t>(MouseBtn::Count)); std::memcpy(mousePrev_.data(), mouseState_.data(), static_cast<size_t>(MouseBtn::Count));
for (int32 i = 0; i < MAX_GAMEPADS; ++i) { for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
if (gamepads_[i]) { if (gamepads_[i]) {
std::memcpy(padPrev_[i].data(), padState_[i].data(), static_cast<size_t>(GamepadBtn::Count)); std::memcpy(padPrev_[i].data(), padState_[i].data(), static_cast<size_t>(GamepadBtn::Count));
} }
} }
mouseWheel_ = 0; mouseWheel_ = 0;
const uint8* state = SDL_GetKeyboardState(nullptr); const uint8* state = SDL_GetKeyboardState(nullptr);
std::memcpy(keyState_.data(), state, KEY_COUNT); std::memcpy(keyState_.data(), state, KEY_COUNT);
uint32 btnState = SDL_GetMouseState(&mouseX_, &mouseY_); uint32 btnState = SDL_GetMouseState(&mouseX_, &mouseY_);
mouseState_[static_cast<size_t>(MouseBtn::Left)] = (btnState & SDL_BUTTON_LMASK) ? 1 : 0; mouseState_[static_cast<size_t>(MouseBtn::Left)] = (btnState & SDL_BUTTON_LMASK) ? 1 : 0;
mouseState_[static_cast<size_t>(MouseBtn::Middle)] = (btnState & SDL_BUTTON_MMASK) ? 1 : 0; mouseState_[static_cast<size_t>(MouseBtn::Middle)] = (btnState & SDL_BUTTON_MMASK) ? 1 : 0;
mouseState_[static_cast<size_t>(MouseBtn::Right)] = (btnState & SDL_BUTTON_RMASK) ? 1 : 0; mouseState_[static_cast<size_t>(MouseBtn::Right)] = (btnState & SDL_BUTTON_RMASK) ? 1 : 0;
mouseState_[static_cast<size_t>(MouseBtn::X1)] = (btnState & SDL_BUTTON_X1MASK) ? 1 : 0; mouseState_[static_cast<size_t>(MouseBtn::X1)] = (btnState & SDL_BUTTON_X1MASK) ? 1 : 0;
mouseState_[static_cast<size_t>(MouseBtn::X2)] = (btnState & SDL_BUTTON_X2MASK) ? 1 : 0; mouseState_[static_cast<size_t>(MouseBtn::X2)] = (btnState & SDL_BUTTON_X2MASK) ? 1 : 0;
for (int32 i = 0; i < MAX_GAMEPADS; ++i) { for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
if (gamepads_[i]) { if (gamepads_[i]) {
SDL_GameController* gc = gamepads_[i]; SDL_GameController* gc = gamepads_[i];
@ -91,22 +146,22 @@ void InputSvc::update(float dt) {
padState_[i][static_cast<size_t>(GamepadBtn::DPadRight)] = SDL_GameControllerGetButton(gc, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); padState_[i][static_cast<size_t>(GamepadBtn::DPadRight)] = SDL_GameControllerGetButton(gc, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
} }
} }
endedTouches_.clear(); endedTouches_.clear();
for (auto& touch : activeTouches_) { for (auto& touch : activeTouches_) {
if (touch.state == TouchState::Ended || touch.state == TouchState::Cancelled) { if (touch.state == TouchState::Ended || touch.state == TouchState::Cancelled) {
endedTouches_.push_back(touch); endedTouches_.push_back(touch);
} }
} }
activeTouches_.erase( activeTouches_.erase(
std::remove_if(activeTouches_.begin(), activeTouches_.end(), std::remove_if(activeTouches_.begin(), activeTouches_.end(),
[](const TouchPoint& t) { [](const TouchPoint& t) {
return t.state == TouchState::Ended || t.state == TouchState::Cancelled; return t.state == TouchState::Ended || t.state == TouchState::Cancelled;
}), }),
activeTouches_.end()); activeTouches_.end());
for (auto& touch : activeTouches_) { for (auto& touch : activeTouches_) {
touch.prevX = touch.x; touch.prevX = touch.x;
touch.prevY = touch.y; touch.prevY = touch.y;
@ -118,38 +173,65 @@ void InputSvc::update(float dt) {
} }
} }
void InputSvc::processEvent(const SDL_Event& evt) { void InputModule::processEvent(const SDL_Event& evt) {
switch (evt.type) { switch (evt.type) {
case SDL_KEYDOWN: case SDL_KEYDOWN:
if (evt.key.repeat == 0 && onKeyDown_) { if (evt.key.repeat == 0) {
onKeyDown_(static_cast<Key>(evt.key.keysym.scancode)); Key key = static_cast<Key>(evt.key.keysym.scancode);
// 发送按键按下事件
events::OnKeyDown::emit(static_cast<int32>(key));
if (onKeyDown_) {
onKeyDown_(key);
}
} }
break; break;
case SDL_KEYUP: case SDL_KEYUP: {
Key key = static_cast<Key>(evt.key.keysym.scancode);
// 发送按键释放事件
events::OnKeyUp::emit(static_cast<int32>(key));
if (onKeyUp_) { if (onKeyUp_) {
onKeyUp_(static_cast<Key>(evt.key.keysym.scancode)); onKeyUp_(key);
} }
break; break;
case SDL_MOUSEBUTTONDOWN: }
case SDL_MOUSEBUTTONDOWN: {
MouseBtn btn = static_cast<MouseBtn>(evt.button.button - 1);
// 发送鼠标按下事件
events::OnMouseDown::emit(static_cast<int32>(btn), evt.button.x, evt.button.y);
if (onMouseDown_) { if (onMouseDown_) {
onMouseDown_(static_cast<MouseBtn>(evt.button.button - 1), evt.button.x, evt.button.y); onMouseDown_(btn, evt.button.x, evt.button.y);
} }
break; break;
case SDL_MOUSEBUTTONUP: }
case SDL_MOUSEBUTTONUP: {
MouseBtn btn = static_cast<MouseBtn>(evt.button.button - 1);
// 发送鼠标释放事件
events::OnMouseUp::emit(static_cast<int32>(btn), evt.button.x, evt.button.y);
if (onMouseUp_) { if (onMouseUp_) {
onMouseUp_(static_cast<MouseBtn>(evt.button.button - 1), evt.button.x, evt.button.y); onMouseUp_(btn, evt.button.x, evt.button.y);
} }
break; break;
}
case SDL_MOUSEMOTION:
// 发送鼠标移动事件
events::OnMouseMove::emit(evt.motion.x, evt.motion.y);
break;
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
mouseWheel_ = evt.wheel.y; mouseWheel_ = evt.wheel.y;
// 发送鼠标滚轮事件
events::OnMouseWheel::emit(evt.wheel.y);
break; break;
case SDL_CONTROLLERDEVICEADDED: case SDL_CONTROLLERDEVICEADDED:
openGamepad(evt.cdevice.which); openGamepad(evt.cdevice.which);
// 发送手柄连接事件
events::OnGamepadConnect::emit(evt.cdevice.which);
break; break;
case SDL_CONTROLLERDEVICEREMOVED: case SDL_CONTROLLERDEVICEREMOVED:
for (int32 i = 0; i < MAX_GAMEPADS; ++i) { for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
if (gamepads_[i] && SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepads_[i])) == evt.cdevice.which) { if (gamepads_[i] && SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepads_[i])) == evt.cdevice.which) {
closeGamepad(i); closeGamepad(i);
// 发送手柄断开事件
events::OnGamepadDisconnect::emit(i);
break; break;
} }
} }
@ -166,12 +248,12 @@ void InputSvc::processEvent(const SDL_Event& evt) {
} }
} }
void InputSvc::processTouchDown(const SDL_TouchFingerEvent& evt) { void InputModule::processTouchDown(const SDL_TouchFingerEvent& evt) {
if (static_cast<int32>(activeTouches_.size()) >= MAX_TOUCHES) return; if (static_cast<int32>(activeTouches_.size()) >= MAX_TOUCHES) return;
int32 winW = 0, winH = 0; int32 winW = 0, winH = 0;
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH); SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
TouchPoint touch; TouchPoint touch;
touch.id = static_cast<int64>(evt.fingerId); touch.id = static_cast<int64>(evt.fingerId);
touch.x = evt.x * static_cast<float>(winW); touch.x = evt.x * static_cast<float>(winW);
@ -182,29 +264,35 @@ void InputSvc::processTouchDown(const SDL_TouchFingerEvent& evt) {
touch.deltaY = 0.0f; touch.deltaY = 0.0f;
touch.state = TouchState::Began; touch.state = TouchState::Began;
touch.pressure = evt.pressure; touch.pressure = evt.pressure;
activeTouches_.push_back(touch); activeTouches_.push_back(touch);
// 发送触摸开始事件
events::OnTouchBegin::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
if (onTouchBegan_) { if (onTouchBegan_) {
onTouchBegan_(touch); onTouchBegan_(touch);
} }
} }
void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) { void InputModule::processTouchUp(const SDL_TouchFingerEvent& evt) {
int64 fingerId = static_cast<int64>(evt.fingerId); int64 fingerId = static_cast<int64>(evt.fingerId);
for (auto& touch : activeTouches_) { for (auto& touch : activeTouches_) {
if (touch.id == fingerId) { if (touch.id == fingerId) {
int32 winW = 0, winH = 0; int32 winW = 0, winH = 0;
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH); SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
touch.x = evt.x * static_cast<float>(winW); touch.x = evt.x * static_cast<float>(winW);
touch.y = evt.y * static_cast<float>(winH); touch.y = evt.y * static_cast<float>(winH);
touch.deltaX = touch.x - touch.prevX; touch.deltaX = touch.x - touch.prevX;
touch.deltaY = touch.y - touch.prevY; touch.deltaY = touch.y - touch.prevY;
touch.state = TouchState::Ended; touch.state = TouchState::Ended;
touch.pressure = evt.pressure; touch.pressure = evt.pressure;
// 发送触摸结束事件
events::OnTouchEnd::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
if (onTouchEnded_) { if (onTouchEnded_) {
onTouchEnded_(touch); onTouchEnded_(touch);
} }
@ -213,21 +301,24 @@ void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) {
} }
} }
void InputSvc::processTouchMotion(const SDL_TouchFingerEvent& evt) { void InputModule::processTouchMotion(const SDL_TouchFingerEvent& evt) {
int64 fingerId = static_cast<int64>(evt.fingerId); int64 fingerId = static_cast<int64>(evt.fingerId);
for (auto& touch : activeTouches_) { for (auto& touch : activeTouches_) {
if (touch.id == fingerId) { if (touch.id == fingerId) {
int32 winW = 0, winH = 0; int32 winW = 0, winH = 0;
SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH); SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH);
touch.x = evt.x * static_cast<float>(winW); touch.x = evt.x * static_cast<float>(winW);
touch.y = evt.y * static_cast<float>(winH); touch.y = evt.y * static_cast<float>(winH);
touch.deltaX = touch.x - touch.prevX; touch.deltaX = touch.x - touch.prevX;
touch.deltaY = touch.y - touch.prevY; touch.deltaY = touch.y - touch.prevY;
touch.state = TouchState::Moved; touch.state = TouchState::Moved;
touch.pressure = evt.pressure; touch.pressure = evt.pressure;
// 发送触摸移动事件
events::OnTouchMove::emit(static_cast<int32>(touch.id), static_cast<int32>(touch.x), static_cast<int32>(touch.y));
if (onTouchMoved_) { if (onTouchMoved_) {
onTouchMoved_(touch); onTouchMoved_(touch);
} }
@ -236,10 +327,10 @@ void InputSvc::processTouchMotion(const SDL_TouchFingerEvent& evt) {
} }
} }
void InputSvc::openGamepad(int32 idx) { void InputModule::openGamepad(int32 idx) {
if (idx < 0 || idx >= MAX_GAMEPADS) return; if (idx < 0 || idx >= MAX_GAMEPADS) return;
if (gamepads_[idx]) return; if (gamepads_[idx]) return;
SDL_GameController* gc = SDL_GameControllerOpen(idx); SDL_GameController* gc = SDL_GameControllerOpen(idx);
if (gc) { if (gc) {
gamepads_[idx] = gc; gamepads_[idx] = gc;
@ -248,7 +339,7 @@ void InputSvc::openGamepad(int32 idx) {
} }
} }
void InputSvc::closeGamepad(int32 idx) { void InputModule::closeGamepad(int32 idx) {
if (idx < 0 || idx >= MAX_GAMEPADS) return; if (idx < 0 || idx >= MAX_GAMEPADS) return;
if (gamepads_[idx]) { if (gamepads_[idx]) {
SDL_GameControllerClose(gamepads_[idx]); SDL_GameControllerClose(gamepads_[idx]);
@ -256,70 +347,70 @@ void InputSvc::closeGamepad(int32 idx) {
} }
} }
bool InputSvc::isKeyDown(Key key) const { bool InputModule::isKeyDown(Key key) const {
if (key < 0 || key >= KEY_COUNT) return false; if (key < 0 || key >= KEY_COUNT) return false;
return keyState_[key] != 0; return keyState_[key] != 0;
} }
bool InputSvc::isKeyPressed(Key key) const { bool InputModule::isKeyPressed(Key key) const {
if (key < 0 || key >= KEY_COUNT) return false; if (key < 0 || key >= KEY_COUNT) return false;
return keyState_[key] != 0 && keyPrev_[key] == 0; return keyState_[key] != 0 && keyPrev_[key] == 0;
} }
bool InputSvc::isKeyReleased(Key key) const { bool InputModule::isKeyReleased(Key key) const {
if (key < 0 || key >= KEY_COUNT) return false; if (key < 0 || key >= KEY_COUNT) return false;
return keyState_[key] == 0 && keyPrev_[key] != 0; return keyState_[key] == 0 && keyPrev_[key] != 0;
} }
void InputSvc::getMousePos(int32& x, int32& y) const { void InputModule::getMousePos(int32& x, int32& y) const {
x = mouseX_; x = mouseX_;
y = mouseY_; y = mouseY_;
} }
void InputSvc::getMousePos(float& x, float& y) const { void InputModule::getMousePos(float& x, float& y) const {
x = static_cast<float>(mouseX_); x = static_cast<float>(mouseX_);
y = static_cast<float>(mouseY_); y = static_cast<float>(mouseY_);
} }
bool InputSvc::isMouseBtnDown(MouseBtn btn) const { bool InputModule::isMouseBtnDown(MouseBtn btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false; if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
return mouseState_[idx] != 0; return mouseState_[idx] != 0;
} }
bool InputSvc::isMouseBtnPressed(MouseBtn btn) const { bool InputModule::isMouseBtnPressed(MouseBtn btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false; if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
return mouseState_[idx] != 0 && mousePrev_[idx] == 0; return mouseState_[idx] != 0 && mousePrev_[idx] == 0;
} }
bool InputSvc::isMouseBtnReleased(MouseBtn btn) const { bool InputModule::isMouseBtnReleased(MouseBtn btn) const {
size_t idx = static_cast<size_t>(btn); size_t idx = static_cast<size_t>(btn);
if (idx >= static_cast<size_t>(MouseBtn::Count)) return false; if (idx >= static_cast<size_t>(MouseBtn::Count)) return false;
return mouseState_[idx] == 0 && mousePrev_[idx] != 0; return mouseState_[idx] == 0 && mousePrev_[idx] != 0;
} }
int32 InputSvc::getMouseWheel() const { int32 InputModule::getMouseWheel() const {
return mouseWheel_; return mouseWheel_;
} }
int32 InputSvc::touchCount() const { int32 InputModule::touchCount() const {
return static_cast<int32>(activeTouches_.size()); return static_cast<int32>(activeTouches_.size());
} }
const TouchPoint* InputSvc::getTouch(int32 idx) const { const TouchPoint* InputModule::getTouch(int32 idx) const {
if (idx < 0 || idx >= static_cast<int32>(activeTouches_.size())) return nullptr; if (idx < 0 || idx >= static_cast<int32>(activeTouches_.size())) return nullptr;
return &activeTouches_[idx]; return &activeTouches_[idx];
} }
const TouchPoint* InputSvc::getTouchById(int64 id) const { const TouchPoint* InputModule::getTouchById(int64 id) const {
for (const auto& touch : activeTouches_) { for (const auto& touch : activeTouches_) {
if (touch.id == id) return &touch; if (touch.id == id) return &touch;
} }
return nullptr; return nullptr;
} }
int32 InputSvc::gamepadCount() const { int32 InputModule::gamepadCount() const {
int32 count = 0; int32 count = 0;
for (int32 i = 0; i < MAX_GAMEPADS; ++i) { for (int32 i = 0; i < MAX_GAMEPADS; ++i) {
if (gamepads_[i]) ++count; if (gamepads_[i]) ++count;
@ -327,7 +418,7 @@ int32 InputSvc::gamepadCount() const {
return count; return count;
} }
bool InputSvc::isGamepadBtnDown(int32 idx, GamepadBtn btn) const { bool InputModule::isGamepadBtnDown(int32 idx, GamepadBtn btn) const {
if (idx < 0 || idx >= MAX_GAMEPADS) return false; if (idx < 0 || idx >= MAX_GAMEPADS) return false;
if (!gamepads_[idx]) return false; if (!gamepads_[idx]) return false;
size_t b = static_cast<size_t>(btn); size_t b = static_cast<size_t>(btn);
@ -335,7 +426,7 @@ bool InputSvc::isGamepadBtnDown(int32 idx, GamepadBtn btn) const {
return padState_[idx][b] != 0; return padState_[idx][b] != 0;
} }
bool InputSvc::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const { bool InputModule::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const {
if (idx < 0 || idx >= MAX_GAMEPADS) return false; if (idx < 0 || idx >= MAX_GAMEPADS) return false;
if (!gamepads_[idx]) return false; if (!gamepads_[idx]) return false;
size_t b = static_cast<size_t>(btn); size_t b = static_cast<size_t>(btn);
@ -343,10 +434,10 @@ bool InputSvc::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const {
return padState_[idx][b] != 0 && padPrev_[idx][b] == 0; return padState_[idx][b] != 0 && padPrev_[idx][b] == 0;
} }
float InputSvc::getGamepadAxis(int32 idx, GamepadAxis axis) const { float InputModule::getGamepadAxis(int32 idx, GamepadAxis axis) const {
if (idx < 0 || idx >= MAX_GAMEPADS) return 0.0f; if (idx < 0 || idx >= MAX_GAMEPADS) return 0.0f;
if (!gamepads_[idx]) return 0.0f; if (!gamepads_[idx]) return 0.0f;
SDL_GameControllerAxis sdlAxis = SDL_CONTROLLER_AXIS_INVALID; SDL_GameControllerAxis sdlAxis = SDL_CONTROLLER_AXIS_INVALID;
switch (axis) { switch (axis) {
case GamepadAxis::LeftX: sdlAxis = SDL_CONTROLLER_AXIS_LEFTX; break; case GamepadAxis::LeftX: sdlAxis = SDL_CONTROLLER_AXIS_LEFTX; break;
@ -357,7 +448,7 @@ float InputSvc::getGamepadAxis(int32 idx, GamepadAxis axis) const {
case GamepadAxis::TriggerRight: sdlAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; break; case GamepadAxis::TriggerRight: sdlAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; break;
default: return 0.0f; default: return 0.0f;
} }
int16 value = SDL_GameControllerGetAxis(gamepads_[idx], sdlAxis); int16 value = SDL_GameControllerGetAxis(gamepads_[idx], sdlAxis);
return static_cast<float>(value) / 32767.0f; return static_cast<float>(value) / 32767.0f;
} }

View File

@ -1,178 +1,241 @@
#include <SDL.h>
#include <platform/input.h>
#include <platform/sdl2.h>
#include <platform/window.h> #include <platform/window.h>
#include <platform/sdl2.h>
#include <event/events.h>
#include <utils/logger.h>
namespace extra2d { namespace extra2d {
WindowSvc &WindowSvc::inst() { WindowModule::WindowModule() = default;
static WindowSvc instance;
return instance; WindowModule::~WindowModule() {
shutdown();
} }
bool WindowSvc::init() { WindowModule::WindowModule(WindowModule&& other) noexcept
if (isInited()) : window_(other.window_)
, glCtx_(other.glCtx_)
, shouldClose_(other.shouldClose_)
, vsync_(other.vsync_)
, onClose_(std::move(other.onClose_))
, onResize_(std::move(other.onResize_)) {
other.window_ = nullptr;
other.glCtx_ = nullptr;
}
WindowModule& WindowModule::operator=(WindowModule&& other) noexcept {
if (this != &other) {
shutdown();
window_ = other.window_;
glCtx_ = other.glCtx_;
shouldClose_ = other.shouldClose_;
vsync_ = other.vsync_;
onClose_ = std::move(other.onClose_);
onResize_ = std::move(other.onResize_);
other.window_ = nullptr;
other.glCtx_ = nullptr;
}
return *this;
}
bool WindowModule::init() {
if (!Sdl2::initVideo()) {
return false;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
return true; return true;
if (!Sdl2::initVideo()) {
return false;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
state_ = SvcState::Inited;
return true;
} }
void WindowSvc::shutdown() { void WindowModule::shutdown() {
if (glCtx_) { if (glCtx_) {
SDL_GL_DeleteContext(glCtx_); SDL_GL_DeleteContext(glCtx_);
glCtx_ = nullptr; glCtx_ = nullptr;
} }
if (window_) { if (window_) {
SDL_DestroyWindow(window_); SDL_DestroyWindow(window_);
window_ = nullptr; window_ = nullptr;
} }
state_ = SvcState::Shutdown;
} }
bool WindowSvc::create(const WindowCfg &cfg) { bool WindowModule::create(const WindowCfg& cfg) {
if (window_) if (window_)
return true;
uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
if (cfg.resizable) {
flags |= SDL_WINDOW_RESIZABLE;
}
if (cfg.fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor);
window_ = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
if (!window_) {
E2D_LOG_ERROR("Failed to create window: {}", SDL_GetError());
return false;
}
glCtx_ = SDL_GL_CreateContext(window_);
if (!glCtx_) {
E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError());
SDL_DestroyWindow(window_);
window_ = nullptr;
return false;
}
setVsync(cfg.vsync);
vsync_ = cfg.vsync;
// 发送窗口显示事件
events::OnShow::emit();
return true; return true;
uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
if (cfg.resizable) {
flags |= SDL_WINDOW_RESIZABLE;
}
if (cfg.fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor);
window_ =
SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags);
if (!window_) {
return false;
}
glCtx_ = SDL_GL_CreateContext(window_);
if (!glCtx_) {
SDL_DestroyWindow(window_);
window_ = nullptr;
return false;
}
setVsync(cfg.vsync);
vsync_ = cfg.vsync;
return true;
} }
bool WindowSvc::pollEvents() { bool WindowModule::pollEvents() {
SDL_Event evt; SDL_Event evt;
while (SDL_PollEvent(&evt)) { while (SDL_PollEvent(&evt)) {
switch (evt.type) { switch (evt.type) {
case SDL_QUIT: case SDL_QUIT:
shouldClose_ = true; shouldClose_ = true;
if (onClose_) // 发送窗口关闭事件
onClose_(); events::OnClose::emit();
break; if (onClose_)
case SDL_WINDOWEVENT: onClose_();
if (evt.window.event == SDL_WINDOWEVENT_RESIZED) { break;
if (onResize_) { case SDL_WINDOWEVENT:
onResize_(evt.window.data1, evt.window.data2); handleWindowEvent(evt.window);
break;
default:
// 输入事件由 InputModule 处理
break;
} }
}
break;
default:
if (INPUT_SVC.isInited()) {
INPUT_SVC.processEvent(evt);
}
break;
} }
} return !shouldClose_;
return !shouldClose_;
} }
void WindowSvc::swapBuffers() { void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) {
if (window_ && glCtx_) { switch (evt.event) {
SDL_GL_SwapWindow(window_); case SDL_WINDOWEVENT_RESIZED:
} // 发送窗口大小改变事件
} events::OnResize::emit(evt.data1, evt.data2);
if (onResize_) {
Size WindowSvc::getSize() const { onResize_(evt.data1, evt.data2);
if (!window_) }
return Size(0, 0); break;
int w, h; case SDL_WINDOWEVENT_FOCUS_GAINED:
SDL_GetWindowSize(window_, &w, &h); // 发送窗口获得焦点事件
return Size(w, h); events::OnFocus::emit();
} break;
case SDL_WINDOWEVENT_FOCUS_LOST:
Vec2 WindowSvc::getPosition() const { // 发送窗口失去焦点事件
if (!window_) events::OnBlur::emit();
return Vec2(0, 0); break;
int x, y; case SDL_WINDOWEVENT_SHOWN:
SDL_GetWindowPosition(window_, &x, &y); // 发送窗口显示事件
return Vec2(static_cast<float>(x), static_cast<float>(y)); events::OnShow::emit();
} break;
case SDL_WINDOWEVENT_HIDDEN:
void WindowSvc::setSize(int32 w, int32 h) { // 发送窗口隐藏事件
if (window_) { events::OnHide::emit();
SDL_SetWindowSize(window_, w, h); break;
} case SDL_WINDOWEVENT_CLOSE:
} shouldClose_ = true;
// 发送窗口关闭事件
void WindowSvc::setTitle(const std::string &title) { events::OnClose::emit();
if (window_) { if (onClose_)
SDL_SetWindowTitle(window_, title.c_str()); onClose_();
} break;
}
void WindowSvc::setFullscreen(bool fullscreen) {
if (window_) {
SDL_SetWindowFullscreen(window_,
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
}
bool WindowSvc::isFullscreen() const {
if (!window_)
return false;
uint32 flags = SDL_GetWindowFlags(window_);
return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
}
void WindowSvc::setVsync(bool vsync) {
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
vsync_ = vsync;
}
bool WindowSvc::isVsync() const { return vsync_; }
void WindowSvc::setVisible(bool visible) {
if (window_) {
if (visible) {
SDL_ShowWindow(window_);
} else {
SDL_HideWindow(window_);
} }
}
} }
bool WindowSvc::isVisible() const { void WindowModule::swapBuffers() {
if (!window_) if (window_ && glCtx_) {
return false; SDL_GL_SwapWindow(window_);
uint32 flags = SDL_GetWindowFlags(window_); }
return (flags & SDL_WINDOW_SHOWN) != 0; }
Size WindowModule::getSize() const {
if (!window_)
return Size(0, 0);
int w, h;
SDL_GetWindowSize(window_, &w, &h);
return Size(w, h);
}
Vec2 WindowModule::getPosition() const {
if (!window_)
return Vec2(0, 0);
int x, y;
SDL_GetWindowPosition(window_, &x, &y);
return Vec2(static_cast<float>(x), static_cast<float>(y));
}
void WindowModule::setSize(int32 w, int32 h) {
if (window_) {
SDL_SetWindowSize(window_, w, h);
}
}
void WindowModule::setTitle(const std::string& title) {
if (window_) {
SDL_SetWindowTitle(window_, title.c_str());
}
}
void WindowModule::setFullscreen(bool fullscreen) {
if (window_) {
SDL_SetWindowFullscreen(window_,
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
}
bool WindowModule::isFullscreen() const {
if (!window_)
return false;
uint32 flags = SDL_GetWindowFlags(window_);
return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
}
void WindowModule::setVsync(bool vsync) {
SDL_GL_SetSwapInterval(vsync ? 1 : 0);
vsync_ = vsync;
}
bool WindowModule::isVsync() const {
return vsync_;
}
void WindowModule::setVisible(bool visible) {
if (window_) {
if (visible) {
SDL_ShowWindow(window_);
// 发送窗口显示事件
events::OnShow::emit();
} else {
SDL_HideWindow(window_);
// 发送窗口隐藏事件
events::OnHide::emit();
}
}
}
bool WindowModule::isVisible() const {
if (!window_)
return false;
uint32 flags = SDL_GetWindowFlags(window_);
return (flags & SDL_WINDOW_SHOWN) != 0;
} }
} // namespace extra2d } // namespace extra2d

View File

@ -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

View File

@ -3,66 +3,110 @@
namespace extra2d { namespace extra2d {
// ============================================================================
// Random 类实现
// ============================================================================
Random::Random() : floatDist_(0.0f, 1.0f) { Random::Random() : floatDist_(0.0f, 1.0f) {
// 使用当前时间作为默认种子 // 使用当前时间作为默认种子
randomize(); randomize();
} }
Random &Random::getInstance() { Random::Random(uint32 seed) : floatDist_(0.0f, 1.0f) {
static Random instance; setSeed(seed);
return instance;
} }
void Random::setSeed(uint32 seed) { generator_.seed(seed); } void Random::setSeed(uint32 seed) {
generator_.seed(seed);
}
void Random::randomize() { void Random::randomize() {
auto now = std::chrono::high_resolution_clock::now(); auto now = std::chrono::high_resolution_clock::now();
auto time = now.time_since_epoch().count(); auto time = now.time_since_epoch().count();
generator_.seed(static_cast<uint32>(time)); generator_.seed(static_cast<uint32>(time));
} }
float Random::getFloat() { return floatDist_(generator_); } float Random::getFloat() {
return floatDist_(generator_);
}
float Random::getFloat(float min, float max) { float Random::getFloat(float min, float max) {
if (min >= max) { if (min >= max) {
return min; return min;
} }
return min + floatDist_(generator_) * (max - min); return min + floatDist_(generator_) * (max - min);
} }
int Random::getInt(int max) { int Random::getInt(int max) {
if (max <= 0) { if (max <= 0) {
return 0; return 0;
} }
std::uniform_int_distribution<int> dist(0, max); std::uniform_int_distribution<int> dist(0, max);
return dist(generator_); return dist(generator_);
} }
int Random::getInt(int min, int max) { int Random::getInt(int min, int max) {
if (min >= max) { if (min >= max) {
return min; return min;
} }
std::uniform_int_distribution<int> dist(min, max); std::uniform_int_distribution<int> dist(min, max);
return dist(generator_); return dist(generator_);
} }
bool Random::getBool() { return floatDist_(generator_) >= 0.5f; } bool Random::getBool() {
return floatDist_(generator_) >= 0.5f;
}
bool Random::getBool(float probability) { bool Random::getBool(float probability) {
if (probability <= 0.0f) { if (probability <= 0.0f) {
return false; return false;
} }
if (probability >= 1.0f) { if (probability >= 1.0f) {
return true; return true;
} }
return floatDist_(generator_) < probability; return floatDist_(generator_) < probability;
} }
float Random::getAngle() { float Random::getAngle() {
static const float TWO_PI = 6.28318530718f; static const float TWO_PI = 6.28318530718f;
return floatDist_(generator_) * TWO_PI; return floatDist_(generator_) * TWO_PI;
} }
float Random::getSigned() { return floatDist_(generator_) * 2.0f - 1.0f; } float Random::getSigned() {
return floatDist_(generator_) * 2.0f - 1.0f;
}
// ============================================================================
// 全局便捷函数 - 使用静态的默认随机数生成器
// ============================================================================
static Random& getDefaultRandom() {
static Random defaultRandom;
return defaultRandom;
}
float randomFloat() {
return getDefaultRandom().getFloat();
}
float randomFloat(float min, float max) {
return getDefaultRandom().getFloat(min, max);
}
int randomInt(int max) {
return getDefaultRandom().getInt(max);
}
int randomInt(int min, int max) {
return getDefaultRandom().getInt(min, max);
}
bool randomBool() {
return getDefaultRandom().getBool();
}
bool randomBool(float probability) {
return getDefaultRandom().getBool(probability);
}
} // namespace extra2d } // namespace extra2d

View File

@ -13,7 +13,7 @@ function define_extra2d_engine()
target("extra2d") target("extra2d")
set_kind("static") set_kind("static")
add_files("src/**.cpp") add_files("src/**.cpp|core/*.cpp|module/module_manager.cpp|plugin/plugin_manager.cpp")
add_files("third_party/glad/src/glad.c") add_files("third_party/glad/src/glad.c")
add_includedirs("include", {public = true}) add_includedirs("include", {public = true})