Skip to content

项目架构概览 — 练习

练习 1:绘制帧渲染流程图

根据你对架构的理解,绘制一个游戏帧从浏览器 requestAnimationFrame 触发到画面显示在 Canvas 上的完整流程。

参考答案

关键步骤:

  1. requestAnimationFrame 回调触发 WasmRunFrame()
  2. WASM 模块执行一帧游戏逻辑,写入 VRAM、OAM、调色板等硬件内存
  3. WasmRunFrame() 返回后,app.js 读取 VRAM 数据
  4. 根据当前显示模式(DISPCNT 寄存器),渲染背景层和精灵层
  5. 将 GBA BGR555 颜色转换为 RGBA 格式
  6. 使用 putImageData 将像素数据写入 240×160 Canvas
  7. 浏览器显示渲染结果

练习 2:对比 GBA 和 WASM 构建

阅读 Makefile,回答以下问题:

  1. make(GBA 构建)和 make wasm 的编译器有什么不同?
  2. -DWASM=1 标志在源码中起什么作用?
  3. 为什么 WASM 构建使用 --no-entry--allow-undefined 链接选项?
参考答案
  1. GBA 构建使用 agbcc(或 modern 的 arm-none-eabi-gcc),目标是 ARM7TDMI;WASM 构建使用 clang --target=wasm32-unknown-unknown,目标是 WebAssembly。
  2. -DWASM=1 定义预处理器宏,让源码中 #if WASM / #ifdef WASM 守卫的条件编译代码生效,用于处理 GBA 和 WASM 之间的行为差异。
  3. --no-entry 因为 WASM 模块不需要标准的 main() 入口(使用导出的 AgbMain);--allow-undefined 因为游戏代码引用了 GBA BIOS 系统调用和硬件寄存器等符号,这些在 WASM 中由运行时通过内存映射提供,不需要真实实现。

练习 3:目录结构分析

分析 pokeemerald-wasm 的目录结构:

  1. src/ 目录中有多少 C 源文件?哪些是 WASM 专用的?
  2. include/wasm/ 中的头文件解决了什么问题?
  3. web/ 目录中哪个文件是整个浏览器运行时的核心?
参考答案
  1. src/ 包含 280+ 个 C 源文件,绝大多数来自 pret/pokeemerald。只有 wasm_field_effect_scripts.c 是 WASM 专用文件。
  2. include/wasm/ 提供了 stdio.hstdlib.hstring.h 等标准库头文件的垫片声明,避免 Clang 的内建函数声明与 GBA 工具链的声明冲突。例如重新声明 memcpymemsetsprintf 等函数。
  3. web/app.js 是浏览器端宿主运行时,负责 WASM 加载、输入处理、存档管理、帧调度,以及把 WASM 合成的 RGBA 搬到 Canvas;GBA 硬件模拟与图形渲染本身在 WASM 模块内(src/wasm_display.c),不在 app.js

拓展挑战

  • 阅读 web/app.jsWasmRunFrame 调用逻辑,理解帧同步机制
  • 对比 make wasmmake modern 的编译选项差异
  • 研究 wrangler.toml 配置,理解 Cloudflare Workers 部署架构