feat(窗口图标): 添加窗口图标支持并完善相关资源处理
添加窗口图标功能,包括: 1. 支持通过配置文件设置窗口图标 2. 添加Windows资源文件处理 3. 更新各平台构建脚本以处理图标资源 4. 添加图标使用文档说明 5. 优化Application类结构 同时修复了Switch平台构建脚本中的团队名称错误
This commit is contained in:
parent
c94e43089b
commit
ab3dd829b8
|
|
@ -0,0 +1,216 @@
|
||||||
|
# Frostbite2D Agent Guide
|
||||||
|
|
||||||
|
This guide helps agentic coding agents work effectively with the Frostbite2D 2D game engine codebase.
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
### Basic Build
|
||||||
|
```bash
|
||||||
|
xmake build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clean Build
|
||||||
|
```bash
|
||||||
|
xmake clean
|
||||||
|
xmake build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Build
|
||||||
|
```bash
|
||||||
|
xmake build -m debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Release Build
|
||||||
|
```bash
|
||||||
|
xmake build -m release
|
||||||
|
```
|
||||||
|
|
||||||
|
### Platform-Specific Build
|
||||||
|
```bash
|
||||||
|
xmake build -p windows
|
||||||
|
xmake build -p linux
|
||||||
|
xmake build -p switch
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
```bash
|
||||||
|
xmake f -c # Show current configuration
|
||||||
|
xmake f # Interactive configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
Currently no automated test framework is configured. Manual testing is performed by running the built binary.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
```
|
||||||
|
Frostbite2D/
|
||||||
|
├── include/frostbite2D/ # Public headers
|
||||||
|
│ ├── core/ # Core engine classes
|
||||||
|
│ ├── types/ # Type definitions
|
||||||
|
│ ├── utils/ # Utility classes
|
||||||
|
│ └── platform/ # Platform-specific code
|
||||||
|
└── src/frostbite2D/ # Implementation files
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
- **Classes/Structs**: PascalCase (`Application`, `Window`, `Vec2`)
|
||||||
|
- **Methods/Functions**: camelCase (`readTextFile`, `getFileName`)
|
||||||
|
- **Member Variables**: camelCase with trailing underscore (`width_`, `height_`, `running_`)
|
||||||
|
- **Constants**: UPPER_CASE (`PI_F`, `DEG_TO_RAD`) or PascalCase static methods (`Vec2::Zero()`)
|
||||||
|
- **Namespaces**: lowercase (`frostbite2D`, `math`)
|
||||||
|
- **Template Parameters**: PascalCase (`T`, `Args`, `Sig`)
|
||||||
|
- **Type Aliases**: using keyword with PascalCase (`using int8 = std::int8_t;`)
|
||||||
|
|
||||||
|
### Imports and Includes
|
||||||
|
- **Engine headers**: Use path relative to frostbite2D root
|
||||||
|
```cpp
|
||||||
|
#include <frostbite2D/core/application.h>
|
||||||
|
#include <frostbite2D/utils/asset.h>
|
||||||
|
```
|
||||||
|
- **Third-party libraries**: Use vendor prefixes
|
||||||
|
```cpp
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
```
|
||||||
|
- **Standard library**: Use angle brackets
|
||||||
|
```cpp
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
- **Indentation**: 2 spaces
|
||||||
|
- **Brace Style**: K&R (opening brace on same line)
|
||||||
|
```cpp
|
||||||
|
void myFunction() {
|
||||||
|
// body
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **Pointer/Reference Style**: Type on left, not centered
|
||||||
|
```cpp
|
||||||
|
void func(T* ptr); // Good
|
||||||
|
void func(T *ptr); // Avoid
|
||||||
|
void func(T& ref); // Good
|
||||||
|
```
|
||||||
|
- **Const Placement**: After type
|
||||||
|
```cpp
|
||||||
|
const std::string& str; // Good
|
||||||
|
std::string const& str; // Avoid
|
||||||
|
```
|
||||||
|
|
||||||
|
### Types
|
||||||
|
- **Use custom type aliases**: `int8`, `int32`, `uint8`, `uint64`, `Ptr<T>`, `SharedPtr<T>`
|
||||||
|
- **Smart pointers**: Prefer `Ptr<T>` alias over `std::shared_ptr<T>`
|
||||||
|
- **constexpr**: Use for compile-time constants
|
||||||
|
- **Struct vs Class**:
|
||||||
|
- Use `struct` for POD types, configurations, and data-only types
|
||||||
|
- Use `class` for objects with methods/encapsulation
|
||||||
|
- **Default member initialization**: Initialize at declaration
|
||||||
|
```cpp
|
||||||
|
bool running_ = false;
|
||||||
|
int width_ = 1280;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- **Return bool for success/failure operations** (especially I/O)
|
||||||
|
- **Use SDL_Log/SDL_LogError for logging**
|
||||||
|
- **Use std::optional for optional returns**
|
||||||
|
- **Avoid exceptions** (not observed in codebase)
|
||||||
|
- **Return early on failure**: Check conditions and return false immediately
|
||||||
|
|
||||||
|
### Singletons
|
||||||
|
- Use static instance pattern with private constructor:
|
||||||
|
```cpp
|
||||||
|
static Application& get();
|
||||||
|
private:
|
||||||
|
Application() = default;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Const Correctness
|
||||||
|
- Mark methods `const` if they don't modify state
|
||||||
|
- Pass by const reference for large types: `const std::string&`
|
||||||
|
- Return const references to member data when appropriate
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- **Doxygen-style comments** with `@brief` tag
|
||||||
|
- **Use `///` for single-line comments in headers**
|
||||||
|
- **Section separators** use `// ---------------------------------------------------------------------------`
|
||||||
|
- **Chinese comments** are present and should be preserved/maintained
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief 读取文本文件
|
||||||
|
* @param path 文件路径
|
||||||
|
* @param outContent 输出文件内容
|
||||||
|
* @return 读取成功返回 true
|
||||||
|
*/
|
||||||
|
bool readTextFile(const std::string& path, std::string& outContent);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Platform Handling
|
||||||
|
- Use `#ifdef` guards for platform-specific code:
|
||||||
|
```cpp
|
||||||
|
#ifdef __SWITCH__
|
||||||
|
switchInit();
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
- Supported platforms: Windows, Linux, macOS, Switch
|
||||||
|
- Platform configs in `platform/` directory
|
||||||
|
|
||||||
|
### STL Usage
|
||||||
|
- Prefer STL algorithms over manual loops
|
||||||
|
- Use `std::filesystem` for file operations (already wrapped in Asset class)
|
||||||
|
- Use `std::optional` for nullable returns
|
||||||
|
- Use `std::function` for callbacks
|
||||||
|
- Use `std::vector` for dynamic arrays
|
||||||
|
- Use `std::string` for text
|
||||||
|
|
||||||
|
### OpenGL/Graphics
|
||||||
|
- GLM library for math operations
|
||||||
|
- Glad for OpenGL function loading
|
||||||
|
- Keep OpenGL-specific code minimal and well-encapsulated
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **SDL2**: Windowing, input, platform abstraction
|
||||||
|
- **GLM**: Math library (vectors, matrices)
|
||||||
|
- **Glad**: OpenGL loader
|
||||||
|
- **STB**: Image/text utilities (single-header libs)
|
||||||
|
|
||||||
|
## Key Patterns
|
||||||
|
|
||||||
|
### Module Pattern
|
||||||
|
```cpp
|
||||||
|
void Application::use(Module& m) {
|
||||||
|
modules_.push_back(&m);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Pattern
|
||||||
|
Use configuration structs with default values:
|
||||||
|
```cpp
|
||||||
|
struct WindowConfig {
|
||||||
|
uint32_t width = 640;
|
||||||
|
uint32_t height = 480;
|
||||||
|
std::string title = "Frostbite2D Game";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Path Handling
|
||||||
|
Always use the Asset singleton for file operations - it handles platform-specific path issues:
|
||||||
|
```cpp
|
||||||
|
Asset& asset = Asset::get();
|
||||||
|
asset.setWorkingDirectory("path/to/assets");
|
||||||
|
std::string content;
|
||||||
|
if (asset.readTextFile("file.txt", content)) {
|
||||||
|
// success
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
- **Path separators**: Use Asset class, it handles platform differences
|
||||||
|
- **Encoding**: Project uses UTF-8, Asset handles Windows UTF-8 paths
|
||||||
|
- **Memory leaks**: Use smart pointers, ensure proper cleanup in destructors
|
||||||
|
- **OpenGL context**: Initialize only after window creation
|
||||||
|
|
@ -73,13 +73,6 @@ public:
|
||||||
*/
|
*/
|
||||||
bool init(const AppConfig& config);
|
bool init(const AppConfig& config);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 使用配置文件初始化
|
|
||||||
* @param configPath 配置文件路径
|
|
||||||
* @return 初始化成功返回 true
|
|
||||||
*/
|
|
||||||
bool init(const std::string& configPath);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 关闭应用程序
|
* @brief 关闭应用程序
|
||||||
*/
|
*/
|
||||||
|
|
@ -117,54 +110,6 @@ public:
|
||||||
*/
|
*/
|
||||||
bool isRunning() const { return running_; }
|
bool isRunning() const { return running_; }
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @brief 获取窗口
|
|
||||||
// * @return 窗口引用
|
|
||||||
// */
|
|
||||||
// IWindow& window() { return *window_; }
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @brief 获取渲染器
|
|
||||||
// * @return 渲染器引用
|
|
||||||
// */
|
|
||||||
// RenderBackend& renderer();
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @brief 获取场景服务
|
|
||||||
// * @return 场景服务共享指针
|
|
||||||
// */
|
|
||||||
// SharedPtr<class ISceneService> scenes();
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @brief 获取计时器服务
|
|
||||||
// * @return 计时器服务共享指针
|
|
||||||
// */
|
|
||||||
// SharedPtr<class ITimerService> timers();
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @brief 获取事件服务
|
|
||||||
// * @return 事件服务共享指针
|
|
||||||
// */
|
|
||||||
// SharedPtr<class IEventService> events();
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @brief 获取相机服务
|
|
||||||
// * @return 相机服务共享指针
|
|
||||||
// */
|
|
||||||
// SharedPtr<class ICameraService> camera();
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @brief 进入场景
|
|
||||||
// * @param scene 场景指针
|
|
||||||
// */
|
|
||||||
// void enterScene(Ptr<class Scene> scene);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取帧间隔时间
|
|
||||||
* @return 帧间隔时间(秒)
|
|
||||||
*/
|
|
||||||
float deltaTime() const { return deltaTime_; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取总运行时间
|
* @brief 获取总运行时间
|
||||||
* @return 总运行时间(秒)
|
* @return 总运行时间(秒)
|
||||||
|
|
@ -183,6 +128,18 @@ public:
|
||||||
*/
|
*/
|
||||||
const AppConfig& getConfig() const;
|
const AppConfig& getConfig() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取窗口
|
||||||
|
* @return 窗口指针
|
||||||
|
*/
|
||||||
|
Window* getWindow() const { return window_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 delta time
|
||||||
|
* @return 帧间隔时间(秒)
|
||||||
|
*/
|
||||||
|
float deltaTime() const { return deltaTime_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application() = default;
|
Application() = default;
|
||||||
~Application();
|
~Application();
|
||||||
|
|
@ -225,6 +182,7 @@ private:
|
||||||
|
|
||||||
std::vector<Module*> modules_;
|
std::vector<Module*> modules_;
|
||||||
Window* window_ = nullptr;
|
Window* window_ = nullptr;
|
||||||
|
AppConfig config_;
|
||||||
|
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
bool running_ = false;
|
bool running_ = false;
|
||||||
|
|
@ -237,6 +195,7 @@ private:
|
||||||
int frameCount_ = 0;
|
int frameCount_ = 0;
|
||||||
float fpsTimer_ = 0.0f;
|
float fpsTimer_ = 0.0f;
|
||||||
int currentFps_ = 0;
|
int currentFps_ = 0;
|
||||||
|
const float fpsUpdateInterval_ = 1.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
#include <algorithm>
|
||||||
#include <frostbite2D/core/application.h>
|
#include <frostbite2D/core/application.h>
|
||||||
#include <frostbite2D/platform/switch.h>
|
#include <frostbite2D/platform/switch.h>
|
||||||
namespace frostbite2D {
|
namespace frostbite2D {
|
||||||
|
|
@ -35,6 +36,8 @@ bool Application::init(const AppConfig &config) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_ = config;
|
||||||
|
|
||||||
// 平台相关初始化
|
// 平台相关初始化
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
switchInit();
|
switchInit();
|
||||||
|
|
@ -47,13 +50,25 @@ bool Application::init(const AppConfig &config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!initCoreModules()) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize core modules");
|
||||||
|
shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::shutdown() {
|
void Application::shutdown() {
|
||||||
if (!initialized_)
|
if (!initialized_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
running_ = false;
|
||||||
|
shouldQuit_ = true;
|
||||||
|
|
||||||
|
destroyAllModules();
|
||||||
|
|
||||||
if (window_) {
|
if (window_) {
|
||||||
window_->destroy();
|
window_->destroy();
|
||||||
window_ = nullptr;
|
window_ = nullptr;
|
||||||
|
|
@ -69,8 +84,162 @@ void Application::shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
if (initialized_) {
|
shutdown();
|
||||||
shutdown();
|
}
|
||||||
|
|
||||||
|
bool Application::initCoreModules() {
|
||||||
|
registerCoreServices();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::registerCoreServices() {
|
||||||
|
SDL_Log("Registering core services...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::setupAllModules() {
|
||||||
|
SDL_Log("Setting up modules (%zu)...", modules_.size());
|
||||||
|
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && !module->isInitialized()) {
|
||||||
|
module->setupModule();
|
||||||
|
module->setInitialized(true);
|
||||||
|
SDL_Log("Module '%s' initialized", module->getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::destroyAllModules() {
|
||||||
|
SDL_Log("Destroying modules (%zu)...", modules_.size());
|
||||||
|
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && module->isInitialized()) {
|
||||||
|
module->destroyModule();
|
||||||
|
module->setInitialized(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::run() {
|
||||||
|
if (!initialized_) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Application not initialized!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (running_) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Application already running!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Log("Starting application...");
|
||||||
|
running_ = true;
|
||||||
|
shouldQuit_ = false;
|
||||||
|
paused_ = false;
|
||||||
|
|
||||||
|
setupAllModules();
|
||||||
|
|
||||||
|
lastFrameTime_ = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
|
||||||
|
totalTime_ = 0.0f;
|
||||||
|
deltaTime_ = 0.0f;
|
||||||
|
frameCount_ = 0;
|
||||||
|
fpsTimer_ = 0.0f;
|
||||||
|
currentFps_ = 0;
|
||||||
|
|
||||||
|
mainLoop();
|
||||||
|
|
||||||
|
destroyAllModules();
|
||||||
|
running_ = false;
|
||||||
|
SDL_Log("Application stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::quit() {
|
||||||
|
SDL_Log("Quit requested");
|
||||||
|
shouldQuit_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::pause() {
|
||||||
|
if (!paused_) {
|
||||||
|
SDL_Log("Application paused");
|
||||||
|
paused_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::resume() {
|
||||||
|
if (paused_) {
|
||||||
|
SDL_Log("Application resumed");
|
||||||
|
paused_ = false;
|
||||||
|
lastFrameTime_ = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::mainLoop() {
|
||||||
|
while (!shouldQuit_) {
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (event.type == SDL_QUIT) {
|
||||||
|
shouldQuit_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && module->isInitialized()) {
|
||||||
|
module->handleEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!paused_) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::update() {
|
||||||
|
double currentTime = SDL_GetPerformanceCounter() / static_cast<double>(SDL_GetPerformanceFrequency());
|
||||||
|
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
|
||||||
|
lastFrameTime_ = currentTime;
|
||||||
|
totalTime_ += deltaTime_;
|
||||||
|
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && module->isInitialized()) {
|
||||||
|
module->onUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frameCount_++;
|
||||||
|
fpsTimer_ += deltaTime_;
|
||||||
|
if (fpsTimer_ >= fpsUpdateInterval_) {
|
||||||
|
currentFps_ = static_cast<int>(frameCount_ / fpsTimer_);
|
||||||
|
frameCount_ = 0;
|
||||||
|
fpsTimer_ = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::render() {
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && module->isInitialized()) {
|
||||||
|
module->beforeRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && module->isInitialized()) {
|
||||||
|
module->onRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* module : modules_) {
|
||||||
|
if (module && module->isInitialized()) {
|
||||||
|
module->afterRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window_) {
|
||||||
|
window_->swap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppConfig& Application::getConfig() const {
|
||||||
|
return config_;
|
||||||
|
}
|
||||||
} // namespace frostbite2D
|
} // namespace frostbite2D
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "SDL_log.h"
|
#include "SDL_log.h"
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <frostbite2D/core/window.h>
|
#include <frostbite2D/core/window.h>
|
||||||
|
#include <frostbite2D/utils/asset.h>
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,6 +85,25 @@ bool Window::create(const WindowConfig &cfg) {
|
||||||
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
fullscreen_ = (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||||
vsync_ = cfg.vsync;
|
vsync_ = cfg.vsync;
|
||||||
|
|
||||||
|
#ifndef __SWITCH__
|
||||||
|
if (!cfg.icon.file_path.empty()) {
|
||||||
|
Asset& asset = Asset::get();
|
||||||
|
std::string resolvedPath = asset.resolveAssetPath(cfg.icon.file_path);
|
||||||
|
SDL_Surface* icon = SDL_LoadBMP(resolvedPath.c_str());
|
||||||
|
if (icon) {
|
||||||
|
SDL_SetWindowIcon(sdlWindow_, icon);
|
||||||
|
SDL_FreeSurface(icon);
|
||||||
|
} else {
|
||||||
|
SDL_Log("Failed to load window icon: %s", resolvedPath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (cfg.icon.resource_id != 0) {
|
||||||
|
SDL_Log("Loading icon from resource ID: %u", cfg.icon.resource_id);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,40 +2,76 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <frostbite2D/core/application.h>
|
#include <frostbite2D/core/application.h>
|
||||||
#include <frostbite2D/core/window.h>
|
#include <frostbite2D/core/window.h>
|
||||||
|
#include <frostbite2D/module/module.h>
|
||||||
#include <frostbite2D/utils/asset.h>
|
#include <frostbite2D/utils/asset.h>
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
using namespace frostbite2D;
|
using namespace frostbite2D;
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
class TestModule : public Module {
|
||||||
|
public:
|
||||||
|
const char* getName() const override { return "TestModule"; }
|
||||||
|
|
||||||
|
void setupModule() override {
|
||||||
|
SDL_Log("TestModule: setupModule()");
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUpdate() override {
|
||||||
|
static int frameCount = 0;
|
||||||
|
frameCount++;
|
||||||
|
|
||||||
|
if (frameCount % 120 == 0) {
|
||||||
|
SDL_Log("TestModule: onUpdate() - Frame %d, FPS: %d", frameCount, Application::get().fps());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frameCount > 600) {
|
||||||
|
SDL_Log("TestModule: Requesting quit after 600 frames");
|
||||||
|
Application::get().quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void beforeRender() override {
|
||||||
|
glClearColor(0.2f, 0.3f, 0.8f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRender() override {
|
||||||
|
}
|
||||||
|
|
||||||
|
void afterRender() override {
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyModule() override {
|
||||||
|
SDL_Log("TestModule: destroyModule()");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
AppConfig config = AppConfig::createDefault();
|
AppConfig config = AppConfig::createDefault();
|
||||||
config.appName = "Frostbite2D Render Test";
|
config.appName = "Frostbite2D Test App";
|
||||||
config.appVersion = "1.0.0";
|
config.appVersion = "1.0.0";
|
||||||
config.windowConfig.width = 800;
|
config.windowConfig.width = 800;
|
||||||
config.windowConfig.height = 600;
|
config.windowConfig.height = 600;
|
||||||
config.windowConfig.title = "Frostbite2D - OpenGL Render Test";
|
config.windowConfig.title = "Frostbite2D - Application Test";
|
||||||
|
|
||||||
Application &app = Application::get();
|
Application &app = Application::get();
|
||||||
|
|
||||||
|
TestModule testModule;
|
||||||
|
app.use(testModule);
|
||||||
|
|
||||||
if (!app.init(config)) {
|
if (!app.init(config)) {
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize application!");
|
||||||
"Failed to initialize application!");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset &asset = Asset::get();
|
SDL_Log("Application initialized successfully");
|
||||||
// asset.setWorkingDirectory("I:/DOF/骑士团");
|
SDL_Log("Running main loop...");
|
||||||
asset.setWorkingDirectory("/switch/testgame");
|
|
||||||
std::string content;
|
app.run();
|
||||||
if (asset.readTextFile("test.txt", content)) {
|
|
||||||
SDL_Log("test.txt content: %s", content.c_str());
|
|
||||||
} else {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read test.txt!");
|
|
||||||
}
|
|
||||||
|
|
||||||
app.shutdown();
|
app.shutdown();
|
||||||
|
|
||||||
SDL_Log("程序正常退出");
|
SDL_Log("Application exited normally");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Icon Setup Guide
|
||||||
|
|
||||||
|
## Window Icon (运行时窗口图标)
|
||||||
|
|
||||||
|
### Windows/Linux
|
||||||
|
- Place `icon.bmp` (32x32 or larger) in `assets/icons/`
|
||||||
|
- Set in code: `config.windowConfig.icon = Icon("assets/icons/icon.bmp");`
|
||||||
|
|
||||||
|
### Switch
|
||||||
|
- Place `icon.jpg` (256x256 JPEG) in `assets/icons/icon.jpg`
|
||||||
|
- Automatically included in NRO build process
|
||||||
|
|
||||||
|
## Application Icon (程序文件图标)
|
||||||
|
|
||||||
|
### Windows (.exe 图标)
|
||||||
|
需要创建 `.ico` 文件并放置在 `assets/icons/app.ico`
|
||||||
|
|
||||||
|
#### 创建 .ico 文件的方法:
|
||||||
|
|
||||||
|
**方法 1: 使用在线工具**
|
||||||
|
1. 访问 https://icoconvert.com/
|
||||||
|
2. 上传 PNG/JPG 图标(推荐 256x256)
|
||||||
|
3.将下载的 .ico 文件保存到 `assets/icons/app.ico`
|
||||||
|
|
||||||
|
**方法 2: 使用 GIMP**
|
||||||
|
1. 打开图像
|
||||||
|
2. 调整为 256x256
|
||||||
|
3. 文件 → 导出为 → `app.ico`
|
||||||
|
4. 保存到 `assets/icons/app.ico`
|
||||||
|
|
||||||
|
**方法 3: 使用 ImageMagick**
|
||||||
|
```bash
|
||||||
|
convert icon.png -define icon:auto-resize=256,128,96,64,48,32,16 app.ico
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Linux (.desktop 图标)
|
||||||
|
- 将图标复制到 `/usr/share/icons/hicolor/256x256/apps/yourapp.png`
|
||||||
|
- 创建 `.desktop` 文件指定 Icon 字段
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
```cpp
|
||||||
|
AppConfig config = AppConfig::createDefault();
|
||||||
|
config.windowConfig.icon = Icon("assets/icons/icon.bmp");
|
||||||
|
Application& app = Application::get();
|
||||||
|
app.init(config);
|
||||||
|
```
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -32,37 +32,20 @@ target("Frostbite2D")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 复制 SDL2 DLL
|
-- 复制图标文件到输出目录
|
||||||
local sdl2_lib = target:pkg("libsdl2")
|
local icons_dir = path.join(os.projectdir(), "assets/icons")
|
||||||
if sdl2_lib then
|
local target_icons_dir = path.join(output_dir, "assets/icons")
|
||||||
local libfiles = sdl2_lib:get("libfiles")
|
|
||||||
if libfiles then
|
if os.isdir(icons_dir) then
|
||||||
for _, libfile in ipairs(libfiles) do
|
if not os.isdir(target_icons_dir) then
|
||||||
-- 查找 DLL 文件
|
os.mkdir(target_icons_dir)
|
||||||
if libfile:endswith(".dll") then
|
|
||||||
local target_dll = path.join(output_dir, path.filename(libfile))
|
|
||||||
os.cp(libfile, target_dll)
|
|
||||||
print("Copy DLL: " .. path.filename(libfile))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
for _, file in ipairs(os.files(path.join(icons_dir, "*.*"))) do
|
||||||
-- 尝试从 xmake 包目录复制 SDL2.dll
|
local filename = path.filename(file)
|
||||||
local sdl2_dll_paths = {
|
local target_file = path.join(target_icons_dir, filename)
|
||||||
path.join(os.getenv("USERPROFILE") or "", ".xmake/packages/l/libsdl2/**/bin/SDL2.dll"),
|
os.cp(file, target_file)
|
||||||
path.join(os.getenv("USERPROFILE") or "", ".xmake/packages/l/libsdl2/**/lib/SDL2.dll"),
|
print("Copy icon: " .. filename)
|
||||||
}
|
|
||||||
|
|
||||||
for _, dll_pattern in ipairs(sdl2_dll_paths) do
|
|
||||||
local dll_files = os.files(dll_pattern)
|
|
||||||
for _, dll_file in ipairs(dll_files) do
|
|
||||||
local target_dll = path.join(output_dir, "SDL2.dll")
|
|
||||||
if not os.isfile(target_dll) then
|
|
||||||
os.cp(dll_file, target_dll)
|
|
||||||
print("Copy SDL2.dll from: " .. dll_file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
target_end()
|
target_end()
|
||||||
|
|
|
||||||
|
|
@ -50,14 +50,16 @@ target("Frostbite2D")
|
||||||
local nro_file = path.join(output_dir, "hello_world.nro")
|
local nro_file = path.join(output_dir, "hello_world.nro")
|
||||||
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
|
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
|
||||||
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe")
|
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe")
|
||||||
|
local icon_file = path.join(os.projectdir(), "assets/icons/icon.bmp")
|
||||||
|
|
||||||
if os.isfile(nacptool) and os.isfile(elf2nro) then
|
if os.isfile(nacptool) and os.isfile(elf2nro) then
|
||||||
os.vrunv(nacptool, {"--create", "Hello World", "Extra2D Team", "1.0.0", nacp_file})
|
os.vrunv(nacptool, {"--create", "Hello World", "Frostbite2D Team", "1.0.0", nacp_file})
|
||||||
local romfs = path.join(example_dir, "romfs")
|
local romfs = path.join(example_dir, "romfs")
|
||||||
|
local icon_param = os.isfile(icon_file) and ("--icon=" .. icon_file) or ""
|
||||||
if os.isdir(romfs) then
|
if os.isdir(romfs) then
|
||||||
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
|
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, icon_param, "--romfsdir=" .. romfs})
|
||||||
else
|
else
|
||||||
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file})
|
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, icon_param})
|
||||||
end
|
end
|
||||||
print("Generated NRO: " .. nro_file)
|
print("Generated NRO: " .. nro_file)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,33 @@ set_toolchains("mingw")
|
||||||
add_requires("libsdl2", {configs = {shared = true}})
|
add_requires("libsdl2", {configs = {shared = true}})
|
||||||
add_requires("glm")
|
add_requires("glm")
|
||||||
|
|
||||||
|
-- 创建自定义规则来编译 .rc 文件
|
||||||
|
rule("windres_compile")
|
||||||
|
set_extensions(".rc")
|
||||||
|
on_build(function (target)
|
||||||
|
local windres = "windres"
|
||||||
|
local rc_file = target:sourcefiles()[1]
|
||||||
|
local obj_file = target:targetfile()
|
||||||
|
local arch = is_arch("x86_64") and "pe-x86-64" or "pe-i386"
|
||||||
|
|
||||||
|
os.execv(windres, {"-i", rc_file, "-o", obj_file, "-O", "coff", "-F", arch})
|
||||||
|
end)
|
||||||
|
rule_end()
|
||||||
|
|
||||||
target("Frostbite2D")
|
target("Frostbite2D")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp"))
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.cpp"))
|
||||||
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
add_files(path.join(os.projectdir(), "Frostbite2D/src/**.c"))
|
||||||
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
add_includedirs(path.join(os.projectdir(), "Frostbite2D/include"))
|
||||||
|
|
||||||
|
-- 添加资源文件(如果存在)
|
||||||
|
local rc_file = path.join(os.projectdir(), "resources/app.rc")
|
||||||
|
local ico_file = path.join(os.projectdir(), "assets/icons/app.ico")
|
||||||
|
if os.isfile(rc_file) and os.isfile(ico_file) then
|
||||||
|
-- 添加 .rc 文件并应用自定义规则
|
||||||
|
add_files(rc_file, {rule = "windres_compile"})
|
||||||
|
end
|
||||||
|
|
||||||
add_packages("libsdl2")
|
add_packages("libsdl2")
|
||||||
add_packages("glm")
|
add_packages("glm")
|
||||||
|
|
||||||
|
|
@ -34,6 +55,22 @@ target("Frostbite2D")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 复制图标文件到输出目录
|
||||||
|
local icons_dir = path.join(os.projectdir(), "assets/icons")
|
||||||
|
local target_icons_dir = path.join(output_dir, "assets/icons")
|
||||||
|
|
||||||
|
if os.isdir(icons_dir) then
|
||||||
|
if not os.isdir(target_icons_dir) then
|
||||||
|
os.mkdir(target_icons_dir)
|
||||||
|
end
|
||||||
|
for _, file in ipairs(os.files(path.join(icons_dir, "*.*"))) do
|
||||||
|
local filename = path.filename(file)
|
||||||
|
local target_file = path.join(target_icons_dir, filename)
|
||||||
|
os.cp(file, target_file)
|
||||||
|
print("Copy icon: " .. filename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- 复制 SDL2 DLL
|
-- 复制 SDL2 DLL
|
||||||
local sdl2_lib = target:pkg("libsdl2")
|
local sdl2_lib = target:pkg("libsdl2")
|
||||||
if sdl2_lib then
|
if sdl2_lib then
|
||||||
|
|
@ -67,4 +104,4 @@ target("Frostbite2D")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
target_end()
|
target_end()
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
1 ICON "assets/icons/app.ico"
|
||||||
Loading…
Reference in New Issue