chore: 移除项目中的示例代码、文档和构建配置

移除不再需要的示例代码、文档和构建配置文件,包括:
- 示例程序代码和资源文件
- 文档文件(构建指南、API参考等)
- xmake构建配置和工具链定义
- 测试和演示相关文件

这些文件已不再使用或已迁移到其他仓库,清理以保持项目整洁。
This commit is contained in:
ChestnutYueyue 2026-02-10 16:13:12 +08:00
parent 2e08bff567
commit 4066abf40f
17 changed files with 0 additions and 4526 deletions

View File

@ -1,292 +0,0 @@
#include <cmath>
#include <extra2d/extra2d.h>
#include <sstream>
using namespace extra2d;
// ============================================================================
// 碰撞测试节点 - 有实际边界框
// ============================================================================
class CollisionBox : public Node {
public:
CollisionBox(float width, float height, const Color &color)
: width_(width), height_(height), color_(color), isColliding_(false) {
// 启用空间索引,这是碰撞检测的关键
setSpatialIndexed(true);
}
void setColliding(bool colliding) { isColliding_ = colliding; }
Rect getBoundingBox() const override {
// 返回实际的矩形边界
Vec2 pos = getPosition();
return Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_);
}
void onRender(RenderBackend &renderer) override {
Vec2 pos = getPosition();
// 绘制填充矩形
Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.8f) : color_;
renderer.fillRect(
Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_),
fillColor);
// 绘制边框
Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f)
: Color(1.0f, 1.0f, 1.0f, 0.5f);
float borderWidth = isColliding_ ? 3.0f : 2.0f;
renderer.drawRect(
Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_),
borderColor, borderWidth);
}
private:
float width_, height_;
Color color_;
bool isColliding_;
};
// ============================================================================
// 碰撞检测场景
// ============================================================================
class CollisionDemoScene : public Scene {
public:
void onEnter() override {
E2D_LOG_INFO("CollisionDemoScene::onEnter - 碰撞检测演示");
// 设置背景色
setBackgroundColor(Color(0.05f, 0.05f, 0.1f, 1.0f));
// 获取屏幕中心
auto &app = Application::instance();
float centerX = app.getConfig().width / 2.0f;
float centerY = app.getConfig().height / 2.0f;
// 创建静态碰撞框
createStaticBoxes(centerX, centerY);
// 创建移动的中心方块
centerBox_ =
makePtr<CollisionBox>(80.0f, 80.0f, Color(0.2f, 0.6f, 1.0f, 0.8f));
centerBox_->setPosition(Vec2(centerX, centerY));
addChild(centerBox_);
// 加载字体
loadFonts();
E2D_LOG_INFO("创建了 {} 个碰撞框", boxes_.size() + 1);
}
void onUpdate(float dt) override {
Scene::onUpdate(dt);
// 旋转中心方块
rotationAngle_ += rotationSpeed_ * dt;
if (rotationAngle_ >= 360.0f)
rotationAngle_ -= 360.0f;
// 让中心方块沿圆形路径移动
float radius = 150.0f;
float rad = rotationAngle_ * 3.14159f / 180.0f;
auto &app = Application::instance();
Vec2 center =
Vec2(app.getConfig().width / 2.0f, app.getConfig().height / 2.0f);
centerBox_->setPosition(Vec2(center.x + std::cos(rad) * radius,
center.y + std::sin(rad) * radius));
centerBox_->setRotation(rotationAngle_);
// 执行碰撞检测
performCollisionDetection();
// 检查退出按键
auto &input = Application::instance().input();
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_START)) {
E2D_LOG_INFO("退出应用");
Application::instance().quit();
}
}
void onRender(RenderBackend &renderer) override {
Scene::onRender(renderer);
// 绘制说明文字
drawUI(renderer);
}
private:
/**
* @brief
*/
void loadFonts() {
auto &resources = Application::instance().resources();
// 使用后备字体加载功能
#ifdef PLATFORM_SWITCH
std::vector<std::string> fontPaths = {
"romfs:/assets/font.ttf" // 备选字体
};
#else
std::vector<std::string> fontPaths = {FileSystem::resolvePath("font.ttf")};
#endif
titleFont_ = resources.loadFontWithFallbacks(fontPaths, 60, true);
infoFont_ = resources.loadFontWithFallbacks(fontPaths, 28, true);
if (!titleFont_) {
E2D_LOG_WARN("无法加载标题字体");
}
if (!infoFont_) {
E2D_LOG_WARN("无法加载信息字体");
}
}
/**
* @brief
*/
void createStaticBoxes(float centerX, float centerY) {
// 创建围绕中心的静态碰撞框
std::vector<std::pair<Vec2, Color>> positions = {
{Vec2(centerX - 200, centerY - 150), Color(0.3f, 1.0f, 0.3f, 0.7f)},
{Vec2(centerX + 200, centerY - 150), Color(1.0f, 0.3f, 0.3f, 0.7f)},
{Vec2(centerX - 200, centerY + 150), Color(0.3f, 0.3f, 1.0f, 0.7f)},
{Vec2(centerX + 200, centerY + 150), Color(1.0f, 1.0f, 0.3f, 0.7f)},
{Vec2(centerX, centerY - 220), Color(1.0f, 0.3f, 1.0f, 0.7f)},
{Vec2(centerX, centerY + 220), Color(0.3f, 1.0f, 1.0f, 0.7f)},
};
for (const auto &[pos, color] : positions) {
auto box = makePtr<CollisionBox>(70.0f, 70.0f, color);
box->setPosition(pos);
addChild(box);
boxes_.push_back(box);
}
}
/**
* @brief
*/
void performCollisionDetection() {
// 清除之前的碰撞状态
centerBox_->setColliding(false);
for (auto &box : boxes_) {
box->setColliding(false);
}
// 使用空间索引进行碰撞检测
auto collisions = queryCollisions();
collisionCount_ = collisions.size();
// 标记碰撞的节点
for (const auto &[nodeA, nodeB] : collisions) {
if (auto boxA = dynamic_cast<CollisionBox *>(nodeA)) {
boxA->setColliding(true);
}
if (auto boxB = dynamic_cast<CollisionBox *>(nodeB)) {
boxB->setColliding(true);
}
}
}
/**
* @brief UI界面
*/
void drawUI(RenderBackend &renderer) {
if (!titleFont_ || !infoFont_)
return;
auto &app = Application::instance();
// 绘制标题
renderer.drawText(*titleFont_, "碰撞检测演示", Vec2(50.0f, 30.0f),
Color(1.0f, 1.0f, 1.0f, 1.0f));
// 绘制说明文字
renderer.drawText(*infoFont_, "蓝色方块旋转并检测碰撞", Vec2(50.0f, 80.0f),
Color(0.8f, 0.8f, 0.8f, 1.0f));
renderer.drawText(*infoFont_, "红色 = 检测到碰撞", Vec2(50.0f, 105.0f),
Color(1.0f, 0.5f, 0.5f, 1.0f));
// 绘制碰撞统计
std::stringstream ss;
ss << "碰撞数: " << collisionCount_;
renderer.drawText(*infoFont_, ss.str(), Vec2(50.0f, 150.0f),
Color(1.0f, 1.0f, 0.5f, 1.0f));
// 绘制 FPS
ss.str("");
ss << "FPS: " << app.fps();
renderer.drawText(*infoFont_, ss.str(), Vec2(50.0f, 175.0f),
Color(0.8f, 1.0f, 0.8f, 1.0f));
// 绘制操作提示
float screenHeight = static_cast<float>(app.getConfig().height);
renderer.drawText(*infoFont_, "按 + 键退出",
Vec2(50.0f, screenHeight - 50.0f),
Color(0.8f, 0.8f, 0.8f, 1.0f));
}
Ptr<CollisionBox> centerBox_;
std::vector<Ptr<CollisionBox>> boxes_;
float rotationAngle_ = 0.0f;
float rotationSpeed_ = 60.0f; // 旋转速度(度/秒)
size_t collisionCount_ = 0;
// 字体资源
Ptr<FontAtlas> titleFont_;
Ptr<FontAtlas> infoFont_;
};
// ============================================================================
// 程序入口
// ============================================================================
#ifdef _WIN32
int main(int argc, char *argv[])
#else
extern "C" int main(int argc, char *argv[])
#endif
{
(void)argc;
(void)argv;
// 初始化日志系统
Logger::init();
Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D 碰撞检测演示");
E2D_LOG_INFO("========================");
// 获取应用实例
auto &app = Application::instance();
// 配置应用
AppConfig config;
config.title = "Easy2D - 碰撞检测演示";
config.width = 1280;
config.height = 720;
config.vsync = true;
config.fpsLimit = 60;
// 初始化应用
if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!");
return -1;
}
// 进入场景
app.enterScene(makePtr<CollisionDemoScene>());
E2D_LOG_INFO("开始主循环...");
// 运行应用
app.run();
E2D_LOG_INFO("应用结束");
return 0;
}

View File

@ -1,207 +0,0 @@
#include <extra2d/extra2d.h>
#include <extra2d/platform/platform_compat.h>
#ifdef PLATFORM_SWITCH
#include <switch.h>
#endif
using namespace extra2d;
// ============================================================================
// 字体配置
// ============================================================================
/**
* @brief
* @return
*/
static std::vector<std::string> getFontCandidates() {
return {
FileSystem::resolvePath("font.ttf"), // 微软雅黑(中文支持)
FileSystem::resolvePath("Gasinamu.ttf"), // 备选字体
FileSystem::resolvePath("default.ttf"), // 默认字体
};
}
/**
* @brief
* @param resources
* @param fontSize
* @param useSDF 使SDF渲染
* @return nullptr
*/
static Ptr<FontAtlas> loadFontWithFallbacks(ResourceManager &resources,
int fontSize, bool useSDF) {
auto candidates = getFontCandidates();
for (const auto &fontPath : candidates) {
auto font = resources.loadFont(fontPath, fontSize, useSDF);
if (font) {
E2D_LOG_INFO("成功加载字体: {}", fontPath);
return font;
}
E2D_LOG_WARN("字体加载失败,尝试下一个: {}", fontPath);
}
E2D_LOG_ERROR("所有字体候选都加载失败!");
return nullptr;
}
// ============================================================================
// Hello World 场景
// ============================================================================
/**
* @brief Hello World
* "Hello World"
*/
class HelloWorldScene : public Scene {
public:
/**
* @brief
*/
void onEnter() override {
E2D_LOG_INFO("HelloWorldScene::onEnter - 进入场景");
// 设置背景颜色为深蓝色
setBackgroundColor(Color(0.1f, 0.1f, 0.3f, 1.0f));
// 加载字体(支持多种字体后备)
auto &resources = Application::instance().resources();
font_ = loadFontWithFallbacks(resources, 48, true);
if (!font_) {
E2D_LOG_ERROR("字体加载失败,文字渲染将不可用!");
}
}
/**
* @brief
* @param dt
*/
void onUpdate(float dt) override {
Scene::onUpdate(dt);
// 检查退出按键
auto &input = Application::instance().input();
#ifdef PLATFORM_SWITCH
// Switch: 使用手柄 START 按钮
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_START)) {
E2D_LOG_INFO("退出应用 (START 按钮)");
Application::instance().quit();
}
#else
// PC: 支持 ESC 键或手柄 START 按钮
if (input.isKeyPressed(Key::Escape) ||
input.isButtonPressed(SDL_CONTROLLER_BUTTON_START)) {
E2D_LOG_INFO("退出应用 (ESC 键或 START 按钮)");
Application::instance().quit();
}
#endif
}
/**
* @brief
* @param renderer
*/
void onRender(RenderBackend &renderer) override {
Scene::onRender(renderer);
if (!font_)
return;
// 屏幕中心位置
float centerX = 640.0f; // 1280 / 2
float centerY = 360.0f; // 720 / 2
// 绘制 "你好世界" 文字(白色,居中)
Color white(1.0f, 1.0f, 1.0f, 1.0f);
renderer.drawText(*font_, "你好世界", Vec2(centerX - 100.0f, centerY),
white);
// 绘制提示文字(黄色)
Color yellow(1.0f, 1.0f, 0.0f, 1.0f);
#ifdef PLATFORM_SWITCH
renderer.drawText(*font_, "退出按键START 按钮)",
Vec2(centerX - 80.0f, centerY + 50.0f), yellow);
#else
renderer.drawText(*font_, "退出按键ESC 或 START 按钮)",
Vec2(centerX - 80.0f, centerY + 50.0f), yellow);
#endif
}
private:
Ptr<FontAtlas> font_; // 字体图集
};
// ============================================================================
// 程序入口
// ============================================================================
/**
* @brief
* @return
*/
static AppConfig createAppConfig() {
AppConfig config;
config.title = "Easy2D - Hello World";
config.width = 1280;
config.height = 720;
config.vsync = true;
config.fpsLimit = 60;
#ifdef PLATFORM_PC
// PC 端默认窗口模式
config.fullscreen = false;
config.resizable = true;
#endif
return config;
}
/**
* @brief
*/
#ifdef _WIN32
int main(int argc, char *argv[])
#else
extern "C" int main(int argc, char *argv[])
#endif
{
(void)argc;
(void)argv;
// 初始化日志系统
Logger::init();
Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D Hello World Demo");
E2D_LOG_INFO("Platform: {}", platform::getPlatformName());
E2D_LOG_INFO("========================");
// 获取应用实例
auto &app = Application::instance();
// 配置应用
auto config = createAppConfig();
// 初始化应用
if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!");
return -1;
}
// 进入 Hello World 场景
app.enterScene(makePtr<HelloWorldScene>());
E2D_LOG_INFO("开始主循环...");
// 运行应用
app.run();
E2D_LOG_INFO("应用结束");
return 0;
}

View File

@ -1,451 +0,0 @@
#include <cmath>
#include <extra2d/extra2d.h>
#include <iomanip>
#include <random>
#include <sstream>
using namespace extra2d;
// ============================================================================
// 性能统计
// ============================================================================
struct PerformanceStats {
double updateTime = 0.0;
double collisionTime = 0.0;
double renderTime = 0.0;
size_t collisionCount = 0;
size_t nodeCount = 0;
const char *strategyName = "Unknown";
};
// ============================================================================
// 碰撞节点 - 使用引擎自带的空间索引功能
// ============================================================================
class PhysicsNode : public Node {
public:
PhysicsNode(float size, const Color &color, int id)
: size_(size), color_(color), id_(id), isColliding_(false) {
// 启用引擎自带的空间索引功能
// 这是关键:设置 spatialIndexed_ = true 让节点参与空间索引
setSpatialIndexed(true);
// 随机速度
std::random_device rd;
std::mt19937 gen(rd() + id);
std::uniform_real_distribution<float> velDist(-150.0f, 150.0f);
velocity_ = Vec2(velDist(gen), velDist(gen));
}
void setColliding(bool colliding) { isColliding_ = colliding; }
bool isColliding() const { return isColliding_; }
int getId() const { return id_; }
// 必须实现 getBoundingBox() 才能参与空间索引碰撞检测
Rect getBoundingBox() const override {
Vec2 pos = getPosition();
return Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_);
}
void update(float dt, float screenWidth, float screenHeight) {
Vec2 pos = getPosition();
pos = pos + velocity_ * dt;
// 边界反弹
if (pos.x < size_ / 2 || pos.x > screenWidth - size_ / 2) {
velocity_.x = -velocity_.x;
pos.x = std::clamp(pos.x, size_ / 2, screenWidth - size_ / 2);
}
if (pos.y < size_ / 2 || pos.y > screenHeight - size_ / 2) {
velocity_.y = -velocity_.y;
pos.y = std::clamp(pos.y, size_ / 2, screenHeight - size_ / 2);
}
setPosition(pos);
}
void onRender(RenderBackend &renderer) override {
Vec2 pos = getPosition();
// 碰撞时变红色
Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.9f) : color_;
renderer.fillRect(Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_),
fillColor);
// 绘制边框
Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f)
: Color(0.3f, 0.3f, 0.3f, 0.5f);
renderer.drawRect(Rect(pos.x - size_ / 2, pos.y - size_ / 2, size_, size_),
borderColor, 1.0f);
}
private:
float size_;
Color color_;
int id_;
bool isColliding_;
Vec2 velocity_;
};
// ============================================================================
// 空间索引演示场景
// ============================================================================
class SpatialIndexDemoScene : public Scene {
public:
void onEnter() override {
E2D_LOG_INFO("SpatialIndexDemoScene::onEnter - 引擎空间索引演示");
auto &app = Application::instance();
screenWidth_ = static_cast<float>(app.getConfig().width);
screenHeight_ = static_cast<float>(app.getConfig().height);
// 设置背景色
setBackgroundColor(Color(0.05f, 0.05f, 0.1f, 1.0f));
// 创建1000个碰撞节点
createNodes(1000);
// 加载字体
loadFonts();
E2D_LOG_INFO("创建了 {} 个碰撞节点", nodes_.size());
E2D_LOG_INFO("空间索引已启用: {}", isSpatialIndexingEnabled());
}
void onUpdate(float dt) override {
Scene::onUpdate(dt);
auto startTime = std::chrono::high_resolution_clock::now();
// 更新所有节点位置
for (auto &node : nodes_) {
node->update(dt, screenWidth_, screenHeight_);
}
auto updateEndTime = std::chrono::high_resolution_clock::now();
stats_.updateTime =
std::chrono::duration<double, std::milli>(updateEndTime - startTime)
.count();
// 使用引擎自带的空间索引进行碰撞检测
performCollisionDetection();
auto collisionEndTime = std::chrono::high_resolution_clock::now();
stats_.collisionTime = std::chrono::duration<double, std::milli>(
collisionEndTime - updateEndTime)
.count();
stats_.nodeCount = nodes_.size();
// 获取当前使用的空间索引策略
stats_.strategyName = getSpatialManager().getStrategyName();
// 检查退出按键
auto &input = Application::instance().input();
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_START)) {
E2D_LOG_INFO("退出应用");
Application::instance().quit();
}
// 按A键添加节点
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_A)) {
addNodes(100);
}
// 按B键减少节点
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_B)) {
removeNodes(100);
}
// 按X键切换空间索引策略
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_X)) {
toggleSpatialStrategy();
}
}
void onRender(RenderBackend &renderer) override {
Scene::onRender(renderer);
auto renderStart = std::chrono::high_resolution_clock::now();
// 节点渲染由Scene自动处理
auto renderEnd = std::chrono::high_resolution_clock::now();
stats_.renderTime =
std::chrono::duration<double, std::milli>(renderEnd - renderStart)
.count();
// 绘制UI
drawUI(renderer);
}
private:
/**
* @brief
*/
void loadFonts() {
auto &resources = Application::instance().resources();
#ifdef PLATFORM_SWITCH
std::vector<std::string> fontPaths = {
"romfs:/assets/msjh.ttf",
"romfs:/assets/default.ttf",
"romfs:/assets/font.ttf",
};
#else
std::vector<std::string> fontPaths = {
FileSystem::resolvePath("msjh.ttf"),
FileSystem::resolvePath("default.ttf"),
FileSystem::resolvePath("font.ttf"),
};
#endif
titleFont_ = resources.loadFontWithFallbacks(fontPaths, 28, true);
infoFont_ = resources.loadFontWithFallbacks(fontPaths, 16, true);
}
/**
* @brief
*/
void createNodes(size_t count) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<float> posX(50.0f, screenWidth_ - 50.0f);
std::uniform_real_distribution<float> posY(50.0f, screenHeight_ - 50.0f);
std::uniform_real_distribution<float> colorR(0.2f, 0.9f);
std::uniform_real_distribution<float> colorG(0.2f, 0.9f);
std::uniform_real_distribution<float> colorB(0.2f, 0.9f);
for (size_t i = 0; i < count; ++i) {
Color color(colorR(gen), colorG(gen), colorB(gen), 0.7f);
auto node = makePtr<PhysicsNode>(20.0f, color, static_cast<int>(i));
node->setPosition(Vec2(posX(gen), posY(gen)));
addChild(node);
nodes_.push_back(node);
}
}
/**
* @brief
*/
void addNodes(size_t count) {
size_t currentCount = nodes_.size();
if (currentCount + count > 5000) {
E2D_LOG_WARN("节点数量已达上限(5000)");
return;
}
createNodes(count);
E2D_LOG_INFO("添加 {} 个节点,当前总数: {}", count, nodes_.size());
}
/**
* @brief
*/
void removeNodes(size_t count) {
if (count >= nodes_.size()) {
count = nodes_.size();
}
if (count == 0)
return;
for (size_t i = 0; i < count; ++i) {
removeChild(nodes_.back());
nodes_.pop_back();
}
E2D_LOG_INFO("移除 {} 个节点,当前总数: {}", count, nodes_.size());
}
/**
* @brief
*/
void toggleSpatialStrategy() {
auto &spatialManager = getSpatialManager();
SpatialStrategy currentStrategy = spatialManager.getCurrentStrategy();
if (currentStrategy == SpatialStrategy::QuadTree) {
spatialManager.setStrategy(SpatialStrategy::SpatialHash);
E2D_LOG_INFO("切换到空间哈希策略");
} else {
spatialManager.setStrategy(SpatialStrategy::QuadTree);
E2D_LOG_INFO("切换到四叉树策略");
}
}
/**
* @brief 使
*
*
* - Scene::queryCollisions() -
* - SpatialManager::queryCollisions() -
*/
void performCollisionDetection() {
// 清除之前的碰撞状态
for (auto &node : nodes_) {
node->setColliding(false);
}
// 使用引擎自带的空间索引进行碰撞检测
// 这是核心Scene::queryCollisions() 会自动使用 SpatialManager
auto collisions = queryCollisions();
stats_.collisionCount = collisions.size();
// 标记碰撞的节点
for (const auto &[nodeA, nodeB] : collisions) {
if (auto boxA = dynamic_cast<PhysicsNode *>(nodeA)) {
boxA->setColliding(true);
}
if (auto boxB = dynamic_cast<PhysicsNode *>(nodeB)) {
boxB->setColliding(true);
}
}
}
/**
* @brief UI界面
*/
void drawUI(RenderBackend &renderer) {
if (!titleFont_ || !infoFont_)
return;
auto &app = Application::instance();
// 绘制标题
renderer.drawText(*titleFont_, "引擎空间索引演示", Vec2(30.0f, 20.0f),
Color(1.0f, 1.0f, 1.0f, 1.0f));
// 绘制性能统计
std::stringstream ss;
float x = 30.0f;
float y = 60.0f;
float lineHeight = 22.0f;
ss << "节点数量: " << stats_.nodeCount;
renderer.drawText(*infoFont_, ss.str(), Vec2(x, y),
Color(0.9f, 0.9f, 0.9f, 1.0f));
y += lineHeight;
ss.str("");
ss << "索引策略: " << stats_.strategyName;
renderer.drawText(*infoFont_, ss.str(), Vec2(x, y),
Color(0.5f, 1.0f, 0.5f, 1.0f));
y += lineHeight;
ss.str("");
ss << "碰撞对数: " << stats_.collisionCount;
renderer.drawText(*infoFont_, ss.str(), Vec2(x, y),
Color(1.0f, 0.5f, 0.5f, 1.0f));
y += lineHeight;
ss.str("");
ss << std::fixed << std::setprecision(2);
ss << "更新时间: " << stats_.updateTime << " ms";
renderer.drawText(*infoFont_, ss.str(), Vec2(x, y),
Color(0.8f, 0.8f, 0.8f, 1.0f));
y += lineHeight;
ss.str("");
ss << "碰撞检测: " << stats_.collisionTime << " ms";
renderer.drawText(*infoFont_, ss.str(), Vec2(x, y),
Color(0.8f, 0.8f, 0.8f, 1.0f));
y += lineHeight;
ss.str("");
ss << "渲染时间: " << stats_.renderTime << " ms";
renderer.drawText(*infoFont_, ss.str(), Vec2(x, y),
Color(0.8f, 0.8f, 0.8f, 1.0f));
y += lineHeight;
ss.str("");
ss << "FPS: " << app.fps();
renderer.drawText(*infoFont_, ss.str(), Vec2(x, y),
Color(0.5f, 1.0f, 0.5f, 1.0f));
y += lineHeight * 1.5f;
// 绘制操作说明
renderer.drawText(*infoFont_, "操作说明:", Vec2(x, y),
Color(1.0f, 1.0f, 0.5f, 1.0f));
y += lineHeight;
renderer.drawText(*infoFont_, "A键 - 添加100个节点", Vec2(x + 10, y),
Color(0.8f, 0.8f, 0.8f, 1.0f));
y += lineHeight;
renderer.drawText(*infoFont_, "B键 - 移除100个节点", Vec2(x + 10, y),
Color(0.8f, 0.8f, 0.8f, 1.0f));
y += lineHeight;
renderer.drawText(*infoFont_, "X键 - 切换索引策略", Vec2(x + 10, y),
Color(0.8f, 0.8f, 0.8f, 1.0f));
y += lineHeight;
renderer.drawText(*infoFont_, "+键 - 退出程序", Vec2(x + 10, y),
Color(0.8f, 0.8f, 0.8f, 1.0f));
// 绘制图例
float legendX = screenWidth_ - 200.0f;
float legendY = 20.0f;
renderer.drawText(*infoFont_, "图例:", Vec2(legendX, legendY),
Color(1.0f, 1.0f, 1.0f, 1.0f));
legendY += 25.0f;
renderer.fillRect(Rect(legendX, legendY, 15.0f, 15.0f),
Color(0.5f, 0.5f, 0.9f, 0.7f));
renderer.drawText(*infoFont_, "- 正常", Vec2(legendX + 20.0f, legendY),
Color(0.8f, 0.8f, 0.8f, 1.0f));
legendY += 25.0f;
renderer.fillRect(Rect(legendX, legendY, 15.0f, 15.0f),
Color(1.0f, 0.2f, 0.2f, 0.9f));
renderer.drawText(*infoFont_, "- 碰撞中", Vec2(legendX + 20.0f, legendY),
Color(0.8f, 0.8f, 0.8f, 1.0f));
}
std::vector<Ptr<PhysicsNode>> nodes_;
PerformanceStats stats_;
float screenWidth_ = 1280.0f;
float screenHeight_ = 720.0f;
Ptr<FontAtlas> titleFont_;
Ptr<FontAtlas> infoFont_;
};
// ============================================================================
// 程序入口
// ============================================================================
#ifdef _WIN32
int main(int argc, char *argv[])
#else
extern "C" int main(int argc, char *argv[])
#endif
{
(void)argc;
(void)argv;
Logger::init();
Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D 引擎空间索引演示");
E2D_LOG_INFO("========================");
auto &app = Application::instance();
AppConfig config;
config.title = "Easy2D - 引擎空间索引演示";
config.width = 1280;
config.height = 720;
config.vsync = true;
config.fpsLimit = 60;
if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!");
return -1;
}
app.enterScene(makePtr<SpatialIndexDemoScene>());
E2D_LOG_INFO("开始主循环...");
app.run();
E2D_LOG_INFO("应用结束");
return 0;
}

View File

@ -1,959 +0,0 @@
# Extra2D API 参考文档
## 目录
- [核心系统](#核心系统)
- [应用管理](#应用管理)
- [场景系统](#场景系统)
- [节点系统](#节点系统)
- [输入系统](#输入系统)
- [资源管理](#资源管理)
- [动画系统](#动画系统)
- [音频系统](#音频系统)
- [文件系统](#文件系统)
---
## 核心系统
### 类型定义
```cpp
namespace extra2d {
// 基本类型
using Vec2 = glm::vec2;
using Vec3 = glm::vec3;
using Vec4 = glm::vec4;
using Mat3 = glm::mat3;
using Mat4 = glm::mat4;
// 智能指针
template<typename T>
using Ptr = std::shared_ptr<T>;
template<typename T, typename... Args>
Ptr<T> makePtr(Args&&... args) {
return std::make_shared<T>(std::forward<Args>(args)...);
}
} // namespace extra2d
```
### 颜色类
```cpp
struct Color {
float r, g, b, a;
Color(float r = 1.0f, float g = 1.0f, float b = 1.0f, float a = 1.0f);
// 预定义颜色
static Color White;
static Color Black;
static Color Red;
static Color Green;
static Color Blue;
static Color Yellow;
static Color Transparent;
};
```
### 矩形类
```cpp
struct Rect {
float x, y, width, height;
Rect(float x = 0, float y = 0, float w = 0, float h = 0);
bool contains(const Vec2& point) const;
bool intersects(const Rect& other) const;
float left() const { return x; }
float right() const { return x + width; }
float top() const { return y; }
float bottom() const { return y + height; }
Vec2 center() const { return Vec2(x + width/2, y + height/2); }
};
```
---
## 应用管理
### AppConfig
应用配置结构体。
```cpp
struct AppConfig {
String title = "Extra2D Application";
int width = 800;
int height = 600;
bool fullscreen = false;
bool resizable = true;
bool vsync = true;
int fpsLimit = 0; // 0 = 不限制
BackendType renderBackend = BackendType::OpenGL;
int msaaSamples = 0;
};
```
### Application
应用主类,单例模式。
```cpp
class Application {
public:
// 获取单例实例
static Application& instance();
// 初始化应用
bool init(const AppConfig& config);
// 运行主循环
void run();
// 退出应用
void quit();
// 进入场景
void enterScene(Ptr<Scene> scene);
void enterScene(Ptr<Scene> scene, Ptr<Transition> transition);
// 获取子系统
Input& input();
AudioEngine& audio();
ResourceManager& resources();
RenderBackend& renderer();
// 获取配置
const AppConfig& getConfig() const;
// 获取当前 FPS
float fps() const;
// Switch 特定:检测是否连接底座
bool isDocked() const;
};
```
**使用示例:**
```cpp
#include <extra2d/extra2d.h>
using namespace extra2d;
int main() {
// 初始化日志
Logger::init();
Logger::setLevel(LogLevel::Debug);
// 配置应用
AppConfig config;
config.title = "My Game";
config.width = 1280;
config.height = 720;
config.vsync = true;
// 初始化应用
auto& app = Application::instance();
if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!");
return -1;
}
// 进入场景
app.enterScene(makePtr<MyScene>());
// 运行主循环
app.run();
return 0;
}
```
---
## 场景系统
### Scene
场景类,作为游戏内容的容器。
```cpp
class Scene : public Node {
public:
// 构造函数
Scene();
// 场景生命周期回调
virtual void onEnter(); // 进入场景时调用
virtual void onExit(); // 退出场景时调用
virtual void onUpdate(float dt); // 每帧更新
virtual void onRender(RenderBackend& renderer); // 渲染
// 设置背景颜色
void setBackgroundColor(const Color& color);
// 空间索引
void setSpatialIndexingEnabled(bool enabled);
bool isSpatialIndexingEnabled() const;
// 查询碰撞
std::vector<std::pair<Node*, Node*>> queryCollisions();
// 获取空间管理器
SpatialManager& getSpatialManager();
};
```
### Transition
场景过渡动画基类。
```cpp
class Transition {
public:
explicit Transition(float duration);
virtual void update(float dt) = 0;
virtual void render(RenderBackend& renderer,
Ptr<Scene> fromScene,
Ptr<Scene> toScene) = 0;
bool isFinished() const;
};
// 内置过渡效果
class FadeTransition : public Transition {
public:
FadeTransition(float duration, const Color& color = Color::Black);
};
class SlideTransition : public Transition {
public:
SlideTransition(float duration, Direction direction);
};
```
---
## 节点系统
### Node
所有场景对象的基类。
```cpp
class Node {
public:
Node();
virtual ~Node();
// 变换
void setPosition(const Vec2& pos);
Vec2 getPosition() const;
void setRotation(float degrees);
float getRotation() const;
void setScale(const Vec2& scale);
Vec2 getScale() const;
void setAnchor(const Vec2& anchor);
Vec2 getAnchor() const;
// 层级
void addChild(Ptr<Node> child);
void removeChild(Ptr<Node> child);
void removeFromParent();
// 可见性
void setVisible(bool visible);
bool isVisible() const;
// 动作
void runAction(Ptr<Action> action);
void stopAllActions();
// 渲染
virtual void onRender(RenderBackend& renderer);
// 更新
virtual void onUpdate(float dt);
// 边界框(用于碰撞检测)
virtual Rect getBoundingBox() const;
// 空间索引
void setSpatialIndexed(bool indexed);
bool isSpatialIndexed() const;
};
```
### Sprite
精灵节点用于显示2D图像。
```cpp
class Sprite : public Node {
public:
// 创建方法
static Ptr<Sprite> create(Ptr<Texture> texture);
static Ptr<Sprite> create(const std::string& texturePath);
// 设置纹理
void setTexture(Ptr<Texture> texture);
Ptr<Texture> getTexture() const;
// 设置纹理矩形(用于精灵表)
void setTextureRect(const Rect& rect);
// 设置颜色调制
void setColor(const Color& color);
Color getColor() const;
// 翻转
void setFlippedX(bool flipped);
void setFlippedY(bool flipped);
};
```
### Text
文本节点。
```cpp
class Text : public Node {
public:
static Ptr<Text> create(const std::string& text = "");
// 文本内容
void setText(const std::string& text);
std::string getText() const;
// 字体
void setFont(Ptr<FontAtlas> font);
void setFontSize(int size);
// 颜色
void setTextColor(const Color& color);
Color getTextColor() const;
// 对齐
void setHorizontalAlignment(Alignment align);
void setVerticalAlignment(Alignment align);
};
```
---
## 输入系统
### Input
输入管理类。
```cpp
class Input {
public:
// 键盘
bool isKeyDown(int keyCode) const; // 按键是否按下
bool isKeyPressed(int keyCode) const; // 按键是否刚按下
bool isKeyReleased(int keyCode) const; // 按键是否刚释放
// 手柄按钮
bool isButtonDown(int button) const;
bool isButtonPressed(int button) const;
bool isButtonReleased(int button) const;
// 摇杆
Vec2 getLeftStick() const;
Vec2 getRightStick() const;
// 鼠标PC端
Vec2 getMousePosition() const;
bool isMouseDown(int button) const;
bool isMousePressed(int button) const;
// 触摸Switch/移动端)
bool isTouching() const;
Vec2 getTouchPosition() const;
int getTouchCount() const;
};
```
### 按键码 (Key)
```cpp
namespace Key {
// 字母
A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
// 数字
Num0, Num1, Num2, Num3, Num4,
Num5, Num6, Num7, Num8, Num9,
// 功能键
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
// 方向键
Up, Down, Left, Right,
// 特殊键
Escape, Enter, Tab, Backspace, Space,
Insert, Delete, Home, End, PageUp, PageDown,
LeftShift, LeftControl, LeftAlt, LeftSuper,
RightShift, RightControl, RightAlt, RightSuper
}
```
**使用示例:**
```cpp
void MyScene::onUpdate(float dt) {
auto& input = Application::instance().input();
// 键盘输入
if (input.isKeyPressed(Key::Space)) {
// 空格键按下
jump();
}
if (input.isKeyDown(Key::Left)) {
// 左方向键按住
moveLeft();
}
// 手柄输入
if (input.isButtonPressed(GamepadButton::A)) {
// A 按钮按下
jump();
}
// 摇杆输入
Vec2 leftStick = input.getLeftStick();
move(leftStick.x * speed, leftStick.y * speed);
}
```
---
## 资源管理
### ResourceManager
资源管理类,负责加载和管理游戏资源。
```cpp
class ResourceManager {
public:
// 纹理
Ptr<Texture> loadTexture(const std::string& path);
Ptr<Texture> getTexture(const std::string& path);
void unloadTexture(const std::string& path);
// 字体
Ptr<FontAtlas> loadFont(const std::string& path, int size, bool useSDF = false);
Ptr<FontAtlas> loadFontWithFallbacks(const std::vector<std::string>& paths,
int size, bool useSDF = false);
// 音频
Ptr<Sound> loadSound(const std::string& path);
Ptr<Music> loadMusic(const std::string& path);
// 精灵表
Ptr<SpriteFrameCache> loadSpriteSheet(const std::string& path);
// 清理
void unloadAll();
void unloadUnused();
};
```
### Texture
纹理类。
```cpp
class Texture {
public:
// 获取尺寸
int getWidth() const;
int getHeight() const;
Vec2 getSize() const;
// 获取 OpenGL 纹理 ID
GLuint getTextureID() const;
};
```
### FontAtlas
字体图集类。
```cpp
class FontAtlas {
public:
// 获取字体信息
int getFontSize() const;
bool isSDF() const;
// 获取字符信息
const Glyph& getGlyph(char32_t charCode) const;
// 获取行高
float getLineHeight() const;
// 获取纹理
Ptr<Texture> getTexture() const;
};
```
**使用示例:**
```cpp
void MyScene::onEnter() {
auto& resources = Application::instance().resources();
// 加载纹理
auto playerTexture = resources.loadTexture("player.png");
auto enemyTexture = resources.loadTexture("enemies/slime.png");
// 加载字体
auto font = resources.loadFont("font.ttf", 24, true);
// 创建精灵
auto player = Sprite::create(playerTexture);
addChild(player);
// 创建文本
auto text = Text::create("Hello World");
text->setFont(font);
addChild(text);
}
```
---
## 动画系统
### Action
动作基类。
```cpp
class Action {
public:
virtual void update(float dt) = 0;
virtual bool isFinished() const = 0;
void reset();
};
```
### 基础动作
```cpp
// 移动
class MoveTo : public Action {
public:
MoveTo(float duration, const Vec2& position);
};
class MoveBy : public Action {
public:
MoveBy(float duration, const Vec2& delta);
};
// 缩放
class ScaleTo : public Action {
public:
ScaleTo(float duration, const Vec2& scale);
ScaleTo(float duration, float scale);
};
class ScaleBy : public Action {
public:
ScaleBy(float duration, const Vec2& scale);
ScaleBy(float duration, float scale);
};
// 旋转
class RotateTo : public Action {
public:
RotateTo(float duration, float degrees);
};
class RotateBy : public Action {
public:
RotateBy(float duration, float degrees);
};
// 淡入淡出
class FadeTo : public Action {
public:
FadeTo(float duration, float opacity);
};
class FadeIn : public Action {
public:
FadeIn(float duration);
};
class FadeOut : public Action {
public:
FadeOut(float duration);
};
// 延迟
class Delay : public Action {
public:
Delay(float duration);
};
// 回调
class CallFunc : public Action {
public:
CallFunc(std::function<void()> callback);
};
```
### 组合动作
```cpp
// 顺序执行
class Sequence : public Action {
public:
Sequence(std::vector<Ptr<Action>> actions);
template<typename... Args>
static Ptr<Sequence> create(Args&&... args) {
return makePtr<Sequence>(std::vector<Ptr<Action>>{std::forward<Args>(args)...});
}
};
// 同时执行
class Spawn : public Action {
public:
Spawn(std::vector<Ptr<Action>> actions);
};
// 重复
class Repeat : public Action {
public:
Repeat(Ptr<Action> action, int times = -1); // -1 = 无限重复
};
// 反向
class Reverse : public Action {
public:
Reverse(Ptr<Action> action);
};
```
### 缓动函数
```cpp
namespace Ease {
// 线性
float linear(float t);
// 二次
float inQuad(float t);
float outQuad(float t);
float inOutQuad(float t);
// 三次
float inCubic(float t);
float outCubic(float t);
float inOutCubic(float t);
// 弹性
float inElastic(float t);
float outElastic(float t);
// 弹跳
float inBounce(float t);
float outBounce(float t);
// 回退
float inBack(float t);
float outBack(float t);
}
```
**使用示例:**
```cpp
// 创建精灵
auto sprite = Sprite::create("player.png");
sprite->setPosition(Vec2(100, 100));
addChild(sprite);
// 简单动作
sprite->runAction(makePtr<MoveTo>(1.0f, Vec2(300, 200)));
// 组合动作
sprite->runAction(makePtr<Sequence>(
makePtr<ScaleTo>(0.5f, Vec2(1.5f, 1.5f)),
makePtr<Delay>(0.2f),
makePtr<ScaleTo>(0.5f, Vec2(1.0f, 1.0f)),
makePtr<CallFunc>([]() {
E2D_LOG_INFO("动画完成!");
})
));
// 重复动画
sprite->runAction(makePtr<Repeat>(
makePtr<Sequence>(
makePtr<RotateBy>(1.0f, 360.0f),
makePtr<Delay>(0.5f)
)
));
```
---
## 音频系统
### AudioEngine
音频引擎类。
```cpp
class AudioEngine {
public:
// 音量控制 (0.0 - 1.0)
void setMasterVolume(float volume);
float getMasterVolume() const;
void setBGMVolume(float volume);
void setSFXVolume(float volume);
// 播放控制
void pauseAll();
void resumeAll();
void stopAll();
};
```
### Sound
音效类(短音频,适合音效)。
```cpp
class Sound {
public:
void play(int loops = 0); // loops: 0=播放1次, -1=无限循环
void stop();
void pause();
void resume();
void setVolume(float volume);
bool isPlaying() const;
};
```
### Music
音乐类(长音频,适合背景音乐)。
```cpp
class Music {
public:
void play(int loops = -1);
void stop();
void pause();
void resume();
void setVolume(float volume);
bool isPlaying() const;
void setLoopPoints(double start, double end);
};
```
**使用示例:**
```cpp
void MyScene::onEnter() {
auto& audio = Application::instance().audio();
auto& resources = Application::instance().resources();
// 加载并播放背景音乐
auto bgm = resources.loadMusic("bgm/level1.ogg");
bgm->play(-1); // 无限循环
bgm->setVolume(0.7f);
// 加载音效
jumpSound_ = resources.loadSound("sfx/jump.wav");
coinSound_ = resources.loadSound("sfx/coin.wav");
}
void MyScene::jump() {
jumpSound_->play();
}
void MyScene::collectCoin() {
coinSound_->play();
}
```
---
## 文件系统
### FileSystem
跨平台文件系统工具类。
```cpp
class FileSystem {
public:
// 路径解析(自动处理平台差异)
// PC: "assets/font.ttf" -> "C:/.../assets/font.ttf"
// Switch: "assets/font.ttf" -> "romfs:/assets/font.ttf"
static std::string resolvePath(const std::string& relativePath);
// 获取资源根目录
static std::string getResourceRoot();
// 获取可执行文件目录
static std::string getExecutableDirectory();
// 获取当前工作目录
static std::string getCurrentWorkingDirectory();
// 路径组合
static std::string combinePath(const std::string& path1,
const std::string& path2);
// 文件/目录检查
static bool fileExists(const std::string& path);
static bool directoryExists(const std::string& path);
// 读取文件内容
static std::vector<uint8_t> readFile(const std::string& path);
static std::string readTextFile(const std::string& path);
// 写入文件
static bool writeFile(const std::string& path,
const std::vector<uint8_t>& data);
static bool writeTextFile(const std::string& path,
const std::string& content);
};
```
**使用示例:**
```cpp
// 解析资源路径(跨平台)
std::string fontPath = FileSystem::resolvePath("fonts/main.ttf");
// PC: "C:/.../assets/fonts/main.ttf"
// Switch: "romfs:/assets/fonts/main.ttf"
// 检查文件是否存在
if (FileSystem::fileExists(fontPath)) {
auto font = resources.loadFont(fontPath, 24);
}
// 读取配置文件
std::string configPath = FileSystem::resolvePath("config/game.json");
std::string jsonContent = FileSystem::readTextFile(configPath);
```
---
## 日志系统
### Logger
日志系统。
```cpp
enum class LogLevel {
Debug,
Info,
Warning,
Error
};
class Logger {
public:
static void init();
static void shutdown();
static void setLevel(LogLevel level);
// 日志宏
#define E2D_LOG_DEBUG(...) // 调试日志
#define E2D_LOG_INFO(...) // 信息日志
#define E2D_LOG_WARN(...) // 警告日志
#define E2D_LOG_ERROR(...) // 错误日志
};
```
**使用示例:**
```cpp
void MyClass::doSomething() {
E2D_LOG_DEBUG("进入函数 doSomething");
if (!loadData()) {
E2D_LOG_ERROR("加载数据失败!");
return;
}
E2D_LOG_INFO("成功加载 {} 条记录", recordCount);
}
```
---
## 平台兼容性
### 平台检测
```cpp
// 编译时检测
#ifdef PLATFORM_SWITCH
// Switch 代码
#elif defined(PLATFORM_PC)
// PC 代码
#ifdef PLATFORM_WINDOWS
// Windows 代码
#elif defined(PLATFORM_LINUX)
// Linux 代码
#elif defined(PLATFORM_MACOS)
// macOS 代码
#endif
#endif
// 运行时检测
namespace platform {
bool isSwitch();
bool isPC();
bool isWindows();
bool isLinux();
bool isMacOS();
const char* getPlatformName();
}
```
---
## 更多文档
- [Switch 构建指南](./SWITCH_BUILD_GUIDE.md)
- [PC 构建指南](./PC_BUILD_GUIDE.md)
- [迁移完成记录](./SWITCH_MIGRATION_COMPLETE.md)
---
**最后更新**: 2026年2月10日
**Extra2D 版本**: 3.1.0

View File

@ -1,822 +0,0 @@
# DataStore 数据持久化系统
## 概述
`DataStore` 是 Easy2D 引擎的数据持久化类,提供 INI 文件格式的数据存储功能,并支持 Nintendo Switch 平台的官方存档系统。
## 特性
- ✅ INI 文件格式读写
- ✅ Nintendo Switch 官方存档系统支持
- ✅ 多用户存档隔离
- ✅ 事务支持(批量操作 + 回滚)
- ✅ 自动保存和脏数据检测
- ✅ 跨平台兼容Switch/PC
---
## 快速开始
### 基本使用
```cpp
#include <extra2d/extra2d.h>
using namespace extra2d;
// 创建 DataStore 实例
DataStore data;
// 加载数据文件
data.load("config.ini");
// 读取数据
int level = data.getInt("Player", "Level", 1);
std::string name = data.getString("Player", "Name", "Unknown");
// 写入数据
data.setInt("Player", "Level", 10);
data.setString("Player", "Name", "Hero");
// 保存到文件
data.save("config.ini");
```
### Switch 存档系统使用
```cpp
DataStore saveData;
// 挂载存档(自动获取当前用户)
if (saveData.mountSaveData(SaveDataType::Account)) {
// 从存档加载
saveData.loadFromSave("savegame.ini");
// 修改数据
saveData.setInt("Progress", "Level", 5);
saveData.setInt("Progress", "Score", 1000);
// 保存到存档(自动提交)
saveData.saveToSave("savegame.ini");
// 卸载存档
saveData.unmountSaveData();
}
```
---
## API 参考
### 枚举类型
#### SaveDataType
存档数据类型枚举:
| 值 | 说明 |
|---|---|
| `SaveDataType::Account` | 用户存档,与特定用户账户关联 |
| `SaveDataType::Common` | 公共存档,所有用户共享 |
| `SaveDataType::Cache` | 缓存数据,可被系统清理 |
| `SaveDataType::Device` | 设备存档,与设备绑定 |
| `SaveDataType::Temporary` | 临时数据,应用退出后清理 |
#### UserId
用户ID结构体
```cpp
struct UserId {
uint64_t uid[2]; // 用户唯一标识
bool isValid() const; // 检查ID是否有效
};
```
---
### 构造函数
```cpp
DataStore();
~DataStore();
```
**说明:**
- 析构时会自动提交未保存的事务
- 如果存档已挂载,会自动卸载
---
### 文件操作
#### load
```cpp
bool load(const std::string &filename);
```
加载 INI 文件。
**参数:**
- `filename` - 文件路径
**返回值:**
- `true` - 加载成功
- `false` - 加载失败
**示例:**
```cpp
if (!data.load("config.ini")) {
E2D_LOG_WARN("配置文件不存在,将创建新文件");
}
```
---
#### save
```cpp
bool save(const std::string &filename = "");
```
保存到 INI 文件。
**参数:**
- `filename` - 文件路径,为空则使用上次加载的文件名
**返回值:**
- `true` - 保存成功
- `false` - 保存失败
**说明:**
- 如果在事务中,只标记为脏数据,不实际写入
- 事务外自动保存(当设置数据时)
---
### Switch 存档系统
#### mountSaveData
```cpp
bool mountSaveData(
SaveDataType type = SaveDataType::Account,
const UserId &userId = UserId(),
const std::string &mountName = "save"
);
```
挂载 Switch 存档数据。
**参数:**
- `type` - 存档类型
- `userId` - 用户IDAccount 类型需要,为空则自动获取当前用户)
- `mountName` - 挂载点名称
**返回值:**
- `true` - 挂载成功
- `false` - 挂载失败
**示例:**
```cpp
// 挂载当前用户的存档
if (data.mountSaveData(SaveDataType::Account)) {
// 存档已挂载,路径为 "save:/"
}
// 挂载公共存档
if (data.mountSaveData(SaveDataType::Common, UserId(), "common")) {
// 公共存档已挂载,路径为 "common:/"
}
```
---
#### unmountSaveData
```cpp
void unmountSaveData(const std::string &mountName = "save");
```
卸载存档挂载。
**参数:**
- `mountName` - 挂载点名称
**说明:**
- 卸载前会自动提交未保存的更改
---
#### commitSaveData
```cpp
bool commitSaveData(const std::string &mountName = "save");
```
提交存档更改。
**⚠️ 重要:** 修改存档数据后必须调用此方法,否则更改不会持久化!
**参数:**
- `mountName` - 挂载点名称
**返回值:**
- `true` - 提交成功
- `false` - 提交失败
**示例:**
```cpp
data.setInt("Game", "Score", 100);
data.save();
data.commitSaveData(); // 必须提交!
```
---
#### isSaveDataMounted
```cpp
bool isSaveDataMounted() const;
```
检查存档是否已挂载。
**返回值:**
- `true` - 已挂载
- `false` - 未挂载
---
#### getSaveDataPath
```cpp
std::string getSaveDataPath(const std::string &path = "") const;
```
获取挂载点路径。
**参数:**
- `path` - 子路径
**返回值:**
- 完整路径(如 "save:/data/config.ini"
**示例:**
```cpp
std::string fullPath = data.getSaveDataPath("config/settings.ini");
// 返回: "save:/config/settings.ini"
```
---
### 用户账户管理
#### getCurrentUserId
```cpp
static UserId getCurrentUserId();
```
获取当前预选用户ID。
**返回值:**
- 用户ID无效时返回空ID
**示例:**
```cpp
UserId user = DataStore::getCurrentUserId();
if (user.isValid()) {
E2D_LOG_INFO("当前用户: 0x{:X}{:X}", user.uid[1], user.uid[0]);
}
```
---
#### setDefaultUserId / getDefaultUserId
```cpp
void setDefaultUserId(const UserId &userId);
UserId getDefaultUserId() const;
```
设置/获取默认用户ID。
**说明:**
- 用于多用户场景下的默认用户选择
---
### 数据读写
#### getString
```cpp
std::string getString(
const std::string &section,
const std::string &key,
const std::string &defaultValue = ""
);
```
获取字符串值。
**参数:**
- `section` - 节名称
- `key` - 键名称
- `defaultValue` - 默认值(键不存在时返回)
**返回值:**
- 字符串值
---
#### getInt
```cpp
int getInt(
const std::string &section,
const std::string &key,
int defaultValue = 0
);
```
获取整数值。
---
#### getFloat
```cpp
float getFloat(
const std::string &section,
const std::string &key,
float defaultValue = 0.0f
);
```
获取浮点数值。
---
#### getBool
```cpp
bool getBool(
const std::string &section,
const std::string &key,
bool defaultValue = false
);
```
获取布尔值。
**说明:**
- 支持 "true"/"false"、"yes"/"no"、"1"/"0" 等格式
---
#### setString
```cpp
void setString(
const std::string &section,
const std::string &key,
const std::string &value
);
```
设置字符串值。
**说明:**
- 自动标记为脏数据
- 事务外自动保存
---
#### setInt
```cpp
void setInt(
const std::string &section,
const std::string &key,
int value
);
```
设置整数值。
---
#### setFloat
```cpp
void setFloat(
const std::string &section,
const std::string &key,
float value
);
```
设置浮点数值。
---
#### setBool
```cpp
void setBool(
const std::string &section,
const std::string &key,
bool value
);
```
设置布尔值。
---
#### removeKey
```cpp
void removeKey(const std::string &section, const std::string &key);
```
删除键。
---
#### removeSection
```cpp
void removeSection(const std::string &section);
```
删除整个 section。
---
#### hasKey
```cpp
bool hasKey(const std::string &section, const std::string &key);
```
检查键是否存在。
---
#### hasSection
```cpp
bool hasSection(const std::string &section);
```
检查 section 是否存在。
---
#### clear
```cpp
void clear();
```
清除所有数据。
---
### 事务支持
#### beginTransaction
```cpp
void beginTransaction();
```
开始事务。
**说明:**
- 事务中的修改不会立即写入文件
- 支持批量操作,提高性能
- 事务可以回滚
**示例:**
```cpp
data.beginTransaction();
// 批量修改
data.setInt("Player", "Level", 10);
data.setInt("Player", "Exp", 1000);
data.setString("Player", "Title", "Knight");
// 提交事务
data.commit();
```
---
#### commit
```cpp
bool commit();
```
提交事务。
**返回值:**
- `true` - 提交成功
- `false` - 提交失败
**说明:**
- 写入文件并提交存档(如果已挂载)
- 事务结束后自动保存
---
#### rollback
```cpp
void rollback();
```
回滚事务。
**说明:**
- 放弃事务中的所有修改
- 重新加载文件恢复数据
**示例:**
```cpp
data.beginTransaction();
data.setInt("Test", "Value", 999);
// 放弃修改
data.rollback();
// Value 恢复为原来的值
```
---
#### isInTransaction
```cpp
bool isInTransaction() const;
```
检查是否在事务中。
---
### 工具方法
#### getAllSections
```cpp
std::vector<std::string> getAllSections() const;
```
获取所有 section 名称。
**返回值:**
- section 名称列表
---
#### getAllKeys
```cpp
std::vector<std::string> getAllKeys(const std::string &section) const;
```
获取指定 section 的所有 key。
**参数:**
- `section` - section 名称
**返回值:**
- key 名称列表
---
#### loadFromSave
```cpp
bool loadFromSave(const std::string &path);
```
从存档加载(自动处理挂载路径)。
**参数:**
- `path` - 存档内文件路径
**返回值:**
- `true` - 加载成功
- `false` - 加载失败(存档未挂载或文件不存在)
**示例:**
```cpp
if (data.loadFromSave("savegame.ini")) {
// 加载成功
}
```
---
#### saveToSave
```cpp
bool saveToSave(const std::string &path);
```
保存到存档(自动处理挂载路径和提交)。
**参数:**
- `path` - 存档内文件路径
**返回值:**
- `true` - 保存成功
- `false` - 保存失败
**说明:**
- 自动调用 `commitSaveData()` 提交更改
---
## 完整示例
### 游戏存档管理
```cpp
#include <extra2d/extra2d.h>
using namespace extra2d;
class SaveManager {
private:
DataStore saveData_;
bool mounted_ = false;
public:
bool initialize() {
// 挂载用户存档
mounted_ = saveData_.mountSaveData(SaveDataType::Account);
if (!mounted_) {
E2D_LOG_ERROR("存档挂载失败");
return false;
}
// 加载存档
if (!saveData_.loadFromSave("game_save.ini")) {
E2D_LOG_INFO("存档不存在,创建新存档");
createNewSave();
}
return true;
}
void shutdown() {
if (mounted_) {
saveData_.unmountSaveData();
mounted_ = false;
}
}
void createNewSave() {
saveData_.beginTransaction();
saveData_.setString("Player", "Name", "Player1");
saveData_.setInt("Player", "Level", 1);
saveData_.setInt("Player", "Exp", 0);
saveData_.setInt("Progress", "Chapter", 1);
saveData_.setInt("Settings", "Difficulty", 1);
saveData_.commit();
saveData_.saveToSave("game_save.ini");
}
void saveGame(const PlayerData &player) {
saveData_.beginTransaction();
saveData_.setInt("Player", "Level", player.level);
saveData_.setInt("Player", "Exp", player.exp);
saveData_.setInt("Player", "Health", player.health);
saveData_.setInt("Player", "Mana", player.mana);
saveData_.setInt("Progress", "Chapter", player.chapter);
saveData_.setString("Progress", "Checkpoint", player.checkpoint);
if (saveData_.commit()) {
saveData_.saveToSave("game_save.ini");
E2D_LOG_INFO("游戏已保存");
} else {
E2D_LOG_ERROR("保存失败");
}
}
void loadGame(PlayerData &player) {
player.level = saveData_.getInt("Player", "Level", 1);
player.exp = saveData_.getInt("Player", "Exp", 0);
player.health = saveData_.getInt("Player", "Health", 100);
player.mana = saveData_.getInt("Player", "Mana", 50);
player.chapter = saveData_.getInt("Progress", "Chapter", 1);
player.checkpoint = saveData_.getString("Progress", "Checkpoint", "start");
}
bool hasSaveFile() {
return saveData_.hasSection("Player");
}
};
```
### 设置管理
```cpp
class SettingsManager {
private:
DataStore settings_;
public:
void load() {
// PC 平台使用普通文件
#ifndef __SWITCH__
settings_.load("settings.ini");
#else
// Switch 平台使用存档
if (settings_.mountSaveData(SaveDataType::Common)) {
settings_.loadFromSave("settings.ini");
}
#endif
}
void save() {
#ifndef __SWITCH__
settings_.save("settings.ini");
#else
settings_.saveToSave("settings.ini");
settings_.unmountSaveData();
#endif
}
int getVolume() {
return settings_.getInt("Audio", "Volume", 80);
}
void setVolume(int volume) {
settings_.setInt("Audio", "Volume", volume);
}
bool isFullscreen() {
return settings_.getBool("Video", "Fullscreen", false);
}
void setFullscreen(bool fullscreen) {
settings_.setBool("Video", "Fullscreen", fullscreen);
}
};
```
---
## 注意事项
### Switch 平台
1. **必须提交存档**:修改存档数据后,必须调用 `commitSaveData()``saveToSave()`,否则更改不会持久化
2. **用户ID管理**
- `Account` 类型存档需要有效的用户ID
- 可以使用 `getCurrentUserId()` 自动获取当前用户
- 公共存档使用 `Common` 类型不需要用户ID
3. **存档挂载**
- 每个挂载点需要唯一的名称
- 应用退出时会自动卸载,但建议显式调用 `unmountSaveData()`
4. **存档空间**
- 注意存档空间限制
- 大数据(如截图)建议使用 `Cache` 类型
### 通用
1. **事务使用**
- 批量修改时使用事务提高性能
- 事务中的错误可以通过 `rollback()` 恢复
2. **自动保存**
- 事务外每次 `set` 操作都会触发自动保存
- 频繁修改建议使用事务批量处理
3. **文件格式**
- 使用标准 INI 格式
- 支持注释(以 `;``#` 开头)
- Section 和 Key 不区分大小写
---
## 相关文件
- [data.h](file:///c:/Users/soulcoco/Desktop/Easy2D/Extra2D/Extra2D/include/extra2d/utils/data.h) - 头文件
- [data.cpp](file:///c:/Users/soulcoco/Desktop/Easy2D/Extra2D/Extra2D/src/utils/data.cpp) - 实现文件

View File

@ -1,225 +0,0 @@
# Extra2D 文档索引
欢迎来到 Extra2D 文档中心!这里包含了使用 Extra2D 游戏引擎所需的所有文档。
## 📚 快速导航
### 入门指南
| 文档 | 描述 |
|------|------|
| [README.md](../README.md) | 项目概述和快速开始 |
| [Switch 构建指南](./SWITCH_BUILD_GUIDE.md) | Nintendo Switch 平台构建教程 |
| [PC 构建指南](./PC_BUILD_GUIDE.md) | Windows/Linux/macOS 构建教程 |
### API 参考
| 文档 | 描述 |
|------|------|
| [API 参考文档](./API_REFERENCE.md) | 完整的 API 文档和示例 |
### 开发文档
| 文档 | 描述 |
|------|------|
| [迁移完成记录](./SWITCH_MIGRATION_COMPLETE.md) | 项目迁移历史记录 |
| [数据存储文档](./DataStore.md) | 数据持久化系统文档 |
---
## 🚀 快速开始
### 1. 选择平台
**Nintendo Switch:**
```bash
# 设置环境变量
$env:DEVKITPRO = "C:/devkitPro"
# 配置并构建
xmake f --plat=switch -a arm64
xmake
```
**Windows PC:**
```bash
# 设置环境变量
$env:VCPKG_ROOT = "C:\vcpkg"
# 配置并构建
xmake f --plat=windows -a x64
xmake
```
### 2. 运行示例
```bash
# Switch生成 .nro 文件)
./build/switch/hello_world.nro
# Windows
./build/windows/hello_world.exe
```
### 3. 开始开发
```cpp
#include <extra2d/extra2d.h>
using namespace extra2d;
int main() {
// 初始化
Logger::init();
AppConfig config;
config.title = "My Game";
config.width = 1280;
config.height = 720;
auto& app = Application::instance();
app.init(config);
// 创建场景
auto scene = makePtr<Scene>();
// 添加精灵
auto sprite = Sprite::create("player.png");
scene->addChild(sprite);
// 运行动画
sprite->runAction(makePtr<MoveTo>(1.0f, Vec2(300, 200)));
// 运行
app.enterScene(scene);
app.run();
return 0;
}
```
---
## 📖 核心概念
### 应用生命周期
```
main()
└── Application::init()
└── Scene::onEnter()
└── Node::onUpdate() [每帧]
└── Node::onRender() [每帧]
└── Scene::onExit()
```
### 场景图结构
```
Scene (场景)
├── Node (节点)
│ ├── Sprite (精灵)
│ ├── Text (文本)
│ └── CustomNode (自定义节点)
└── Node
└── ...
```
### 坐标系统
- **原点**: 左上角 (0, 0)
- **X轴**: 向右为正
- **Y轴**: 向下为正
- **单位**: 像素
---
## 🛠️ 平台差异
| 功能 | Switch | PC |
|------|--------|-----|
| 窗口 | 固定全屏 | 可调整大小 |
| 输入 | 手柄/触摸 | 键盘/鼠标/手柄 |
| 资源路径 | `romfs:/` | `./assets/` |
| 渲染 | OpenGL ES | OpenGL ES (Angle) |
---
## 💡 示例代码
### 基础示例
- [Hello World](../Extra2D/examples/hello_world/main.cpp) - 基础窗口和文本
- [Collision Demo](../Extra2D/examples/collision_demo/main.cpp) - 碰撞检测
- [Spatial Index Demo](../Extra2D/examples/spatial_index_demo/main.cpp) - 空间索引
### 常用模式
**场景切换:**
```cpp
auto newScene = makePtr<GameScene>();
auto transition = makePtr<FadeTransition>(0.5f);
app.enterScene(newScene, transition);
```
**输入处理:**
```cpp
void onUpdate(float dt) {
auto& input = app.input();
if (input.isKeyPressed(Key::Space)) {
jump();
}
if (input.isButtonPressed(GamepadButton::A)) {
jump();
}
}
```
**资源加载:**
```cpp
auto& resources = app.resources();
auto texture = resources.loadTexture("player.png");
auto font = resources.loadFont("font.ttf", 24);
auto sound = resources.loadSound("jump.wav");
```
---
## 📦 项目结构
```
Extra2D/
├── docs/ # 文档
├── Extra2D/
│ ├── include/extra2d/ # 头文件
│ ├── src/ # 源文件
│ └── examples/ # 示例程序
├── squirrel/ # Squirrel 脚本引擎
├── xmake/ # xmake 配置
│ ├── toolchains/ # 工具链定义
│ └── targets/ # 构建目标
├── xmake.lua # 主构建配置
└── README.md # 项目说明
```
---
## 🔗 相关链接
- **GitHub**: https://github.com/ChestnutYueyue/extra2d
- **Issues**: https://github.com/ChestnutYueyue/extra2d/issues
- **devkitPro**: https://devkitpro.org/
- **Switch 开发**: https://switchbrew.org/
---
## 📝 许可证
Extra2D 使用 [MIT 许可证](../LICENSE)。
---
**最后更新**: 2026年2月10日
**版本**: 3.1.0

View File

@ -1,262 +0,0 @@
# Easy2D PC 端编译指南
本文档说明如何在 PC 端Windows/Linux/macOS编译和运行 Easy2D 引擎。
## 概述
Easy2D 现在支持多平台:
- **Nintendo Switch** (ARM64) - 原始平台
- **Windows** (x64) - 新增
- **Linux** (x64) - 新增
- **macOS** (x64/ARM64) - 新增
PC 端使用 OpenGL ES 3.2(通过 Angle 或 Mesa保持与 Switch 的代码兼容性。
## 前置条件
### Windows
1. **Visual Studio 2019/2022** - 安装 C++ 桌面开发工作负载
2. **xmake** - 构建系统 (https://xmake.io)
3. **SDL2** - 可以通过以下方式安装:
- vcpkg: `vcpkg install sdl2 sdl2-mixer`
- 手动下载https://www.libsdl.org/download-2.0.php
4. **Angle** (可选) - Google 的 GLES 实现
- 下载 Angle 二进制文件或从 Chromium 构建
- 放置到 `third_party/angle/` 目录
### Linux (Ubuntu/Debian)
```bash
# 安装依赖
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y libsdl2-dev libsdl2-mixer-dev
sudo apt-get install -y libgles2-mesa-dev libegl1-mesa-dev
# 安装 xmake
sudo add-apt-repository ppa:xmake-io/xmake
sudo apt-get update
sudo apt-get install xmake
```
### macOS
```bash
# 安装 Homebrew (如果还没有)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 安装依赖
brew install sdl2 sdl2_mixer
brew install angle # 或 mesa
# 安装 xmake
brew install xmake
```
## 编译步骤
### 1. 配置项目
```bash
# 进入项目目录
cd c:\Users\soulcoco\Desktop\Easy2D\Extra2D
# 配置 PC 平台构建
xmake config -p pc -m release
# 或使用环境变量
set E2D_PLATFORM=pc # Windows
export E2D_PLATFORM=pc # Linux/macOS
xmake config
```
### 2. 编译引擎库
```bash
# 编译 Easy2D 静态库
xmake build extra2d
```
### 3. 编译示例程序
```bash
# 编译 Hello World 示例
xmake build hello_world
# 编译所有示例
xmake build -a
```
### 4. 运行示例
```bash
# 运行 Hello World
xmake run hello_world
# 或手动运行
./build/windows/hello_world.exe # Windows
./build/linux/hello_world # Linux
./build/macos/hello_world # macOS
```
## 项目结构
```
Extra2D/
├── xmake.lua # 主构建配置
├── xmake/
│ ├── toolchains/
│ │ ├── switch.lua # Switch 工具链
│ │ └── pc.lua # PC 工具链 (新增)
│ └── targets/
│ ├── extra2d.lua # 引擎库配置
│ └── examples.lua # 示例程序配置
├── Extra2D/
│ ├── include/
│ │ └── extra2d/
│ │ └── platform/
│ │ ├── platform_compat.h # 跨平台兼容性 (新增)
│ │ ├── file_system.h # 文件系统工具 (新增)
│ │ ├── window.h # 窗口系统 (修改)
│ │ └── input.h # 输入系统 (修改)
│ └── src/
│ └── platform/
│ ├── window.cpp # (修改)
│ ├── input.cpp # (修改)
│ └── file_system.cpp # (新增)
└── examples/
└── hello_world/
└── main.cpp # (修改,支持多平台)
```
## 平台差异
### 窗口系统
| 功能 | Switch | PC |
|------|--------|-----|
| 窗口模式 | 始终全屏 (1280x720) | 可配置全屏/窗口 |
| 窗口大小 | 固定 | 可调整 |
| DPI 缩放 | 1.0 | 自动检测 |
| 鼠标光标 | 无 | 支持多种形状 |
### 输入系统
| 功能 | Switch | PC |
|------|--------|-----|
| 键盘 | 映射到手柄 | 原生支持 |
| 鼠标 | 映射到触摸 | 原生支持 |
| 手柄 | SDL GameController | SDL GameController |
| 触摸屏 | 原生 | 可选支持 |
### 文件系统
| 功能 | Switch | PC |
|------|--------|-----|
| 资源路径 | `romfs:/assets/` | `./assets/` 或 exe 目录 |
| 文件访问 | RomFS | 标准文件系统 |
## 开发指南
### 编写跨平台代码
```cpp
#include <extra2d/platform/platform_compat.h>
#include <extra2d/platform/file_system.h>
// 检查平台
if (platform::isSwitch()) {
// Switch 特定代码
} else if (platform::isPC()) {
// PC 特定代码
}
// 解析资源路径(自动处理平台差异)
std::string fontPath = FileSystem::resolvePath("assets/font.ttf");
// Switch: "romfs:/assets/font.ttf"
// PC: "./assets/font.ttf"
// 条件编译
#ifdef PLATFORM_SWITCH
// Switch 代码
#elif defined(PLATFORM_PC)
// PC 代码
#endif
```
### 输入处理
```cpp
// 跨平台输入(推荐)
auto& input = Application::instance().input();
// 键盘PC 原生Switch 映射到手柄)
if (input.isKeyPressed(Key::Space)) {
// 处理空格键
}
// 手柄(所有平台)
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_A)) {
// 处理 A 按钮
}
// 鼠标PC 原生Switch 映射到触摸)
if (input.isMouseDown(MouseButton::Left)) {
// 处理鼠标左键
}
```
## 故障排除
### 找不到 SDL2
**Windows:**
```bash
# 设置 SDL2 路径
set SDL2_DIR=C:\SDL2-2.28.0
xmake config
```
**Linux:**
```bash
# 安装 SDL2 开发库
sudo apt-get install libsdl2-dev libsdl2-mixer-dev
```
### 找不到 GLES
**Windows (Angle):**
```bash
# 下载 Angle 并设置路径
set ANGLE_DIR=C:\angle
xmake config
```
**Linux (Mesa):**
```bash
# 安装 Mesa GLES
sudo apt-get install libgles2-mesa-dev libegl1-mesa-dev
```
### 编译错误
1. 确保使用 C++17 或更高版本
2. 检查 xmake 版本是否最新:`xmake --version`
3. 清理构建缓存:`xmake clean -a`
## 下一步
1. 尝试编译和运行 `hello_world` 示例
2. 阅读 `platform_compat.h` 了解平台 API
3. 使用 `FileSystem` 类处理跨平台文件路径
4. 参考示例代码编写跨平台应用
## 许可证
Easy2D 采用 MIT 许可证。详见 LICENSE 文件。
---
**最后更新**: 2026年2月10日
**Easy2D 版本**: 3.1.0

View File

@ -1,310 +0,0 @@
# Easy2D Nintendo Switch 编译指南
## 概述
本文档说明如何使用 xmake 为 Nintendo Switch 编译 Easy2D 引擎及其示例程序。
## 前置条件
### 1. 必需工具
- **devkitPro** - Nintendo Switch 开发工具包
- **xmake** - 跨平台构建系统v3.0.6+
- **devkitA64** - ARM64编译器工具链devkitPro 的一部分)
### 2. 安装 devkitPro
#### Windows
1. 从 https://devkitpro.org/wiki/Getting_Started/devkitPro_installer 下载 devkitPro 安装程序
2. 运行安装程序,选择以下组件:
- devkitA64 (ARM64)
- libnx (Nintendo Switch库)
- mesa (OpenGL ES)
- tools (nacptool, elf2nro 等)
3. 默认安装路径:`C:\devkitPro`
#### Linux/macOS
请参考官方文档https://devkitpro.org/wiki/Getting_Started
### 3. 验证安装
```bash
# 检查devkitPro是否正确安装
$env:DEVKITPRO = "C:\devkitPro" # Windows PowerShell
export DEVKITPRO=/opt/devkitpro # Linux/macOS
# 检查工具链
aarch64-none-elf-gcc --version # 应该显示 GCC 版本
# 检查xmake
xmake --version # 应该显示 v3.0.6 或更高
```
## 编译步骤
### 1. 配置项目
```bash
cd C:\Users\soulcoco\Desktop\Easy2D\Easy2D-dev
# 配置编译使用Switch工具链
xmake config -p switch -a arm64
# 或者使用默认配置
xmake config
```
### 2. 编译核心库
编译 Easy2D 静态库:
```bash
xmake build easy2d
```
**输出:**
- Release: `build/switch/libeasy2d.a`
- Debug: `build/switch/libeasy2dd.a`
### 3. 编译示例程序
#### 编译音频演示
```bash
xmake build switch_audio_demo
```
**输出:**
- ELF: `build/switch/switch_audio_demo`
- NACP: `build/switch/switch_audio_demo.nacp`
- NRO: `build/switch/switch_audio_demo.nro` (Switch可执行文件)
#### 编译动画演示
```bash
xmake build switch_animation_demo
```
**输出:**
- NRO: `build/switch/switch_animation_demo.nro`
### 4. 一次编译所有目标
```bash
xmake build -a
```
## 项目结构
```
Easy2D-dev/
├── xmake.lua # 构建配置
├── Easy2D/
│ ├── include/ # 头文件
│ │ ├── easy2d/ # 引擎头文件
│ │ │ ├── app/ # 应用系统
│ │ │ ├── platform/ # 平台层
│ │ │ ├── graphics/ # 图形系统
│ │ │ ├── audio/ # 音频系统
│ │ │ ├── scene/ # 场景管理
│ │ │ ├── resource/ # 资源管理
│ │ │ └── utils/ # 工具类
│ │ ├── glm/ # GLM数学库
│ │ ├── stb/ # STB图像库
│ │ └── pfd/ # 文件对话框库
│ ├── src/ # 实现文件
│ │ ├── app/ # 应用实现
│ │ ├── platform/ # 平台实现Switch优化
│ │ │ └── switch/ # Switch特定代码
│ │ ├── graphics/ # 图形实现
│ │ ├── audio/ # 音频实现
│ │ └── ...
│ └── examples/ # 示例程序
│ ├── switch_audio_demo/ # Switch音频演示
│ ├── switch_animation_demo/ # Switch动画演示
│ └── ...
└── squirrel/ # Squirrel脚本引擎
```
## 编译配置详解
### xmake.lua 中的关键配置
#### 1. Switch 工具链定义 (行 15-51)
```lua
toolchain("switch")
set_kind("standalone")
set_description("Nintendo Switch devkitA64 toolchain")
local devkitPro = "C:/devkitPro"
local devkitA64 = path.join(devkitPro, "devkitA64")
-- 编译器
set_toolset("cc", path.join(devkitA64, "bin/aarch64-none-elf-gcc.exe"))
set_toolset("cxx", path.join(devkitA64, "bin/aarch64-none-elf-g++.exe"))
-- 架构标志
local arch_flags = "-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE"
-- 链接 EGL 和 OpenGL ES (mesa)
add_syslinks("EGL", "GLESv2", "glapi", "drm_nouveau")
```
#### 2. Easy2D 静态库配置 (行 60-134)
```lua
target("easy2d")
set_kind("static")
set_plat("switch") -- 平台
set_arch("arm64") -- 架构
set_toolchains("switch") -- 工具链
add_files(SRC_DIR .. "/**.cpp") -- 源文件
add_files("squirrel/squirrel/*.cpp") -- 脚本引擎
add_includedirs(INC_DIR) -- 头文件目录
```
#### 3. Switch 示例程序配置 (行 139-257)
xmake 自动处理:
- **编译** ELF 文件
- **生成 NACP** 应用元数据
- **转换为 NRO** (Switch 可执行格式)
- **打包 RomFS** (资源文件系统)
## 故障排除
### 编译错误
#### 错误:找不到 Switch 工具链
```
Error: toolchain 'switch' not found
```
**解决方案:**
1. 确认 `DEVKITPRO` 环境变量已设置
2. 检查 devkitPro 安装路径:`C:\devkitPro` (Windows) 或 `/opt/devkitpro` (Linux)
3. 验证 `devkitA64/bin` 下的编译器存在
#### 错误:找不到 libnx 库
```
Error: cannot find -lnx
```
**解决方案:**
1. 验证 devkitPro 安装了 libnx 包
2. 检查 `DEVKITPRO/libnx` 目录是否存在
#### 错误OpenGL ES 头文件缺失
```
Error: GL/gl.h: No such file or directory
```
**解决方案:**
1. 验证 mesa 已安装:`DEVKITPRO/portlibs/switch/include`
2. 检查 xmake.lua 中的包含目录配置
### 链接错误
#### 未定义的引用到 EGL 函数
```
undefined reference to 'eglInitialize'
```
**解决方案:**
- 确保 EGL 库链接顺序正确xmake.lua 第 93 行)
### 运行时错误
#### NRO 文件无法在 Switch 上运行
**检查清单:**
1. 确认 `DEVKITPRO` 环境变量设置正确
2. 验证 RomFS 资源已正确打包(如果需要)
3. 检查应用元数据NACP 文件)是否正确生成
## Switch 开发资源
- **官方文档**: https://switchbrew.org/wiki/Main_Page
- **libnx 文档**: https://libnx.readthedocs.io/
- **devkitPro 论坛**: https://devkitpro.org/
- **Easy2D 文档**: https://github.com/easy2d/Easy2D
## 编译选项
### 编译模式
```bash
# Debug 模式(包含调试符号)
xmake config -m debug
xmake build easy2d
# Release 模式(优化编译)
xmake config -m release
xmake build easy2d
```
### 并行编译
```bash
# 使用 8 个线程编译
xmake build -j 8
```
### 清理构建
```bash
# 清理所有构建文件
xmake clean
# 仅清理目标
xmake clean easy2d
```
## 下一步
1. **修改示例程序** - 编辑 `Easy2D/examples/switch_*_demo/main.cpp`
2. **添加资源** - 将资源放在 `assets/` 目录
3. **优化性能** - 使用 Release 模式编译
4. **部署到 Switch** - 将 NRO 文件复制到 Switch SD 卡
## 常见问题
### Q: 如何调试 Switch 应用?
A: 可以使用以下方法:
- 使用 `nxlink` 输出日志到 PC
- 在应用中使用 `E2D_LOG_INFO()` 宏输出调试信息
- 使用支持 Switch 的调试器(如 GDB with nxlink
### Q: 如何部署资源到 Switch
A:
1. 将资源放在 `examples/switch_*_demo/assets/` 目录
2. xmake 会自动将资源打包到 RomFS
3. 在代码中使用 `romfs:/` 前缀访问资源
### Q: 支持哪些音频格式?
A: miniaudio 支持:
- WAV
- FLAC
- MP3
- VORBIS
## 许可证
Easy2D 采用 MIT 许可证。详见 LICENSE 文件。
---
**最后更新**: 2026年2月9日
**Easy2D 版本**: 3.1.0
**Switch 工具链**: devkitA64 (devkitPro)

View File

@ -1,357 +0,0 @@
# Easy2D Nintendo Switch 移植项目完成总结
## 项目概述
完成了 Easy2D v3.1.0 游戏引擎到 Nintendo Switch 平台的完整移植,包括所有核心系统、示例程序和编译配置。
## 完成的工作
### Phase 1: 核心平台系统重构
| 步骤 | 组件 | 状态 | 说明 |
|------|------|------|------|
| 1 | **Window/EGL系统** | ✅ 完成 | 从GLFW→EGL+libnx支持Switch固定分辨率1280×720 |
| 2 | **输入系统** | ✅ 完成 | 从GLFW键鼠→libnx HID支持手柄+触摸屏 |
| 3 | **图形后端** | ✅ 完成 | 从GLEW→mesa OpenGL ES链接EGL/GLESv2 |
| 4 | **渲染初始化** | ✅ 完成 | 适配Switch OpenGL ES限制帧缓冲配置 |
| 5 | **音频系统** | ✅ 完成 | 使用miniaudio替代SDL2_mixerSwitch优化 |
| 6 | **日志系统** | ✅ 完成 | 从spdlog→printf输出支持nxlink调试 |
### Phase 2: 应用生命周期与示例
| 步骤 | 组件 | 状态 | 说明 |
|------|------|------|------|
| 7 | **应用生命周期** | ✅ 完成 | 完整的Switch主循环、初始化、清理、RomFS支持 |
| 8.1 | **Switch音频演示** | ✅ 完成 | 创建switch_audio_demo示例程序 |
| 8.2 | **Switch动画演示** | ✅ 完成 | 创建switch_animation_demo示例程序 |
| 8.3 | **编译配置与文档** | ✅ 完成 | xmake配置、编译脚本、build guide文档 |
## 关键文件变更
### 新创建的文件
```
Easy2D-dev/
├── SWITCH_BUILD_GUIDE.md # Switch编译指南620行
├── Easy2D/
│ ├── include/easy2d/platform/
│ │ └── switch_compat.h # Switch兼容性头文件70行
│ └── examples/
│ ├── switch_audio_demo/
│ │ ├── main.cpp # 音频演示程序106行
│ │ └── assets/ # 音频资源目录
│ └── switch_animation_demo/
│ ├── main.cpp # 动画演示程序120行
│ └── assets/ # 动画资源目录
```
### 修改的文件第1-6步
```
Easy2D/src/
├── app/application.cpp # Switch主循环、初始化、关闭
├── platform/
│ ├── window.cpp/window.h # EGL窗口管理
│ ├── input.cpp/input.h # libnx HID输入
│ └── switch/ # Switch特定实现
├── graphics/opengl/gl_renderer.cpp # OpenGL ES渲染
├── audio/audio_engine.cpp/sound.cpp # miniaudio系统
├── resource/resource_manager.cpp # RomFS资源加载
└── utils/logger.cpp # printf日志系统
```
### xmake.lua 更新
**新增配置:**
1. **Switch工具链定义** (行15-51)
- devkitA64编译器配置
- ARM64架构标志
- libnx/EGL/OpenGL ES库链接
2. **Easy2D静态库** (行60-134)
- Platform选择`set_plat("switch")`
- 编译标志优化
- Squirrel脚本引擎集成
3. **Switch演示程序** (行139-257)
- switch_audio_demo目标
- switch_animation_demo目标
- 自动NACP生成
- NRO格式转换
- RomFS资源打包
## 技术亮点
### 1. 完整的平台抽象
```cpp
// 平台检测宏switch_compat.h
#ifdef __SWITCH__
#include <switch.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#endif
```
### 2. Switch初始化流程
```cpp
// application.cpp 中的完整Switch初始化
socketInitializeDefault(); // nxlink调试输出
romfsInit(); // RomFS文件系统
// ... 图形/音频初始化 ...
romfsExit(); // 清理
socketExit();
```
### 3. EGL上下文管理
```cpp
// window.cpp - Switch固定分辨率1280×720
eglInitialize(display_, nullptr, nullptr);
eglBindAPI(EGL_OPENGL_ES_BIT);
eglCreateWindowSurface(display_, config_, window, nullptr);
```
### 4. libnx HID输入处理
```cpp
// input.cpp - Switch手柄+触摸屏
hidScanInput();
u32 kdown = hidKeyboardDown(0);
HidTouchScreenState touchState = {0};
hidGetTouchScreenStates(&touchState, 1);
```
### 5. RomFS资源加载
```cpp
// 资源搜索路径配置
resourceManager_->addSearchPath("romfs:/");
auto tex = resourceManager_->loadTexture("romfs:/textures/sprite.png");
```
## 编译状态
### 配置验证 ✅
- xmake配置识别三个目标
- `easy2d` (静态库)
- `switch_audio_demo` (音频演示)
- `switch_animation_demo` (动画演示)
### 构建准备就绪 ✅
编译命令已测试:
```bash
xmake config -p switch -a arm64
xmake build -a # 编译所有目标
```
## 性能优化考虑
1. **编译优化**
- Release模式`-O2` 优化
- Debug模式保留符号用于调试
2. **内存优化**
- 预分配纹理缓存
- 精灵批处理优化
- 场景对象池管理
3. **渲染优化**
- OpenGL ES 2.0兼容性
- VAO/VBO使用
- 后处理管道支持
4. **音频优化**
- miniaudio支持硬件加速
- 立体声输出支持
- 低延迟播放
## Switch特定限制与处理
| 功能 | 限制 | 处理方案 |
|------|------|---------|
| 分辨率 | 固定1280×720 | 硬编码分辨率 |
| 输入 | 无鼠标 | 仅支持手柄+触摸 |
| 窗口 | 无标题栏、全屏 | WindowConfig强制全屏 |
| 光标 | 不可见 | 应用层隐藏光标 |
| 文件I/O | 仅RomFS | 使用"romfs:/"前缀 |
| 调试 | nxlink输出 | 集成nxlink支持 |
## 测试清单
- [x] xmake配置正确识别Switch工具链
- [x] 头文件包含路径正确配置
- [x] 静态库编译配置完整
- [x] 示例程序编译配置完整
- [x] NRO后处理脚本配置完整
- [x] 日志系统输出配置完整
- [x] 音频系统配置完整
- [x] 平台抽象层完整
- [x] 编译文档完整
## 使用方法
### 快速开始
1. **设置环境**
```bash
$env:DEVKITPRO = "C:\devkitPro" # Windows
cd C:\Users\soulcoco\Desktop\Easy2D\Easy2D-dev
```
2. **配置项目**
```bash
xmake config -p switch -a arm64
```
3. **编译核心库**
```bash
xmake build easy2d
```
4. **编译示例程序**
```bash
xmake build switch_audio_demo
xmake build switch_animation_demo
```
5. **生成NRO文件**
- 自动输出到 `build/switch/switch_*_demo.nro`
### 部署到Switch
1. 将 NRO 文件复制到 Switch SD 卡
2. 在Switch主菜单中运行应用
### 开发工作流
1. 编辑源代码
2. 运行 `xmake build -a`
3. 测试输出的NRO文件
4. 迭代改进
## 后续改进建议
### 短期1-2周
1. **添加更多示例**
- 物理系统演示
- UI系统演示
- 脚本系统演示
2. **性能优化**
- FPS显示优化
- 内存使用分析
- 渲染性能测试
3. **错误处理**
- Switch特定的异常处理
- 内存不足处理
- 文件I/O错误处理
### 中期1个月
1. **功能扩展**
- 网络支持Switch WiFi
- 多人游戏支持
- 存档系统
2. **工具链改进**
- CMake支持可选
- CI/CD集成
- 自动化测试
3. **文档完善**
- API文档生成Doxygen
- 教程编写
- 示例代码注释
### 长期3个月+
1. **商业化支持**
- Nintendo Developer Program集成
- 官方分发支持
- 许可证管理
2. **社区建设**
- 示例库扩展
- 插件系统
- 社区论坛
## 项目统计
| 指标 | 数值 |
|------|------|
| 新增文件 | 5个 |
| 修改文件 | 8个+ |
| 代码行数(新增) | ~900行 |
| 文档行数 | ~620行 |
| 编译目标数 | 3个 |
| 示例程序数 | 2个 |
| Switch适配覆盖率 | ~95% |
## 已知问题与解决方案
### 问题1: pfd库禁用
**原因**portable-file-dialogs库与Switch不兼容
**解决方案**使用Switch原生文件选择器future
**状态**xmake.lua中已注释禁用
### 问题2: 网络功能
**原因**Switch网络需要特殊初始化
**解决方案**:待实现
**建议**使用libnx网络API
### 问题3: 光标支持
**原因**Switch屏幕无光标
**解决方案**:应用层自行绘制光标图形
**建议**:使用精灵系统实现光标
## 许可证
- **Easy2D**: MIT License
- **devkitPro工具链**: GPL v2+
- **libnx**: Zlib License
- **miniaudio**: 无许可(公开领域)
## 致谢
- Easy2D 原作者与维护者
- Nintendo 开发者社区
- devkitPro 项目贡献者
---
## 总结
✨ **Easy2D Nintendo Switch 移植项目已成功完成!**
这是一个完整、专业的游戏引擎移植项目,包括:
- 核心系统的完全适配
- 两个功能完整的演示程序
- 详细的编译指南和文档
- 生产级别的构建配置
项目已准备好用于Nintendo Switch游戏开发
**项目版本**: v1.0
**完成日期**: 2026年2月9日
**状态**: ✅ 生产就绪
---
### 快速链接
- 📖 [Switch编译指南](./SWITCH_BUILD_GUIDE.md)
- 🎮 [音频演示源码](./Easy2D/examples/switch_audio_demo/main.cpp)
- 🎬 [动画演示源码](./Easy2D/examples/switch_animation_demo/main.cpp)
- ⚙️ [xmake配置](./xmake.lua)
- 🛠️ [平台兼容性头文件](./Easy2D/include/easy2d/platform/switch_compat.h)
**问题与反馈**: 请提交至项目Issue追踪器

View File

@ -1,88 +0,0 @@
-- ==============================================
-- Extra2D - Xmake Build Script
-- Purpose: Build Extra2D static library and demo programs
-- Platform: Nintendo Switch (ARM64) / PC (Windows/Linux/macOS)
-- Graphics: OpenGL ES 3.2 via Mesa/Angle
-- Audio: SDL2_mixer
-- ==============================================
set_project("Extra2D")
set_version("3.1.0")
set_languages("c++17")
set_encodings("utf-8")
add_rules("mode.debug", "mode.release")
-- ==============================================
-- 平台检测与配置 (必须在 includes 之前)
-- ==============================================
-- 检测目标平台 - 优先级:命令行 > 环境变量 > 默认值
local target_platform = "switch" -- 默认 Switch 平台
-- 方式1: 检查命令行传入的平台配置 (最高优先级)
local plat_config = get_config("plat")
if plat_config and plat_config ~= "" then
if plat_config == "windows" or plat_config == "linux" or plat_config == "macosx" then
target_platform = "pc"
elseif plat_config == "switch" then
target_platform = "switch"
end
-- 方式2: 通过环境变量检测 PC 平台
elseif os.getenv("E2D_PLATFORM") == "pc" then
target_platform = "pc"
-- 方式3: 检查 platform 配置
elseif has_config("platform") then
local platform_val = get_config("platform")
if platform_val == "pc" then
target_platform = "pc"
end
end
-- 设置默认平台和架构 (必须在 includes 之前调用)
if target_platform == "switch" then
set_config("plat", "switch")
set_config("arch", "arm64")
else
-- PC 平台:根据主机自动选择
if is_host("windows") then
set_config("plat", "windows")
set_config("arch", "x64")
elseif is_host("linux") then
set_config("plat", "linux")
elseif is_host("macosx") then
set_config("plat", "macosx")
end
end
print("Extra2D Build Configuration:")
print(" Platform: " .. target_platform)
print(" Mode: " .. (is_mode("debug") and "debug" or "release"))
-- ==============================================
-- 包含子模块配置
-- ==============================================
-- 包含工具链定义
includes("xmake/toolchains/switch.lua")
includes("xmake/toolchains/pc.lua")
-- 根据平台定义工具链
if target_platform == "switch" then
define_switch_toolchain()
else
define_pc_toolchain()
end
-- 包含目标定义
includes("xmake/targets/extra2d.lua")
includes("xmake/targets/examples.lua")
-- ==============================================
-- 定义构建目标
-- ==============================================
-- Extra2D 引擎库
define_extra2d_target()
-- 示例程序
define_example_targets()

View File

@ -1,211 +0,0 @@
-- ==============================================
-- Extra2D 示例程序构建目标
-- 支持平台: Nintendo Switch, Windows, Linux, macOS
-- ==============================================
-- 获取 devkitPro 路径
local function get_devkitpro_path()
return "C:/devkitPro"
end
-- 生成 Switch NRO 文件的通用后构建函数
-- @param target_name 目标名称
-- @param app_title 应用标题
-- @param app_author 应用作者
-- @param app_version 应用版本
-- @param romfs_dir RomFS 目录路径(相对于项目根目录)
local function generate_nro_after_build(target_name, app_title, app_author, app_version, romfs_dir)
after_build(function (target)
local devkitPro = get_devkitpro_path()
local elf_file = target:targetfile()
local output_dir = path.directory(elf_file)
local nacp_file = path.join(output_dir, target_name .. ".nacp")
local nro_file = path.join(output_dir, target_name .. ".nro")
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe")
if not os.isfile(nacptool) then
print("Warning: nacptool not found at " .. nacptool)
return
end
if not os.isfile(elf2nro) then
print("Warning: elf2nro not found at " .. elf2nro)
return
end
-- 生成 .nacp 文件
os.vrunv(nacptool, {"--create", app_title, app_author, app_version, nacp_file})
print("Built " .. path.filename(nacp_file))
-- 生成 .nro 文件(包含 RomFS
local romfs_absolute = path.absolute(romfs_dir)
if os.isdir(romfs_absolute) then
print("Packing RomFS from: " .. romfs_absolute)
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs_absolute})
print("Built " .. path.filename(nro_file) .. " (with RomFS)")
else
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file})
print("Built " .. path.filename(nro_file))
end
end)
end
-- 定义 Switch 示例程序的通用配置
-- @param name 目标名称
-- @param options 配置选项表
local function define_switch_example_target(name, options)
target(name)
set_kind("binary")
set_plat("switch")
set_arch("arm64")
set_toolchains("switch")
set_targetdir("build/switch")
-- 添加源文件
add_files(options.source_file or ("Extra2D/examples/" .. name .. "/main.cpp"))
-- 添加头文件路径
add_includedirs("Extra2D/include")
-- 链接 extra2d 库
add_deps("extra2d")
-- Windows 控制台应用程序(仅 PC 平台)
if is_plat("windows") then
add_ldflags("-mconsole", {force = true})
end
-- 可选:添加链接器标志
if options.ldflags then
add_ldflags(options.ldflags, {force = true})
end
-- 构建后生成 .nro 文件
generate_nro_after_build(
name,
options.app_title or ("Extra2D " .. name),
options.app_author or "Extra2D Team",
options.app_version or "1.0.0",
options.romfs_dir or ("Extra2D/examples/" .. name .. "/romfs")
)
target_end()
end
-- 定义 PC 示例程序的通用配置
-- @param name 目标名称
-- @param options 配置选项表
local function define_pc_example_target(name, options)
target(name)
set_kind("binary")
set_toolchains("pc")
-- 设置输出目录
if is_host("windows") then
set_targetdir("build/windows")
elseif is_host("linux") then
set_targetdir("build/linux")
elseif is_host("macosx") then
set_targetdir("build/macos")
else
set_targetdir("build/pc")
end
-- 添加源文件
add_files(options.source_file or ("Extra2D/examples/" .. name .. "/main.cpp"))
-- 添加头文件路径
add_includedirs("Extra2D/include")
-- 链接 extra2d 库
add_deps("extra2d")
-- 可选:添加链接器标志
if options.ldflags then
add_ldflags(options.ldflags, {force = true})
end
-- PC 端构建后复制资源文件和 DLL
after_build(function (target)
local target_file = target:targetfile()
local output_dir = path.directory(target_file)
local romfs_dir = options.romfs_dir or ("Extra2D/examples/" .. name .. "/romfs")
local romfs_absolute = path.absolute(romfs_dir)
-- 复制 vcpkg DLL 到输出目录
local vcpkg_root = os.getenv("VCPKG_ROOT")
if vcpkg_root then
local triplet = is_arch("x64") and "x64-windows" or "x86-windows"
local vcpkg_bin = path.join(vcpkg_root, "installed", triplet, "bin")
if os.isdir(vcpkg_bin) then
-- 复制 SDL2 相关 DLL
local dlls = {"SDL2.dll", "SDL2_mixer.dll", "ogg.dll", "vorbis.dll", "vorbisfile.dll", "wavpackdll.dll"}
for _, dll in ipairs(dlls) do
local dll_path = path.join(vcpkg_bin, dll)
if os.isfile(dll_path) then
os.cp(dll_path, output_dir)
end
end
print("Copied DLLs from: " .. vcpkg_bin)
end
end
-- 复制资源文件到输出目录
if os.isdir(romfs_absolute) then
local assets_dir = path.join(output_dir, "assets")
os.mkdir(assets_dir)
-- 复制 romfs 内容到 assets 目录
local romfs_assets = path.join(romfs_absolute, "assets")
if os.isdir(romfs_assets) then
print("Copying assets from: " .. romfs_assets .. " to " .. assets_dir)
os.cp(romfs_assets .. "/*", assets_dir)
end
print("Built " .. path.filename(target_file) .. " (with assets)")
else
print("Built " .. path.filename(target_file))
end
end)
target_end()
end
-- 定义所有示例程序目标
function define_example_targets()
-- 根据平台选择示例程序定义方式
if is_plat("switch") then
-- ============================================
-- Switch 示例程序
-- ============================================
define_switch_example_target("hello_world", {
app_title = "Extra2D hello_world",
app_author = "Extra2D hello_world",
app_version = "1.0.0"
})
define_switch_example_target("spatial_index_demo", {
app_title = "Extra2D Spatial Index Demo",
app_version = "1.0.0",
ldflags = "-Wl,-Map=build/switch/spatial_index_demo.map"
})
define_switch_example_target("collision_demo", {
app_title = "Extra2D Collision Demo",
app_version = "1.0.0"
})
else
-- ============================================
-- PC 示例程序 (Windows/Linux/macOS)
-- ============================================
define_pc_example_target("hello_world", {
romfs_dir = "Extra2D/examples/hello_world/romfs"
})
define_pc_example_target("spatial_index_demo", {
romfs_dir = "Extra2D/examples/spatial_index_demo/romfs"
})
define_pc_example_target("collision_demo", {
romfs_dir = "Extra2D/examples/collision_demo/romfs"
})
end
end

View File

@ -1,109 +0,0 @@
-- ==============================================
-- Extra2D 引擎库构建目标
-- 支持平台: Nintendo Switch, Windows, Linux, macOS
-- ==============================================
-- 核心路径定义
local SRC_DIR = "Extra2D/src"
local INC_DIR = "Extra2D/include"
-- 定义 Extra2D 引擎库目标
function define_extra2d_target()
target("extra2d")
set_kind("static")
set_basename(is_mode("debug") and "libeasy2dd" or "libeasy2d")
-- ==============================================
-- 源文件配置
-- ==============================================
-- 引擎源文件
add_files(path.join(SRC_DIR, "**.cpp"))
add_files(path.join(SRC_DIR, "glad/glad.c"))
-- Squirrel 3.2 源文件
add_files("squirrel/squirrel/*.cpp")
add_files("squirrel/sqstdlib/*.cpp")
-- ==============================================
-- 头文件路径配置
-- ==============================================
-- 公开头文件目录
add_includedirs(INC_DIR, {public = true})
-- 第三方头文件目录
add_includedirs("squirrel/include", {public = true})
-- 平台兼容性头文件路径
add_includedirs(path.join(INC_DIR, "extra2d/platform"), {public = true})
-- ==============================================
-- 平台特定配置
-- ==============================================
if is_plat("switch") then
-- ==============================================
-- Nintendo Switch 平台配置
-- ==============================================
set_plat("switch")
set_arch("arm64")
set_toolchains("switch")
-- devkitPro mesa 路径EGL + OpenGL ES
local devkitPro = "C:/devkitPro"
add_includedirs(path.join(devkitPro, "portlibs/switch/include"), {public = true})
add_linkdirs(path.join(devkitPro, "portlibs/switch/lib"))
-- 链接 EGL、OpenGL ES 3.0mesa和 SDL2 音频
-- 注意:链接顺序很重要!被依赖的库必须放在后面
add_syslinks("SDL2_mixer", "SDL2",
"opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC",
"GLESv2",
"EGL",
"glapi",
"drm_nouveau",
{public = true})
else
-- ==============================================
-- PC 平台配置 (Windows/Linux/macOS)
-- ==============================================
set_toolchains("pc")
-- PC 平台使用标准 OpenGL (通过 MinGW)
-- 依赖库在 pc.lua 工具链中配置 (使用 vcpkg)
add_syslinks("SDL2_mixer", "SDL2", "opengl32", {public = true})
end
-- ==============================================
-- 编译器配置
-- ==============================================
-- ==============================================
-- 编译器标志 (MinGW GCC)
-- ==============================================
add_cxflags("-Wall", "-Wextra", {force = true})
add_cxflags("-Wno-unused-variable", "-Wno-unused-function", {force = true})
add_cxflags("-Wno-unused-parameter", {force = true})
-- Squirrel 第三方库警告抑制
add_cxflags("-Wno-deprecated-copy", "-Wno-strict-aliasing", "-Wno-implicit-fallthrough", "-Wno-class-memaccess", {force = true})
-- 调试/发布模式配置
if is_mode("debug") then
add_defines("E2D_DEBUG", "_DEBUG", {public = true})
add_cxxflags("-O0", "-g", {force = true})
else
add_defines("NDEBUG", {public = true})
add_cxxflags("-O2", {force = true})
end
-- ==============================================
-- 头文件安装配置
-- ==============================================
add_headerfiles(path.join(INC_DIR, "extra2d/**.h"), {prefixdir = "extra2d"})
add_headerfiles(path.join(INC_DIR, "stb/**.h"), {prefixdir = "stb"})
add_headerfiles(path.join(INC_DIR, "simpleini/**.h"), {prefixdir = "simpleini"})
target_end()
end

View File

@ -1,150 +0,0 @@
-- ==============================================
-- PC 平台工具链定义 (Windows/Linux/macOS)
-- ==============================================
function define_pc_toolchain()
toolchain("pc")
set_kind("standalone")
set_description("PC Platform Toolchain (Windows/Linux/macOS)")
on_load(function (toolchain)
-- ==============================================
-- 平台检测与配置
-- ==============================================
if is_host("windows") then
-- Windows: 使用 MinGW
toolchain:set("toolset", "cc", "gcc")
toolchain:set("toolset", "cxx", "g++")
toolchain:set("toolset", "ld", "g++")
toolchain:set("toolset", "ar", "ar")
else
-- Linux/macOS: 使用 GCC/Clang
toolchain:set("toolset", "cc", "gcc")
toolchain:set("toolset", "cxx", "g++")
toolchain:set("toolset", "ld", "g++")
toolchain:set("toolset", "ar", "ar")
end
-- ==============================================
-- PC 平台宏定义
-- ==============================================
toolchain:add("defines", "__PC__")
if is_host("windows") then
toolchain:add("defines", "_WIN32", "NOMINMAX", "WIN32_LEAN_AND_MEAN")
elseif is_host("linux") then
toolchain:add("defines", "__linux__")
elseif is_host("macosx") then
toolchain:add("defines", "__APPLE__", "__MACH__")
end
-- SimpleIni 配置
toolchain:add("defines", "SI_NO_CONVERSION")
-- ==============================================
-- OpenGL 配置
-- ==============================================
if is_host("windows") then
-- Windows: 使用标准 OpenGL
toolchain:add("links", "opengl32")
else
-- Linux/macOS: 使用 Mesa OpenGL
toolchain:add("links", "GL")
end
-- ==============================================
-- vcpkg 依赖配置
-- ==============================================
-- 获取 vcpkg 路径
local vcpkg_root = os.getenv("VCPKG_ROOT")
if vcpkg_root then
local triplet = is_arch("x64") and "x64-windows" or "x86-windows"
local vcpkg_installed = path.join(vcpkg_root, "installed", triplet)
if os.isdir(vcpkg_installed) then
-- 添加头文件路径
toolchain:add("includedirs", path.join(vcpkg_installed, "include"))
toolchain:add("includedirs", path.join(vcpkg_installed, "include", "SDL2"))
-- 添加库路径
toolchain:add("linkdirs", path.join(vcpkg_installed, "lib"))
print("vcpkg packages: " .. vcpkg_installed)
end
end
-- ==============================================
-- 链接库
-- ==============================================
-- SDL2 及其扩展
toolchain:add("links", "SDL2_mixer", "SDL2")
-- 音频编解码库 (vcpkg 中可用的)
toolchain:add("links", "ogg")
-- OpenGL (Windows 使用标准 OpenGL)
toolchain:add("links", "opengl32")
-- 系统库
if is_host("windows") then
toolchain:add("syslinks", "gdi32", "user32", "shell32", "winmm", "imm32", "ole32", "oleaut32", "version", "uuid", "advapi32", "setupapi")
else
toolchain:add("syslinks", "m", "dl", "pthread")
if is_host("linux") then
toolchain:add("syslinks", "X11", "Xext")
end
end
-- ==============================================
-- 编译器标志 (MinGW GCC)
-- ==============================================
toolchain:add("cxflags", "-Wall", "-Wextra", {force = true})
toolchain:add("cxflags", "-Wno-unused-parameter", "-Wno-unused-variable", {force = true})
-- Windows 控制台应用程序
toolchain:add("ldflags", "-mconsole", {force = true})
if is_mode("debug") then
toolchain:add("defines", "E2D_DEBUG", "_DEBUG")
toolchain:add("cxflags", "-O0", "-g", {force = true})
else
toolchain:add("defines", "NDEBUG")
toolchain:add("cxflags", "-O2", {force = true})
end
end)
end
-- 获取 PC 平台包含路径
function get_pc_includedirs()
local dirs = {}
-- Angle
local angle_dir = os.getenv("ANGLE_DIR") or "third_party/angle"
if os.isdir(angle_dir) then
table.insert(dirs, path.join(angle_dir, "include"))
end
return dirs
end
-- 获取 PC 平台库路径
function get_pc_linkdirs()
local dirs = {}
-- Angle
local angle_dir = os.getenv("ANGLE_DIR") or "third_party/angle"
if os.isdir(angle_dir) then
table.insert(dirs, path.join(angle_dir, "lib"))
end
return dirs
end
-- 获取 PC 平台系统链接库
function get_pc_syslinks()
return {
"SDL2_mixer", "SDL2",
"opengl32"
}
end

View File

@ -1,83 +0,0 @@
-- ==============================================
-- Nintendo Switch 工具链定义
-- ==============================================
function define_switch_toolchain()
toolchain("switch")
set_kind("standalone")
set_description("Nintendo Switch devkitA64 toolchain")
-- 检查 DEVKITPRO 环境变量Windows 上使用 C:/devkitPro
local devkitPro = "C:/devkitPro"
local devkitA64 = path.join(devkitPro, "devkitA64")
-- 设置工具链路径
set_toolset("cc", path.join(devkitA64, "bin/aarch64-none-elf-gcc.exe"))
set_toolset("cxx", path.join(devkitA64, "bin/aarch64-none-elf-g++.exe"))
set_toolset("ld", path.join(devkitA64, "bin/aarch64-none-elf-g++.exe"))
set_toolset("ar", path.join(devkitA64, "bin/aarch64-none-elf-gcc-ar.exe"))
set_toolset("strip", path.join(devkitA64, "bin/aarch64-none-elf-strip.exe"))
-- 架构标志
local arch_flags = "-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE"
add_cxflags(arch_flags)
-- 使用 devkitPro 提供的 switch.specs 文件
add_ldflags("-specs=" .. path.join(devkitPro, "libnx/switch.specs"), "-g", arch_flags)
-- 定义 Switch 平台宏
add_defines("__SWITCH__", "__NX__", "MA_SWITCH", "PFD_SWITCH")
-- SimpleIni 配置:不使用 Windows API
add_defines("SI_NO_CONVERSION")
-- libnx 路径 - 必须在工具链级别添加
add_includedirs(path.join(devkitPro, "libnx/include"))
add_linkdirs(path.join(devkitPro, "libnx/lib"))
-- portlibs 路径EGL + 桌面 OpenGL + SDL2
add_includedirs(path.join(devkitPro, "portlibs/switch/include"))
add_includedirs(path.join(devkitPro, "portlibs/switch/include/SDL2"))
add_linkdirs(path.join(devkitPro, "portlibs/switch/lib"))
add_syslinks("nx", "m")
end
-- 获取 devkitPro 路径
function get_devkitpro_path()
return "C:/devkitPro"
end
-- 获取 Switch 平台包含路径
function get_switch_includedirs()
local devkitPro = get_devkitpro_path()
return {
path.join(devkitPro, "libnx/include"),
path.join(devkitPro, "portlibs/switch/include"),
path.join(devkitPro, "portlibs/switch/include/SDL2")
}
end
-- 获取 Switch 平台库路径
function get_switch_linkdirs()
local devkitPro = get_devkitpro_path()
return {
path.join(devkitPro, "libnx/lib"),
path.join(devkitPro, "portlibs/switch/lib")
}
end
-- 获取 Switch 平台系统链接库
function get_switch_syslinks()
-- 注意:链接顺序很重要!被依赖的库必须放在后面
-- 依赖链SDL2 -> EGL -> drm_nouveau
-- GLESv2 -> glapi -> drm_nouveau
return {
"SDL2_mixer", "SDL2",
"opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC",
"GLESv2",
"EGL",
"glapi",
"drm_nouveau"
}
end