chore: 移除项目中的示例代码、文档和构建配置
移除不再需要的示例代码、文档和构建配置文件,包括: - 示例程序代码和资源文件 - 文档文件(构建指南、API参考等) - xmake构建配置和工具链定义 - 测试和演示相关文件 这些文件已不再使用或已迁移到其他仓库,清理以保持项目整洁。
This commit is contained in:
parent
2e08bff567
commit
4066abf40f
|
|
@ -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;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -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;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -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;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -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
|
||||
|
|
@ -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` - 用户ID(Account 类型需要,为空则自动获取当前用户)
|
||||
- `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 §ion,
|
||||
const std::string &key,
|
||||
const std::string &defaultValue = ""
|
||||
);
|
||||
```
|
||||
|
||||
获取字符串值。
|
||||
|
||||
**参数:**
|
||||
- `section` - 节名称
|
||||
- `key` - 键名称
|
||||
- `defaultValue` - 默认值(键不存在时返回)
|
||||
|
||||
**返回值:**
|
||||
- 字符串值
|
||||
|
||||
---
|
||||
|
||||
#### getInt
|
||||
|
||||
```cpp
|
||||
int getInt(
|
||||
const std::string §ion,
|
||||
const std::string &key,
|
||||
int defaultValue = 0
|
||||
);
|
||||
```
|
||||
|
||||
获取整数值。
|
||||
|
||||
---
|
||||
|
||||
#### getFloat
|
||||
|
||||
```cpp
|
||||
float getFloat(
|
||||
const std::string §ion,
|
||||
const std::string &key,
|
||||
float defaultValue = 0.0f
|
||||
);
|
||||
```
|
||||
|
||||
获取浮点数值。
|
||||
|
||||
---
|
||||
|
||||
#### getBool
|
||||
|
||||
```cpp
|
||||
bool getBool(
|
||||
const std::string §ion,
|
||||
const std::string &key,
|
||||
bool defaultValue = false
|
||||
);
|
||||
```
|
||||
|
||||
获取布尔值。
|
||||
|
||||
**说明:**
|
||||
- 支持 "true"/"false"、"yes"/"no"、"1"/"0" 等格式
|
||||
|
||||
---
|
||||
|
||||
#### setString
|
||||
|
||||
```cpp
|
||||
void setString(
|
||||
const std::string §ion,
|
||||
const std::string &key,
|
||||
const std::string &value
|
||||
);
|
||||
```
|
||||
|
||||
设置字符串值。
|
||||
|
||||
**说明:**
|
||||
- 自动标记为脏数据
|
||||
- 事务外自动保存
|
||||
|
||||
---
|
||||
|
||||
#### setInt
|
||||
|
||||
```cpp
|
||||
void setInt(
|
||||
const std::string §ion,
|
||||
const std::string &key,
|
||||
int value
|
||||
);
|
||||
```
|
||||
|
||||
设置整数值。
|
||||
|
||||
---
|
||||
|
||||
#### setFloat
|
||||
|
||||
```cpp
|
||||
void setFloat(
|
||||
const std::string §ion,
|
||||
const std::string &key,
|
||||
float value
|
||||
);
|
||||
```
|
||||
|
||||
设置浮点数值。
|
||||
|
||||
---
|
||||
|
||||
#### setBool
|
||||
|
||||
```cpp
|
||||
void setBool(
|
||||
const std::string §ion,
|
||||
const std::string &key,
|
||||
bool value
|
||||
);
|
||||
```
|
||||
|
||||
设置布尔值。
|
||||
|
||||
---
|
||||
|
||||
#### removeKey
|
||||
|
||||
```cpp
|
||||
void removeKey(const std::string §ion, const std::string &key);
|
||||
```
|
||||
|
||||
删除键。
|
||||
|
||||
---
|
||||
|
||||
#### removeSection
|
||||
|
||||
```cpp
|
||||
void removeSection(const std::string §ion);
|
||||
```
|
||||
|
||||
删除整个 section。
|
||||
|
||||
---
|
||||
|
||||
#### hasKey
|
||||
|
||||
```cpp
|
||||
bool hasKey(const std::string §ion, const std::string &key);
|
||||
```
|
||||
|
||||
检查键是否存在。
|
||||
|
||||
---
|
||||
|
||||
#### hasSection
|
||||
|
||||
```cpp
|
||||
bool hasSection(const std::string §ion);
|
||||
```
|
||||
|
||||
检查 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 §ion) 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) - 实现文件
|
||||
225
docs/INDEX.md
225
docs/INDEX.md
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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_mixer,Switch优化 |
|
||||
| 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追踪器
|
||||
88
xmake.lua
88
xmake.lua
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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.0(mesa)和 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue