refactor: 重构日志系统并优化智能指针命名
重构日志系统,移除Registry日志级别,增加带类别的日志宏 将makeUnique重命名为unique以保持一致性 更新所有相关文件和文档中的日志调用 优化代码格式和头文件包含
This commit is contained in:
parent
e600730105
commit
037b8599c7
|
|
@ -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 {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -27,26 +28,25 @@ namespace extra2d {
|
|||
* 存储资源引用和访问信息,用于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;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -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();
|
||||
auto it = lruList_.insert(lruList_.end(), id);
|
||||
|
||||
lruList_.erase(it->second.lruIterator);
|
||||
it->second.lruIterator = lruList_.insert(lruList_.end(), id);
|
||||
entries_[id] = CacheEntryData{
|
||||
ptr::unique<CacheEntry>(std::static_pointer_cast<Asset>(asset)), it};
|
||||
|
||||
++stats_.hits;
|
||||
++stats_.count;
|
||||
|
||||
auto typedAsset = std::static_pointer_cast<T>(it->second.entry->asset);
|
||||
return AssetHandle<T>(id, Weak<T>(typedAsset));
|
||||
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_;
|
||||
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_;
|
||||
size_t limit_ = 0;
|
||||
size_t bytes_ = 0;
|
||||
mutable CacheStats stats_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -20,41 +20,41 @@ namespace extra2d {
|
|||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -69,36 +69,32 @@ public:
|
|||
*
|
||||
* @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);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -113,29 +109,29 @@ public:
|
|||
*/
|
||||
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-4),0表示自动
|
||||
*/
|
||||
void setDesiredChannels(int channels);
|
||||
/**
|
||||
* @brief 设置期望的通道数
|
||||
* @param channels 通道数(1-4),0表示自动
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -149,11 +145,11 @@ private:
|
|||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -169,38 +165,39 @@ public:
|
|||
*/
|
||||
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);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -215,20 +212,20 @@ private:
|
|||
*/
|
||||
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);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -242,11 +239,11 @@ private:
|
|||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -261,52 +258,52 @@ public:
|
|||
*/
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 constexpr size_t MAX_MODULES = 64;
|
||||
|
||||
static Registry& instance();
|
||||
static Registry &instance();
|
||||
|
||||
Registry(const Registry&) = delete;
|
||||
Registry& operator=(const Registry&) = delete;
|
||||
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");
|
||||
/**
|
||||
* @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>();
|
||||
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;
|
||||
// 数组查找,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 函数别名
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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 "服务系统"
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace extra2d {
|
|||
// AssetService 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
AssetService::AssetService() : cache_(ptr::makeUnique<AssetCache>()) {}
|
||||
AssetService::AssetService() : cache_(ptr::unique<AssetCache>()) {}
|
||||
|
||||
AssetService::~AssetService() { shutdown(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
/**
|
||||
* @brief 解析日志消息,提取类别和内容
|
||||
*/
|
||||
static bool parseCategory(const char *msg, std::string &cat,
|
||||
const char *&content) {
|
||||
cat = "应用";
|
||||
content = msg;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
Uint32 ticks = SDL_GetTicks();
|
||||
Uint32 seconds = ticks / 1000;
|
||||
Uint32 minutes = seconds / 60;
|
||||
Uint32 hours = minutes / 60;
|
||||
static void SDLCALL logOutput(void *userdata, int category,
|
||||
SDL_LogPriority priority, const char *message) {
|
||||
if (!g_loggerInstance)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
g_loggerInstance = this;
|
||||
|
||||
SDL_LogSetOutputFunction(sdlLogOutputFunction, this);
|
||||
impl_->customOutputSet_ = true;
|
||||
SDL_LogSetOutputFunction(logOutput, this);
|
||||
impl_->customOutputSet_ = true;
|
||||
|
||||
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, toSDLPriority(level_));
|
||||
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, toSDL(level_));
|
||||
|
||||
setState(ServiceState::Running);
|
||||
return true;
|
||||
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);
|
||||
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;
|
||||
if (!enabled(lvl))
|
||||
return;
|
||||
|
||||
SDL_LogPriority priority = toSDLPriority(lvl);
|
||||
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", msg.c_str());
|
||||
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;
|
||||
if (!enabled(LogLevel::Trace))
|
||||
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_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
}
|
||||
|
||||
void ConsoleLogger::debug(const char *fmt, ...) {
|
||||
if (!enabled(LogLevel::Debug)) return;
|
||||
if (!enabled(LogLevel::Debug))
|
||||
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_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
}
|
||||
|
||||
void ConsoleLogger::info(const char *fmt, ...) {
|
||||
if (!enabled(LogLevel::Info)) return;
|
||||
if (!enabled(LogLevel::Info))
|
||||
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_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
}
|
||||
|
||||
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);
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
}
|
||||
|
||||
void ConsoleLogger::warn(const char *fmt, ...) {
|
||||
if (!enabled(LogLevel::Warn)) return;
|
||||
if (!enabled(LogLevel::Warn))
|
||||
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_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
}
|
||||
|
||||
void ConsoleLogger::error(const char *fmt, ...) {
|
||||
if (!enabled(LogLevel::Error)) return;
|
||||
if (!enabled(LogLevel::Error))
|
||||
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_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
}
|
||||
|
||||
void ConsoleLogger::fatal(const char *fmt, ...) {
|
||||
if (!enabled(LogLevel::Fatal)) return;
|
||||
if (!enabled(LogLevel::Fatal))
|
||||
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_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 §ion) const {
|
||||
std::vector<std::string>
|
||||
DataStore::getAllKeys(const std::string §ion) 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 §ion) 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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("错误信息");
|
||||
```
|
||||
|
||||
## 下一步
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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("切换到四叉树策略");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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_);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}}
|
||||
|
|
|
|||
Loading…
Reference in New Issue