Add Switch game framework target
This commit is contained in:
@@ -237,6 +237,64 @@ Frostbite2D Switch smoke test
|
|||||||
Press PLUS to exit.
|
Press PLUS to exit.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 2.4 编译 Switch 游戏框架 NRO
|
||||||
|
|
||||||
|
当前游戏框架已经接入 Switch 构建目标:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Frostbite2DGameSwitch
|
||||||
|
```
|
||||||
|
|
||||||
|
该目标使用:
|
||||||
|
|
||||||
|
```text
|
||||||
|
game\src\switch_game_main.cpp
|
||||||
|
```
|
||||||
|
|
||||||
|
作为 Switch 专用入口,运行同一套 `WhiteboxScene`、`StageRenderer`、
|
||||||
|
`DebugOverlay`、`PlayerActor` 和 `LevelDefinition` 游戏框架。
|
||||||
|
|
||||||
|
构建命令:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:PATH='C:\devkitPro\devkitA64\bin;C:\devkitPro\tools\bin;C:\devkitPro\msys2\usr\bin;C:\Program Files\CMake\bin;C:\Program Files\xmake;' + $env:PATH
|
||||||
|
$env:DEVKITPRO='C:\devkitPro'
|
||||||
|
$env:DEVKITA64='C:\devkitPro\devkitA64'
|
||||||
|
|
||||||
|
& 'C:\Program Files\xmake\xmake.exe' f -c -p switch -a aarch64 -m release --toolchain=cross --sdk='C:\devkitPro\devkitA64' --bin='C:\devkitPro\devkitA64\bin' --cross='aarch64-none-elf-' -y
|
||||||
|
& 'C:\Program Files\xmake\xmake.exe' -b Frostbite2DGameSwitch
|
||||||
|
```
|
||||||
|
|
||||||
|
产物:
|
||||||
|
|
||||||
|
```text
|
||||||
|
build\switch\aarch64\release\Frostbite2DGame.elf
|
||||||
|
build\switch\aarch64\release\switch_game\NSUnknownGame.nro
|
||||||
|
build\switch\aarch64\release\switch_game\control.nacp
|
||||||
|
build\switch\aarch64\release\switch_game\assets\
|
||||||
|
```
|
||||||
|
|
||||||
|
`xmake.lua` 会在构建后自动:
|
||||||
|
|
||||||
|
- 复制 `game\assets` 到 `build\switch\aarch64\release\switch_game\assets`
|
||||||
|
- 生成 `control.nacp`
|
||||||
|
- 调用 `elf2nro` 生成带元数据的 `NSUnknownGame.nro`
|
||||||
|
|
||||||
|
Switch 运行时资源目录当前由引擎解析为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/switch/Frostbite2D/NS Unknown Game/assets
|
||||||
|
```
|
||||||
|
|
||||||
|
在真机 SD 卡或模拟器文件系统中测试完整游戏时,需要确保资源也放到该目录。
|
||||||
|
构建输出目录中的 `switch_game\assets` 是用于部署的资源副本。
|
||||||
|
|
||||||
|
当前已验证:
|
||||||
|
|
||||||
|
- `Frostbite2DGameSwitch` 可成功编译为 ELF/NRO
|
||||||
|
- PC 和 Switch 共用同一套 GLES 300 es shader
|
||||||
|
- Windows 游戏目标仍可正常编译和启动
|
||||||
|
|
||||||
## 3. 常见问题
|
## 3. 常见问题
|
||||||
|
|
||||||
### 3.1 切换平台后编译异常
|
### 3.1 切换平台后编译异常
|
||||||
@@ -267,3 +325,21 @@ Frostbite2D_smoke_meta.nro
|
|||||||
|
|
||||||
不要优先使用裸的 `Frostbite2D_smoke.nro`。
|
不要优先使用裸的 `Frostbite2D_smoke.nro`。
|
||||||
|
|
||||||
|
### 3.4 Switch 游戏 NRO 启动后资源加载失败
|
||||||
|
|
||||||
|
确认资源目录存在:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/switch/Frostbite2D/NS Unknown Game/assets
|
||||||
|
```
|
||||||
|
|
||||||
|
至少应包含:
|
||||||
|
|
||||||
|
```text
|
||||||
|
assets\shaders\shaders.json
|
||||||
|
assets\shaders\sprite.vert
|
||||||
|
assets\shaders\sprite.frag
|
||||||
|
assets\shaders\colored_quad.vert
|
||||||
|
assets\shaders\colored_quad.frag
|
||||||
|
assets\character\male\*.png
|
||||||
|
```
|
||||||
|
|||||||
+2
-2
@@ -37,7 +37,7 @@ function add_frostbite2d_target()
|
|||||||
local libnx = path.join(devkitpro, "libnx")
|
local libnx = path.join(devkitpro, "libnx")
|
||||||
|
|
||||||
add_cxflags("-D__SWITCH__", "-march=armv8-a+crc+crypto", "-mtune=cortex-a57", "-mtp=soft", {public = true})
|
add_cxflags("-D__SWITCH__", "-march=armv8-a+crc+crypto", "-mtune=cortex-a57", "-mtp=soft", {public = true})
|
||||||
add_cxflags("-ffunction-sections", "-fdata-sections", {public = true})
|
add_cxflags("-ffunction-sections", "-fdata-sections", "-fPIE", {public = true})
|
||||||
add_cxflags("-specs=" .. path.join(libnx, "switch.specs"), {public = true, force = true})
|
add_cxflags("-specs=" .. path.join(libnx, "switch.specs"), {public = true, force = true})
|
||||||
add_ldflags("-specs=" .. path.join(libnx, "switch.specs"), {public = true, force = true})
|
add_ldflags("-specs=" .. path.join(libnx, "switch.specs"), {public = true, force = true})
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ function add_frostbite2d_target()
|
|||||||
add_includedirs(path.join(libnx, "include"), {public = true})
|
add_includedirs(path.join(libnx, "include"), {public = true})
|
||||||
add_linkdirs(path.join(portlibs, "lib"), path.join(libnx, "lib"), {public = true})
|
add_linkdirs(path.join(portlibs, "lib"), path.join(libnx, "lib"), {public = true})
|
||||||
add_links("SDL2_ttf", "freetype", "SDL2_mixer", "vorbisidec", "opusfile", "opus", "modplug", "mpg123", "mikmod", "FLAC", "ogg", {public = true})
|
add_links("SDL2_ttf", "freetype", "SDL2_mixer", "vorbisidec", "opusfile", "opus", "modplug", "mpg123", "mikmod", "FLAC", "ogg", {public = true})
|
||||||
add_links("SDL2_image", "png", "jpeg", "webp", "z", "SDL2", "EGL", "GLESv2", "glapi", "drm_nouveau", "nx", "m", "pthread", {public = true})
|
add_links("SDL2_image", "png", "jpeg", "webp", "z", "SDL2", "EGL", "GLESv2", "glapi", "drm_nouveau", "nx", "m", "pthread", "stdc++", {public = true})
|
||||||
else
|
else
|
||||||
add_packages("libsdl2", {public = true})
|
add_packages("libsdl2", {public = true})
|
||||||
add_packages("libsdl2_image", {public = true})
|
add_packages("libsdl2_image", {public = true})
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#version 330 core
|
#version 300 es
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
in vec4 v_color;
|
in vec4 v_color;
|
||||||
|
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#version 330 core
|
#version 300 es
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
layout (location = 0) in vec2 a_position;
|
layout (location = 0) in vec2 a_position;
|
||||||
layout (location = 1) in vec2 a_texCoord;
|
layout (location = 1) in vec2 a_texCoord;
|
||||||
layout (location = 2) in vec4 a_color;
|
layout (location = 2) in vec4 a_color;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#version 330 core
|
#version 300 es
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
in vec2 v_texCoord;
|
in vec2 v_texCoord;
|
||||||
in vec4 v_color;
|
in vec4 v_color;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#version 330 core
|
#version 300 es
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
layout (location = 0) in vec2 a_position;
|
layout (location = 0) in vec2 a_position;
|
||||||
layout (location = 1) in vec2 a_texCoord;
|
layout (location = 1) in vec2 a_texCoord;
|
||||||
layout (location = 2) in vec4 a_color;
|
layout (location = 2) in vec4 a_color;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ game/
|
|||||||
├─ movement/ 可复用的移动和碰撞辅助工具
|
├─ movement/ 可复用的移动和碰撞辅助工具
|
||||||
├─ scene/ 游戏场景和关卡编排
|
├─ scene/ 游戏场景和关卡编排
|
||||||
├─ stage/ 关卡视觉层渲染
|
├─ stage/ 关卡视觉层渲染
|
||||||
|
├─ switch_game_main.cpp Switch/NS 游戏入口
|
||||||
└─ main.cpp 游戏可执行文件入口点
|
└─ main.cpp 游戏可执行文件入口点
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,8 +100,8 @@ main.cpp
|
|||||||
|
|
||||||
## 当前构建目标
|
## 当前构建目标
|
||||||
|
|
||||||
`xmake.lua` 为 Windows 定义 `Frostbite2DGame`。它编译所有
|
`xmake.lua` 为 Windows 定义 `Frostbite2DGame`。它编译桌面游戏入口
|
||||||
`game/src/**.cpp` 文件,链接到 `Frostbite2D`,并在构建后
|
`game/src/main.cpp` 和所有通用游戏源码,链接到 `Frostbite2D`,并在构建后
|
||||||
将 `game/assets` 复制到可执行文件旁边。
|
将 `game/assets` 复制到可执行文件旁边。
|
||||||
|
|
||||||
构建命令:
|
构建命令:
|
||||||
@@ -115,6 +116,29 @@ xmake -b Frostbite2DGame
|
|||||||
build/windows/x64/release/Frostbite2DGame.exe
|
build/windows/x64/release/Frostbite2DGame.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Switch/NS 游戏框架目标为 `Frostbite2DGameSwitch`。它使用
|
||||||
|
`game/src/switch_game_main.cpp` 作为平台入口,复用同一套 Scene、Stage、
|
||||||
|
Actor、Movement、Debug 和 Data 模块。
|
||||||
|
|
||||||
|
构建命令:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
xmake -b Frostbite2DGameSwitch
|
||||||
|
```
|
||||||
|
|
||||||
|
输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
build/switch/aarch64/release/Frostbite2DGame.elf
|
||||||
|
build/switch/aarch64/release/switch_game/NSUnknownGame.nro
|
||||||
|
```
|
||||||
|
|
||||||
|
Switch 运行时资源目录:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/switch/Frostbite2D/NS Unknown Game/assets
|
||||||
|
```
|
||||||
|
|
||||||
## 后续架构步骤
|
## 后续架构步骤
|
||||||
|
|
||||||
1. 添加 `actor/enemy_actor.*` 并复用 `PlatformMover`
|
1. 添加 `actor/enemy_actor.*` 并复用 `PlatformMover`
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ mindmap
|
|||||||
Hitbox可视化
|
Hitbox可视化
|
||||||
帧率与日志
|
帧率与日志
|
||||||
构建文档
|
构建文档
|
||||||
|
NS平台构建
|
||||||
```
|
```
|
||||||
|
|
||||||
## 白板总览
|
## 白板总览
|
||||||
@@ -295,6 +296,26 @@ mindmap
|
|||||||
- 绘制 hitbox / hurtbox。
|
- 绘制 hitbox / hurtbox。
|
||||||
- 输出当前场景、角色状态、FPS。
|
- 输出当前场景、角色状态、FPS。
|
||||||
|
|
||||||
|
### 11. Platform
|
||||||
|
|
||||||
|
职责:
|
||||||
|
|
||||||
|
- 保持 PC 和 NS 共用游戏逻辑。
|
||||||
|
- 只在入口、构建、资源部署、平台输入处做差异化。
|
||||||
|
|
||||||
|
已有:
|
||||||
|
|
||||||
|
- `game/src/main.cpp`:Windows/PC 游戏入口。
|
||||||
|
- `game/src/switch_game_main.cpp`:Switch/NS 游戏入口。
|
||||||
|
- `Frostbite2DGame`:Windows 游戏目标。
|
||||||
|
- `Frostbite2DGameSwitch`:Switch 游戏目标,生成 `NSUnknownGame.nro`。
|
||||||
|
|
||||||
|
下一步:
|
||||||
|
|
||||||
|
- 将 Switch Joy-Con 输入接入 `InputStateProvider`。
|
||||||
|
- 整理资源部署脚本,把 `assets` 放到 `/switch/Frostbite2D/NS Unknown Game/assets`。
|
||||||
|
- 在 Ryujinx 或真机上验证主循环、shader 和角色纹理。
|
||||||
|
|
||||||
## 数据流白板
|
## 数据流白板
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -380,10 +401,11 @@ mindmap
|
|||||||
下一步建议按这个顺序做:
|
下一步建议按这个顺序做:
|
||||||
|
|
||||||
1. `BattleZone`:做清版推进的核心框架。
|
1. `BattleZone`:做清版推进的核心框架。
|
||||||
2. `EnemyActor`:做第一个敌人占位。
|
2. `SwitchInput`:把 Joy-Con/手柄输入接入 `InputStateProvider`。
|
||||||
3. `CombatSystem`:把 hitbox/hurtbox 真正跑起来。
|
3. `EnemyActor`:做第一个敌人占位。
|
||||||
4. `SpriteAnimator`:把玩家动画从硬编码改成组件。
|
4. `CombatSystem`:把 hitbox/hurtbox 真正跑起来。
|
||||||
5. `StageLayer`:把白盒视觉层扩展为背景、tiles 和 props。
|
5. `SpriteAnimator`:把玩家动画从硬编码改成组件。
|
||||||
|
6. `StageLayer`:把白盒视觉层扩展为背景、tiles 和 props。
|
||||||
|
|
||||||
## 素材协作提示
|
## 素材协作提示
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#include "core/game_config.h"
|
||||||
|
#include "scene/whitebox_scene.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include <frostbite2D/core/application.h>
|
||||||
|
#include <frostbite2D/scene/scene_manager.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
|
||||||
|
auto config = frostbite2D::AppConfig::createDefault();
|
||||||
|
config.appName = "NS Unknown Game";
|
||||||
|
config.windowConfig.title = "NS Unknown Game";
|
||||||
|
config.windowConfig.width = ns_game::config::kVirtualWidth;
|
||||||
|
config.windowConfig.height = ns_game::config::kVirtualHeight;
|
||||||
|
config.windowConfig.resizable = false;
|
||||||
|
config.windowConfig.vsync = true;
|
||||||
|
config.useVirtualResolution = true;
|
||||||
|
config.virtualWidth = ns_game::config::kVirtualWidth;
|
||||||
|
config.virtualHeight = ns_game::config::kVirtualHeight;
|
||||||
|
config.resolutionMode = frostbite2D::ResolutionScaleMode::Fit;
|
||||||
|
config.renderStyleProfile = frostbite2D::RenderStyleProfileId::PixelArt2D;
|
||||||
|
config.maxFps = 60;
|
||||||
|
|
||||||
|
auto& app = frostbite2D::Application::get();
|
||||||
|
if (!app.init(config)) {
|
||||||
|
std::puts("NS Unknown Game Switch init failed.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.run([]() {
|
||||||
|
frostbite2D::SceneManager::get().ReplaceScene(
|
||||||
|
frostbite2D::MakePtr<ns_game::WhiteboxScene>());
|
||||||
|
});
|
||||||
|
|
||||||
|
app.shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ if is_plat("windows") then
|
|||||||
target("Frostbite2DGame")
|
target("Frostbite2DGame")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_files("game/src/**.cpp")
|
add_files("game/src/**.cpp")
|
||||||
|
remove_files("game/src/switch_game_main.cpp")
|
||||||
add_deps("Frostbite2D")
|
add_deps("Frostbite2D")
|
||||||
add_packages("libsdl2")
|
add_packages("libsdl2")
|
||||||
add_packages("libsdl2_image")
|
add_packages("libsdl2_image")
|
||||||
@@ -49,3 +50,35 @@ if is_plat("windows") then
|
|||||||
end)
|
end)
|
||||||
target_end()
|
target_end()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if is_plat("switch") then
|
||||||
|
target("Frostbite2DGameSwitch")
|
||||||
|
set_kind("binary")
|
||||||
|
set_filename("Frostbite2DGame.elf")
|
||||||
|
add_files("game/src/**.cpp")
|
||||||
|
remove_files("game/src/main.cpp")
|
||||||
|
add_deps("Frostbite2D")
|
||||||
|
add_cxflags("-D__SWITCH__", "-fPIE", {public = true})
|
||||||
|
add_ldflags("-specs=" .. path.join(os.getenv("DEVKITPRO") or "C:/devkitPro", "libnx/switch.specs"), "-fPIE", {force = true})
|
||||||
|
after_build(function (target)
|
||||||
|
local outdir = path.join(target:targetdir(), "switch_game")
|
||||||
|
os.mkdir(outdir)
|
||||||
|
os.cp(path.join(os.projectdir(), "game/assets"),
|
||||||
|
path.join(outdir, "assets"))
|
||||||
|
|
||||||
|
local devkitpro = os.getenv("DEVKITPRO") or "C:/devkitPro"
|
||||||
|
local tools = path.join(devkitpro, "tools/bin")
|
||||||
|
local libnx = path.join(devkitpro, "libnx")
|
||||||
|
local nacp = path.join(outdir, "control.nacp")
|
||||||
|
local nro = path.join(outdir, "NSUnknownGame.nro")
|
||||||
|
os.execv(path.join(tools, "nacptool.exe"), {
|
||||||
|
"--create", "NS Unknown Game", "Frostbite2D", "1.0.0", nacp
|
||||||
|
})
|
||||||
|
os.execv(path.join(tools, "elf2nro.exe"), {
|
||||||
|
target:targetfile(), nro,
|
||||||
|
"--nacp=" .. nacp,
|
||||||
|
"--icon=" .. path.join(libnx, "default_icon.jpg")
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
target_end()
|
||||||
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user