refactor(platform): 重构平台兼容层与资源管理

- 移除 platform_compat.h 和 switch_compat.h,改为直接在 extra2d.h 中包含平台特定头文件
- 删除废弃的 FileSystem 类,简化资源路径处理逻辑
- 重构 ResourceManager 移除搜索路径管理,改为简单路径解析
- 优化 Window 类,移除平台判断代码,通过配置控制功能
- 更新示例项目使用新的资源加载方式
- 统一构建输出目录到项目根目录的 build 文件夹
- 添加 PlatformType 枚举支持显式指定平台类型
- 改进 Switch 平台初始化逻辑,支持配置控制
This commit is contained in:
ChestnutYueyue 2026-02-10 17:40:15 +08:00
parent 120b272abf
commit 4d81331a57
22 changed files with 169 additions and 1126 deletions

View File

@ -21,16 +21,24 @@ class Camera;
// ============================================================================
// Application 配置
// ============================================================================
enum class PlatformType {
Auto = 0,
PC,
Switch
};
struct AppConfig {
String title = "Easy2D Application";
int width = 800;
int height = 600;
bool fullscreen = false;
bool resizable = true; // 窗口是否可调整大小
bool resizable = true;
bool vsync = true;
int fpsLimit = 0; // 0 = 不限制
int fpsLimit = 0;
BackendType renderBackend = BackendType::OpenGL;
int msaaSamples = 0;
PlatformType platform = PlatformType::Auto;
};
// ============================================================================

View File

@ -12,7 +12,6 @@
// Platform
#include <extra2d/platform/window.h>
#include <extra2d/platform/input.h>
#include <extra2d/platform/file_system.h>
// Graphics
#include <extra2d/graphics/render_backend.h>
@ -96,3 +95,7 @@
// Script
#include <extra2d/script/script_engine.h>
#include <extra2d/script/script_node.h>
#ifdef __SWITCH__
#include <switch.h>
#endif

View File

@ -1,166 +0,0 @@
#pragma once
/**
* @file file_system.h
* @brief
*
*
* : Nintendo Switch, Windows, Linux, macOS
*/
#include <extra2d/platform/platform_compat.h>
#include <string>
#include <vector>
namespace extra2d {
// ============================================================================
// 文件系统工具类
// ============================================================================
class FileSystem {
public:
/**
* @brief
* @return
*
* Switch: "romfs:/"
* PC: + "/assets/"
*/
static std::string getResourceRoot();
/**
* @brief
* @param relativePath ( "assets/font.ttf")
* @return
*
* Switch: "romfs:/assets/font.ttf"
* PC: "./assets/font.ttf" exe目录/assets/font.ttf
*/
static std::string resolvePath(const std::string& relativePath);
/**
* @brief
* @param path
* @return true
*/
static bool fileExists(const std::string& path);
/**
* @brief
* @param path
* @return true
*/
static bool directoryExists(const std::string& path);
/**
* @brief
* @return
*/
static std::string getExecutableDirectory();
/**
* @brief
* @return
*/
static std::string getCurrentWorkingDirectory();
/**
* @brief
* @param base
* @param relative
* @return
*/
static std::string combinePath(const std::string& base, const std::string& relative);
/**
* @brief
* @param path
* @return
*/
static std::string getFileName(const std::string& path);
/**
* @brief
* @param path
* @return ".ttf"
*/
static std::string getFileExtension(const std::string& path);
/**
* @brief
* @param path
* @return
*/
static std::string getDirectoryName(const std::string& path);
/**
* @brief
* @param path
* @return
*/
static std::string normalizePath(const std::string& path);
/**
* @brief
* @param path
* @return
*/
static std::string readFileText(const std::string& path);
/**
* @brief
* @param path
* @return vector
*/
static std::vector<uint8_t> readFileBytes(const std::string& path);
/**
* @brief
* @param path
* @return -1
*/
static int64_t getFileSize(const std::string& path);
/**
* @brief
* @param path
* @return true
*/
static bool createDirectory(const std::string& path);
/**
* @brief
* @param path
* @return true
*/
static bool createDirectories(const std::string& path);
private:
// 禁止实例化
FileSystem() = delete;
~FileSystem() = delete;
};
// ============================================================================
// 便捷函数
// ============================================================================
/**
* @brief 便
* @param path
* @return
*/
inline std::string resolvePath(const std::string& path) {
return FileSystem::resolvePath(path);
}
/**
* @brief 便
* @param path
* @return true
*/
inline bool fileExists(const std::string& path) {
return FileSystem::fileExists(path);
}
} // namespace extra2d

View File

@ -3,7 +3,6 @@
#include <array>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/platform/platform_compat.h>
#include <extra2d/event/input_codes.h>
#include <SDL.h>

View File

@ -1,263 +0,0 @@
#pragma once
/**
* @file platform_compat.h
* @brief
*
*
* : Nintendo Switch, Windows, Linux, macOS
*/
// ============================================================================
// 平台检测
// ============================================================================
#ifdef __SWITCH__
// Nintendo Switch 平台
#define PLATFORM_SWITCH 1
#define PLATFORM_NAME "Nintendo Switch"
#elif defined(_WIN32)
// Windows 平台
#define PLATFORM_PC 1
#define PLATFORM_WINDOWS 1
#define PLATFORM_NAME "Windows"
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
// 禁用 SDL 的 main 重定义,使用标准 main 函数
#define SDL_MAIN_HANDLED
#elif defined(__linux__)
// Linux 平台
#define PLATFORM_PC 1
#define PLATFORM_LINUX 1
#define PLATFORM_NAME "Linux"
#elif defined(__APPLE__) && defined(__MACH__)
// macOS 平台
#define PLATFORM_PC 1
#define PLATFORM_MACOS 1
#define PLATFORM_NAME "macOS"
#else
#error "Unsupported platform"
#endif
// ============================================================================
// Nintendo Switch 平台包含
// ============================================================================
#ifdef PLATFORM_SWITCH
#include <switch.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <string>
// RomFS路径前缀
#define ROMFS_PREFIX "romfs:/"
// Switch 特定的编译器属性
#define E2D_LIKELY(x) __builtin_expect(!!(x), 1)
#define E2D_UNLIKELY(x) __builtin_expect(!!(x), 0)
// Switch 调试输出
#ifdef E2D_DEBUG
#define E2D_PLATFORM_LOG(fmt, ...) printf("[Extra2D] " fmt "\n", ##__VA_ARGS__)
#else
#define E2D_PLATFORM_LOG(fmt, ...) ((void)0)
#endif
#endif // PLATFORM_SWITCH
// ============================================================================
// PC 平台包含 (Windows/Linux/macOS)
// ============================================================================
#ifdef PLATFORM_PC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <string>
#include <cstdint>
// PC 平台使用标准文件路径
#define ROMFS_PREFIX ""
// PC 平台编译器属性
#if defined(__GNUC__) || defined(__clang__)
#define E2D_LIKELY(x) __builtin_expect(!!(x), 1)
#define E2D_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define E2D_LIKELY(x) (x)
#define E2D_UNLIKELY(x) (x)
#endif
// PC 调试输出
#ifdef E2D_DEBUG
#ifdef PLATFORM_WINDOWS
#include <windows.h>
#define E2D_PLATFORM_LOG(fmt, ...) OutputDebugStringA((std::string("[Extra2D] ") + fmt + "\n").c_str())
#else
#define E2D_PLATFORM_LOG(fmt, ...) printf("[Extra2D] " fmt "\n", ##__VA_ARGS__)
#endif
#else
#define E2D_PLATFORM_LOG(fmt, ...) ((void)0)
#endif
#endif // PLATFORM_PC
// ============================================================================
// 跨平台通用定义
// ============================================================================
namespace extra2d {
namespace platform {
/**
* @brief
* @return
*/
inline const char* getPlatformName() {
return PLATFORM_NAME;
}
/**
* @brief Switch
* @return true Switch
*/
inline bool isSwitch() {
#ifdef PLATFORM_SWITCH
return true;
#else
return false;
#endif
}
/**
* @brief PC
* @return true PC (Windows/Linux/macOS)
*/
inline bool isPC() {
#ifdef PLATFORM_PC
return true;
#else
return false;
#endif
}
/**
* @brief Windows
* @return true Windows
*/
inline bool isWindows() {
#ifdef PLATFORM_WINDOWS
return true;
#else
return false;
#endif
}
/**
* @brief Linux
* @return true Linux
*/
inline bool isLinux() {
#ifdef PLATFORM_LINUX
return true;
#else
return false;
#endif
}
/**
* @brief macOS
* @return true macOS
*/
inline bool isMacOS() {
#ifdef PLATFORM_MACOS
return true;
#else
return false;
#endif
}
} // namespace platform
// ============================================================================
// 文件系统路径工具
// ============================================================================
namespace romfs {
/**
* @brief RomFS
*/
#ifdef PLATFORM_SWITCH
static constexpr const char* ROOT = "romfs:/";
#else
static constexpr const char* ROOT = "";
#endif
/**
* @brief
* @param path
* @return true
*/
inline bool fileExists(const char* path) {
struct stat st;
return stat(path, &st) == 0;
}
/**
* @brief romfs (Switch) (PC)
* @param path
* @return true romfs
*/
inline bool isRomfsPath(const char* path) {
return path && (strncmp(path, "romfs:/", 7) == 0 || strncmp(path, "romfs:\\", 7) == 0);
}
/**
* @brief
* @param relativePath
* @return (Switch romfs:/ PC )
*/
inline std::string makePath(const char* relativePath) {
#ifdef PLATFORM_SWITCH
std::string result = ROOT;
result += relativePath;
return result;
#else
return std::string(relativePath);
#endif
}
} // namespace romfs
} // namespace extra2d
// ============================================================================
// 向后兼容:保留 switch_compat.h 的宏定义
// ============================================================================
#ifdef PLATFORM_SWITCH
#define IS_SWITCH_PLATFORM 1
#define SWITCH_ROMFS_PREFIX "romfs:/"
#define SWITCH_LIKELY(x) E2D_LIKELY(x)
#define SWITCH_UNLIKELY(x) E2D_UNLIKELY(x)
#ifdef E2D_DEBUG
#define SWITCH_DEBUG_PRINTF(fmt, ...) E2D_PLATFORM_LOG(fmt, ##__VA_ARGS__)
#else
#define SWITCH_DEBUG_PRINTF(fmt, ...) ((void)0)
#endif
#endif

View File

@ -1,17 +0,0 @@
#pragma once
/**
* @file switch_compat.h
* @brief Nintendo Switch ()
*
* @deprecated 使 platform_compat.h
*
*/
// 包含新的跨平台兼容性头文件
#include "platform_compat.h"
// 发出弃用警告(仅在非 Switch 平台或调试模式下)
#if !defined(__SWITCH__) && defined(E2D_DEBUG)
#warning "switch_compat.h is deprecated, use platform_compat.h instead"
#endif

View File

@ -3,7 +3,6 @@
#include <extra2d/core/types.h>
#include <extra2d/core/string.h>
#include <extra2d/core/math_types.h>
#include <extra2d/platform/platform_compat.h>
#include <functional>
#include <SDL.h>
@ -21,11 +20,13 @@ struct WindowConfig {
String title = "Extra2D Application";
int width = 1280;
int height = 720;
bool fullscreen = true; // Switch 始终全屏PC 可配置
bool resizable = false; // PC 端可调整大小
bool fullscreen = true;
bool resizable = false;
bool vsync = true;
int msaaSamples = 0;
bool centerWindow = true; // PC 端窗口居中
bool centerWindow = true;
bool enableCursors = true;
bool enableDpiScale = true;
};
// ============================================================================
@ -132,6 +133,7 @@ private:
bool focused_;
float contentScaleX_;
float contentScaleY_;
bool enableDpiScale_;
void* userData_;
EventQueue* eventQueue_;
UniquePtr<Input> input_;

View File

@ -8,7 +8,6 @@
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
@ -22,27 +21,6 @@ public:
// ------------------------------------------------------------------------
static ResourceManager &getInstance();
// ------------------------------------------------------------------------
// 搜索路径管理
// ------------------------------------------------------------------------
/// 添加资源搜索路径
void addSearchPath(const std::string &path);
/// 移除资源搜索路径
void removeSearchPath(const std::string &path);
/// 清空所有搜索路径
void clearSearchPaths();
/// 获取搜索路径列表
const std::vector<std::string> &getSearchPaths() const {
return searchPaths_;
}
/// 查找资源文件完整路径
std::string findResourcePath(const std::string &filename) const;
// ------------------------------------------------------------------------
// 纹理资源
// ------------------------------------------------------------------------
@ -83,14 +61,6 @@ public:
Ptr<FontAtlas> loadFont(const std::string &filepath, int fontSize,
bool useSDF = false);
/// 尝试从多个候选路径加载字体,返回第一个成功加载的字体
Ptr<FontAtlas> loadFontWithFallbacks(const std::vector<std::string> &fontPaths,
int fontSize, bool useSDF = false);
/// 加载字体,使用默认系统字体作为后备
Ptr<FontAtlas> loadFontWithDefaultFallback(const std::string &filepath,
int fontSize, bool useSDF = false);
/// 通过key获取已缓存的字体图集
Ptr<FontAtlas> getFont(const std::string &key) const;
@ -151,9 +121,6 @@ public:
mutable std::mutex fontMutex_;
mutable std::mutex soundMutex_;
// 搜索路径
std::vector<std::string> searchPaths_;
// 资源缓存 - 使用弱指针实现自动清理
std::unordered_map<std::string, WeakPtr<Texture>> textureCache_;
std::unordered_map<std::string, WeakPtr<FontAtlas>> fontCache_;

View File

@ -53,27 +53,39 @@ bool Application::init(const AppConfig &config) {
config_ = config;
// 确定平台类型
PlatformType platform = config_.platform;
if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
// ========================================
// 1. 初始化 RomFS 文件系统Switch 平台)
// ========================================
Result rc;
rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
platform = PlatformType::Switch;
#else
platform = PlatformType::PC;
#endif
}
// ========================================
// 2. 初始化 nxlink 调试输出Switch 平台)
// ========================================
rc = socketInitializeDefault();
if (R_FAILED(rc)) {
E2D_LOG_WARN(
"socketInitializeDefault failed, nxlink will not be available");
}
if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
// ========================================
// 1. 初始化 RomFS 文件系统Switch 平台)
// ========================================
Result rc;
rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc);
}
// ========================================
// 2. 初始化 nxlink 调试输出Switch 平台)
// ========================================
rc = socketInitializeDefault();
if (R_FAILED(rc)) {
E2D_LOG_WARN(
"socketInitializeDefault failed, nxlink will not be available");
}
#endif
}
// ========================================
// 3. 创建窗口(包含 SDL_Init + GLES 3.2 上下文创建)
@ -83,14 +95,18 @@ bool Application::init(const AppConfig &config) {
winConfig.title = config.title;
winConfig.width = 1280;
winConfig.height = 720;
#ifdef __SWITCH__
winConfig.fullscreen = true;
winConfig.resizable = false;
#else
// PC 平台默认窗口模式
winConfig.fullscreen = config.fullscreen;
winConfig.resizable = true;
#endif
if (platform == PlatformType::Switch) {
winConfig.fullscreen = true;
winConfig.resizable = false;
winConfig.enableCursors = false;
winConfig.enableDpiScale = false;
} else {
// PC 平台默认窗口模式
winConfig.fullscreen = config.fullscreen;
winConfig.resizable = true;
winConfig.enableCursors = true;
winConfig.enableDpiScale = true;
}
winConfig.vsync = config.vsync;
winConfig.msaaSamples = config.msaaSamples;
@ -138,14 +154,6 @@ bool Application::init(const AppConfig &config) {
// 初始化音频引擎
AudioEngine::getInstance().initialize();
#ifdef __SWITCH__
// 添加 romfs:/ 到资源搜索路径Switch 平台)
resourceManager_->addSearchPath("romfs:/");
#endif
// 添加默认资源路径
resourceManager_->addSearchPath("assets/");
resourceManager_->addSearchPath("./");
initialized_ = true;
running_ = true;
@ -185,11 +193,21 @@ void Application::shutdown() {
window_.reset();
}
#ifdef __SWITCH__
// Switch 平台清理
romfsExit();
socketExit();
PlatformType platform = config_.platform;
if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
platform = PlatformType::Switch;
#else
platform = PlatformType::PC;
#endif
}
if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
romfsExit();
socketExit();
#endif
}
initialized_ = false;
running_ = false;

View File

@ -1,6 +0,0 @@
#include <extra2d/graphics/render_command.h>
namespace extra2d {
// This file exists to allow compilation of the render_command header
// The RenderCommand struct is header-only for performance
} // namespace extra2d

View File

@ -1,315 +0,0 @@
#include <extra2d/platform/file_system.h>
#include <extra2d/utils/logger.h>
#include <sys/stat.h>
#include <fstream>
#include <sstream>
#include <algorithm>
#ifdef PLATFORM_WINDOWS
#include <windows.h>
#include <direct.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#else
#include <unistd.h>
#include <libgen.h>
#include <sys/types.h>
#include <dirent.h>
#include <limits.h>
#endif
namespace extra2d {
// ============================================================================
// 平台特定辅助函数
// ============================================================================
#ifdef PLATFORM_WINDOWS
static const char PATH_SEPARATOR = '\\';
static const char PATH_SEPARATOR_ALT = '/';
#else
static const char PATH_SEPARATOR = '/';
static const char PATH_SEPARATOR_ALT = '\\';
#endif
static std::string normalizeSeparators(const std::string& path) {
std::string result = path;
std::replace(result.begin(), result.end(), PATH_SEPARATOR_ALT, PATH_SEPARATOR);
return result;
}
// ============================================================================
// 资源根目录
// ============================================================================
std::string FileSystem::getResourceRoot() {
#ifdef PLATFORM_SWITCH
return "romfs:/";
#else
// PC 端:优先使用可执行文件目录下的 assets 文件夹
std::string exeDir = getExecutableDirectory();
std::string assetsDir = combinePath(exeDir, "assets");
if (directoryExists(assetsDir)) {
return assetsDir;
}
// 备选:当前工作目录
std::string cwd = getCurrentWorkingDirectory();
assetsDir = combinePath(cwd, "assets");
if (directoryExists(assetsDir)) {
return assetsDir;
}
// 默认返回当前工作目录
return cwd;
#endif
}
// ============================================================================
// 路径解析
// ============================================================================
std::string FileSystem::resolvePath(const std::string& relativePath) {
std::string normalized = normalizeSeparators(relativePath);
#ifdef PLATFORM_SWITCH
// Switch: 添加 romfs:/ 前缀
if (normalized.find("romfs:/") == 0) {
return normalized;
}
return "romfs:/" + normalized;
#else
// PC: 如果已经是绝对路径,直接返回
#ifdef PLATFORM_WINDOWS
if (normalized.size() >= 2 && normalized[1] == ':') {
return normalized;
}
#else
if (!normalized.empty() && normalized[0] == '/') {
return normalized;
}
#endif
// 组合资源根目录和相对路径
return combinePath(getResourceRoot(), normalized);
#endif
}
// ============================================================================
// 文件/目录检查
// ============================================================================
bool FileSystem::fileExists(const std::string& path) {
struct stat st;
if (stat(path.c_str(), &st) != 0) {
return false;
}
return (st.st_mode & S_IFREG) != 0;
}
bool FileSystem::directoryExists(const std::string& path) {
struct stat st;
if (stat(path.c_str(), &st) != 0) {
return false;
}
return (st.st_mode & S_IFDIR) != 0;
}
// ============================================================================
// 路径操作
// ============================================================================
std::string FileSystem::getExecutableDirectory() {
std::string exePath;
#ifdef PLATFORM_WINDOWS
char buffer[MAX_PATH];
DWORD len = GetModuleFileNameA(NULL, buffer, MAX_PATH);
if (len > 0 && len < MAX_PATH) {
exePath = buffer;
}
#elif defined(PLATFORM_SWITCH)
// Switch: 返回当前工作目录(不支持获取可执行文件路径)
return getCurrentWorkingDirectory();
#else
char buffer[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
if (len != -1) {
buffer[len] = '\0';
exePath = buffer;
}
#endif
if (!exePath.empty()) {
size_t lastSep = exePath.find_last_of("/\\");
if (lastSep != std::string::npos) {
return exePath.substr(0, lastSep);
}
}
return getCurrentWorkingDirectory();
}
std::string FileSystem::getCurrentWorkingDirectory() {
#ifdef PLATFORM_WINDOWS
char buffer[MAX_PATH];
DWORD len = GetCurrentDirectoryA(MAX_PATH, buffer);
if (len > 0 && len < MAX_PATH) {
return std::string(buffer);
}
#else
char buffer[PATH_MAX];
if (getcwd(buffer, sizeof(buffer)) != nullptr) {
return std::string(buffer);
}
#endif
return ".";
}
std::string FileSystem::combinePath(const std::string& base, const std::string& relative) {
if (base.empty()) {
return normalizeSeparators(relative);
}
if (relative.empty()) {
return normalizeSeparators(base);
}
std::string result = normalizeSeparators(base);
std::string rel = normalizeSeparators(relative);
// 移除末尾的分隔符
while (!result.empty() && result.back() == PATH_SEPARATOR) {
result.pop_back();
}
// 移除开头的分隔符
size_t relStart = 0;
while (relStart < rel.size() && rel[relStart] == PATH_SEPARATOR) {
++relStart;
}
if (relStart < rel.size()) {
result += PATH_SEPARATOR;
result += rel.substr(relStart);
}
return result;
}
std::string FileSystem::getFileName(const std::string& path) {
std::string normalized = normalizeSeparators(path);
size_t lastSep = normalized.find_last_of(PATH_SEPARATOR);
if (lastSep != std::string::npos) {
return normalized.substr(lastSep + 1);
}
return normalized;
}
std::string FileSystem::getFileExtension(const std::string& path) {
std::string fileName = getFileName(path);
size_t lastDot = fileName.find_last_of('.');
if (lastDot != std::string::npos && lastDot > 0) {
return fileName.substr(lastDot);
}
return "";
}
std::string FileSystem::getDirectoryName(const std::string& path) {
std::string normalized = normalizeSeparators(path);
size_t lastSep = normalized.find_last_of(PATH_SEPARATOR);
if (lastSep != std::string::npos) {
return normalized.substr(0, lastSep);
}
return ".";
}
std::string FileSystem::normalizePath(const std::string& path) {
return normalizeSeparators(path);
}
// ============================================================================
// 文件读写
// ============================================================================
std::string FileSystem::readFileText(const std::string& path) {
std::ifstream file(path, std::ios::in | std::ios::binary);
if (!file.is_open()) {
E2D_LOG_ERROR("Failed to open file: {}", path);
return "";
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
std::vector<uint8_t> FileSystem::readFileBytes(const std::string& path) {
std::ifstream file(path, std::ios::in | std::ios::binary | std::ios::ate);
if (!file.is_open()) {
E2D_LOG_ERROR("Failed to open file: {}", path);
return {};
}
auto size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> buffer(static_cast<size_t>(size));
file.read(reinterpret_cast<char*>(buffer.data()), size);
return buffer;
}
int64_t FileSystem::getFileSize(const std::string& path) {
struct stat st;
if (stat(path.c_str(), &st) != 0) {
return -1;
}
return static_cast<int64_t>(st.st_size);
}
// ============================================================================
// 目录操作
// ============================================================================
bool FileSystem::createDirectory(const std::string& path) {
std::string normalized = normalizeSeparators(path);
#ifdef PLATFORM_WINDOWS
int result = _mkdir(normalized.c_str());
#else
int result = mkdir(normalized.c_str(), 0755);
#endif
return result == 0 || errno == EEXIST;
}
bool FileSystem::createDirectories(const std::string& path) {
std::string normalized = normalizeSeparators(path);
if (normalized.empty()) {
return true;
}
// 逐级创建目录
size_t pos = 0;
while (pos < normalized.size()) {
pos = normalized.find(PATH_SEPARATOR, pos + 1);
if (pos == std::string::npos) {
pos = normalized.size();
}
std::string subPath = normalized.substr(0, pos);
if (!subPath.empty() && !directoryExists(subPath)) {
if (!createDirectory(subPath)) {
return false;
}
}
}
return true;
}
} // namespace extra2d

View File

@ -13,7 +13,7 @@ Window::Window()
: sdlWindow_(nullptr), glContext_(nullptr), currentCursor_(nullptr),
width_(1280), height_(720), vsync_(true), shouldClose_(false),
fullscreen_(true), focused_(true), contentScaleX_(1.0f), contentScaleY_(1.0f),
userData_(nullptr), eventQueue_(nullptr) {
enableDpiScale_(true), userData_(nullptr), eventQueue_(nullptr) {
// 初始化光标数组
for (int i = 0; i < 9; ++i) {
sdlCursors_[i] = nullptr;
@ -32,6 +32,7 @@ bool Window::create(const WindowConfig &config) {
height_ = config.height;
vsync_ = config.vsync;
fullscreen_ = config.fullscreen;
enableDpiScale_ = config.enableDpiScale;
// 初始化 SDL2 + 创建窗口 + GL 上下文
if (!initSDL(config)) {
@ -43,13 +44,12 @@ bool Window::create(const WindowConfig &config) {
input_ = makeUnique<Input>();
input_->init();
// PC 端初始化光标
#ifdef PLATFORM_PC
initCursors();
#endif
// 初始化光标
if (config.enableCursors) {
initCursors();
}
E2D_LOG_INFO("Window created: {}x{} (Platform: {})",
width_, height_, platform::getPlatformName());
E2D_LOG_INFO("Window created: {}x{}", width_, height_);
return true;
}
@ -79,13 +79,7 @@ bool Window::initSDL(const WindowConfig &config) {
// 创建 SDL2 窗口
Uint32 windowFlags = SDL_WINDOW_OPENGL;
#ifdef PLATFORM_SWITCH
// Switch 始终全屏
windowFlags |= SDL_WINDOW_FULLSCREEN;
width_ = 1280;
height_ = 720;
#else
// PC 端根据配置设置
// 根据配置设置窗口模式
if (config.fullscreen) {
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} else {
@ -95,7 +89,6 @@ bool Window::initSDL(const WindowConfig &config) {
// 注意SDL_WINDOWPOS_CENTERED 是位置参数,不是窗口标志
// 窗口居中在 SDL_CreateWindow 的位置参数中处理
}
#endif
sdlWindow_ = SDL_CreateWindow(
config.title.c_str(),
@ -143,10 +136,10 @@ bool Window::initSDL(const WindowConfig &config) {
// 设置 VSync
SDL_GL_SetSwapInterval(vsync_ ? 1 : 0);
// PC 端更新 DPI 缩放
#ifndef PLATFORM_SWITCH
updateContentScale();
#endif
// 更新 DPI 缩放
if (config.enableDpiScale) {
updateContentScale();
}
E2D_LOG_INFO("SDL2 + GLES 3.2 initialized successfully");
E2D_LOG_INFO("OpenGL Version: {}",
@ -199,9 +192,7 @@ void Window::pollEvents() {
case SDL_WINDOWEVENT_SIZE_CHANGED:
width_ = event.window.data1;
height_ = event.window.data2;
#ifndef PLATFORM_SWITCH
updateContentScale();
#endif
if (resizeCallback_) {
resizeCallback_(width_, height_);
}
@ -240,49 +231,31 @@ bool Window::shouldClose() const { return shouldClose_; }
void Window::setShouldClose(bool close) { shouldClose_ = close; }
void Window::setTitle(const String &title) {
#ifdef PLATFORM_PC
if (sdlWindow_) {
SDL_SetWindowTitle(sdlWindow_, title.c_str());
}
#else
(void)title;
#endif
}
void Window::setSize(int width, int height) {
#ifdef PLATFORM_PC
if (sdlWindow_) {
SDL_SetWindowSize(sdlWindow_, width, height);
width_ = width;
height_ = height;
}
#else
(void)width;
(void)height;
#endif
}
void Window::setPosition(int x, int y) {
#ifdef PLATFORM_PC
if (sdlWindow_) {
SDL_SetWindowPosition(sdlWindow_, x, y);
}
#else
(void)x;
(void)y;
#endif
}
void Window::setFullscreen(bool fullscreen) {
#ifdef PLATFORM_PC
if (sdlWindow_) {
Uint32 flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
SDL_SetWindowFullscreen(sdlWindow_, flags);
fullscreen_ = fullscreen;
}
#else
(void)fullscreen;
#endif
}
void Window::setVSync(bool enabled) {
@ -291,40 +264,26 @@ void Window::setVSync(bool enabled) {
}
void Window::setResizable(bool resizable) {
#ifdef PLATFORM_PC
if (sdlWindow_) {
SDL_SetWindowResizable(sdlWindow_, resizable ? SDL_TRUE : SDL_FALSE);
}
#else
(void)resizable;
#endif
}
Vec2 Window::getPosition() const {
#ifdef PLATFORM_PC
if (sdlWindow_) {
int x, y;
SDL_GetWindowPosition(sdlWindow_, &x, &y);
return Vec2(static_cast<float>(x), static_cast<float>(y));
}
#endif
return Vec2::Zero();
}
float Window::getContentScaleX() const {
#ifdef PLATFORM_SWITCH
return 1.0f;
#else
return contentScaleX_;
#endif
return enableDpiScale_ ? contentScaleX_ : 1.0f;
}
float Window::getContentScaleY() const {
#ifdef PLATFORM_SWITCH
return 1.0f;
#else
return contentScaleY_;
#endif
return enableDpiScale_ ? contentScaleY_ : 1.0f;
}
Vec2 Window::getContentScale() const {
@ -332,27 +291,22 @@ Vec2 Window::getContentScale() const {
}
bool Window::isMinimized() const {
#ifdef PLATFORM_PC
if (sdlWindow_) {
Uint32 flags = SDL_GetWindowFlags(sdlWindow_);
return (flags & SDL_WINDOW_MINIMIZED) != 0;
}
#endif
return false;
}
bool Window::isMaximized() const {
#ifdef PLATFORM_PC
if (sdlWindow_) {
Uint32 flags = SDL_GetWindowFlags(sdlWindow_);
return (flags & SDL_WINDOW_MAXIMIZED) != 0;
}
#endif
return true;
}
void Window::initCursors() {
#ifdef PLATFORM_PC
sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
sdlCursors_[2] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
@ -362,11 +316,9 @@ void Window::initCursors() {
sdlCursors_[6] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
sdlCursors_[7] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
sdlCursors_[8] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
#endif
}
void Window::deinitCursors() {
#ifdef PLATFORM_PC
for (int i = 0; i < 9; ++i) {
if (sdlCursors_[i]) {
SDL_FreeCursor(sdlCursors_[i]);
@ -374,38 +326,26 @@ void Window::deinitCursors() {
}
}
currentCursor_ = nullptr;
#endif
}
void Window::setCursor(CursorShape shape) {
#ifdef PLATFORM_PC
int index = static_cast<int>(shape);
if (index >= 0 && index < 9 && sdlCursors_[index]) {
SDL_SetCursor(sdlCursors_[index]);
currentCursor_ = sdlCursors_[index];
}
#else
(void)shape;
#endif
}
void Window::resetCursor() {
#ifdef PLATFORM_PC
SDL_SetCursor(SDL_GetDefaultCursor());
currentCursor_ = nullptr;
#endif
}
void Window::setMouseVisible(bool visible) {
#ifdef PLATFORM_PC
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
#else
(void)visible;
#endif
}
void Window::updateContentScale() {
#ifndef PLATFORM_SWITCH
if (sdlWindow_) {
// 使用 DPI 计算内容缩放比例
int displayIndex = SDL_GetWindowDisplayIndex(sdlWindow_);
@ -418,7 +358,6 @@ void Window::updateContentScale() {
}
}
}
#endif
}
} // namespace extra2d

View File

@ -9,50 +9,6 @@
namespace extra2d {
ResourceManager::ResourceManager() {
#ifdef __SWITCH__
addSearchPath("romfs:/");
addSearchPath("sdmc:/");
#endif
}
ResourceManager::~ResourceManager() = default;
ResourceManager &ResourceManager::getInstance() {
static ResourceManager instance;
return instance;
}
// ============================================================================
// 搜索路径管理
// ============================================================================
void ResourceManager::addSearchPath(const std::string &path) {
std::lock_guard<std::mutex> lock(textureMutex_);
// 避免重复添加
auto it = std::find(searchPaths_.begin(), searchPaths_.end(), path);
if (it == searchPaths_.end()) {
searchPaths_.push_back(path);
E2D_LOG_DEBUG("ResourceManager: added search path: {}", path);
}
}
void ResourceManager::removeSearchPath(const std::string &path) {
std::lock_guard<std::mutex> lock(textureMutex_);
auto it = std::find(searchPaths_.begin(), searchPaths_.end(), path);
if (it != searchPaths_.end()) {
searchPaths_.erase(it);
E2D_LOG_DEBUG("ResourceManager: removed search path: {}", path);
}
}
void ResourceManager::clearSearchPaths() {
std::lock_guard<std::mutex> lock(textureMutex_);
searchPaths_.clear();
E2D_LOG_DEBUG("ResourceManager: cleared all search paths");
}
// 辅助函数:检查文件是否存在
static bool fileExists(const std::string &path) {
struct stat st;
@ -64,51 +20,41 @@ static bool isRomfsPath(const std::string &path) {
return path.find("romfs:/") == 0 || path.find("romfs:\\") == 0;
}
// 辅助函数:拼接路径
static std::string joinPath(const std::string &dir,
const std::string &filename) {
if (dir.empty())
return filename;
char lastChar = dir.back();
if (lastChar == '/' || lastChar == '\\') {
return dir + filename;
}
return dir + "/" + filename;
}
std::string
ResourceManager::findResourcePath(const std::string &filename) const {
// 首先检查是否是 romfs 路径Switch 平台)
if (isRomfsPath(filename)) {
if (fileExists(filename)) {
return filename;
}
return "";
// 解析资源路径(优先尝试 romfs:/ 前缀,然后 sdmc:/
static std::string resolveResourcePath(const std::string &filepath) {
// 如果已经是 romfs 或 sdmc 路径,直接返回
if (isRomfsPath(filepath) || filepath.find("sdmc:/") == 0) {
return filepath;
}
// 首先检查是否是绝对路径或相对当前目录存在
if (fileExists(filename)) {
return filename;
}
// 在搜索路径中查找
std::lock_guard<std::mutex> lock(textureMutex_);
for (const auto &path : searchPaths_) {
std::string fullPath = joinPath(path, filename);
if (fileExists(fullPath)) {
return fullPath;
}
}
// 最后尝试在 romfs 中查找(自动添加 romfs:/ 前缀)
std::string romfsPath = "romfs:/" + filename;
// 优先尝试 romfs:/ 前缀的路径Switch 平台)
std::string romfsPath = "romfs:/" + filepath;
if (fileExists(romfsPath)) {
return romfsPath;
}
// 尝试 sdmc:/ 前缀的路径Switch SD卡
std::string sdmcPath = "sdmc:/" + filepath;
if (fileExists(sdmcPath)) {
return sdmcPath;
}
// 如果都不存在,尝试原路径
if (fileExists(filepath)) {
return filepath;
}
return "";
}
ResourceManager::ResourceManager() = default;
ResourceManager::~ResourceManager() = default;
ResourceManager &ResourceManager::getInstance() {
static ResourceManager instance;
return instance;
}
// ============================================================================
// 纹理资源
// ============================================================================
@ -127,14 +73,14 @@ Ptr<Texture> ResourceManager::loadTexture(const std::string &filepath) {
textureCache_.erase(it);
}
// 查找完整路径
std::string fullPath = findResourcePath(filepath);
// 解析资源路径(优先尝试 romfs:/ 前缀)
std::string fullPath = resolveResourcePath(filepath);
if (fullPath.empty()) {
E2D_LOG_ERROR("ResourceManager: texture file not found: {}", filepath);
return nullptr;
}
// 创建新纹理(根据扩展名自动选择加载路径)
// 创建新纹理
try {
auto texture = makePtr<GLTexture>(fullPath);
if (!texture->isValid()) {
@ -261,8 +207,8 @@ Ptr<FontAtlas> ResourceManager::loadFont(const std::string &filepath,
fontCache_.erase(it);
}
// 查找完整路径
std::string fullPath = findResourcePath(filepath);
// 解析资源路径(优先尝试 romfs:/ 前缀)
std::string fullPath = resolveResourcePath(filepath);
if (fullPath.empty()) {
E2D_LOG_ERROR("ResourceManager: font file not found: {}", filepath);
return nullptr;
@ -313,92 +259,6 @@ void ResourceManager::unloadFont(const std::string &key) {
E2D_LOG_DEBUG("ResourceManager: unloaded font: {}", key);
}
// ============================================================================
// 多字体后备加载
// ============================================================================
Ptr<FontAtlas> ResourceManager::loadFontWithFallbacks(
const std::vector<std::string> &fontPaths, int fontSize, bool useSDF) {
// 尝试加载每一个候选字体
for (const auto &fontPath : fontPaths) {
auto font = loadFont(fontPath, fontSize, useSDF);
if (font) {
E2D_LOG_INFO("ResourceManager: successfully loaded font from fallback list: {}",
fontPath);
return font;
}
}
E2D_LOG_ERROR("ResourceManager: failed to load any font from fallback list ({} candidates)",
fontPaths.size());
return nullptr;
}
Ptr<FontAtlas> ResourceManager::loadFontWithDefaultFallback(
const std::string &filepath, int fontSize, bool useSDF) {
// 首先尝试加载用户指定的字体
auto font = loadFont(filepath, fontSize, useSDF);
if (font) {
return font;
}
E2D_LOG_WARN("ResourceManager: failed to load font '{}', trying system fallbacks...",
filepath);
// 定义系统默认字体候选列表
std::vector<std::string> fallbackFonts;
#ifdef __SWITCH__
// Switch 平台默认字体路径
fallbackFonts = {
"romfs:/assets/font.ttf", // 应用自带字体
"romfs:/assets/default.ttf", // 默认字体备选
"romfs:/font.ttf", // 根目录字体
"sdmc:/switch/fonts/default.ttf", // SD卡字体目录
"sdmc:/switch/fonts/font.ttf",
};
#else
// PC 平台系统字体路径Windows/Linux/macOS
#ifdef _WIN32
fallbackFonts = {
"C:/Windows/Fonts/arial.ttf",
"C:/Windows/Fonts/segoeui.ttf",
"C:/Windows/Fonts/calibri.ttf",
"C:/Windows/Fonts/tahoma.ttf",
"C:/Windows/Fonts/msyh.ttc", // 微软雅黑
};
#elif __APPLE__
fallbackFonts = {
"/System/Library/Fonts/Helvetica.ttc",
"/System/Library/Fonts/SFNSDisplay.ttf",
"/Library/Fonts/Arial.ttf",
};
#else
// Linux
fallbackFonts = {
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/truetype/freefont/FreeSans.ttf",
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
"/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf",
};
#endif
#endif
// 尝试加载后备字体
for (const auto &fallbackPath : fallbackFonts) {
font = loadFont(fallbackPath, fontSize, useSDF);
if (font) {
E2D_LOG_INFO("ResourceManager: loaded fallback font: {}", fallbackPath);
return font;
}
}
E2D_LOG_ERROR("ResourceManager: all font fallbacks exhausted, no font available");
return nullptr;
}
// ============================================================================
// 音效资源
// ============================================================================
@ -422,8 +282,8 @@ Ptr<Sound> ResourceManager::loadSound(const std::string &name,
soundCache_.erase(it);
}
// 查找完整路径
std::string fullPath = findResourcePath(filepath);
// 解析资源路径(优先尝试 romfs:/ 前缀)
std::string fullPath = resolveResourcePath(filepath);
if (fullPath.empty()) {
E2D_LOG_ERROR("ResourceManager: sound file not found: {}", filepath);
return nullptr;

View File

@ -123,13 +123,8 @@ private:
*/
void loadFonts() {
auto &resources = Application::instance().resources();
std::vector<std::string> fontPaths = {
"romfs:/assets/font.ttf" // 备选字体
};
titleFont_ = resources.loadFontWithFallbacks(fontPaths, 60, true);
infoFont_ = resources.loadFontWithFallbacks(fontPaths, 28, true);
titleFont_ = resources.loadFont("assets/font.ttf", 60, true);
infoFont_ = resources.loadFont("assets/font.ttf", 28, true);
if (!titleFont_) {
E2D_LOG_WARN("无法加载标题字体");

View File

@ -7,6 +7,9 @@
local host_plat = os.host()
local target_plat = get_config("plat") or host_plat
-- 获取当前脚本所在目录(示例根目录)
local example_dir = os.scriptdir()
-- 可执行文件目标
target("collision_demo")
set_kind("binary")
@ -18,7 +21,7 @@ target("collision_demo")
set_plat("switch")
set_arch("arm64")
set_toolchains("switch")
set_targetdir("build/switch")
set_targetdir("../../build/examples/collision_demo")
after_build(function (target)
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
@ -31,7 +34,7 @@ target("collision_demo")
if os.isfile(nacptool) and os.isfile(elf2nro) then
os.vrunv(nacptool, {"--create", "Collision Demo", "Extra2D Team", "1.0.0", nacp_file})
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
else
@ -43,11 +46,12 @@ target("collision_demo")
elseif target_plat == "mingw" then
set_plat("mingw")
set_arch("x86_64")
set_targetdir("build/mingw")
set_targetdir("../../build/examples/collision_demo")
add_ldflags("-mwindows", {force = true})
-- 复制资源
after_build(function (target)
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
local target_dir = path.directory(target:targetfile())
local assets_dir = path.join(target_dir, "assets")
@ -55,6 +59,9 @@ target("collision_demo")
os.mkdir(assets_dir)
end
os.cp(path.join(romfs, "assets/*"), assets_dir)
print("Copied assets from " .. romfs .. " to " .. assets_dir)
else
print("Warning: romfs directory not found at " .. romfs)
end
end)
end

View File

@ -85,7 +85,6 @@ int main(int argc, char **argv)
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D Hello World Demo");
E2D_LOG_INFO("Platform: {}", platform::getPlatformName());
E2D_LOG_INFO("========================");
// 获取应用实例

View File

@ -7,6 +7,9 @@
local host_plat = os.host()
local target_plat = get_config("plat") or host_plat
-- 获取当前脚本所在目录(示例根目录)
local example_dir = os.scriptdir()
-- 可执行文件目标
target("hello_world")
set_kind("binary")
@ -18,7 +21,7 @@ target("hello_world")
set_plat("switch")
set_arch("arm64")
set_toolchains("switch")
set_targetdir("build/switch")
set_targetdir("../../build/examples/hello_world")
-- 生成 NRO
after_build(function (target)
@ -32,7 +35,7 @@ target("hello_world")
if os.isfile(nacptool) and os.isfile(elf2nro) then
os.vrunv(nacptool, {"--create", "Hello World", "Extra2D Team", "1.0.0", nacp_file})
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
else
@ -44,12 +47,12 @@ target("hello_world")
elseif target_plat == "mingw" then
set_plat("mingw")
set_arch("x86_64")
set_targetdir("build/mingw")
set_targetdir("../../build/examples/hello_world")
add_ldflags("-mwindows", {force = true})
-- 复制资源
after_build(function (target)
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
local target_dir = path.directory(target:targetfile())
local assets_dir = path.join(target_dir, "assets")
@ -57,6 +60,9 @@ target("hello_world")
os.mkdir(assets_dir)
end
os.cp(path.join(romfs, "assets/*"), assets_dir)
print("Copied assets from " .. romfs .. " to " .. assets_dir)
else
print("Warning: romfs directory not found at " .. romfs)
end
end)
end

View File

@ -16,7 +16,7 @@ StartScene::StartScene() {
static extra2d::Ptr<extra2d::FontAtlas> loadMenuFont() {
auto& resources = extra2d::Application::instance().resources();
auto font = resources.loadFont("assets/font.ttf", 28);
auto font = resources.loadFont("assets/font.ttf", 28,true);
return font;
}

View File

@ -16,7 +16,6 @@ int main(int argc, char **argv)
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Extra2D push_box");
E2D_LOG_INFO("Platform: {}", platform::getPlatformName());
E2D_LOG_INFO("========================");
auto &app = Application::instance();

View File

@ -7,6 +7,9 @@
local host_plat = os.host()
local target_plat = get_config("plat") or host_plat
-- 获取当前脚本所在目录(示例根目录)
local example_dir = os.scriptdir()
-- 可执行文件目标
target("push_box")
set_kind("binary")
@ -18,7 +21,7 @@ target("push_box")
set_plat("switch")
set_arch("arm64")
set_toolchains("switch")
set_targetdir("build/switch")
set_targetdir("../../build/examples/push_box")
after_build(function (target)
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
@ -31,7 +34,7 @@ target("push_box")
if os.isfile(nacptool) and os.isfile(elf2nro) then
os.vrunv(nacptool, {"--create", "Push Box", "Extra2D Team", "1.0.0", nacp_file})
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
else
@ -43,11 +46,12 @@ target("push_box")
elseif target_plat == "mingw" then
set_plat("mingw")
set_arch("x86_64")
set_targetdir("build/mingw")
set_targetdir("../../build/examples/push_box")
add_ldflags("-mwindows", {force = true})
-- 复制资源
after_build(function (target)
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
local target_dir = path.directory(target:targetfile())
local assets_dir = path.join(target_dir, "assets")
@ -55,6 +59,9 @@ target("push_box")
os.mkdir(assets_dir)
end
os.cp(path.join(romfs, "assets/*"), assets_dir)
print("Copied assets from " .. romfs .. " to " .. assets_dir)
else
print("Warning: romfs directory not found at " .. romfs)
end
end)
end

View File

@ -185,14 +185,8 @@ private:
void loadFonts() {
auto &resources = Application::instance().resources();
std::vector<std::string> fontPaths = {
"romfs:/assets/msjh.ttf",
"romfs:/assets/default.ttf",
"romfs:/assets/font.ttf",
};
titleFont_ = resources.loadFontWithFallbacks(fontPaths, 28, true);
infoFont_ = resources.loadFontWithFallbacks(fontPaths, 16, true);
titleFont_ = resources.loadFont("assets/font.ttf", 28, true);
infoFont_ = resources.loadFont("assets/font.ttf", 16, true);
}
/**

View File

@ -7,6 +7,9 @@
local host_plat = os.host()
local target_plat = get_config("plat") or host_plat
-- 获取当前脚本所在目录(示例根目录)
local example_dir = os.scriptdir()
-- 可执行文件目标
target("spatial_index_demo")
set_kind("binary")
@ -18,7 +21,7 @@ target("spatial_index_demo")
set_plat("switch")
set_arch("arm64")
set_toolchains("switch")
set_targetdir("build/switch")
set_targetdir("../../build/examples/spatial_index_demo")
after_build(function (target)
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
@ -31,7 +34,7 @@ target("spatial_index_demo")
if os.isfile(nacptool) and os.isfile(elf2nro) then
os.vrunv(nacptool, {"--create", "Spatial Index Demo", "Extra2D Team", "1.0.0", nacp_file})
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
else
@ -43,11 +46,12 @@ target("spatial_index_demo")
elseif target_plat == "mingw" then
set_plat("mingw")
set_arch("x86_64")
set_targetdir("build/mingw")
set_targetdir("../../build/examples/spatial_index_demo")
add_ldflags("-mwindows", {force = true})
-- 复制资源
after_build(function (target)
local romfs = path.absolute("romfs")
local romfs = path.join(example_dir, "romfs")
if os.isdir(romfs) then
local target_dir = path.directory(target:targetfile())
local assets_dir = path.join(target_dir, "assets")
@ -55,6 +59,9 @@ target("spatial_index_demo")
os.mkdir(assets_dir)
end
os.cp(path.join(romfs, "assets/*"), assets_dir)
print("Copied assets from " .. romfs .. " to " .. assets_dir)
else
print("Warning: romfs directory not found at " .. romfs)
end
end)
end