Android SurfaceView同速设置背景图实际移动速率不一致问题
Android双背景同速参数下移动不同步问题
问题现象
Android游戏开发中,为两张滚动背景设置了相同的移动速度参数,但实际运行时两张图移动速率不一致、出现错位。
- 涉及代码模块:
GameView(负责背景渲染、移动逻辑)、Background(背景实体类) - 问题效果参考:问题演示截图
- 实现参考教程:无限滚动背景实现教程
问题相关原始代码
GameView类原始实现
package com.example.warren; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.view.MotionEvent; import android.view.SurfaceView; import java.util.ArrayList; import java.util.List; import java.util.Random; public class GameView extends SurfaceView implements Runnable { private Thread thread; private boolean isPlaying; private int screenX, screenY; public static float screenRatioX, screenRatioY; private Paint paint; private Background background1, background2; private int Δx = 0, Δy = 0, x = 0, y = 0, pressed = 0; int backgroundSpeed; private boolean backgroundOneMove = true, backgroundTwoMove = false; public GameView(Context context, int screenX, int screenY) { super(context); this.screenX = screenX; this.screenY = screenY; screenRatioX = 1080f / screenX; screenRatioY = 1920f / screenY; backgroundSpeed = 10; // 两张背景共用的速度参数 background1 = new Background(screenX, screenY, getResources()); background2 = new Background(screenX, screenY, getResources()); background2.x = screenX; paint = new Paint(); } @Override public void run() { while (isPlaying) { update(); draw(); sleep(); } } private void update() { // 背景左移逻辑 background1.x -= backgroundSpeed * screenRatioY; background2.x -= backgroundSpeed * screenRatioY; // 背景1移出屏幕重置位置 if (background1.x + background1.background.getWidth() < 0 && backgroundOneMove){ background1.x = screenX; backgroundOneMove = false; backgroundTwoMove = true; } // 背景2移出屏幕重置位置 if (background2.x + background2.background.getWidth() < 0 && backgroundTwoMove) { background2.y = screenX; backgroundOneMove = true; backgroundTwoMove = false; } } private void draw() { if (getHolder().getSurface().isValid()) { Canvas canvas = getHolder().lockCanvas(); canvas.drawBitmap(background1.background, background1.x, background1.y, paint); canvas.drawBitmap(background2.background, background2.x, background2.y, paint); getHolder().unlockCanvasAndPost(canvas); } } private void sleep() { try { Thread.sleep(17); } catch (InterruptedException e) { e.printStackTrace(); } } public void resume() { isPlaying = true; thread = new Thread(this); thread.start(); } public void pause() { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
Background类原始实现
package com.example.warren; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public class Background { int x = 0, y = 0; Bitmap background; Background(int screenX, int screenY, Resources res){ background = BitmapFactory.decodeResource(res, R.drawable.oolala); background = Bitmap.createScaledBitmap(background, screenX, screenY, false); } }
根因定位
代码存在3个直接引发问题的错误:
- 重置位置时属性写错:背景2移出屏幕需要重置x坐标时,代码错误给
background2.y赋值为screenX,完全没有修改background2.x的值。这会导致背景2的x坐标持续向左偏移永远不会归位,同时y坐标被错误修改出现垂直错位,视觉上直接表现为两张图移动不同步。 - 速度缩放系数用错:x轴是水平移动方向,计算位移时错误使用了垂直方向的缩放系数
screenRatioY,会导致不同分辨率设备上水平移动速度适配错误。 - 冗余开关逻辑打乱重置时机:额外添加的
backgroundOneMove、backgroundTwoMove布尔标记完全没有存在必要,会打断两张背景的连续位置判断,导致背景重置时机和实际位置不匹配,进一步加剧错位问题。
额外隐患:固定使用Thread.sleep(17)控制帧率不可靠,不同设备的线程调度、渲染耗时存在差异,实际帧间隔不固定,会导致移动速度出现波动。
修复方案
- 删除多余的
backgroundOneMove、backgroundTwoMove布尔变量,简化重置逻辑:每帧直接检查两张背景的位置,某张背景完全移出左边缘时,直接将其x坐标设置为另一张背景的x坐标加背景宽度,保证两张图永远首尾相接,不需要依赖互相的状态标记。 - 修正属性赋值错误:重置背景2位置时,给
background2.x赋值,不要误修改y坐标。 - 修正缩放系数:水平位移计算使用
screenRatioX做适配。 - (可选优化)后续可替换固定sleep逻辑,根据实际帧间隔计算每帧位移距离,保证不同设备上移动速度一致。
修复后的核心update方法代码
private void update() { // 水平移动使用X轴缩放系数,两张背景统一位移量 float moveDistance = backgroundSpeed * screenRatioX; background1.x -= moveDistance; background2.x -= moveDistance; int bgWidth = background1.background.getWidth(); // 背景1移出屏幕时,直接拼接在背景2右侧 if (background1.x + bgWidth < 0) { background1.x = (int)(background2.x + bgWidth); } // 背景2移出屏幕时,直接拼接在背景1右侧,注意赋值给x属性 if (background2.x + bgWidth < 0) { background2.x = (int)(background1.x + bgWidth); } }
内容的提问来源于stack exchange,提问作者John Casedy
相关产品推荐
相关产品推荐

