为什么这么分层#
移植平台最容易变成"一锅粥":UI、引擎、平台 API 全搅在一起。Bocchi House 从一开始就按依赖方向严格分了四层:
products (启动器)
│ startAbility()
▼
game_sources (游戏) ←── 调用 ──► features/adapters (图形/音频/输入)
│ │ 调用
├── 生命周期事件 │
▼ ▼
features/game_framework common (平台封装)
│ 转发事件 │ 封装
▼ ▼
features/adapters HarmonyOS / Vulkan核心原则:上层依赖下层,下层不感知上层。游戏代码永远不直接碰 VkInstance,UI 代码永远不直接调 vkQueueSubmit。
各层职责#
| 层 | 模块 | 职责 |
|---|---|---|
| 启动器 | products/laptop、products/mobile | 游戏列表 + startAbility() 启动游戏 |
| 游戏 | game_sources/paladin、game_sources/unholy_heights | 游戏核心逻辑 |
| 生命周期桥接 | features/game_framework | BHFrameworkCore 单例:Ability 生命周期 → 各 adapter;App 级窗口控制 |
| 游戏框架 | features/game_framework | Game(XNA 风格)、ScreenManager、GameScreen、GameTime |
| 适配层 | features/graphics_adapter | VulkanRenderer 编排器、SpriteBatch 2D 渲染、TextureManager、SpriteFont |
| 适配层 | features/audio_adapter、input_adapter | 音频 / 输入接口(框架完成,待实现) |
| 平台封装 | common/platform_abstraction | Vulkan RAII(12 类)、BHWindowManager、NodeContentHandleTool、bh_log |
生命周期事件流#
鸿蒙的入口是 ArkTS Ability,游戏是 C++。中间的桥接是 BHFrameworkCore:
ArkTS Ability 生命周期
│
▼
game_framework (BHFrameworkCore)
│ OnCreate(rm) / OnDestroy() / OnBackground() / OnForeground()
│
└──→ Game::Run()
├─ Game::LoadContent() ← 加载纹理/音频
├─ [帧循环] ← 驱动 ScreenManager
│ ├─ InputState::BeginFrame()
│ ├─ Game::Update(time)
│ │ └─ ScreenManager::Update(time) + HandleInput(input)
│ ├─ Game::Draw(time)
│ │ └─ ScreenManager::Draw(time)
│ │ └─ SpriteBatch + TextureManager
│ └─ InputState::EndFrame()
└─ Game::UnloadContent() ← 清理资源这个结构几乎是 XNA/MonoGame 的经典形状——因为移植的目标游戏(Unholy Heights 等)本来就是 XNA 架构,游戏开发者面对的是熟悉的 Update/Draw 循环。
NAPI 接口设计#
ArkTS 与 C++ 之间通过 NAPI 桥接,做了静态方法与实例方法分离:
NapiBridge:静态方法(模块级),如模块注册、初始化BHFrameworkCore:实例方法(能力级),如create / destroy / onBackground / onForeground、窗口控制(9 个)、帧生成(2 个)
目前注册了 15+ 个 NAPI 接口,全部通过 bridge/ 目录转发到 C++ 侧实现,保持接口面清晰。
数据流示例:原生渲染表面#
图形适配器创建渲染表面的链路(已经跑通):
ArkTS GameScreen
→ NAPI: gfx.createNativeNode(nodeContent, tag)
→ PluginManager::CreateNativeNode()
→ Core::GenerateXComponentBasedSurfaceHolder()
→ CreateNodeHandleUsingSurfaceHolder()
→ XComponent + SurfaceHolder + SurfaceCallback
→ OHNativeWindow* 存入 Core::surfaces_
→ Surface 就绪后自动触发:
→ OnSurfaceCreated → 记录 OHNativeWindow*
→ OnSurfaceChanged → 自动初始化 Vulkan
→ NAPI: gfx.initRenderer(tag)
→ VulkanRenderer::Init(window)
→ Instance → Surface → Device → Swapchain → RenderPass → Command → Sync
→ Shader → Pipeline → Buffer
→ NAPI: gfx.testDraw()
→ DrawTestTriangle() → 红色三角形上屏从 createNativeNode 到红色三角形上屏,这条链路是项目第一个完整跑通的"里程碑时刻"。