Flipper Zero 上的 DnD Dice:桌面角色扮演骰子应用与 FAP 源码全解析
2026/9/15 0:27:30 网站建设 项目流程

Flipper Zero 上的 DnD Dice:桌面角色扮演骰子应用与 FAP 源码全解析

【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper

DnD Dice是一款运行在Flipper Zero上的骰子投掷应用,专为龙与地下城(D&D)等桌面角色扮演游戏的投骰场景设计。本文将以 DnD Dice 官方 README 为骨架,结合其完整源码(dice_app.c、constants.h、application.fam),带你掌握这款应用的操作方式、界面设计、底层状态机与动画实现,并手把手完成从源码到.fap安装包的编译与部署。

一、应用简介:一枚随身携带的虚拟骰袋

实体骰子容易丢失、滚动不可控,在旅途中开团时尤其不便。DnD Dice 把桌面角色扮演中最常用的骰子全部装进 Flipper Zero,只需按几下按键就能完成投掷并自动累加结果。根据 README 与源码中的dice_types[]表(constants.h),应用共内置8 种骰型

骰型面数(type)说明
Coin2硬币正反,用于判定类场景
d44四面体骰
d66六面骰,最常见的标准骰
d88八面体骰
d1010十面骰
d1212十二面体骰
d2020二十面骰,D&D 的核心检定骰
d100100百面骰,常以两个 d10 模拟

每种骰型在源码中都携带type(面数)、坐标x/y与显示名称name,界面上的所有骰子图标都由assets/目录下的像素图提供(如assets/d4_1.pngassets/d20_4.png等,均为 35×35 的 XBM 资源),并配套保留了sources/目录下的.pixil源文件供二次绘制。

二、界面与操作指南

应用采用全屏 GUI(GuiLayerFullscreen)与像素风界面,主界面布局如下:

主界面从上到下依次是:当前骰型图标与名称、左右切换箭头、骰子数量设置、底部 EXIT / OK ROLL 按钮。完整按键操作逻辑对应 dice_app.c 中的按键分发:

  • 左 / 右方向键:在 8 种骰型之间切换。切换时会触发SwipeLeftState/SwipeRightState滑动动画,代码以SWIPE_DIST = 11像素步进、DICE_GAP = 44像素间距让骰子图标整体平移(见 constants.h)。
  • 上 / 下方向键:调整单次投掷的骰子数量,范围1 ~ 10(上限由MAX_DICE_COUNT = 10定义)。特别地,isOneDice()会锁定Coin 与 d100两种骰型的数量恒为 1(constants.h),因为这两者本身就是"单枚"语义。
  • OK 键(中心键):触发投掷,进入动画播放与结果展示。
  • BACK 键:在结果界面按下返回骰子选择;在选择界面按下则退出应用。

投掷完成后的结果界面如下:

结果界面用大字号显示点数和,并在其下方以次级字体列出每一枚骰子的单独结果(如4,4,1,3,2,6),多枚投掷时清晰可读,符合跑团记录习惯。

三、源码结构一览

整个应用只有四个源码/配置文件,结构非常精简,非常适合作为 Flipper Zero 应用开发的入门范本:

文件职责
dice_app.c应用入口、事件循环、状态更新、UI 绘制
constants.h常量、骰型表、动画帧表、状态机定义与工具函数
application.famFAP 应用清单(构建元数据)
LICENSE.mdGPL v3 开源许可证

其中application.fam是 Flipper 构建工具 fbt 识别应用的清单文件,内容如下:

App( appid="DND_Dice_app", name="DnD Dice [Ka3u6y6a]", apptype=FlipperAppType.EXTERNAL, entry_point="dice_dnd_app", cdefines=["APP_DICE"], requires=["gui"], stack_size=1 * 1024, order=90, fap_icon="icon.png", fap_category="Games", fap_icon_assets="assets", )

各字段含义:

  • appid / name:应用唯一标识与显示名;
  • apptype = EXTERNAL:编译为独立.fap插件而非烧入固件;
  • entry_point:入口函数名dice_dnd_app,对应源码中的int32_t dice_dnd_app(void* p)(dice_app.c);
  • requires = ["gui"]:声明依赖 GUI 子系统;
  • stack_size:任务栈 1KB;
  • fap_category = "Games":决定安装到 SD 卡后出现在apps/Games分类下(README 的安装路径正是该目录);
  • fap_icon / fap_icon_assets:应用图标与图标资源目录。

四、核心实现深度解析

4.1 六状态状态机

应用的全部逻辑建立在一个简洁的状态机上,枚举定义在 constants.h:

SelectState → SwipeLeftState / SwipeRightState(骰型滑动切换) → AnimState(投掷动画)→ AnimResultState(结果滑入动画)→ ResultState(结果展示)

每个定时器 tick(见下文 4.4)都会驱动update()(dice_app.c)推进状态:滑动状态逐帧平移骰子直到归位;AnimState播放骰子旋转帧;AnimResultState让结果边框沿result_frame_pos_y[] = {-30, -20, -10, 0}逐帧下落,落到0时进入最终ResultState

4.2 随机数与点数计算

投掷核心逻辑在roll()(dice_app.c):

state->rolled_dices[i] = (rand() % dice_types[state->dice_index].type) + 1; state->roll_result += state->rolled_dices[i];

每一枚骰子的点数通过rand() % 面数 + 1生成(d4 得到 1~4,d20 得到 1~20,Coin 得到 1~2),单枚结果存入rolled_dices[MAX_DICE_COUNT]数组,同时累加到roll_result作为点数总和。结果界面即同时展示总和与逐枚明细。

4.3 双套动画帧系统

动画分为硬币与骰子两套(constants.h):

  • 硬币MAX_COIN_FRAMES = 9帧。正/反面结果由coin_set_start()/coin_set_end()在投掷前动态改写首尾帧——正面结果为"正-正"头尾、反面为"反-反"头尾,从而让动画结束时正确落定在结果面上;
  • 骰子:每种骰型MAX_DICE_FRAMES = 4帧旋转动画,按(骰型序号 - 1) * 4 + 帧号索引到dice_frames[]表中,d4/d6/d8/d10/d12/d20/d100 共 28 帧。

所有帧都是assets/下的 35×35 像素图标(I_d4_1I_d6_3……),由 fbt 构建时自动从fap_icon_assets="assets"生成头文件引用。

4.4 事件驱动架构与渲染

应用遵循 Flipper Zero 标准的事件驱动范式(dice_app.c):

  1. 分配FuriMessageQueue事件队列(容量 8),事件类型分为EventTypeTick(定时器)与EventTypeKey(按键);
  2. 创建ValueMutex保护共享的State,避免绘制与逻辑线程竞争;
  3. 注册ViewPort的绘制回调draw_callback与输入回调input_callback,并以furi_timer_alloc(FuriTimerTypePeriodic, ...)建立0.2 秒周期的动画 tick(furi_kernel_get_tick_frequency() * 0.2);
  4. 主循环furi_message_queue_get()阻塞取事件,超时 100ms;按键事件里按InputTypePress处理方向键、OK 与 BACK;
  5. 每次循环末尾调用view_port_update()触发重绘;
  6. 退出时按序释放定时器、队列、ViewPort 并furi_record_close(RECORD_GUI)

绘制层将 UI 拆成draw_ui(骰名、箭头、数量、按钮)、draw_dice(骰子图标)与draw_results(结果框与文字)三个函数,按状态机决定绘制哪部分——例如ResultState下不再绘制骰子而绘制结果。

五、编译与部署:从源码到 FAP 插件

README 给出的完整编译流程如下(以官方固件为例,其他固件同理):

  1. 准备固件源码:克隆 flipperzero-firmware(FlipperDevices 官方仓库),或你所使用的第三方固件(例如 Unleashed 系固件)源码;

  2. 建立符号链接:在固件仓库的applications_user目录下创建一个名为dice的符号链接,指向本应用的源码目录(即Applications/Official/source-OLDER/grnch/dice2/),例如执行ln -s /path/to/dice2 dice

  3. 执行编译:在固件仓库根目录运行:

    ./fbt fap_dice_dnd_app

    fbt 会根据application.fam中的appid="DND_Dice_app"entry_point="dice_dnd_app"自动定位入口函数并产出插件;

  4. 安装到设备:将产物

    build/f7-firmware-D/.extapps/dice_dnd_app.fap

    复制到 SD 卡的apps/Games目录(与application.famfap_category="Games"对应),或通过qFlipper桌面工具安装到设备。

安装后进入 Flipper Zero 的Applications(应用)菜单即可找到该游戏。需要说明两点适用前提:

  • FAP 与固件的 API 版本强绑定。按照本仓库 Applications/ReadMe.md 的说明,RogueMaster 与 Unleashed 固件之间的 FAP 文件可以互换,但由于 API 差异,它们与官方固件之间不通用——这也是 README 要求"为你的固件分别编译"的根本原因;
  • 如果不想搭建完整固件环境,也可以使用uFBT(micro Flipper Build Tool,FlipperDevices 提供的轻量构建工具)直接编译独立的 FAP,这是仓库 README 中推荐的另一种构建路径。

六、在仓库中的版本脉络

本目录位于 Applications/Official/source-OLDER 下的grnch/dice2/,对应作者 grnch 维护的官方固件兼容版本。仓库中还保留了相关演化痕迹,可供对照学习:

  • grnch/dice/:同一作者的早期单文件版本(dice.c);
  • kyhwana/dice2/:与grnch/dice2同源的镜像版本(kyhwana/dice2/README.md);
  • xMasterX/下的dice/rmdice/flipperzero-yatzee-main/:同主题的骰子/游戏变体实现。

对比阅读这些版本,可以直观看到从单文件到constants.h抽离常量、从简单投掷到完整动画状态机的演进思路。

七、开源许可

本应用以GNU General Public License v3.0(GPL v3)授权发布,完整许可文本见 LICENSE.md。这意味着你可以自由使用、修改与再分发,但衍生作品需以相同许可证开源。

结语

DnD Dice 虽是一个体量极小的 Flipper Zero 应用,却完整覆盖了"骰型配置 → 数量设置 → 随机投掷 → 动画反馈 → 结果展示"的闭环,其六状态状态机、双套动画帧表与事件驱动 GUI 范式,是学习 Flipper Zero FAP 开发的高质量范例。无论你是想用它替代实体骰子,还是想以此为模板写出自己的第一个像素风应用,都可以从本仓库的这份源码直接上手。

【免费下载链接】FlipperPlayground (and dump) of stuff I make or modify for the Flipper Zero项目地址: https://gitcode.com/GitHub_Trending/fl/Flipper

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询