【OpenHarmony/HarmonyOS】ArkUI 游戏结算弹窗:统计展示、结果分支与操作闭环
【OpenHarmony/HarmonyOS】ArkUI 游戏结算弹窗统计展示、结果分支与操作闭环结算弹窗不是在画面上放一句“胜利”就结束了。它位于游戏会话、统计快照、排行榜写入、资产刷新和下一步操作的交叉点显示早了数据还没同步显示晚了玩家以为游戏卡住按钮连续点击可能启动两局遮罩没有拦截触摸玩家还能在弹窗下开火。本文通过一个真实 ArkUIGameOverDialog拆解结算组件应该如何划分职责、传递数据并处理边界。一、结算流程先于结算 UI项目的完整结束链路由 GameEngine、Index 页面和弹窗组件共同完成sequenceDiagram participant EngineasGameEngine participant PageasIndex 页面 participant StoreasManager participant DialogasGameOverDialog Engine-Engine: 锁定一次性结算 Engine-Engine: 设置 game_over Engine-Page: onGameEnd(result) Page-Page: 复制统计快照 Page-Store: 保存排行榜/用户统计 Page-Page: isGameOvertruePage-Dialog: result stats callbacks Dialog--Page: onRestart 或 onExit层次应负责的内容不应负责的内容GameEngine判定胜负、停止世界规则、产出结果构建 ArkUI 弹窗Index 页面同步快照、保存统计、决定是否展示逐个绘制结算指标GameOverDialog展示结果、展示只读统计、发出用户意图直接重置引擎或写 Preferences当前实现基本遵守了这个边界弹窗没有导入 GameEngine 或 Manager只接收输入并调用回调因此可在不同模式中复用。二、组件 API结果、统计和两个命令核心接口非常小Componentexportstruct GameOverDialog { Prop result:win|lose|p2_winlose; Prop stats: GameStats newGameStats(); onRestart:()void(){}; onExit:()void(){}; }Prop表示数据由父组件传入弹窗只读取不拥有权威状态。默认值使组件即使在预览或调用遗漏时也能构建不会因undefined直接崩溃。两个函数属性把“用户点击了什么”传回父页面弹窗不需要知道重开的具体时序。当前result支持p2_win而 Index 页的gameResult类型只有win | loseGameEngine 回调也按本机玩家视角产出胜负。因此p2_win更像预留分支在当前主调用链中不可达。类型设计应以领域语义为准若多人模式需要展示具体获胜队伍可以使用winnerTeam: A | B | null而不是继续增加p2_win、p3_win。三、父页面如何提供一次统计快照引擎回调到达后页面新建GameStats并复制字段this.gameEngine.onGameEnd (result) {this.gameResult result;this.gameStats new GameStats();this.gameStats.targetsDestroyed this.gameEngine!.gameStats.targetsDestroyed;this.gameStats.survivalTime this.gameEngine!.gameStats.survivalTime;this.gameStats.score this.gameEngine!.gameStats.score;this.isGameOver true;this.isGameRunning false; };新建对象而不是直接把引擎对象交给 UI有“冻结快照”的意图结算显示不再随着引擎内部变化。但手工逐字段复制容易漏项项目当前没有复制coinsCollected和部分其他字段。虽然当前弹窗网格只展示击毁数、分数和时间个人资料里的其他结算视图可能因此看到默认值。更稳定的做法是让引擎产出明确 DTOinterface GameResultSnapshot { result:win|lose; score: number; targetsDestroyed: number; survivalTimeSeconds: number; coinsCollected: number; mode: GameModeType; wave: number; }这是演进设计。DTO 一旦建立弹窗、排行榜和用户统计都读取同一份不可变结果避免三套字段复制规则。四、结果标题和颜色必须来自同一判断弹窗通过方法映射标题资源getResultTitle(): ResourceStr {if(this.result win) {return$r(app.string.result_victory); }if(this.result p2_win) {return$r(app.string.result_p2_win); }return$r(app.string.result_defeat); }标题颜色则把win和p2_win都视为绿色否则使用红色。当前两处判断语义一致但写了两遍。模式变多后可能出现标题写成胜利、颜色仍走失败分支。可以先计算视图模型interfaceResultPresentation{ title: ResourceStr; accentColor: string; glowColor: string; }privategetPresentation(): ResultPresentation {constpositive this.result win||this.result p2_win;return{ title:this.getResultTitle(), accentColor: positive ?#69F0AE:#FF5252, glowColor: positive ?rgba(105, 240, 174, 0.5):rgba(255, 82, 82, 0.5)}; }这段属于重构方向不是当前代码。集中映射能让视觉与语义共同变化。五、全屏遮罩的第一职责是阻断下层交互弹窗最外层使用全屏 Stack先绘制半透明矩形Stack() { Rect() .width(100%) .height(100%) .fill(rgba(0, 0, 0, 0.7)) .onClick(() {});Column() { // 标题、统计与按钮 } } .width(100%) .height(100%) .alignContent(Alignment.Center)空的onClick并不是多余代码它让遮罩成为命中目标防止触摸穿透到下方 Canvas、摇杆或开火按钮。页面还通过!isGameOver隐藏底部操作区形成第二层保护。遮罩是否允许点击空白关闭需要谨慎。普通确认框可以游戏结算不适合因为关闭后应该进入主页还是继续停在冻结世界并不明确。当前只允许“退出”和“再来一局”状态闭环更清楚。六、统计网格稳定尺寸比动态卡片更重要弹窗使用三列 Grid 展示击毁、总分和生存时间Grid(){GridItem(){ this.StatCell(labelKills,killsText)}GridItem(){ this.StatCell(labelScore,scoreText)}GridItem(){ this.StatCell(labelTime,timeText)} } .columnsTemplate(1fr 1fr 1fr).width(100%) .height(60)指标来源格式化击毁数stats.targetsDestroyed整数文本总分stats.score整数文本生存时间stats.survivalTimem:ss固定三列能让数字变化时布局不跳动。统计值较大时应考虑最大宽度、缩写或动态字体但不能让一个超长数字把按钮挤出弹窗。当前第一个标签使用的资源名是stat_score_title显示的值却是targetsDestroyed。资源内容需要结合 string.json 再确认命名本身已经不够清晰。推荐为每个指标建立明确资源键避免“标题资源复用后语义漂移”。七、时间格式化的边界组件内部格式化秒数privateformatTime(seconds:number):string{constminutes Math.floor(seconds /60);constremain seconds %60;return${minutes}:${remain.toString().padStart(2,0)}; }对正常非负整数这会得到0:05、12:34。但输入若为小数、负数或NaN输出会变得不可预测。引擎和页面都可能计算时间因此格式化边界最好统一functionformatDuration(rawSeconds:number):string{consttotal Number.isFinite(rawSeconds) ?Math.max(0,Math.floor(rawSeconds)) :0;constminutes Math.floor(total /60);constseconds total %60;return${minutes}:${seconds.toString().padStart(2,0)}; }统一工具还能避免 HUD 使用00:05、结算使用0:05的格式差异。八、按钮只发出意图不承担业务按钮代码保持简单Button($r(app.string.btn_exit)) .onClick(() { this.onExit(); });Button($r(app.string.btn_play_again)) .onClick(() { this.onRestart(); });父组件决定实际行为GameOverDialog({ result:this.gameResult, stats:this.gameStats, onRestart: () this.restartGame(), onExit: () this.stopGame() });这使弹窗可以被预览、测试也不依赖路由。点击再来一局由页面关闭弹窗、停止旧循环、重置初始化状态并启动新局退出则处理未结算资源、停止循环并回主页。当前按钮没有提交中状态。快速连续点击可能多次触发restartGame()排入多个定时器。可以由父页面在第一次点击后进入restarting或exiting状态并把disabled传给弹窗。九、显示弹窗前还发生了哪些副作用页面在onGameEnd中还保存排行榜与累计用户统计if(score 0) { ScoreManager.getInstance().saveScore({ date: Date.now(), score, mode:this.gameMode multiplayer?pvp:pve, playerName:this.userName, avatar:this.userAvatar }); } UserManager.getInstance().updateUserStats( result win?win:lose, score,this.gameStats.targetsDestroyed,this.gameStats.survivalTime );这些方法返回异步结果但回调没有等待弹窗立即出现。对于本地单机体验这能减少等待如果保存失败玩家却没有看到任何提示。可以把结束过程拆成两个阶段先冻结并展示结果后台保存按钮可立即操作但页面记录失败并稍后重试。若排行榜是云端权威则应显示独立的“成绩同步中/同步失败”状态不要阻塞整个结算弹窗。十、尺寸与响应式问题 内容容器当前固定宽度 360Column({ space:15}){// ...}.width(360).padding(24).backgroundColor(rgba(30, 30, 40, 0.6)).backdropBlur(40).borderRadius(24)横屏手机通常足够但在半折叠区域、分屏或高字体缩放下360vp 加左右安全区可能过宽。更稳妥的是width(90%)加constraintSize({ maxWidth: 360 })让窄屏收缩、宽屏不无限拉长。固定 60 高的统计 Grid 也要测试多语言。日文、韩文或系统大字体可能让标签换行与数值重叠。可以给标签两行空间、使用最小高度并允许内容增长。十一、可访问性与反馈胜负不能只靠绿/红颜色表达当前标题文本已经提供语义这是正确方向。还可以补充为结果标题设置适合辅助功能读取的描述弹窗出现后将焦点移到结果标题或主操作保证按钮触控区域不少于系统建议尺寸高对比度下仍能识别半透明边框“退出”和“再来一局”的焦点顺序符合视觉顺序减弱动画设置开启时不强制复杂缩放或闪烁。触感反馈应该由结束事件触发而不是弹窗每次重新构建都触发否则状态刷新会重复振动。当前音频与振动位于 GameEngine 结算链路方向正确。十二、错误与重复结算边界 ⚠️结算弹窗自身不负责防重复真正的闩锁在引擎isGameOverSequenceStarted。这是正确位置因为在 UI 出现之前就要阻止重复加币和重复回调。但仍要注意onGameEnd可能因旧会话延时回调在新局中到达页面离开后回调仍引用页面状态快速双击按钮会重复安排重开多人网络结果与本地死亡可能竞争手工复制统计可能形成不完整快照。可使用会话 ID 验证回调归属并让页面阶段从settling只能迁移一次到game_over。十三、建议测试清单 场景预期resultwin胜利资源标题与正向颜色resultlose失败标题与警示颜色resultp2_win当前预留分支能正确展示空 stats所有指标显示安全默认值生存 5 秒显示0:05生存时间异常钳制后不出现 NaN/负号点击遮罩不关闭不穿透到底层连续点击重开父状态只接收一次有效命令保存排行榜失败弹窗仍可操作失败可观测窄屏与大字体文本、网格和按钮不重叠中日韩资源标签不会溢出固定区域旧会话回调不能覆盖当前新局结果组件测试可以直接传入不同GameStats和回调计数器不需要启动完整游戏引擎端到端测试再覆盖引擎锁、数据保存和页面迁移。十四、总结 ✨一个好的结算弹窗应该“展示事实、发出意图”而不是成为第二个游戏控制器。当前GameOverDialog的优点是 API 小、输入单向、回调清楚、遮罩能阻断下层交互、统计布局稳定父页面负责快照、排行榜和重开退出职责边界基本合理。真实改进点同样明确结果类型存在不可达预留值统计快照手工复制会漏字段固定宽度需适配窄屏和大字体按钮需要防连点格式化函数需要处理异常输入异步保存应有可观测状态。把这些细节补齐结算弹窗才不只是“好看的浮层”而是一次游戏会话可靠结束和下一次行动自然开始的连接器。推荐标签OpenHarmonyHarmonyOSArkTSArkUI游戏开发组件设计状态管理响应式布局

相关新闻

DeepSeek    LeetCode 3710. 最大划分因子 Java实现

DeepSeek LeetCode 3710. 最大划分因子 Java实现

这道题的核心解法是 二分答案 二分图判定。 核心思路:要最大化“划分因子”,可以将其转化为判定问题:对于某个距离 d,能否将所有点分成两组,使得同一组内任意两点的曼哈顿距离都 ≥ d。Java 代码实现java import java…

2026/7/25 1:38:10 阅读更多 →
电商AI作图技术选型:五种方案的技术架构与适用场景分析——为什么栖影AI是电商场景的最优解

电商AI作图技术选型:五种方案的技术架构与适用场景分析——为什么栖影AI是电商场景的最优解

摘要 本文从技术架构角度,对电商AI作图的五种主流方案进行横向对比分析,帮助技术背景的电商从业者做出合理选型决策。结论很明确:对于以批量出图、降本增效为核心目标的电商卖家,栖影AI所代表的电商垂直AI平台是当前最具性价比的选…

2026/7/25 1:38:10 阅读更多 →
vivado使用那些事之优化时序利用implemention实现策略

vivado使用那些事之优化时序利用implemention实现策略

Vivado Implementation 参数 一、怎么打开GUI设置界面 TOOLS -> settings再点击左边的Implemention这是 Xilinx Vivado 设计套件中 Implementation(实现) 阶段的参数设置界面。实现阶段是将综合后的网表映射到具体 FPGA 器件资源、进行布局布线并最终…

2026/7/25 1:38:10 阅读更多 →

最新新闻

魔搭社区:AI开发者的开源协作平台与实战应用

魔搭社区:AI开发者的开源协作平台与实战应用

1. 魔搭社区:AI开发者的开源协作平台第一次听说"魔搭"这个名字时,我还以为是个游戏模组网站。直到真正用起来才发现,这可能是国内AI工程师最该收藏的开发者社区之一。作为一个长期在算法部署一线踩坑的老兵,我见证过太多…

2026/7/25 1:46:13 阅读更多 →
llama.cpp多模态实践:本地视频与音频输入的端到端推理指南

llama.cpp多模态实践:本地视频与音频输入的端到端推理指南

上周在调试一个多模态项目时,我发现了一个被很多人忽略的事实:当我们讨论大模型的多模态能力时,往往只关注最新的云端API,却忽略了一个已经在本地运行了相当长时间的技术方案——llama.cpp的视频和音频输入支持。 这个发现源于一…

2026/7/25 1:46:12 阅读更多 →
AI工程化实践:从代码生成到智能运维的IT项目落地指南

AI工程化实践:从代码生成到智能运维的IT项目落地指南

在 IT 行业招聘整体收缩 3% 的背景下,AI 相关岗位需求却逆势增长 16%,这一现象背后反映的是企业技术栈和人才结构的深层调整。AI 不再只是实验室里的概念验证,而是已经进入工程化落地阶段,直接影响企业的开发效率、运维成本和业务创新能力。对于一线开发者和技术管理者来说…

2026/7/25 1:46:12 阅读更多 →
SQL Server CPU飙升90%:从性能断崖到根因排查的完整实战指南

SQL Server CPU飙升90%:从性能断崖到根因排查的完整实战指南

昨天跑 50 毫秒的 SQL,今天突然飙到 5 秒,数据库 CPU 直接冲到 90%。这不是一个假设性问题,而是很多 DBA 和开发者在生产环境里真实踩过的坑。问题出现时,业务方在催,监控在报警,而你手头只有一堆零散的线索…

2026/7/25 1:46:12 阅读更多 →
【2027最新】基于SpringBoot+Vue的智慧草莓基地管理系统管理系统源码+MyBatis+MySQL

【2027最新】基于SpringBoot+Vue的智慧草莓基地管理系统管理系统源码+MyBatis+MySQL

博主介绍:🎓 计算机科学与技术专业在读研究生 | CSDN博客专家 | Java技术爱好者 在校期间积极参与实验室项目研发,现为CSDN特邀作者、掘金优质创作者。专注于Java开发、Spring Boot框架、前后端分离技术及常见毕设项目实现。 📊 数…

2026/7/25 1:46:12 阅读更多 →
阿里开源前端AI代理Page Agent:零后端依赖,用自然语言控制网页

阿里开源前端AI代理Page Agent:零后端依赖,用自然语言控制网页

这次我们来看一个阿里开源的前端 AI 代理项目 Page Agent。它不是一个需要本地部署、消耗显存的 AI 模型,而是一个纯前端的 JavaScript 库,核心目标是让你能用自然语言直接控制网页界面。简单来说,你可以在自己的网页里嵌入一段脚本,用户就能通过聊天框或语音指令,让 AI 自…

2026/7/25 1:45:12 阅读更多 →

日新闻

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:00:35 阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:35 阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:35 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻