使用Math.floor()或.toFixed(2)时增量游戏数值显示异常求助
增量游戏数值显示异常问题解决
问题背景
开发增量游戏时,尝试格式化atoms.amount的显示(无小数或最多两位小数),出现两种异常:
- 使用
Math.floor()处理累加值时,数值一直显示0,切换标签页后随机跳变 - 使用
.toFixed(2)处理时,出现字符串拼接式的异常格式(如00.020.02...)
相关代码:
Pinia Store代码
import { defineStore } from 'pinia' export const useUserStore = defineStore('user', () => { const stats = { timePlayed: 0 } const atoms = { amount: 0, increment: 0.001, max: 10 } return { atoms, stats } })
GameLoop代码
import { useUserStore } from '@/stores/user' const user = useUserStore() let lastTime = null let timeTotal = 0 setInterval(function gameLoop() { const time = performance.now() if (lastTime === null) { lastTime = time } const timeDelta = time - lastTime timeTotal += timeDelta lastTime = time updateGame(timeDelta, timeTotal) }, 1000 / 60) function updateGame(timeDelta, timeTotal) { user.atoms.amount += user.atoms.increment * timeDelta user.stats.timePlayed = Math.floor(timeTotal / 1000) }
错误原因分析
Math.floor()导致数值不更新:
60fps下timeDelta约为16ms,0.001 * 16 = 0.016,Math.floor(0.016)结果为0,每次累加0自然数值一直是0。切换标签页后浏览器会降低定时器执行频率,timeDelta会大幅增加(比如1000ms),此时0.001*1000=1,Math.floor后得到1,所以数值跳变。.toFixed(2)导致字符串拼接:
.toFixed()返回的是字符串类型,而user.atoms.amount初始是数字0,数字和字符串相加会触发字符串拼接,比如:- 第一次累加:
0 + "0.02" = "00.02" - 第二次累加:
"00.02" + "0.02" = "00.020.02"
最终出现异常格式。
- 第一次累加:
解决方案
核心原则:存储时保持数值类型,只在显示阶段做格式化处理,避免破坏数值的计算逻辑。
方案1:显示时格式化(推荐)
保持gameLoop和Store的数值计算逻辑不变,在组件模板中直接格式化显示:
- 无小数显示:
<!-- Vue组件模板中 --> <div>Atoms: {{ Math.floor(user.atoms.amount) }}</div>
- 最多两位小数显示:
<div>Atoms: {{ user.atoms.amount.toFixed(2) }}</div>
或者使用更灵活的国际化格式化:
<div>Atoms: {{ new Intl.NumberFormat('zh-CN', { maximumFractionDigits: 2 }).format(user.atoms.amount) }}</div>
方案2:强制存储时限制小数位数(不推荐,会丢失精度)
如果一定要在累加时限制小数位数,需要把.toFixed()的结果转成数字类型:
function updateGame(timeDelta, timeTotal) { // 转成数字后再累加 user.atoms.amount += Number((user.atoms.increment * timeDelta).toFixed(2)) user.stats.timePlayed = Math.floor(timeTotal / 1000) }
注意:这种方式会丢失部分精度,长期累加可能和实际数值有偏差,仅适合对精度要求极低的场景。
内容的提问来源于stack exchange,提问作者Tristan Lovasz
相关产品推荐
相关产品推荐

