大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向:前端 / 跨端 / 小程序 / 移动端工程化
内容平台:掘金、知乎、CSDN、简书
创作特点:实战导向、源码拆解、少空谈多落地
文章状态:长期稳定更新,大量原创输出
我的内容主要围绕前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学“明白”,也用“到位”
持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
- 引言
- 一个反直觉但必须接受的前提
- 第一类:即时交互状态,必须可丢
- 常见错误写法
- 正确做法:输入是“一次性意图”
- 第二类:帧内 / 帧间过渡状态,必须可丢
- 错误示例:试图恢复“中间进度”
- 正确设计方式
- 第三类:与系统资源强绑定的状态,必须可丢
- 高风险代码形态
- 正确模型:资源随时可失效
- 第四类:短期策略性状态,必须可丢
- 容易踩雷的写法
- 正确做法:缓存只是优化,不是依赖
- 那什么状态绝对不能丢?
- 一个简单但好用的判断方法
- 总结
引言
如果你已经接受了前两个结论:
- 有些 Bug 永远改不好
- 有些异常在 HarmonyOS 里是被允许的
那接下来这一关,往往更难迈过去:
你得亲手决定:哪些状态可以直接丢。
这一步,对很多游戏开发者来说是心理门槛。
因为我们太习惯把“状态完整”当成安全感来源了。
一个反直觉但必须接受的前提
在 HarmonyOS 的游戏运行态里:
状态越多,活得越危险。
原因很简单:
- Ability 可能被重建
- 进程可能被回收
- 调度可能被打断
- 输入和焦点可能失效
如果你试图:
把“当前一切”都保存下来
那你很快会发现:
你根本不知道该从哪个时间点恢复才是对的。
第一类:即时交互状态,必须可丢
这一类状态最常见,也最容易被误判。
典型包括:
- 当前按键是否按下
- 摇杆方向
- 连击计数
- 某一帧内的输入缓存
很多人会下意识想:
“这些不保存,体验会不会断?”
但在 HarmonyOS 的模型下:
输入本来就不保证连续。
一旦 Ability 切换、焦点变化、窗口变化:
- 输入流就会被打断
- 当前交互语义自然失效
常见错误写法
letisAttacking=falseonKeyDown(event){if(event.key==='J'){isAttacking=true}}onKeyUp(event){if(event.key==='J'){isAttacking=false}}这段代码隐含了一个危险假设:KeyUp 一定会回来
在 HarmonyOS 下,这个假设是假的。
结果就是你会遇到:
- 角色自己攻击
- 技能卡死
- 线上偶发复现不了的问题
正确做法:输入是“一次性意图”
letinputFrame={attack:false}onKeyDown(event){if(event.key==='J'){inputFrame.attack=true}}functionupdate(){if(inputFrame.attack){player.attack()}// 帧结束,直接丢inputFrame.attack=false}结论很直接:
即时交互状态,不仅可以丢,而且应该丢。
第二类:帧内 / 帧间过渡状态,必须可丢
很多游戏逻辑里都有大量“中间态”:
- 动画播放到一半
- 插值计算进行中
- 状态切换过渡阶段
- 帧同步用的临时变量
这些状态最大的特点是:
- 生命周期极短
- 只在连续帧中有意义
但问题是:
HarmonyOS 不保证帧是连续的。
错误示例:试图恢复“中间进度”
interfacePlayerState{animation:stringprogress:number}state.progress+=delta/duration一旦发生:
- 帧跳过
- Ability 重建
- 渲染延迟
这个progress就变成了无意义数字。
正确设计方式
interfacePlayerStableState{posture:'idle'|'run'|'attack'}functionrestore(state:PlayerStableState){playAnimation(state.posture)}核心原则只有一句:
动画播到哪不重要,播“什么状态”才重要。
所以:
- 插值值
- tween 进度
- 帧计数
全部可丢。
第三类:与系统资源强绑定的状态,必须可丢
这一类状态最危险,也最容易埋雷。
包括:
- 打开的音频通道
- 渲染上下文
- 物理世界实例
- 原生资源句柄
这些状态有一个共同点:
它们不完全归你所有。
高风险代码形态
letaudioPlayer:AudioPlayerfunctioninit(){audioPlayer=createAudioPlayer()}functionresume(){audioPlayer.play()}这段代码的问题在于:
你假设
audioPlayer永远有效。
在 HarmonyOS 下,这是错的。
正确模型:资源随时可失效
letaudioPlayer:AudioPlayer|null=nullfunctionensureAudio(){if(!audioPlayer){audioPlayer=createAudioPlayer()}}functionresume(){ensureAudio()audioPlayer!.play()}functiononBackground(){audioPlayer?.release()audioPlayer=null}结论很明确:
资源状态必须可丢,只能重建。
第四类:短期策略性状态,必须可丢
很多游戏为了“聪明一点”,会引入策略缓存:
- 最近一次 AI 决策
- 动态难度调整参数
- 临时推荐权重
容易踩雷的写法
letlastStrategy:Strategy|null=nullfunctiondecide(enemy){returnlastStrategy??compute(enemy)}这段代码的问题在于:
lastStrategy强依赖上下文- 上下文一旦被系统打断,就失效
正确做法:缓存只是优化,不是依赖
functiondecide(enemy){returncompute(enemy)}或者:
constcache=newMap<string,Strategy>()functiondecide(enemy){returncache.get(enemy.type)??compute(enemy)}并且允许:
cache.clear()策略缓存,随时可以清空。
那什么状态绝对不能丢?
在 HarmonyOS 游戏运行态里,真正值得保留的只有三类:
- 玩家长期进度
- 明确的业务节点(关卡、章节、奖励结算点)
- 可验证的一致性数据
它们的共同特征是:
- 不依赖帧
- 不依赖输入
- 不依赖资源句柄
interfaceSaveData{level:numbergold:numberunlockedSkills:string[]}一个简单但好用的判断方法
每遇到一个状态,问自己三个问题:
- 如果 Ability 被重建,它还有意义吗?
- 如果输入被打断,它还能自洽吗?
- 如果系统延迟 100ms,它会不会变成错误?
只要有一个答案是「否」:
那它就该是可丢弃状态。
总结
HarmonyOS 游戏运行态设计的成熟标志,不是:
状态保存得有多全。
而是:
你清楚知道,哪些东西随时可以放手。
当你开始主动设计“可丢弃状态”时,
很多你以为的顽固 Bug,会自己消失。