refactor: 重构日志系统并优化智能指针命名

重构日志系统,移除Registry日志级别,增加带类别的日志宏
将makeUnique重命名为unique以保持一致性
更新所有相关文件和文档中的日志调用
优化代码格式和头文件包含
This commit is contained in:
ChestnutYueyue 2026-02-23 02:15:30 +08:00
parent e600730105
commit 037b8599c7
28 changed files with 14620 additions and 894 deletions

View File

@ -1,13 +1,13 @@
#pragma once
#include <extra2d/asset/asset.h>
#include <extra2d/asset/asset_handle.h>
#include <extra2d/asset/asset_types.h>
#include <extra2d/core/types.h>
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <deque>
#include <extra2d/asset/asset.h>
#include <extra2d/asset/asset_handle.h>
#include <extra2d/asset/asset_types.h>
#include <extra2d/core/types.h>
#include <list>
#include <memory>
#include <mutex>
@ -15,6 +15,7 @@
#include <unordered_map>
#include <vector>
namespace extra2d {
// ---------------------------------------------------------------------------
@ -23,30 +24,29 @@ namespace extra2d {
/**
* @brief
*
*
* 访LRU缓存管理
*/
struct CacheEntry {
Ref<Asset> asset;
std::chrono::steady_clock::time_point lastAccess;
size_t accessCount = 0;
Ref<Asset> asset;
std::chrono::steady_clock::time_point lastAccess;
size_t accessCount = 0;
/**
* @brief
* @param a
*/
explicit CacheEntry(Ref<Asset> a)
: asset(std::move(a)),
lastAccess(std::chrono::steady_clock::now()),
accessCount(1) {}
/**
* @brief
* @param a
*/
explicit CacheEntry(Ref<Asset> a)
: asset(std::move(a)), lastAccess(std::chrono::steady_clock::now()),
accessCount(1) {}
/**
* @brief 访
*/
void touch() {
lastAccess = std::chrono::steady_clock::now();
++accessCount;
}
/**
* @brief 访
*/
void touch() {
lastAccess = std::chrono::steady_clock::now();
++accessCount;
}
};
// ---------------------------------------------------------------------------
@ -55,9 +55,9 @@ struct CacheEntry {
/**
* @brief
*
*
*
*
*
*
* - LRU缓存淘汰策略
* - 线
@ -67,197 +67,191 @@ struct CacheEntry {
*/
class AssetCache {
public:
/**
* @brief
* @param limit 0
*/
explicit AssetCache(size_t limit = 0);
/**
* @brief
* @param limit 0
*/
explicit AssetCache(size_t limit = 0);
~AssetCache() = default;
~AssetCache() = default;
AssetCache(const AssetCache&) = delete;
AssetCache& operator=(const AssetCache&) = delete;
AssetCache(const AssetCache &) = delete;
AssetCache &operator=(const AssetCache &) = delete;
// -------------------------------------------------------------------------
// 资源管理
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// 资源管理
// -------------------------------------------------------------------------
/**
* @brief
* @tparam T
* @param asset
* @return
*/
template<typename T>
AssetHandle<T> add(Ref<T> asset) {
static_assert(std::is_base_of_v<Asset, T>,
"T must derive from Asset");
/**
* @brief
* @tparam T
* @param asset
* @return
*/
template <typename T> AssetHandle<T> add(Ref<T> asset) {
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
if (!asset) {
return AssetHandle<T>();
}
std::unique_lock<std::shared_mutex> lock(mutex_);
AssetID id = asset->id();
size_t memSize = asset->memSize();
bytes_ += memSize;
auto it = lruList_.insert(lruList_.end(), id);
entries_[id] = CacheEntryData{
ptr::makeUnique<CacheEntry>(std::static_pointer_cast<Asset>(asset)),
it
};
++stats_.count;
if (limit_ > 0 && bytes_ > limit_) {
evict();
}
return AssetHandle<T>(id, Weak<T>(asset));
if (!asset) {
return AssetHandle<T>();
}
/**
* @brief
* @tparam T
* @param id ID
* @return
*/
template<typename T>
AssetHandle<T> get(const AssetID& id) {
static_assert(std::is_base_of_v<Asset, T>,
"T must derive from Asset");
std::unique_lock<std::shared_mutex> lock(mutex_);
std::unique_lock<std::shared_mutex> lock(mutex_);
AssetID id = asset->id();
auto it = entries_.find(id);
if (it == entries_.end()) {
++stats_.misses;
return AssetHandle<T>();
}
size_t memSize = asset->memSize();
bytes_ += memSize;
it->second.entry->touch();
lruList_.erase(it->second.lruIterator);
it->second.lruIterator = lruList_.insert(lruList_.end(), id);
auto it = lruList_.insert(lruList_.end(), id);
++stats_.hits;
entries_[id] = CacheEntryData{
ptr::unique<CacheEntry>(std::static_pointer_cast<Asset>(asset)), it};
auto typedAsset = std::static_pointer_cast<T>(it->second.entry->asset);
return AssetHandle<T>(id, Weak<T>(typedAsset));
++stats_.count;
if (limit_ > 0 && bytes_ > limit_) {
evict();
}
/**
* @brief
* @param id ID
* @return true
*/
bool has(const AssetID& id) const;
return AssetHandle<T>(id, Weak<T>(asset));
}
/**
* @brief
* @param id ID
* @return true
*/
bool remove(const AssetID& id);
/**
* @brief
* @tparam T
* @param id ID
* @return
*/
template <typename T> AssetHandle<T> get(const AssetID &id) {
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
// -------------------------------------------------------------------------
// 缓存管理
// -------------------------------------------------------------------------
std::unique_lock<std::shared_mutex> lock(mutex_);
/**
* @brief
* @param limit 0
*/
void setLimit(size_t limit);
auto it = entries_.find(id);
if (it == entries_.end()) {
++stats_.misses;
return AssetHandle<T>();
}
/**
* @brief
* @return
*/
size_t limit() const { return limit_; }
it->second.entry->touch();
/**
* @brief 使
* @return 使
*/
size_t bytes() const { return bytes_; }
lruList_.erase(it->second.lruIterator);
it->second.lruIterator = lruList_.insert(lruList_.end(), id);
/**
* @brief
* @return
*/
size_t count() const;
++stats_.hits;
/**
* @brief 使
* @return 使
*/
size_t size() const { return bytes_; }
auto typedAsset = std::static_pointer_cast<T>(it->second.entry->asset);
return AssetHandle<T>(id, Weak<T>(typedAsset));
}
/**
* @brief
*/
void hit() { ++stats_.hits; }
/**
* @brief
* @param id ID
* @return true
*/
bool has(const AssetID &id) const;
/**
* @brief
*/
void miss() { ++stats_.misses; }
/**
* @brief
* @param id ID
* @return true
*/
bool remove(const AssetID &id);
/**
* @brief
* @return
*/
size_t purge();
// -------------------------------------------------------------------------
// 缓存管理
// -------------------------------------------------------------------------
/**
* @brief
*/
void clear();
/**
* @brief
* @param limit 0
*/
void setLimit(size_t limit);
/**
* @brief
* @return
*/
CacheStats stats() const;
/**
* @brief
* @return
*/
size_t limit() const { return limit_; }
/**
* @brief
*/
void resetStats();
/**
* @brief 使
* @return 使
*/
size_t bytes() const { return bytes_; }
/**
* @brief
* @return
*/
size_t count() const;
/**
* @brief 使
* @return 使
*/
size_t size() const { return bytes_; }
/**
* @brief
*/
void hit() { ++stats_.hits; }
/**
* @brief
*/
void miss() { ++stats_.misses; }
/**
* @brief
* @return
*/
size_t purge();
/**
* @brief
*/
void clear();
/**
* @brief
* @return
*/
CacheStats stats() const;
/**
* @brief
*/
void resetStats();
private:
/**
* @brief LRU迭代器
*/
struct CacheEntryData {
Ref<CacheEntry> entry;
std::list<AssetID>::iterator lruIterator;
};
/**
* @brief LRU迭代器
*/
struct CacheEntryData {
Ref<CacheEntry> entry;
std::list<AssetID>::iterator lruIterator;
};
/**
* @brief LRU策略
*/
void evict();
/**
* @brief LRU策略
*/
void evict();
/**
* @brief
* @param entry
* @return true
*/
bool canEvict(const CacheEntry& entry) const;
/**
* @brief
* @param entry
* @return true
*/
bool canEvict(const CacheEntry &entry) const;
mutable std::shared_mutex mutex_;
std::unordered_map<AssetID, CacheEntryData> entries_;
std::list<AssetID> lruList_;
size_t limit_ = 0;
size_t bytes_ = 0;
mutable CacheStats stats_;
mutable std::shared_mutex mutex_;
std::unordered_map<AssetID, CacheEntryData> entries_;
std::list<AssetID> lruList_;
size_t limit_ = 0;
size_t bytes_ = 0;
mutable CacheStats stats_;
};
}
} // namespace extra2d

View File

@ -15,46 +15,46 @@ namespace extra2d {
/**
* @brief
*
*
*
*/
class AssetLoaderBase {
public:
virtual ~AssetLoaderBase() = default;
virtual ~AssetLoaderBase() = default;
/**
* @brief Asset
* @param path
* @return
*/
virtual Ref<Asset> loadBase(const std::string& path) = 0;
/**
* @brief Asset
* @param path
* @return
*/
virtual Ref<Asset> loadBase(const std::string &path) = 0;
/**
* @brief
* @param data
* @param size
* @return
*/
virtual Ref<Asset> loadFromMemoryBase(const u8* data, size_t size) = 0;
/**
* @brief
* @param data
* @param size
* @return
*/
virtual Ref<Asset> loadFromMemoryBase(const u8 *data, size_t size) = 0;
/**
* @brief
* @param path
* @return true
*/
virtual bool canLoad(const std::string& path) const = 0;
/**
* @brief
* @param path
* @return true
*/
virtual bool canLoad(const std::string &path) const = 0;
/**
* @brief
* @return
*/
virtual AssetType type() const = 0;
/**
* @brief
* @return
*/
virtual AssetType type() const = 0;
/**
* @brief
* @return
*/
virtual std::vector<std::string> extensions() const = 0;
/**
* @brief
* @return
*/
virtual std::vector<std::string> extensions() const = 0;
};
// ---------------------------------------------------------------------------
@ -63,42 +63,38 @@ public:
/**
* @brief
*
*
* 使
*
*
*
* @tparam T
*/
template<typename T>
class AssetLoader : public AssetLoaderBase {
static_assert(std::is_base_of_v<Asset, T>,
"T must derive from Asset");
template <typename T> class AssetLoader : public AssetLoaderBase {
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
public:
virtual ~AssetLoader() = default;
virtual ~AssetLoader() = default;
/**
* @brief
* @param path
* @return nullptr
*/
virtual Ref<T> load(const std::string& path) = 0;
/**
* @brief
* @param path
* @return nullptr
*/
virtual Ref<T> load(const std::string &path) = 0;
/**
* @brief
* @param data
* @param size
* @return nullptr
*/
virtual Ref<T> loadFromMemory(const u8* data, size_t size) = 0;
/**
* @brief
* @param data
* @param size
* @return nullptr
*/
virtual Ref<T> loadFromMemory(const u8 *data, size_t size) = 0;
Ref<Asset> loadBase(const std::string& path) override {
return load(path);
}
Ref<Asset> loadBase(const std::string &path) override { return load(path); }
Ref<Asset> loadFromMemoryBase(const u8* data, size_t size) override {
return loadFromMemory(data, size);
}
Ref<Asset> loadFromMemoryBase(const u8 *data, size_t size) override {
return loadFromMemory(data, size);
}
};
// ---------------------------------------------------------------------------
@ -107,35 +103,35 @@ public:
/**
* @brief
*
*
* 使 stb_image
* PNG, JPG, BMP, TGA, GIF, PSD, HDR, PIC
*/
class TextureLoader : public AssetLoader<TextureAsset> {
public:
TextureLoader();
~TextureLoader() override;
TextureLoader();
~TextureLoader() override;
Ref<TextureAsset> load(const std::string& path) override;
Ref<TextureAsset> loadFromMemory(const u8* data, size_t size) override;
bool canLoad(const std::string& path) const override;
AssetType type() const override { return AssetType::Texture; }
std::vector<std::string> extensions() const override;
Ref<TextureAsset> load(const std::string &path) override;
Ref<TextureAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Texture; }
std::vector<std::string> extensions() const override;
/**
* @brief
* @param channels 1-40
*/
void setDesiredChannels(int channels);
/**
* @brief
* @param channels 1-40
*/
void setDesiredChannels(int channels);
/**
* @brief
* @return
*/
int desiredChannels() const { return desiredChannels_; }
/**
* @brief
* @return
*/
int desiredChannels() const { return desiredChannels_; }
private:
int desiredChannels_ = 4;
int desiredChannels_ = 4;
};
// ---------------------------------------------------------------------------
@ -144,16 +140,16 @@ private:
/**
* @brief
*
*
* TrueType .ttf, .otf
*/
class FontLoader : public AssetLoader<FontAsset> {
public:
Ref<FontAsset> load(const std::string& path) override;
Ref<FontAsset> loadFromMemory(const u8* data, size_t size) override;
bool canLoad(const std::string& path) const override;
AssetType type() const override { return AssetType::Font; }
std::vector<std::string> extensions() const override;
Ref<FontAsset> load(const std::string &path) override;
Ref<FontAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Font; }
std::vector<std::string> extensions() const override;
};
// ---------------------------------------------------------------------------
@ -162,45 +158,46 @@ public:
/**
* @brief
*
*
*
* - .vert/.frag: /
* - .glsl: 使
*/
class ShaderLoader : public AssetLoader<ShaderAsset> {
public:
Ref<ShaderAsset> load(const std::string& path) override;
Ref<ShaderAsset> loadFromMemory(const u8* data, size_t size) override;
bool canLoad(const std::string& path) const override;
AssetType type() const override { return AssetType::Shader; }
std::vector<std::string> extensions() const override;
Ref<ShaderAsset> load(const std::string &path) override;
Ref<ShaderAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Shader; }
std::vector<std::string> extensions() const override;
/**
* @brief
* @param marker "[VERTEX]"
*/
void setVertexMarker(const std::string& marker) { vertexMarker_ = marker; }
/**
* @brief
* @param marker "[VERTEX]"
*/
void setVertexMarker(const std::string &marker) { vertexMarker_ = marker; }
/**
* @brief
* @param marker "[FRAGMENT]"
*/
void setFragmentMarker(const std::string& marker) { fragmentMarker_ = marker; }
/**
* @brief
* @param marker "[FRAGMENT]"
*/
void setFragmentMarker(const std::string &marker) {
fragmentMarker_ = marker;
}
private:
std::string vertexMarker_ = "[VERTEX]";
std::string fragmentMarker_ = "[FRAGMENT]";
std::string vertexMarker_ = "[VERTEX]";
std::string fragmentMarker_ = "[FRAGMENT]";
/**
* @brief
* @param content
* @param vertex
* @param fragment
* @return true
*/
bool parseCombined(const std::string& content,
std::string& vertex,
std::string& fragment);
/**
* @brief
* @param content
* @param vertex
* @param fragment
* @return true
*/
bool parseCombined(const std::string &content, std::string &vertex,
std::string &fragment);
};
// ---------------------------------------------------------------------------
@ -209,26 +206,26 @@ private:
/**
* @brief
*
*
* WAV
* MP3, OGG
*/
class AudioLoader : public AssetLoader<AudioAsset> {
public:
Ref<AudioAsset> load(const std::string& path) override;
Ref<AudioAsset> loadFromMemory(const u8* data, size_t size) override;
bool canLoad(const std::string& path) const override;
AssetType type() const override { return AssetType::Audio; }
std::vector<std::string> extensions() const override;
Ref<AudioAsset> load(const std::string &path) override;
Ref<AudioAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Audio; }
std::vector<std::string> extensions() const override;
private:
/**
* @brief WAV
* @param data
* @param size
* @return
*/
Ref<AudioAsset> loadWav(const u8* data, size_t size);
/**
* @brief WAV
* @param data
* @param size
* @return
*/
Ref<AudioAsset> loadWav(const u8 *data, size_t size);
};
// ---------------------------------------------------------------------------
@ -237,16 +234,16 @@ private:
/**
* @brief
*
*
*
*/
class DataLoader : public AssetLoader<DataAsset> {
public:
Ref<DataAsset> load(const std::string& path) override;
Ref<DataAsset> loadFromMemory(const u8* data, size_t size) override;
bool canLoad(const std::string& path) const override;
AssetType type() const override { return AssetType::Data; }
std::vector<std::string> extensions() const override;
Ref<DataAsset> load(const std::string &path) override;
Ref<DataAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Data; }
std::vector<std::string> extensions() const override;
};
// ---------------------------------------------------------------------------
@ -255,58 +252,58 @@ public:
/**
* @brief
*
*
*
* 使
*/
class AssetLoaderFactory {
public:
/**
* @brief
* @return
*/
static Unique<TextureLoader> createTextureLoader() {
return ptr::makeUnique<TextureLoader>();
}
/**
* @brief
* @return
*/
static Unique<TextureLoader> createTextureLoader() {
return ptr::unique<TextureLoader>();
}
/**
* @brief
* @return
*/
static Unique<FontLoader> createFontLoader() {
return ptr::makeUnique<FontLoader>();
}
/**
* @brief
* @return
*/
static Unique<FontLoader> createFontLoader() {
return ptr::unique<FontLoader>();
}
/**
* @brief
* @return
*/
static Unique<ShaderLoader> createShaderLoader() {
return ptr::makeUnique<ShaderLoader>();
}
/**
* @brief
* @return
*/
static Unique<ShaderLoader> createShaderLoader() {
return ptr::unique<ShaderLoader>();
}
/**
* @brief
* @return
*/
static Unique<AudioLoader> createAudioLoader() {
return ptr::makeUnique<AudioLoader>();
}
/**
* @brief
* @return
*/
static Unique<AudioLoader> createAudioLoader() {
return ptr::unique<AudioLoader>();
}
/**
* @brief
* @return
*/
static Unique<DataLoader> createDataLoader() {
return ptr::makeUnique<DataLoader>();
}
/**
* @brief
* @return
*/
static Unique<DataLoader> createDataLoader() {
return ptr::unique<DataLoader>();
}
/**
* @brief
* @param extension ".png"
* @return AssetType::Unknown
*/
static AssetType getTypeByExtension(const std::string& extension);
/**
* @brief
* @param extension ".png"
* @return AssetType::Unknown
*/
static AssetType getTypeByExtension(const std::string &extension);
};
}
} // namespace extra2d

View File

@ -1,12 +1,10 @@
#pragma once
#include <array>
#include <extra2d/core/module.h>
#include <extra2d/core/types.h>
#include <array>
#include <typeindex>
#include <vector>
#include <memory>
#include <future>
namespace extra2d {
@ -18,153 +16,151 @@ class Application;
using TypeId = size_t;
namespace detail {
inline TypeId nextTypeId() {
static TypeId id = 0;
return ++id;
}
template<typename T>
inline TypeId getTypeId() {
static TypeId id = nextTypeId();
return id;
}
inline TypeId nextTypeId() {
static TypeId id = 0;
return ++id;
}
template <typename T> inline TypeId getTypeId() {
static TypeId id = nextTypeId();
return id;
}
} // namespace detail
/**
* @brief
*
*/
class Registry {
public:
static constexpr size_t MAX_MODULES = 64;
static Registry& instance();
static constexpr size_t MAX_MODULES = 64;
Registry(const Registry&) = delete;
Registry& operator=(const Registry&) = delete;
static Registry &instance();
/**
* @brief
* @tparam T
* @tparam Args
* @param args
* @return
*/
template<typename T, typename... Args>
T* use(Args&&... args) {
static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
TypeId typeId = detail::getTypeId<T>();
// 数组查找O(n) 但 n 很小,缓存友好
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].id == typeId) {
return static_cast<T*>(modules_[i].module.get());
}
}
// 添加新模块
if (moduleCount_ >= MAX_MODULES) {
return nullptr; // 模块数量超过上限
}
auto module = ptr::makeUnique<T>(std::forward<Args>(args)...);
T* ptr = module.get();
module->setApp(app_);
modules_[moduleCount_].id = typeId;
modules_[moduleCount_].module = std::move(module);
modules_[moduleCount_].valid = true;
++moduleCount_;
return ptr;
Registry(const Registry &) = delete;
Registry &operator=(const Registry &) = delete;
/**
* @brief
* @tparam T
* @tparam Args
* @param args
* @return
*/
template <typename T, typename... Args> T *use(Args &&...args) {
static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
TypeId typeId = detail::getTypeId<T>();
// 数组查找O(n) 但 n 很小,缓存友好
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].id == typeId) {
return static_cast<T *>(modules_[i].module.get());
}
}
/**
* @brief
* @tparam T
* @return nullptr
*/
template<typename T>
T* get() const {
TypeId typeId = detail::getTypeId<T>();
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].id == typeId && modules_[i].valid) {
return static_cast<T*>(modules_[i].module.get());
}
}
return nullptr;
// 添加新模块
if (moduleCount_ >= MAX_MODULES) {
return nullptr; // 模块数量超过上限
}
/**
* @brief
* @param typeIdx
* @return
*/
Module* get(std::type_index typeIdx) const {
// 这里仍然使用type_index作为后备方案
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].valid &&
std::type_index(typeid(*modules_[i].module)) == typeIdx) {
return modules_[i].module.get();
}
}
return nullptr;
auto module = ptr::unique<T>(std::forward<Args>(args)...);
T *ptr = module.get();
module->setApp(app_);
modules_[moduleCount_].id = typeId;
modules_[moduleCount_].module = std::move(module);
modules_[moduleCount_].valid = true;
++moduleCount_;
return ptr;
}
/**
* @brief
* @tparam T
* @return nullptr
*/
template <typename T> T *get() const {
TypeId typeId = detail::getTypeId<T>();
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].id == typeId && modules_[i].valid) {
return static_cast<T *>(modules_[i].module.get());
}
}
return nullptr;
}
/**
* @brief Application
*/
void setApp(Application* app) { app_ = app; }
/**
* @brief
* @param typeIdx
* @return
*/
Module *get(std::type_index typeIdx) const {
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].valid) {
Module *mod = modules_[i].module.get();
if (std::type_index(typeid(*mod)) == typeIdx) {
return mod;
}
}
}
return nullptr;
}
/**
* @brief
* @return true
*/
bool init();
/**
* @brief Application
*/
void setApp(Application *app) { app_ = app; }
/**
* @brief
*/
void shutdown();
/**
* @brief
* @return true
*/
bool init();
/**
* @brief
*/
void clear();
/**
* @brief
*/
void shutdown();
/**
* @brief
*/
size_t size() const { return moduleCount_; }
/**
* @brief
*/
void clear();
/**
* @brief
*/
size_t size() const { return moduleCount_; }
private:
Registry() = default;
~Registry() = default;
Registry() = default;
~Registry() = default;
struct ModuleEntry {
TypeId id = 0;
Unique<Module> module;
bool valid = false;
};
struct ModuleEntry {
TypeId id = 0;
Unique<Module> module;
bool valid = false;
};
/**
* @brief
* @return
*/
std::vector<Module*> sort();
/**
* @brief
* @return
*/
std::vector<Module *> sort();
/**
* @brief
*
* @return
*/
std::vector<std::vector<Module*>> group();
/**
* @brief
*
* @return
*/
std::vector<std::vector<Module *>> group();
std::array<ModuleEntry, MAX_MODULES> modules_;
size_t moduleCount_ = 0;
Application* app_ = nullptr;
std::array<ModuleEntry, MAX_MODULES> modules_;
size_t moduleCount_ = 0;
Application *app_ = nullptr;
};
} // namespace extra2d

View File

@ -3,7 +3,6 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
namespace extra2d {
@ -22,18 +21,18 @@ template <typename T> using Weak = std::weak_ptr<T>;
/// 智能指针工厂函数命名空间
namespace ptr {
/// 创建 Ref 的便捷函数
template <typename T, typename... Args> inline Ref<T> make(Args &&...args) {
return std::make_shared<T>(std::forward<Args>(args)...);
}
/// 创建 Unique 的便捷函数
template <typename T, typename... Args>
inline Unique<T> makeUnique(Args &&...args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
/// 创建 Ref 的便捷函数
template <typename T, typename... Args> inline Ref<T> make(Args &&...args) {
return std::make_shared<T>(std::forward<Args>(args)...);
}
/// 创建 Unique 的便捷函数
template <typename T, typename... Args>
inline Unique<T> unique(Args &&...args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
} // namespace ptr
// ---------------------------------------------------------------------------
// 函数别名
// ---------------------------------------------------------------------------

View File

@ -36,11 +36,10 @@ enum class LogLevel : u8 {
Trace = 0,
Debug = 1,
Info = 2,
Registry = 3,
Warn = 4,
Error = 5,
Fatal = 6,
Off = 7
Warn = 3,
Error = 4,
Fatal = 5,
Off = 6
};
/**
@ -60,7 +59,6 @@ public:
virtual void trace(const char *fmt, ...) = 0;
virtual void debug(const char *fmt, ...) = 0;
virtual void info(const char *fmt, ...) = 0;
virtual void registry(const char *fmt, ...) = 0;
virtual void warn(const char *fmt, ...) = 0;
virtual void error(const char *fmt, ...) = 0;
virtual void fatal(const char *fmt, ...) = 0;
@ -99,7 +97,6 @@ public:
void trace(const char *fmt, ...) override;
void debug(const char *fmt, ...) override;
void info(const char *fmt, ...) override;
void registry(const char *fmt, ...) override;
void warn(const char *fmt, ...) override;
void error(const char *fmt, ...) override;
void fatal(const char *fmt, ...) override;
@ -109,11 +106,12 @@ public:
void colors(bool on) override;
bool colors() const override;
private:
void output(LogLevel lvl, const char *msg);
const char *levelString(LogLevel lvl);
std::string ansiColor(LogLevel lvl);
private:
void output(LogLevel lvl, const char *msg);
LogLevel level_ = LogLevel::Info;
bool colors_ = true;
LogColor levelColors_[7];
@ -130,8 +128,11 @@ namespace extra2d {
namespace detail {
template <typename T> std::string to_string(T &&value) {
using D = std::decay_t<T>;
using Raw = std::remove_reference_t<T>;
if constexpr (std::is_same_v<D, std::string>)
return value;
else if constexpr (std::is_array_v<Raw>)
return std::string(value);
else if constexpr (std::is_same_v<D, const char *>)
return value ? value : "(null)";
else if constexpr (std::is_same_v<D, bool>)
@ -176,10 +177,36 @@ std::string format_str(const char *fmt, Args &&...args) {
log->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \
} while (0)
#define E2D_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_DEBUG(...) E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
#define E2D_INFO(...) E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_REGISTRY(...) E2D_LOG(::extra2d::LogLevel::Registry, __VA_ARGS__)
#define E2D_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
#define E2D_LOG_CAT(lvl, cat, ...) \
do { \
if (auto log = ::extra2d::ServiceLocator::instance() \
.tryGet<::extra2d::ILogger>()) \
if (log->enabled(lvl)) \
log->log(lvl, ::extra2d::format_str("[{}] {}", cat, __VA_ARGS__)); \
} while (0)
// 带类别的日志宏
#define E2D_TRACE(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Trace, cat, __VA_ARGS__)
#define E2D_DEBUG(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Debug, cat, __VA_ARGS__)
#define E2D_INFO(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Info, cat, __VA_ARGS__)
#define E2D_WARN(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Warn, cat, __VA_ARGS__)
#define E2D_ERROR(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Error, cat, __VA_ARGS__)
#define E2D_FATAL(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Fatal, cat, __VA_ARGS__)
// 常用日志类别
#define CAT_APP "应用"
#define CAT_RENDER "渲染"
#define CAT_SCENE "场景"
#define CAT_INPUT "输入"
#define CAT_AUDIO "音频"
#define CAT_ASSET "资源"
#define CAT_SYSTEM "系统"
#define CAT_DATA "存档"
#define CAT_MODULES "模块系统"
#define CAT_SERVICES "服务系统"

View File

@ -1,7 +1,6 @@
#pragma once
#include <extra2d/core/types.h>
#include <functional>
#include <string>
#include <vector>
@ -202,7 +201,7 @@ public:
private:
class Impl;
UniquePtr<Impl> impl_;
Unique<Impl> impl_;
std::string filename_;
std::string mountName_;
UserId defaultUserId_;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,623 @@
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// Before #including,
//
// #define STB_RECT_PACK_IMPLEMENTATION
//
// in the file that you want to have the implementation.
//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// github:IntellectualKitty
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
// Fabian Giesen
//
// Version history:
//
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
// 0.09 (2016-08-27) fix compiler warnings
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
//
// LICENSE
//
// See end of file for license information.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
#define STB_RECT_PACK_VERSION 1
#ifdef STBRP_STATIC
#define STBRP_DEF static
#else
#define STBRP_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
typedef int stbrp_coord;
#define STBRP__MAXVAL 0x7fffffff
// Mostly for internal use, but this is the maximum supported coordinate value.
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
// Rectangles which are successfully packed have the 'was_packed' flag
// set to a non-zero value and 'x' and 'y' store the minimum location
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
// if you imagine y increasing downwards). Rectangles which do not fit
// have the 'was_packed' flag set to 0.
//
// You should not try to access the 'rects' array from another thread
// while this function is running, as the function temporarily reorders
// the array while it executes.
//
// To pack into another rectangle, you need to call stbrp_init_target
// again. To continue packing into the same rectangle, you can call
// this function again. Calling this multiple times with multiple rect
// arrays will probably produce worse packing results than calling it
// a single time with the full rectangle array, but the option is
// available.
//
// The function returns 1 if all of the rectangles were successfully
// packed and 0 otherwise.
struct stbrp_rect
{
// reserved for your use:
int id;
// input:
stbrp_coord w, h;
// output:
stbrp_coord x, y;
int was_packed; // non-zero if valid packing
}; // 16 bytes, nominally
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
// Initialize a rectangle packer to:
// pack a rectangle that is 'width' by 'height' in dimensions
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
//
// You must call this function every time you start packing into a new target.
//
// There is no "shutdown" function. The 'nodes' memory must stay valid for
// the following stbrp_pack_rects() call (or calls), but can be freed after
// the call (or calls) finish.
//
// Note: to guarantee best results, either:
// 1. make sure 'num_nodes' >= 'width'
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
//
// If you don't do either of the above things, widths will be quantized to multiples
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
//
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
// may run out of temporary storage and be unable to pack some rectangles.
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
// Optionally call this function after init but before doing any packing to
// change the handling of the out-of-temp-memory scenario, described above.
// If you call init again, this will be reset to the default (false).
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
// Optionally select which packing heuristic the library should use. Different
// heuristics will produce better/worse results for different data sets.
// If you call init again, this will be reset to the default.
enum
{
STBRP_HEURISTIC_Skyline_default=0,
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
STBRP_HEURISTIC_Skyline_BF_sortHeight
};
//////////////////////////////////////////////////////////////////////////////
//
// the details of the following structures don't matter to you, but they must
// be visible so you can handle the memory allocations for them
struct stbrp_node
{
stbrp_coord x,y;
stbrp_node *next;
};
struct stbrp_context
{
int width;
int height;
int align;
int init_mode;
int heuristic;
int num_nodes;
stbrp_node *active_head;
stbrp_node *free_head;
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
};
#ifdef __cplusplus
}
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#define STBRP_ASSERT assert
#endif
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
#endif
enum
{
STBRP__INIT_skyline = 1
};
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
{
switch (context->init_mode) {
case STBRP__INIT_skyline:
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
STBRP_ASSERT(0);
}
}
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
{
if (allow_out_of_mem)
// if it's ok to run out of memory, then don't bother aligning them;
// this gives better packing, but may fail due to OOM (even though
// the rectangles easily fit). @TODO a smarter approach would be to only
// quantize once we've hit OOM, then we could get rid of this parameter.
context->align = 1;
else {
// if it's not ok to run out of memory, then quantize the widths
// so that num_nodes is always enough nodes.
//
// I.e. num_nodes * align >= width
// align >= width / num_nodes
// align = ceil(width/num_nodes)
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
}
}
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
nodes[i].next = NULL;
context->init_mode = STBRP__INIT_skyline;
context->heuristic = STBRP_HEURISTIC_Skyline_default;
context->free_head = &nodes[0];
context->active_head = &context->extra[0];
context->width = width;
context->height = height;
context->num_nodes = num_nodes;
stbrp_setup_allow_out_of_mem(context, 0);
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context->extra[0].x = 0;
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
context->extra[1].y = (1<<30);
context->extra[1].next = NULL;
}
// find minimum y position if it starts at x1
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
{
stbrp_node *node = first;
int x1 = x0 + width;
int min_y, visited_width, waste_area;
STBRP__NOTUSED(c);
STBRP_ASSERT(first->x <= x0);
#if 0
// skip in case we're past the node
while (node->next->x <= x0)
++node;
#else
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
#endif
STBRP_ASSERT(node->x <= x0);
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
node = node->next;
}
*pwaste = waste_area;
return min_y;
}
typedef struct
{
int x,y;
stbrp_node **prev_link;
} stbrp__findresult;
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
{
int best_waste = (1<<30), best_x, best_y = (1 << 30);
stbrp__findresult fr;
stbrp_node **prev, *node, *tail, **best = NULL;
// align to multiple of c->align
width = (width + c->align - 1);
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
// if it can't possibly fit, bail immediately
if (width > c->width || height > c->height) {
fr.prev_link = NULL;
fr.x = fr.y = 0;
return fr;
}
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
// bottom left
if (y < best_y) {
best_y = y;
best = prev;
}
} else {
// best-fit
if (y + height <= c->height) {
// can only use it if it first vertically
if (y < best_y || (y == best_y && waste < best_waste)) {
best_y = y;
best_waste = waste;
best = prev;
}
}
}
prev = &node->next;
node = node->next;
}
best_x = (best == NULL) ? 0 : (*best)->x;
// if doing best-fit (BF), we also have to try aligning right edge to each node position
//
// e.g, if fitting
//
// ____________________
// |____________________|
//
// into
//
// | |
// | ____________|
// |____________|
//
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
//
// This makes BF take about 2x the time
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
tail = c->active_head;
node = c->active_head;
prev = &c->active_head;
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail) {
int xpos = tail->x - width;
int y,waste;
STBRP_ASSERT(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node->next;
node = node->next;
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height <= c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
STBRP_ASSERT(y <= best_y);
best_y = y;
best_waste = waste;
best = prev;
}
}
}
tail = tail->next;
}
}
fr.prev_link = best;
fr.x = best_x;
fr.y = best_y;
return fr;
}
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
{
// find best position according to heuristic
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
stbrp_node *node, *cur;
// bail if:
// 1. it failed
// 2. the best node doesn't fit (we don't always check this)
// 3. we're out of memory
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
res.prev_link = NULL;
return res;
}
// on success, create new node
node = context->free_head;
node->x = (stbrp_coord) res.x;
node->y = (stbrp_coord) (res.y + height);
context->free_head = node->next;
// insert the new node into the right starting point, and
// let 'cur' point to the remaining nodes needing to be
// stiched back in
cur = *res.prev_link;
if (cur->x < res.x) {
// preserve the existing one, so start testing with the next one
stbrp_node *next = cur->next;
cur->next = node;
cur = next;
} else {
*res.prev_link = node;
}
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;
context->free_head = cur;
cur = next;
}
// stitch the list back in
node->next = cur;
if (cur->x < res.x + width)
cur->x = (stbrp_coord) (res.x + width);
#ifdef _DEBUG
cur = context->active_head;
while (cur->x < context->width) {
STBRP_ASSERT(cur->x < cur->next->x);
cur = cur->next;
}
STBRP_ASSERT(cur->next == NULL);
{
int count=0;
cur = context->active_head;
while (cur) {
cur = cur->next;
++count;
}
cur = context->free_head;
while (cur) {
cur = cur->next;
++count;
}
STBRP_ASSERT(count == context->num_nodes+2);
}
#endif
return res;
}
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
if (p->h > q->h)
return -1;
if (p->h < q->h)
return 1;
return (p->w > q->w) ? -1 : (p->w < q->w);
}
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
// we use the 'was_packed' field internally to allow sorting/unsorting
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = i;
}
// sort according to heuristic
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
if (rects[i].w == 0 || rects[i].h == 0) {
rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
if (fr.prev_link) {
rects[i].x = (stbrp_coord) fr.x;
rects[i].y = (stbrp_coord) fr.y;
} else {
rects[i].x = rects[i].y = STBRP__MAXVAL;
}
}
}
// unsort
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags and all_rects_packed status
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
if (!rects[i].was_packed)
all_rects_packed = 0;
}
// return the all_rects_packed status
return all_rects_packed;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

File diff suppressed because it is too large Load Diff

View File

@ -18,13 +18,13 @@ bool Application::init() {
if (initialized_)
return true;
E2D_INFO("Application initializing: {}", name);
E2D_INFO(CAT_APP, "Application initializing: {}", name);
ServiceLocator::instance().init();
window_ = new Window();
if (!window_->create({.title = name, .width = 1280, .height = 720})) {
E2D_ERROR("Failed to create window");
E2D_ERROR(CAT_APP, "Failed to create window");
delete window_;
window_ = nullptr;
return false;
@ -32,7 +32,7 @@ bool Application::init() {
auto &device = RenderDevice::instance();
if (!device.init(window_->sdlWindow())) {
E2D_ERROR("Failed to initialize render device");
E2D_ERROR(CAT_APP, "Failed to initialize render device");
window_->destroy();
delete window_;
window_ = nullptr;
@ -51,7 +51,7 @@ void Application::shutdown() {
if (!initialized_)
return;
E2D_INFO("Application shutting down");
E2D_INFO(CAT_APP, "Application shutting down");
RenderDevice::instance().shutdown();

View File

@ -11,52 +11,53 @@ namespace extra2d {
class FontAsset::Impl {
public:
stbtt_fontinfo info;
bool initialized = false;
stbtt_fontinfo info;
bool initialized = false;
};
// ---------------------------------------------------------------------------
// FontAsset 实现
// ---------------------------------------------------------------------------
FontAsset::FontAsset() : impl_(ptr::makeUnique<Impl>()) {}
FontAsset::FontAsset() : impl_(ptr::unique<Impl>()) {}
FontAsset::~FontAsset() = default;
bool FontAsset::loaded() const {
return state_.load(std::memory_order_acquire) == AssetState::Loaded && impl_->initialized;
bool FontAsset::loaded() const {
return state_.load(std::memory_order_acquire) == AssetState::Loaded &&
impl_->initialized;
}
float FontAsset::scaleForPixelHeight(float pixels) const {
if (!impl_->initialized || data_.empty()) {
return 0.0f;
}
return stbtt_ScaleForPixelHeight(&impl_->info, pixels);
if (!impl_->initialized || data_.empty()) {
return 0.0f;
}
return stbtt_ScaleForPixelHeight(&impl_->info, pixels);
}
bool FontAsset::setData(std::vector<u8> data) {
if (data.empty()) {
return false;
}
if (data.empty()) {
return false;
}
data_ = std::move(data);
data_ = std::move(data);
if (!stbtt_InitFont(&impl_->info, data_.data(), 0)) {
data_.clear();
impl_->initialized = false;
setState(AssetState::Failed);
return false;
}
if (!stbtt_InitFont(&impl_->info, data_.data(), 0)) {
data_.clear();
impl_->initialized = false;
setState(AssetState::Failed);
return false;
}
impl_->initialized = true;
setState(AssetState::Loaded);
return true;
impl_->initialized = true;
setState(AssetState::Loaded);
return true;
}
void FontAsset::release() {
data_.clear();
impl_->initialized = false;
setState(AssetState::Unloaded);
data_.clear();
impl_->initialized = false;
setState(AssetState::Unloaded);
}
}
} // namespace extra2d

View File

@ -21,13 +21,13 @@ bool AudioEngine::initialize() {
// 初始化 SDL 音频子系统
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
E2D_LOG_ERROR("Failed to initialize SDL audio: {}", SDL_GetError());
E2D_ERROR(CAT_AUDIO, "Failed to initialize SDL audio: {}", SDL_GetError());
return false;
}
// 打开音频设备
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0) {
E2D_LOG_ERROR("Failed to open audio: {}", Mix_GetError());
E2D_ERROR(CAT_AUDIO, "Failed to open audio: {}", Mix_GetError());
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return false;
}
@ -36,7 +36,7 @@ bool AudioEngine::initialize() {
Mix_AllocateChannels(32);
initialized_ = true;
E2D_LOG_INFO("AudioEngine initialized successfully (SDL2_mixer)");
E2D_INFO(CAT_AUDIO, "AudioEngine initialized successfully (SDL2_mixer)");
return true;
}
@ -51,7 +51,7 @@ void AudioEngine::shutdown() {
SDL_QuitSubSystem(SDL_INIT_AUDIO);
initialized_ = false;
E2D_LOG_INFO("AudioEngine shutdown");
E2D_INFO(CAT_AUDIO, "AudioEngine shutdown");
}
std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &filePath) {
@ -61,7 +61,7 @@ std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &filePath) {
std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &name,
const std::string &filePath) {
if (!initialized_) {
E2D_LOG_ERROR("AudioEngine not initialized");
E2D_ERROR(CAT_AUDIO, "AudioEngine not initialized");
return nullptr;
}
@ -73,14 +73,14 @@ std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &name,
Mix_Chunk *chunk = Mix_LoadWAV(filePath.c_str());
if (!chunk) {
E2D_LOG_ERROR("Failed to load sound: {} ({})", filePath, Mix_GetError());
E2D_ERROR("Failed to load sound: {} ({})", filePath, Mix_GetError());
return nullptr;
}
auto sound = std::shared_ptr<Sound>(new Sound(name, filePath, chunk));
sounds_[name] = sound;
E2D_LOG_DEBUG("Loaded sound: {}", filePath);
E2D_DEBUG("Loaded sound: {}", filePath);
return sound;
}
@ -96,14 +96,14 @@ void AudioEngine::unloadSound(const std::string &name) {
auto it = sounds_.find(name);
if (it != sounds_.end()) {
sounds_.erase(it);
E2D_LOG_DEBUG("Unloaded sound: {}", name);
E2D_DEBUG(CAT_AUDIO, "Unloaded sound: {}", name);
}
}
void AudioEngine::unloadAllSounds() {
stopAll();
sounds_.clear();
E2D_LOG_DEBUG("Unloaded all sounds");
E2D_DEBUG(CAT_AUDIO, "Unloaded all sounds");
}
void AudioEngine::setMasterVolume(float volume) {

View File

@ -21,7 +21,7 @@ Sound::~Sound() {
bool Sound::play() {
if (!chunk_) {
E2D_LOG_WARN("Sound::play() failed: chunk is null for {}", name_);
E2D_WARN(CAT_AUDIO, "Sound::play() failed: chunk is null for {}", name_);
return false;
}
@ -29,7 +29,8 @@ bool Sound::play() {
int newChannel = Mix_PlayChannel(-1, chunk_, loops);
if (newChannel < 0) {
E2D_LOG_WARN("Sound::play() failed: no free channel for {} ({})", name_, Mix_GetError());
E2D_WARN(CAT_AUDIO, "Sound::play() failed: no free channel for {} ({})", name_,
Mix_GetError());
return false;
}

View File

@ -12,8 +12,8 @@ Registry &Registry::instance() {
bool Registry::init() {
auto levels = group();
E2D_REGISTRY("正在初始化 {} 个模块,共 {} 个层级...", moduleCount_,
levels.size());
E2D_INFO(CAT_MODULES, "正在初始化 {} 个模块,共 {} 个层级...", moduleCount_,
levels.size());
for (size_t level = 0; level < levels.size(); ++level) {
auto &modules = levels[level];
@ -30,19 +30,20 @@ bool Registry::init() {
// 如果只有一个模块或不支持并行,使用串行初始化
if (modules.size() <= 1 || !hasParallelModules) {
for (auto *module : modules) {
E2D_REGISTRY("正在初始化模块: {} (层级 {})", module->name(), level);
E2D_INFO(CAT_MODULES, "正在初始化模块: {} (层级 {})", module->name(),
level);
if (!module->init()) {
E2D_ERROR("初始化模块失败: {}", module->name());
E2D_ERROR(CAT_MODULES, "初始化模块失败: {}", module->name());
return false;
}
E2D_REGISTRY("模块 {} 初始化成功", module->name());
E2D_INFO(CAT_MODULES, "模块 {} 初始化成功", module->name());
}
} else {
// 并行初始化当前层级的模块
E2D_REGISTRY("正在并行初始化 {} 个模块 (层级 {})...", modules.size(),
level);
E2D_INFO(CAT_MODULES, "正在并行初始化 {} 个模块 (层级 {})...",
modules.size(), level);
std::vector<std::future<std::pair<Module *, bool>>> futures;
std::vector<Module *> serialModules;
@ -62,28 +63,28 @@ bool Registry::init() {
for (auto &future : futures) {
auto [module, success] = future.get();
if (!success) {
E2D_ERROR("初始化模块失败: {}", module->name());
E2D_ERROR(CAT_MODULES, "初始化模块失败: {}", module->name());
return false;
}
E2D_REGISTRY("模块 {} 初始化成功 (并行)", module->name());
E2D_INFO(CAT_MODULES, "模块 {} 初始化成功 (并行)", module->name());
}
// 串行初始化不支持并行的模块
for (auto *module : serialModules) {
E2D_REGISTRY("正在初始化模块: {} (串行, 层级 {})", module->name(),
level);
E2D_INFO(CAT_MODULES, "正在初始化模块: {} (串行, 层级 {})",
module->name(), level);
if (!module->init()) {
E2D_ERROR("初始化模块失败: {}", module->name());
E2D_ERROR(CAT_MODULES, "初始化模块失败: {}", module->name());
return false;
}
E2D_REGISTRY("模块 {} 初始化成功", module->name());
E2D_INFO(CAT_MODULES, "模块 {} 初始化成功", module->name());
}
}
E2D_REGISTRY("层级 {} 初始化完成", level);
E2D_INFO(CAT_MODULES, "层级 {} 初始化完成", level);
}
E2D_REGISTRY("所有模块初始化完成");
E2D_INFO(CAT_MODULES, "所有模块初始化完成");
return true;
}
@ -98,7 +99,7 @@ void Registry::shutdown() {
void Registry::clear() {
shutdown();
// 销毁所有模块
for (size_t i = 0; i < moduleCount_; ++i) {
modules_[i].module.reset();
@ -116,12 +117,12 @@ std::vector<Module *> Registry::sort() {
for (size_t i = 0; i < moduleCount_; ++i) {
if (!modules_[i].valid)
continue;
auto deps = modules_[i].module->deps();
for (auto &depType : deps) {
// 查找依赖模块的索引
for (size_t j = 0; j < moduleCount_; ++j) {
if (modules_[j].valid &&
if (modules_[j].valid &&
std::type_index(typeid(*modules_[j].module)) == depType) {
adj[j].push_back(i);
inDegree[i]++;
@ -168,11 +169,11 @@ std::vector<std::vector<Module *>> Registry::group() {
for (size_t i = 0; i < moduleCount_; ++i) {
if (!modules_[i].valid)
continue;
auto deps = modules_[i].module->deps();
for (auto &depType : deps) {
for (size_t j = 0; j < moduleCount_; ++j) {
if (modules_[j].valid &&
if (modules_[j].valid &&
std::type_index(typeid(*modules_[j].module)) == depType) {
adj[j].push_back(i);
inDegree[i]++;

View File

@ -28,10 +28,10 @@ bool RenderDevice::init(SDL_Window *window, const RenderDeviceConfig &config) {
initialized_ = true;
E2D_INFO("RenderDevice initialized");
E2D_INFO("OpenGL Version: {}", caps_.version);
E2D_INFO("OpenGL Renderer: {}", caps_.renderer);
E2D_INFO("GLSL Version: {}", caps_.glslVersion);
E2D_INFO(CAT_RENDER, "RenderDevice initialized");
E2D_INFO(CAT_RENDER, "OpenGL Version: {}", caps_.version);
E2D_INFO(CAT_RENDER, "OpenGL Renderer: {}", caps_.renderer);
E2D_INFO(CAT_RENDER, "GLSL Version: {}", caps_.glslVersion);
return true;
}
@ -61,12 +61,12 @@ bool RenderDevice::initGL(const RenderDeviceConfig &config) {
glContext_ = SDL_GL_CreateContext(window_);
if (!glContext_) {
E2D_ERROR("SDL_GL_CreateContext failed: {}", SDL_GetError());
E2D_ERROR(CAT_RENDER, "SDL_GL_CreateContext failed: {}", SDL_GetError());
return false;
}
if (SDL_GL_MakeCurrent(window_, glContext_) != 0) {
E2D_ERROR("SDL_GL_MakeCurrent failed: {}", SDL_GetError());
E2D_ERROR(CAT_RENDER, "SDL_GL_MakeCurrent failed: {}", SDL_GetError());
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
@ -74,7 +74,7 @@ bool RenderDevice::initGL(const RenderDeviceConfig &config) {
if (gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) ==
0) {
E2D_ERROR("gladLoadGLLoader failed");
E2D_ERROR(CAT_RENDER, "gladLoadGLLoader failed");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
return false;
@ -98,7 +98,7 @@ void RenderDevice::shutdown() {
currentProgram_ = 0;
initialized_ = false;
E2D_INFO("RenderDevice shutdown");
E2D_INFO(CAT_RENDER, "RenderDevice shutdown");
}
void RenderDevice::swapBuffers() {

View File

@ -15,7 +15,7 @@ void SceneManager::runWithScene(Ref<Scene> scene) {
}
if (!sceneStack_.empty()) {
E2D_WARN("SceneManager: runWithScene should only be called once");
E2D_WARN(CAT_SCENE, "SceneManager: runWithScene should only be called once");
return;
}
@ -178,18 +178,18 @@ void SceneManager::render(Renderer& renderer) {
clearColor = sceneStack_.top()->getBackgroundColor();
}
E2D_TRACE("SceneManager::render - beginFrame");
E2D_TRACE(CAT_SCENE, "SceneManager::render - beginFrame");
renderer.beginFrame(clearColor);
if (!sceneStack_.empty()) {
E2D_TRACE("SceneManager::render - rendering scene content");
E2D_TRACE(CAT_SCENE, "SceneManager::render - rendering scene content");
sceneStack_.top()->renderContent(renderer);
} else {
E2D_WARN("SceneManager::render - no scene to render");
E2D_WARN(CAT_SCENE, "SceneManager::render - no scene to render");
}
renderer.endFrame();
E2D_TRACE("SceneManager::render - endFrame");
E2D_TRACE(CAT_SCENE, "SceneManager::render - endFrame");
}
void SceneManager::end() {

View File

@ -8,7 +8,7 @@ namespace extra2d {
// AssetService 实现
// ---------------------------------------------------------------------------
AssetService::AssetService() : cache_(ptr::makeUnique<AssetCache>()) {}
AssetService::AssetService() : cache_(ptr::unique<AssetCache>()) {}
AssetService::~AssetService() { shutdown(); }

View File

@ -1,267 +1,288 @@
#include <SDL.h>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <extra2d/services/logger_service.h>
#include <mutex>
#include <SDL.h>
namespace extra2d {
class ConsoleLogger::Impl {
public:
std::mutex mutex_;
SDL_LogPriority sdlPriorities_[7];
bool customOutputSet_ = false;
std::mutex mutex_;
SDL_LogPriority sdlPriorities_[7];
bool customOutputSet_ = false;
};
static SDL_LogPriority toSDLPriority(LogLevel lvl) {
switch (lvl) {
case LogLevel::Trace: return SDL_LOG_PRIORITY_VERBOSE;
case LogLevel::Debug: return SDL_LOG_PRIORITY_DEBUG;
case LogLevel::Info: return SDL_LOG_PRIORITY_INFO;
case LogLevel::Registry: return SDL_LOG_PRIORITY_INFO;
case LogLevel::Warn: return SDL_LOG_PRIORITY_WARN;
case LogLevel::Error: return SDL_LOG_PRIORITY_ERROR;
case LogLevel::Fatal: return SDL_LOG_PRIORITY_CRITICAL;
default: return SDL_LOG_PRIORITY_INFO;
}
static SDL_LogPriority toSDL(LogLevel lvl) {
switch (lvl) {
case LogLevel::Trace:
return SDL_LOG_PRIORITY_VERBOSE;
case LogLevel::Debug:
return SDL_LOG_PRIORITY_DEBUG;
case LogLevel::Info:
return SDL_LOG_PRIORITY_INFO;
case LogLevel::Warn:
return SDL_LOG_PRIORITY_WARN;
case LogLevel::Error:
return SDL_LOG_PRIORITY_ERROR;
case LogLevel::Fatal:
return SDL_LOG_PRIORITY_CRITICAL;
default:
return SDL_LOG_PRIORITY_INFO;
}
}
static LogLevel fromSDLPriority(SDL_LogPriority priority) {
switch (priority) {
case SDL_LOG_PRIORITY_VERBOSE: return LogLevel::Trace;
case SDL_LOG_PRIORITY_DEBUG: return LogLevel::Debug;
case SDL_LOG_PRIORITY_INFO: return LogLevel::Info;
case SDL_LOG_PRIORITY_WARN: return LogLevel::Warn;
case SDL_LOG_PRIORITY_ERROR: return LogLevel::Error;
case SDL_LOG_PRIORITY_CRITICAL: return LogLevel::Fatal;
default: return LogLevel::Info;
}
static LogLevel fromSDL(SDL_LogPriority priority) {
switch (priority) {
case SDL_LOG_PRIORITY_VERBOSE:
return LogLevel::Trace;
case SDL_LOG_PRIORITY_DEBUG:
return LogLevel::Debug;
case SDL_LOG_PRIORITY_INFO:
return LogLevel::Info;
case SDL_LOG_PRIORITY_WARN:
return LogLevel::Warn;
case SDL_LOG_PRIORITY_ERROR:
return LogLevel::Error;
case SDL_LOG_PRIORITY_CRITICAL:
return LogLevel::Fatal;
default:
return LogLevel::Info;
}
}
static ConsoleLogger* g_loggerInstance = nullptr;
static ConsoleLogger *g_loggerInstance = nullptr;
static void SDLCALL sdlLogOutputFunction(void* userdata, int category,
SDL_LogPriority priority,
const char* message) {
if (!g_loggerInstance) return;
ConsoleLogger* logger = static_cast<ConsoleLogger*>(g_loggerInstance);
LogLevel lvl = fromSDLPriority(priority);
if (!logger->enabled(lvl)) return;
const char* levelStr = logger->levelString(lvl);
const char* categoryStr = "";
switch (category) {
case SDL_LOG_CATEGORY_APPLICATION: categoryStr = "APP"; break;
case SDL_LOG_CATEGORY_ERROR: categoryStr = "ERR"; break;
case SDL_LOG_CATEGORY_ASSERT: categoryStr = "ASR"; break;
case SDL_LOG_CATEGORY_SYSTEM: categoryStr = "SYS"; break;
case SDL_LOG_CATEGORY_AUDIO: categoryStr = "AUD"; break;
case SDL_LOG_CATEGORY_VIDEO: categoryStr = "VID"; break;
case SDL_LOG_CATEGORY_RENDER: categoryStr = "RDR"; break;
case SDL_LOG_CATEGORY_INPUT: categoryStr = "INP"; break;
case SDL_LOG_CATEGORY_TEST: categoryStr = "TST"; break;
default: categoryStr = "GEN"; break;
}
Uint32 ticks = SDL_GetTicks();
Uint32 seconds = ticks / 1000;
Uint32 minutes = seconds / 60;
Uint32 hours = minutes / 60;
if (logger->colors()) {
std::string color = logger->ansiColor(lvl);
const char* reset = "\033[0m";
printf("%s[%02u:%02u:%02u.%03u] [%s][%s] %s%s\n",
color.c_str(),
hours % 24, minutes % 60, seconds % 60, ticks % 1000,
levelStr, categoryStr, message, reset);
} else {
printf("[%02u:%02u:%02u.%03u] [%s][%s] %s\n",
hours % 24, minutes % 60, seconds % 60, ticks % 1000,
levelStr, categoryStr, message);
/**
* @brief
*/
static bool parseCategory(const char *msg, std::string &cat,
const char *&content) {
cat = "应用";
content = msg;
if (msg[0] == '[') {
const char *end = strchr(msg + 1, ']');
if (end && end > msg + 1) {
size_t len = end - msg - 1;
if (len > 0 && len < 20) {
cat = std::string(msg + 1, len);
content = end + 1;
while (*content == ' ')
content++;
return true;
}
}
}
return false;
}
static void SDLCALL logOutput(void *userdata, int category,
SDL_LogPriority priority, const char *message) {
if (!g_loggerInstance)
return;
ConsoleLogger *logger = static_cast<ConsoleLogger *>(g_loggerInstance);
LogLevel lvl = fromSDL(priority);
if (!logger->enabled(lvl))
return;
const char *levelStr = logger->levelString(lvl);
// 解析类别和内容
std::string cat;
const char *content;
parseCategory(message, cat, content);
Uint32 ticks = SDL_GetTicks();
Uint32 seconds = ticks / 1000;
Uint32 minutes = seconds / 60;
Uint32 hours = minutes / 60;
if (logger->colors()) {
std::string color = logger->ansiColor(lvl);
const char *reset = "\033[0m";
printf("%s[%02u:%02u:%02u.%03u] [%s][%s] %s%s\n", color.c_str(), hours % 24,
minutes % 60, seconds % 60, ticks % 1000, levelStr, cat.c_str(),
content, reset);
} else {
printf("[%02u:%02u:%02u.%03u] [%s][%s] %s\n", hours % 24, minutes % 60,
seconds % 60, ticks % 1000, levelStr, cat.c_str(), content);
}
}
ConsoleLogger::ConsoleLogger()
: level_(LogLevel::Info), colors_(true),
impl_(std::make_unique<Impl>()) {
info_.name = "ConsoleLogger";
info_.priority = ServicePriority::Core;
: level_(LogLevel::Info), colors_(true), impl_(std::make_unique<Impl>()) {
info_.name = "ConsoleLogger";
info_.priority = ServicePriority::Core;
levelColors_[static_cast<int>(LogLevel::Trace)] = LogColor::Gray();
levelColors_[static_cast<int>(LogLevel::Debug)] = LogColor::Cyan();
levelColors_[static_cast<int>(LogLevel::Info)] = LogColor::SkyLight();
levelColors_[static_cast<int>(LogLevel::Registry)] = LogColor::IndigoLight();
levelColors_[static_cast<int>(LogLevel::Warn)] = LogColor::Yellow();
levelColors_[static_cast<int>(LogLevel::Error)] = LogColor::Red();
levelColors_[static_cast<int>(LogLevel::Fatal)] = LogColor::Magenta();
levelColors_[static_cast<int>(LogLevel::Trace)] = LogColor::Gray();
levelColors_[static_cast<int>(LogLevel::Debug)] = LogColor::Cyan();
levelColors_[static_cast<int>(LogLevel::Info)] = LogColor::SkyLight();
levelColors_[static_cast<int>(LogLevel::Warn)] = LogColor::Yellow();
levelColors_[static_cast<int>(LogLevel::Error)] = LogColor::Red();
levelColors_[static_cast<int>(LogLevel::Fatal)] = LogColor::Magenta();
for (int i = 0; i < 7; ++i) {
impl_->sdlPriorities_[i] = toSDLPriority(static_cast<LogLevel>(i));
}
for (int i = 0; i < 7; ++i) {
impl_->sdlPriorities_[i] = toSDL(static_cast<LogLevel>(i));
}
}
ConsoleLogger::~ConsoleLogger() {
if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr);
}
g_loggerInstance = nullptr;
if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr);
}
g_loggerInstance = nullptr;
}
bool ConsoleLogger::init() {
g_loggerInstance = this;
SDL_LogSetOutputFunction(sdlLogOutputFunction, this);
impl_->customOutputSet_ = true;
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, toSDLPriority(level_));
setState(ServiceState::Running);
return true;
g_loggerInstance = this;
SDL_LogSetOutputFunction(logOutput, this);
impl_->customOutputSet_ = true;
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, toSDL(level_));
setState(ServiceState::Running);
return true;
}
void ConsoleLogger::shutdown() {
if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr);
impl_->customOutputSet_ = false;
}
g_loggerInstance = nullptr;
setState(ServiceState::Stopped);
if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr);
impl_->customOutputSet_ = false;
}
g_loggerInstance = nullptr;
setState(ServiceState::Stopped);
}
void ConsoleLogger::level(LogLevel lvl) {
level_ = lvl;
SDL_LogPriority priority = toSDLPriority(lvl);
SDL_LogSetAllPriority(priority);
void ConsoleLogger::level(LogLevel lvl) {
level_ = lvl;
SDL_LogPriority priority = toSDL(lvl);
SDL_LogSetAllPriority(priority);
}
LogLevel ConsoleLogger::level() const {
return level_;
}
LogLevel ConsoleLogger::level() const { return level_; }
bool ConsoleLogger::enabled(LogLevel lvl) const {
return static_cast<int>(lvl) >= static_cast<int>(level_);
return static_cast<int>(lvl) >= static_cast<int>(level_);
}
void ConsoleLogger::log(LogLevel lvl, const char *fmt, ...) {
if (!enabled(lvl)) return;
if (!enabled(lvl))
return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogPriority priority = toSDLPriority(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", buffer);
SDL_LogPriority priority = toSDL(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", buffer);
}
void ConsoleLogger::log(LogLevel lvl, const std::string &msg) {
if (!enabled(lvl)) return;
SDL_LogPriority priority = toSDLPriority(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", msg.c_str());
if (!enabled(lvl))
return;
SDL_LogPriority priority = toSDL(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", msg.c_str());
}
void ConsoleLogger::trace(const char *fmt, ...) {
if (!enabled(LogLevel::Trace)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
if (!enabled(LogLevel::Trace))
return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
void ConsoleLogger::debug(const char *fmt, ...) {
if (!enabled(LogLevel::Debug)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
if (!enabled(LogLevel::Debug))
return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
void ConsoleLogger::info(const char *fmt, ...) {
if (!enabled(LogLevel::Info)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
if (!enabled(LogLevel::Info))
return;
void ConsoleLogger::registry(const char *fmt, ...) {
if (!enabled(LogLevel::Registry)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[REGISTRY] %s", buffer);
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
void ConsoleLogger::warn(const char *fmt, ...) {
if (!enabled(LogLevel::Warn)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
if (!enabled(LogLevel::Warn))
return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
void ConsoleLogger::error(const char *fmt, ...) {
if (!enabled(LogLevel::Error)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
if (!enabled(LogLevel::Error))
return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
void ConsoleLogger::fatal(const char *fmt, ...) {
if (!enabled(LogLevel::Fatal)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
if (!enabled(LogLevel::Fatal))
return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
void ConsoleLogger::levelColor(LogLevel lvl, const LogColor &c) {
int idx = static_cast<int>(lvl);
if (idx >= 0 && idx < 7) {
levelColors_[idx] = c;
}
int idx = static_cast<int>(lvl);
if (idx >= 0 && idx < 7) {
levelColors_[idx] = c;
}
}
LogColor ConsoleLogger::levelColor(LogLevel lvl) const {
int idx = static_cast<int>(lvl);
if (idx >= 0 && idx < 7) {
return levelColors_[idx];
}
return LogColor::White();
int idx = static_cast<int>(lvl);
if (idx >= 0 && idx < 7) {
return levelColors_[idx];
}
return LogColor::White();
}
void ConsoleLogger::colors(bool on) { colors_ = on; }
@ -269,23 +290,29 @@ void ConsoleLogger::colors(bool on) { colors_ = on; }
bool ConsoleLogger::colors() const { return colors_; }
std::string ConsoleLogger::ansiColor(LogLevel lvl) {
const LogColor &c = levelColor(lvl);
char buf[32];
snprintf(buf, sizeof(buf), "\033[38;2;%d;%d;%dm", c.r, c.g, c.b);
return std::string(buf);
const LogColor &c = levelColor(lvl);
char buf[32];
snprintf(buf, sizeof(buf), "\033[38;2;%d;%d;%dm", c.r, c.g, c.b);
return std::string(buf);
}
const char *ConsoleLogger::levelString(LogLevel lvl) {
switch (lvl) {
case LogLevel::Trace: return "TRACE";
case LogLevel::Debug: return "DEBUG";
case LogLevel::Info: return "INFO";
case LogLevel::Registry: return "REGISTRY";
case LogLevel::Warn: return "WARN";
case LogLevel::Error: return "ERROR";
case LogLevel::Fatal: return "FATAL";
default: return "UNKNOWN";
}
switch (lvl) {
case LogLevel::Trace:
return "TRACE";
case LogLevel::Debug:
return "DEBUG";
case LogLevel::Info:
return "INFO";
case LogLevel::Warn:
return "WARN";
case LogLevel::Error:
return "ERROR";
case LogLevel::Fatal:
return "FATAL";
default:
return "UNKNOWN";
}
}
} // namespace extra2d

View File

@ -1,5 +1,5 @@
#include <extra2d/utils/data.h>
#include <extra2d/services/logger_service.h>
#include <extra2d/utils/data.h>
#include <simpleini/SimpleIni.h>
// Switch 平台特定头文件
@ -15,7 +15,7 @@ public:
CSimpleIniA ini;
};
DataStore::DataStore() : impl_(makeUnique<Impl>()) {}
DataStore::DataStore() : impl_(ptr::unique<Impl>()) {}
DataStore::~DataStore() {
// 如果在事务中,尝试提交
@ -48,7 +48,7 @@ bool DataStore::save(const std::string &filename) {
const std::string &targetFile = filename.empty() ? filename_ : filename;
if (targetFile.empty()) {
E2D_LOG_ERROR("DataStore::save: 没有指定文件名");
E2D_ERROR(CAT_APP, "DataStore::save: 没有指定文件名");
return false;
}
@ -58,7 +58,7 @@ bool DataStore::save(const std::string &filename) {
bool DataStore::internalSave(const std::string &filename) {
SI_Error rc = impl_->ini.SaveFile(filename.c_str());
if (rc < 0) {
E2D_LOG_ERROR("DataStore::save: 保存文件失败: {}", filename);
E2D_ERROR(CAT_DATA, "DataStore::save: 保存文件失败: {}", filename);
return false;
}
dirty_ = false;
@ -87,7 +87,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
uid.uid[0] = currentUid.uid[0];
uid.uid[1] = currentUid.uid[1];
if (uid.uid[0] == 0 && uid.uid[1] == 0) {
E2D_LOG_ERROR("DataStore::mountSaveData: 无法获取当前用户ID");
E2D_ERROR(CAT_DATA, "DataStore::mountSaveData: 无法获取当前用户ID");
return false;
}
}
@ -98,7 +98,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
rc = fsdevMountSaveData(mountName.c_str(), applicationId, uid);
if (R_FAILED(rc)) {
E2D_LOG_ERROR("DataStore::mountSaveData: 挂载失败: 0x{:X}", rc);
E2D_ERROR(CAT_DATA, "DataStore::mountSaveData: 挂载失败: 0x{:X}", rc);
return false;
}
@ -106,7 +106,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
saveDataMounted_ = true;
defaultUserId_ = UserId{uid.uid[0], uid.uid[1]};
E2D_LOG_INFO("DataStore::mountSaveData: 成功挂载存档: {}", mountName);
E2D_INFO(CAT_DATA, "DataStore::mountSaveData: 成功挂载存档: {}", mountName);
return true;
}
@ -124,22 +124,22 @@ void DataStore::unmountSaveData(const std::string &mountName) {
saveDataMounted_ = false;
mountName_.clear();
E2D_LOG_INFO("DataStore::unmountSaveData: 已卸载存档");
E2D_INFO("DataStore::unmountSaveData: 已卸载存档");
}
bool DataStore::commitSaveData(const std::string &mountName) {
if (!saveDataMounted_) {
E2D_LOG_WARN("DataStore::commitSaveData: 存档未挂载");
E2D_WARN(CAT_DATA, "DataStore::commitSaveData: 存档未挂载");
return false;
}
Result rc = fsdevCommitDevice(mountName.c_str());
if (R_FAILED(rc)) {
E2D_LOG_ERROR("DataStore::commitSaveData: 提交失败: 0x{:X}", rc);
E2D_ERROR("DataStore::commitSaveData: 提交失败: 0x{:X}", rc);
return false;
}
E2D_LOG_DEBUG("DataStore::commitSaveData: 提交成功");
E2D_DEBUG("DataStore::commitSaveData: 提交成功");
return true;
}
@ -155,8 +155,9 @@ UserId DataStore::getCurrentUserId() {
Result rc = accountInitialize(AccountServiceType_Application);
if (R_FAILED(rc)) {
E2D_LOG_ERROR("DataStore::getCurrentUserId: accountInitialize 失败: 0x{:X}",
rc);
E2D_ERROR(CAT_DATA,
"DataStore::getCurrentUserId: accountInitialize 失败: 0x{:X}",
rc);
return result;
}
@ -167,10 +168,10 @@ UserId DataStore::getCurrentUserId() {
if (R_SUCCEEDED(rc)) {
result.uid[0] = uid.uid[0];
result.uid[1] = uid.uid[1];
E2D_LOG_DEBUG("DataStore::getCurrentUserId: 获取成功: 0x{:X}{:X}",
result.uid[1], result.uid[0]);
E2D_DEBUG("DataStore::getCurrentUserId: 获取成功: 0x{:X}{:X}",
result.uid[1], result.uid[0]);
} else {
E2D_LOG_ERROR("DataStore::getCurrentUserId: 获取失败: 0x{:X}", rc);
E2D_ERROR(CAT_DATA, "DataStore::getCurrentUserId: 获取失败: 0x{:X}", rc);
}
return result;
@ -185,7 +186,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
(void)type;
(void)userId;
(void)mountName;
E2D_LOG_WARN("DataStore::mountSaveData: 非 Switch 平台,存档功能不可用");
E2D_WARN(CAT_DATA, "非 Switch 平台,存档功能不可用");
return false;
}
@ -203,9 +204,7 @@ std::string DataStore::getSaveDataPath(const std::string &path) const {
return path;
}
UserId DataStore::getCurrentUserId() {
return UserId();
}
UserId DataStore::getCurrentUserId() { return UserId(); }
#endif
@ -329,19 +328,19 @@ void DataStore::clear() {
void DataStore::beginTransaction() {
if (inTransaction_) {
E2D_LOG_WARN("DataStore::beginTransaction: 已经处于事务中");
E2D_WARN(CAT_DATA, "DataStore::beginTransaction: 已经处于事务中");
return;
}
inTransaction_ = true;
dirty_ = false;
E2D_LOG_DEBUG("DataStore::beginTransaction: 事务开始");
E2D_DEBUG(CAT_DATA, "DataStore::beginTransaction: 事务开始");
}
bool DataStore::commit() {
if (!inTransaction_) {
E2D_LOG_WARN("DataStore::commit: 不在事务中");
E2D_WARN(CAT_DATA, "DataStore::commit: 不在事务中");
return false;
}
@ -358,13 +357,14 @@ bool DataStore::commit() {
inTransaction_ = false;
E2D_LOG_DEBUG("DataStore::commit: 事务提交 {}", result ? "成功" : "失败");
E2D_DEBUG(CAT_DATA, "DataStore::commit: 事务提交 {}",
result ? "成功" : "失败");
return result;
}
void DataStore::rollback() {
if (!inTransaction_) {
E2D_LOG_WARN("DataStore::rollback: 不在事务中");
E2D_WARN(CAT_DATA, "DataStore::rollback: 不在事务中");
return;
}
@ -373,7 +373,8 @@ void DataStore::rollback() {
impl_->ini.Reset();
SI_Error rc = impl_->ini.LoadFile(filename_.c_str());
if (rc < 0) {
E2D_LOG_ERROR("DataStore::rollback: 重新加载文件失败: {}", filename_);
E2D_ERROR(CAT_DATA, "DataStore::rollback: 重新加载文件失败: {}",
filename_);
}
} else {
// 如果没有文件名,清空数据
@ -383,7 +384,7 @@ void DataStore::rollback() {
inTransaction_ = false;
dirty_ = false;
E2D_LOG_DEBUG("DataStore::rollback: 事务已回滚");
E2D_DEBUG(CAT_DATA, "DataStore::rollback: 事务已回滚");
}
// ============================================================================
@ -402,7 +403,8 @@ std::vector<std::string> DataStore::getAllSections() const {
return sections;
}
std::vector<std::string> DataStore::getAllKeys(const std::string &section) const {
std::vector<std::string>
DataStore::getAllKeys(const std::string &section) const {
std::vector<std::string> keys;
CSimpleIniA::TNamesDepend keyList;
impl_->ini.GetAllKeys(section.c_str(), keyList);
@ -416,7 +418,7 @@ std::vector<std::string> DataStore::getAllKeys(const std::string &section) const
bool DataStore::loadFromSave(const std::string &path) {
if (!saveDataMounted_) {
E2D_LOG_ERROR("DataStore::loadFromSave: 存档未挂载");
E2D_ERROR(CAT_DATA, "DataStore::loadFromSave: 存档未挂载");
return false;
}
@ -426,7 +428,7 @@ bool DataStore::loadFromSave(const std::string &path) {
bool DataStore::saveToSave(const std::string &path) {
if (!saveDataMounted_) {
E2D_LOG_ERROR("DataStore::saveToSave: 存档未挂载");
E2D_ERROR(CAT_DATA, "DataStore::saveToSave: 存档未挂载");
return false;
}

View File

@ -25,7 +25,7 @@ public:
* @brief 场景进入时调用
*/
void onEnter() override {
E2D_LOG_INFO("HelloWorldScene::onEnter - 进入场景");
E2D_INFO("HelloWorldScene::onEnter - 进入场景");
// 设置背景颜色为深蓝色
setBackgroundColor(Color(0.1f, 0.1f, 0.3f, 1.0f));
@ -35,7 +35,7 @@ public:
font_ = resources.loadFont("assets/font.ttf", 48, true);
if (!font_) {
E2D_LOG_ERROR("字体加载失败,文字渲染将不可用!");
E2D_ERROR("字体加载失败,文字渲染将不可用!");
return;
}
@ -68,7 +68,7 @@ public:
// 使用手柄 START 按钮退出 (GamepadButton::Start)
if (input.isButtonPressed(GamepadButton::Start)) {
E2D_LOG_INFO("退出应用 (START 按钮)");
E2D_INFO("退出应用 (START 按钮)");
Application::instance().quit();
}
}
@ -100,7 +100,7 @@ int main(int argc, char **argv)
// 初始化应用
if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!");
E2D_ERROR("应用初始化失败!");
return -1;
}
@ -203,10 +203,10 @@ auto texture = resources.loadTexture("assets/image.png");
使用宏进行日志输出:
```cpp
E2D_LOG_DEBUG("调试信息");
E2D_LOG_INFO("普通信息");
E2D_LOG_WARN("警告信息");
E2D_LOG_ERROR("错误信息");
E2D_DEBUG("调试信息");
E2D_INFO("普通信息");
E2D_WARN("警告信息");
E2D_ERROR("错误信息");
```
## 下一步

View File

@ -286,7 +286,7 @@ button->setBackgroundColor(
// 设置点击回调
button->setOnClick([]() {
E2D_LOG_INFO("按钮被点击!");
E2D_INFO("按钮被点击!");
});
addChild(button);
@ -661,7 +661,7 @@ node->runAction(loop);
auto action = makePtr<MoveTo>(2.0f, Vec2(100, 100));
action->setProgressCallback([](float progress) {
// progress: 0.0 - 1.0
E2D_LOG_INFO("动画进度: {}%", progress * 100);
E2D_INFO("动画进度: {}%", progress * 100);
});
node->runAction(action);
```

View File

@ -281,14 +281,14 @@ void ResLoader::init() {
// 加载图集纹理
atlasTexture_ = resources.loadTexture("assets/images/atlas.png");
if (!atlasTexture_) {
E2D_LOG_ERROR("无法加载图集纹理 atlas.png");
E2D_ERROR("无法加载图集纹理 atlas.png");
return;
}
// 加载 JSON 文件
std::string jsonContent = resources.loadJsonFile("assets/images/atlas.json");
if (jsonContent.empty()) {
E2D_LOG_ERROR("无法加载 atlas.json 文件");
E2D_ERROR("无法加载 atlas.json 文件");
return;
}
@ -307,9 +307,9 @@ void ResLoader::init() {
imageMap_[name] = info;
}
E2D_LOG_INFO("成功加载 {} 个精灵帧", imageMap_.size());
E2D_INFO("成功加载 {} 个精灵帧", imageMap_.size());
} catch (const std::exception &e) {
E2D_LOG_ERROR("解析 atlas.json 失败: {}", e.what());
E2D_ERROR("解析 atlas.json 失败: {}", e.what());
return;
}
@ -320,14 +320,14 @@ void ResLoader::init() {
soundMap_[MusicType::Point] = resources.loadSound("assets/sound/point.wav");
soundMap_[MusicType::Swoosh] = resources.loadSound("assets/sound/swoosh.wav");
E2D_LOG_INFO("资源加载完成");
E2D_INFO("资源加载完成");
}
extra2d::Ptr<extra2d::SpriteFrame>
ResLoader::getKeyFrame(const std::string &name) {
auto it = imageMap_.find(name);
if (it == imageMap_.end()) {
E2D_LOG_WARN("找不到精灵帧: %s", name.c_str());
E2D_WARN("找不到精灵帧: %s", name.c_str());
return nullptr;
}

View File

@ -132,7 +132,7 @@ public:
// 创建100个碰撞节点
createPhysicsNodes(100);
E2D_LOG_INFO("空间索引已启用: {}", isSpatialIndexingEnabled());
E2D_INFO("空间索引已启用: {}", isSpatialIndexingEnabled());
}
void onUpdate(float dt) override {
@ -184,10 +184,10 @@ private:
if (currentStrategy == SpatialStrategy::QuadTree) {
spatialManager.setStrategy(SpatialStrategy::SpatialHash);
E2D_LOG_INFO("切换到空间哈希策略");
E2D_INFO("切换到空间哈希策略");
} else {
spatialManager.setStrategy(SpatialStrategy::QuadTree);
E2D_LOG_INFO("切换到四叉树策略");
E2D_INFO("切换到四叉树策略");
}
}

View File

@ -70,7 +70,7 @@ button->setBorder(Colors::White, 2.0f);
// 设置点击回调
button->setOnClick([]() {
E2D_LOG_INFO("按钮被点击!");
E2D_INFO("按钮被点击!");
});
addChild(button);
@ -146,12 +146,12 @@ toggleBtn->toggle();
// 状态改变回调
toggleBtn->setOnStateChange([](bool isOn) {
E2D_LOG_INFO("切换按钮状态: {}", isOn ? "开启" : "关闭");
E2D_INFO("切换按钮状态: {}", isOn ? "开启" : "关闭");
});
// 点击回调(切换模式也会触发此回调)
toggleBtn->setOnClick([]() {
E2D_LOG_INFO("按钮被点击");
E2D_INFO("按钮被点击");
});
```
@ -384,7 +384,7 @@ checkBox->setTextColor(Colors::White);
// 状态改变回调
checkBox->setOnStateChange([](bool checked) {
E2D_LOG_INFO("复选框状态: {}", checked);
E2D_INFO("复选框状态: {}", checked);
});
addChild(checkBox);
@ -441,7 +441,7 @@ group->addButton(radio3.get());
// 选择改变回调
group->setOnSelectionChange([](RadioButton* selected) {
if (selected) {
E2D_LOG_INFO("选中: {}", selected->getLabel());
E2D_INFO("选中: {}", selected->getLabel());
}
});
@ -489,15 +489,15 @@ auto slider = Slider::create(0.0f, 100.0f, 50.0f);
// 值改变回调
slider->setOnValueChange([](float value) {
E2D_LOG_INFO("滑块值: {}", value);
E2D_INFO("滑块值: {}", value);
});
// 拖动开始/结束回调
slider->setOnDragStart([]() {
E2D_LOG_INFO("开始拖动");
E2D_INFO("开始拖动");
});
slider->setOnDragEnd([]() {
E2D_LOG_INFO("结束拖动");
E2D_INFO("结束拖动");
});
addChild(slider);
@ -643,7 +643,7 @@ public:
soundToggle_->setStateBackgroundImage(soundOff, soundOn);
soundToggle_->setOn(true);
soundToggle_->setOnStateChange([](bool isOn) {
E2D_LOG_INFO("音效: {}", isOn ? "开启" : "关闭");
E2D_INFO("音效: {}", isOn ? "开启" : "关闭");
AudioManager::instance().setEnabled(isOn);
});
addChild(soundToggle_);
@ -657,7 +657,7 @@ public:
volumeSlider_->setPosition(Vec2(350, 280));
volumeSlider_->setSize(200, 20);
volumeSlider_->setOnValueChange([](float value) {
E2D_LOG_INFO("音量: {:.0f}%", value * 100);
E2D_INFO("音量: {:.0f}%", value * 100);
});
addChild(volumeSlider_);

View File

@ -86,7 +86,7 @@ sprite->runAction(move);
```cpp
auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setCompletionCallback([]() {
E2D_LOG_INFO("动作完成!");
E2D_INFO("动作完成!");
});
sprite->runAction(action);
```
@ -194,7 +194,7 @@ auto sequence = Sequence::create(
MoveTo::create(1.0f, Vec2(100, 100)),
DelayTime::create(0.5f),
FadeOut::create(1.0f),
CallFunc::create([]() { E2D_LOG_INFO("完成"); }),
CallFunc::create([]() { E2D_INFO("完成"); }),
nullptr // 必须以 nullptr 结尾
);
sprite->runAction(sequence);
@ -373,12 +373,12 @@ spriteA->runAction(targeted);
```cpp
// 无参数回调
auto callFunc = CallFunc::create([]() {
E2D_LOG_INFO("回调执行");
E2D_INFO("回调执行");
});
// 带节点参数的回调
auto callFuncN = CallFuncN::create([](Node* node) {
E2D_LOG_INFO("节点: %p", node);
E2D_INFO("节点: %p", node);
});
```

View File

@ -52,7 +52,7 @@ private:
class CollisionDemoScene : public Scene {
public:
void onEnter() override {
E2D_LOG_INFO("CollisionDemoScene::onEnter - 碰撞检测演示");
E2D_INFO(CAT_SCENE, "CollisionDemoScene::onEnter - 碰撞检测演示");
// 设置背景色
setBackgroundColor(Color(0.05f, 0.05f, 0.1f, 1.0f));
@ -74,7 +74,7 @@ public:
// 加载字体并创建UI
loadFonts();
E2D_LOG_INFO("创建了 {} 个碰撞框", boxes_.size() + 1);
E2D_INFO("创建了 {} 个碰撞框", boxes_.size() + 1);
}
void onUpdate(float dt) override {
@ -106,7 +106,7 @@ public:
// 检查退出按键
auto &input = Application::instance().input();
if (input.isButtonPressed(GamepadButton::Start)) {
E2D_LOG_INFO("退出应用");
E2D_INFO(CAT_APP, "退出应用");
Application::instance().quit();
}
}
@ -149,7 +149,8 @@ private:
addChild(fpsText_);
// 创建退出提示文本
float screenHeight = static_cast<float>(Application::instance().getConfig().height);
float screenHeight =
static_cast<float>(Application::instance().getConfig().height);
exitHintText_ = Text::create("按 + 键退出", infoFont_);
exitHintText_->setPosition(50.0f, screenHeight - 50.0f);
exitHintText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f));
@ -238,15 +239,10 @@ private:
// 程序入口
// ============================================================================
int main(int argc, char **argv)
{
// 初始化日志系统
Logger::init();
Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D 碰撞检测演示");
E2D_LOG_INFO("========================");
int main(int argc, char **argv) {
E2D_INFO(CAT_APP, "========================");
E2D_INFO(CAT_APP, "Easy2D 碰撞检测演示");
E2D_INFO(CAT_APP, "========================");
// 获取应用实例
auto &app = Application::instance();
@ -261,19 +257,19 @@ int main(int argc, char **argv)
// 初始化应用
if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!");
E2D_ERROR(CAT_APP, "应用初始化失败!");
return -1;
}
// 进入场景
app.enterScene(makePtr<CollisionDemoScene>());
E2D_LOG_INFO("开始主循环...");
E2D_INFO(CAT_APP, "开始主循环...");
// 运行应用
app.run();
E2D_LOG_INFO("应用结束");
E2D_INFO(CAT_APP, "应用结束");
return 0;
}

View File

@ -16,7 +16,7 @@ public:
* @brief
*/
void onEnter() override {
E2D_LOG_INFO("HelloWorldScene::onEnter - 进入场景");
E2D_INFO("HelloWorldScene::onEnter - 进入场景");
// 设置背景颜色为深蓝色
setBackgroundColor(Color(0.1f, 0.1f, 0.3f, 1.0f));
@ -26,7 +26,7 @@ public:
font_ = resources.loadFont("assets/font.ttf", 48, true);
if (!font_) {
E2D_LOG_ERROR("字体加载失败,文字渲染将不可用!");
E2D_ERROR(CAT_ASSET, "字体加载失败,文字渲染将不可用!");
return;
}
@ -75,7 +75,7 @@ public:
// 使用手柄 START 按钮退出 (GamepadButton::Start)
if (input.isButtonPressed(GamepadButton::Start)) {
E2D_LOG_INFO("退出应用 (START 按钮)");
E2D_INFO("退出应用 (START 按钮)");
Application::instance().quit();
}
}
@ -89,13 +89,9 @@ private:
// ============================================================================
int main(int argc, char **argv) {
// 初始化日志系统
Logger::init();
Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D Hello World Demo");
E2D_LOG_INFO("========================");
E2D_INFO(CAT_APP, "========================");
E2D_INFO(CAT_APP, "Easy2D Hello World Demo");
E2D_INFO(CAT_APP, "========================");
// 获取应用实例
auto &app = Application::instance();
@ -110,19 +106,19 @@ int main(int argc, char **argv) {
// 初始化应用
if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!");
E2D_ERROR(CAT_APP, "应用初始化失败!");
return -1;
}
// 进入 Hello World 场景
app.enterScene(makePtr<HelloWorldScene>());
E2D_LOG_INFO("开始主循环...");
E2D_INFO(CAT_APP, "开始主循环...");
// 运行应用
app.run();
E2D_LOG_INFO("应用结束");
E2D_INFO(CAT_APP, "应用结束");
return 0;
}

View File

@ -240,7 +240,7 @@ public:
* @brief 场景进入时调用
*/
void onEnter() override {{
E2D_LOG_INFO("MainScene::onEnter - 进入场景");
E2D_INFO("MainScene::onEnter - 进入场景");
// 设置背景颜色
setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f));
@ -250,7 +250,7 @@ public:
font_ = resources.loadFont("assets/font.ttf", 32, true);
if (!font_) {{
E2D_LOG_ERROR("字体加载失败!请确保 assets 目录下有 font.ttf 文件");
E2D_ERROR("字体加载失败!请确保 assets 目录下有 font.ttf 文件");
return;
}}
@ -281,7 +281,7 @@ public:
// 检查退出按键
auto &input = Application::instance().input();
if (input.isButtonPressed(GamepadButton::Start)) {{
E2D_LOG_INFO("退出应用");
E2D_INFO("退出应用");
Application::instance().quit();
}}
}}
@ -299,9 +299,9 @@ int main(int argc, char **argv) {{
Logger::init();
Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================");
E2D_LOG_INFO("{self.project_name}");
E2D_LOG_INFO("========================");
E2D_INFO("========================");
E2D_INFO("{self.project_name}");
E2D_INFO("========================");
// 获取应用实例
auto &app = Application::instance();
@ -316,19 +316,19 @@ int main(int argc, char **argv) {{
// 初始化应用
if (!app.init(config)) {{
E2D_LOG_ERROR("应用初始化失败!");
E2D_ERROR("应用初始化失败!");
return -1;
}}
// 进入主场景
app.enterScene(makePtr<MainScene>());
E2D_LOG_INFO("开始主循环...");
E2D_INFO("开始主循环...");
// 运行应用
app.run();
E2D_LOG_INFO("应用结束");
E2D_INFO("应用结束");
return 0;
}}