前言在制作游戏中任何图片的移动都会因为FPS设置的问题导致游戏体验度相对弱了很多但是很多时候又不能放开了做毕竟图片大了刷新率在高一些很多手机或PC就无法正常的跑起来这就很难受我今天做了个对比测试用一个60FPS的与低FPS的做了个对比后面我对10FPS的单独用AI做了个优化效果还是有一些的希望能给大家创造一些价值。项目开始我这里纯纯的使用手机端做测试效果。核心 FPS 处理函数与算法逻辑阶段核心逻辑实现算法说明视觉效果1. 物理位置计算this.posX_smooth this.speed * deltaTime;基于真实时间增量DeltaTime计算物体的理论物理位置作为全局参考坐标系。绝对丝滑 (60Hz)2. 低频采样模拟if (now - lastUpdate jitterInterval) { targetPos posX_smooth; }模拟低性能环境下的非连续采样。只有在达到设定的时间间隔如 100ms时才同步物理坐标。视觉断裂、跳变感3. 线性插值 (LERP)lastPos (targetPos - lastPos) * lerpFactor;核心优化算法。在两次采样点之间根据当前帧距离上次采样的时间进度手动计算中间补帧。模拟高帧率的连贯性4. 运动预测补偿posX_optimized this.speed * deltaTime;当插值进度完成但新采样尚未到达时根据物体历史速度进行惯性预测消除微小的停顿感。消除采样延迟抖动无算法自己跑的各数值FPS与60FPS对比Entry Component struct Index { State posX_smooth: number 0; State posX_jitter: number 0; State selectedFps: number 15; State isRunning: boolean true; private screenWidth: number 360; private lastTime: number 0; private jitterLastUpdate: number 0; private speed: number 80; // 降低移动速度从200调至80更方便观察刷新率细节 onPageShow() { this.runLoop(); } runLoop() { let interval 16; // ~60fps 的物理更新频率 setInterval(() { if (!this.isRunning) return; const now Date.now(); if (this.lastTime 0) { this.lastTime now; this.jitterLastUpdate now; } const deltaTime (now - this.lastTime) / 1000; this.lastTime now; // 1. 标准移动逻辑平滑参考每 16ms 计算一次位移 this.posX_smooth this.speed * deltaTime; if (this.posX_smooth 300) { this.posX_smooth -50; // 循环滚动 } // 2. 刷新率问题演示仅在满足用户选择的 FPS 时间间隔时才更新视觉坐标 const jitterInterval 1000 / this.selectedFps; if (now - this.jitterLastUpdate jitterInterval) { // 将“物理位置”同步到“视觉位置”造成跳变感 this.posX_jitter this.posX_smooth; this.jitterLastUpdate now; } }, interval); } build() { Stack() { // 1. 梯度渐变背景 (深邃生命科学风格) Column() .width(100%) .height(100%) .linearGradient({ angle: 135, colors: [[#0F2027, 0], [#203A43, 0.5], [#2C5364, 1]] }) // 2. 内容层 Column({ space: 30 }) { Text(生命体运动刷新率对比) .fontSize(28) .fontWeight(FontWeight.Bold) .fontColor(#FFFFFF) .margin({ top: 60 }) .textShadow({ radius: 10, color: #44FFFFFF, offsetX: 2, offsetY: 2 }) Text(模拟微观粒子在不同采样率下的视觉差异) .fontSize(14) .fontColor(#A0A0A0) // 演示区域 Column({ space: 50 }) { // 顺滑组 VStack({ title: 标准刷新 (60Hz), posX: this.posX_smooth, opacityValue: 1, color: #4CAF50 }) // 模拟组 VStack({ title: 模拟刷新 (${this.selectedFps}Hz), posX: this.posX_jitter, opacityValue: 0.8, color: this.selectedFps 30 ? #F44336 : #FFEB3B }) } .width(90%) .padding(20) .backgroundColor(#1AFFFFFF) .borderRadius(20) .border({ width: 1, color: #33FFFFFF }) // 玻璃拟态效果 .backgroundBlurStyle(BlurStyle.Thin) // 控制区 Column({ space: 20 }) { Text(调节模拟刷新率) .fontColor(#FFFFFF) .fontSize(16) Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center, alignContent: FlexAlign.Center }) { ForEach([5, 10, 15, 24, 30, 60], (fps: number) { Button(${fps} FPS) .onClick(() { this.selectedFps fps; }) .backgroundColor(this.selectedFps fps ? #007DFF : #33FFFFFF) .fontColor(#FFFFFF) .borderRadius(10) .width(40%) .margin(5) }) } .width(100%) } .width(90%) .padding(20) .backgroundColor(#0D000000) .borderRadius(20) } .width(100%) .height(100%) } } } Component struct VStack { Prop title: string ; Prop posX: number 0; Prop opacityValue: number 1; Prop color: string #FFFFFF; build() { Column() { Row() { Circle({ width: 8, height: 8 }).fill(this.color).margin({ right: 8 }) Text(this.title) .fontColor(#E0E0E0) .fontSize(14) } .width(100%) .margin({ bottom: 15 }) // 运动轨道 Stack({ alignContent: Alignment.Start }) { // 轨道线 Rect() .width(100%) .height(2) .fill(#22FFFFFF) // 粒子 (demo.png) Image($r(app.media.demo)) .width(60) .height(60) .offset({ x: this.posX, y: 0 }) .shadow({ radius: 20, color: this.color }) .interpolation(ImageInterpolation.High) } .width(100%) .height(80) .opacity(this.opacityValue) } } }优化算法效果算法核心函数抽离/** * 刷新率优化核心循环 (每 16ms 执行一次) * param now 当前系统时间 * param deltaTime 距离上一帧的时间间隔 */functionupdateFrameOptimized(now:number,deltaTime:number){// A. 计算物理参考位置 (标准速度同步)this.posX_smooththis.speed*deltaTime;// B. 模拟低频采样 (例如 10FPS)constjitterInterval1000/this.selectedFps;if(now-this.jitterLastUpdatejitterInterval){this.lastJitterPosthis.posX_jitter;// 记录起点this.targetJitterPosthis.posX_smooth;// 设定终点this.posX_jitterthis.posX_smooth;// 更新采样点this.jitterLastUpdatenow;this.lerpFactor0;// 重置插值进度}// C. 插值算法优化在 10FPS 的采样缝隙中“凭空”创造流畅度if(this.lerpFactor1){// 计算插值进度在采样周期内完成 0.0 到 1.0 的平滑过渡this.lerpFactordeltaTime*this.selectedFps;if(this.lerpFactor1)this.lerpFactor1;// 执行线性插值 (Linear Interpolation)this.posX_optimizedthis.lastJitterPos(this.targetJitterPos-this.lastJitterPos)*this.lerpFactor;}else{// 预测移动在等待下一次采样时按照速度惯性继续微移防止“原地踏步”this.posX_optimizedthis.speed*deltaTime;}}Entry Component struct Index { State posX_smooth: number 0; State posX_jitter: number 0; State posX_optimized: number 0; // 算法优化后的位置 State selectedFps: number 10; State isRunning: boolean true; private screenWidth: number 360; private lastTime: number 0; private jitterLastUpdate: number 0; private speed: number 80; // 算法插值相关变量 private lastJitterPos: number 0; private targetJitterPos: number 0; private lerpFactor: number 0; onPageShow() { this.runLoop(); } runLoop() { let interval 16; setInterval(() { if (!this.isRunning) return; const now Date.now(); if (this.lastTime 0) { this.lastTime now; this.jitterLastUpdate now; } const deltaTime (now - this.lastTime) / 1000; this.lastTime now; // 1. 标准移动 (参考系) this.posX_smooth this.speed * deltaTime; if (this.posX_smooth 300) { this.posX_smooth -50; } // 2. 10FPS 采样逻辑 const jitterInterval 1000 / this.selectedFps; if (now - this.jitterLastUpdate jitterInterval) { this.lastJitterPos this.posX_jitter; this.targetJitterPos this.posX_smooth; this.posX_jitter this.posX_smooth; this.jitterLastUpdate now; this.lerpFactor 0; // 重置插值进度 } // 3. 算法优化线性插值 (LERP) 预测 // 虽然采样只有 10FPS但我们在每帧 (16ms) 进行平滑补偿 if (this.lerpFactor 1) { this.lerpFactor deltaTime * (this.selectedFps); // 在采样周期内完成移动 if (this.lerpFactor 1) this.lerpFactor 1; // 核心算法当前视觉位置 上一次采样点 (目标采样点 - 上一次采样点) * 进度 this.posX_optimized this.lastJitterPos (this.targetJitterPos - this.lastJitterPos) * this.lerpFactor; } else { // 预测移动在等待下一次采样时按照速度惯性继续微移 this.posX_optimized this.speed * deltaTime; } }, interval); } build() { Stack() { // 1. 梯度渐变背景 Column() .width(100%) .height(100%) .linearGradient({ angle: 135, colors: [[#0F2027, 0], [#203A43, 0.5], [#2C5364, 1]] }) // 2. 内容层 Column({ space: 20 }) { Text(AI 插值算法优化演示) .fontSize(28) .fontWeight(FontWeight.Bold) .fontColor(#FFFFFF) .margin({ top: 40 }) Text(在 10FPS 采样下实现 60FPS 的丝滑度) .fontSize(14) .fontColor(#00E5FF) // 演示区域 Column({ space: 30 }) { // 顺滑组 (60Hz) VStack({ title: 原生 60Hz 采样 (丝滑), posX: this.posX_smooth, opacityValue: 1, color: #4CAF50 }) // 优化组 (10Hz 算法) VStack({ title: 10Hz 采样 线性插值算法 (模拟丝滑), posX: this.posX_optimized, opacityValue: 1, color: #007DFF }) // 原始低帧率组 (10Hz) VStack({ title: 原生 10Hz 采样 (肉眼可见卡顿), posX: this.posX_jitter, opacityValue: 0.6, color: #F44336 }) } .width(95%) .padding(15) .backgroundColor(#1AFFFFFF) .borderRadius(20) .backgroundBlurStyle(BlurStyle.Thin) // 控制区 Column({ space: 15 }) { Text(调节基础采样率) .fontColor(#FFFFFF) .fontSize(16) Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center }) { ForEach([5, 10, 24, 60], (fps: number) { Button(${fps} FPS) .onClick(() { this.selectedFps fps; }) .backgroundColor(this.selectedFps fps ? #007DFF : #33FFFFFF) .fontColor(#FFFFFF) .borderRadius(10) .width(20%) .margin(5) }) } .width(100%) } .width(95%) .padding(15) .backgroundColor(#0D000000) .borderRadius(20) } .width(100%) .height(100%) } } } Component struct VStack { Prop title: string ; Prop posX: number 0; Prop opacityValue: number 1; Prop color: string #FFFFFF; build() { Column() { Row() { Circle({ width: 8, height: 8 }).fill(this.color).margin({ right: 8 }) Text(this.title) .fontColor(#E0E0E0) .fontSize(14) } .width(100%) .margin({ bottom: 15 }) // 运动轨道 Stack({ alignContent: Alignment.Start }) { // 轨道线 Rect() .width(100%) .height(2) .fill(#22FFFFFF) // 粒子 (demo.png) Image($r(app.media.demo)) .width(60) .height(60) .offset({ x: this.posX, y: 0 }) .shadow({ radius: 20, color: this.color }) .interpolation(ImageInterpolation.High) } .width(100%) .height(80) .opacity(this.opacityValue) } } }效果总结原生 10FPS每秒刷新 10 次物体移动呈【瞬移】状态容易引起视觉疲劳。10FPS LERP 优化虽然有效数据只有 10 次但屏幕每秒依然渲染 60 次中间的 50 次渲染由算法根据轨迹预测生成。视觉上几乎等同于 60FPS 的流畅度。升级优化效果5~120FPS全优化这套 Demo 实现了一套模拟现代游戏引擎中【客户端预测与平滑】(Client-side Prediction Interpolation)的核心算法。核心解释无论屏幕刷新率是多少物体的真实物理位置posX_smooth始终基于真实时间增量DeltaTime进行线性累加。人为地“掐断”物理层的实时反馈。只有当到达设定的采样时间点如 10FPS即每 100ms时才会从物理层“偷”一个坐标给它。公式当前位置 上一帧位置 (移动速度 * 帧间隔时间)。预算层的处理动态速度估算算法不依赖于代码中写死的 speed而是通过计算最近两次采样点currentSamplePos - lastSamplePos的位移差除以采样时间间隔计算出物体的体感速度estimatedVelocity。外推预测 (Extrapolation)在等待下一次采样的过程中算法会启动一个本地计时器renderTime。它假设物体会按照刚才计算出的体感速度继续运动。计算公式预测位置 最后一次采样坐标 (体感速度 * 距离上次采样的时间)。Entry Component struct Index { State posX_smooth: number 0; State posX_jitter: number 0; State posX_optimized: number 0; State selectedFps: number 10; State isRunning: boolean true; // 核心增强变量 private lastTime: number 0; private jitterLastUpdate: number 0; private speed: number 80; // 运动状态捕捉 private lastSamplePos: number 0; private currentSamplePos: number 0; private estimatedVelocity: number 0; // 动态速度估算 private renderTime: number 0; // 自定义渲染时钟 onPageShow() { this.runLoop(); } runLoop() { let interval 8; // 将物理更新频率提升至 ~120fps (1000ms / 120 ≈ 8ms) setInterval(() { if (!this.isRunning) return; const now Date.now(); if (this.lastTime 0) { this.lastTime now; this.jitterLastUpdate now; } const deltaTime (now - this.lastTime) / 1000; this.lastTime now; this.renderTime deltaTime; // 1. 物理引擎参考 (物理层) this.posX_smooth this.speed * deltaTime; if (this.posX_smooth 320) this.posX_smooth -40; // 2. 动态采样逻辑 (传输层) const jitterInterval 1000 / this.selectedFps; if (now - this.jitterLastUpdate jitterInterval) { const intervalSec (now - this.jitterLastUpdate) / 1000; this.lastSamplePos this.currentSamplePos; this.currentSamplePos this.posX_smooth; // 核心升级动态计算采样间的瞬时速度用于更精准的预测 if (intervalSec 0) { this.estimatedVelocity (this.currentSamplePos - this.lastSamplePos) / intervalSec; } this.posX_jitter this.currentSamplePos; this.jitterLastUpdate now; this.renderTime 0; // 重置渲染本地时钟同步采样点 } // 3. 全局通用优化算法 (表现层 - 状态预测渲染) // 使用 Hermite 插值或简单的二次预测这里采用更稳定的速度增量预测法 // 这种算法不依赖于特定的 FPS它会自动适应任何采样频率 let predictionOffset this.estimatedVelocity * this.renderTime; // 限制预测范围防止在极低 FPS 或剧烈加减速时产生飞出去的幻觉 (Error Correction) let rawOptimized this.currentSamplePos predictionOffset; // 软同步让优化后的位置向物理位置进行平滑靠拢 this.posX_optimized rawOptimized * 0.8 this.posX_smooth * 0.2; }, interval); } build() { Stack() { // 1. 梯度渐变背景 Column() .width(100%) .height(100%) .linearGradient({ angle: 135, colors: [[#0F2027, 0], [#203A43, 0.5], [#2C5364, 1]] }) // 2. 内容层 Column({ space: 20 }) { Text(AI 插值算法优化演示) .fontSize(28) .fontWeight(FontWeight.Bold) .fontColor(#FFFFFF) .margin({ top: 40 }) Text(通用状态预测与误差纠正算法 (支持至 120Hz)) .fontSize(14) .fontColor(#00E5FF) // 演示区域 Column({ space: 30 }) { // 顺滑组 (120Hz) VStack({ title: 原生 120Hz 采样 (极度丝滑), posX: this.posX_smooth, opacityValue: 1, color: #4CAF50 }) // 优化组 (通用预测算法) VStack({ title: 全帧率通用预测算法 (当前采样: ${this.selectedFps}Hz), posX: this.posX_optimized, opacityValue: 1, color: #007DFF }) // 原始低帧率组 VStack({ title: 原生 ${this.selectedFps}Hz 采样 (未优化), posX: this.posX_jitter, opacityValue: 0.6, color: #F44336 }) } .width(95%) .padding(15) .backgroundColor(#1AFFFFFF) .borderRadius(20) .backgroundBlurStyle(BlurStyle.Thin) // 控制区 Column({ space: 15 }) { Text(调节基础采样率) .fontColor(#FFFFFF) .fontSize(16) Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center }) { ForEach([5, 15, 30, 60, 90, 120], (fps: number) { Button(${fps} FPS) .onClick(() { this.selectedFps fps; }) .backgroundColor(this.selectedFps fps ? #007DFF : #33FFFFFF) .fontColor(#FFFFFF) .borderRadius(10) .width(25%) .margin(5) }) } .width(100%) } .width(95%) .padding(15) .backgroundColor(#0D000000) .borderRadius(20) } .width(100%) .height(100%) } } } Component struct VStack { Prop title: string ; Prop posX: number 0; Prop opacityValue: number 1; Prop color: string #FFFFFF; build() { Column() { Row() { Circle({ width: 8, height: 8 }).fill(this.color).margin({ right: 8 }) Text(this.title) .fontColor(#E0E0E0) .fontSize(14) } .width(100%) .margin({ bottom: 15 }) // 运动轨道 Stack({ alignContent: Alignment.Start }) { // 轨道线 Rect() .width(100%) .height(2) .fill(#22FFFFFF) // 粒子 (demo.png) Image($r(app.media.demo)) .width(60) .height(60) .offset({ x: this.posX, y: 0 }) .shadow({ radius: 20, color: this.color }) .interpolation(ImageInterpolation.High) } .width(100%) .height(80) .opacity(this.opacityValue) } } }所有代码均已贡献希望能对大家有些帮助。