You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android SurfaceView同速设置背景图实际移动速率不一致问题

Android双背景同速参数下移动不同步问题

问题现象

Android游戏开发中,为两张滚动背景设置了相同的移动速度参数,但实际运行时两张图移动速率不一致、出现错位。

问题相关原始代码

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个直接引发问题的错误:

  1. 重置位置时属性写错:背景2移出屏幕需要重置x坐标时,代码错误给background2.y赋值为screenX,完全没有修改background2.x的值。这会导致背景2的x坐标持续向左偏移永远不会归位,同时y坐标被错误修改出现垂直错位,视觉上直接表现为两张图移动不同步。
  2. 速度缩放系数用错:x轴是水平移动方向,计算位移时错误使用了垂直方向的缩放系数screenRatioY,会导致不同分辨率设备上水平移动速度适配错误。
  3. 冗余开关逻辑打乱重置时机:额外添加的backgroundOneMove、backgroundTwoMove布尔标记完全没有存在必要,会打断两张背景的连续位置判断,导致背景重置时机和实际位置不匹配,进一步加剧错位问题。
    额外隐患:固定使用Thread.sleep(17)控制帧率不可靠,不同设备的线程调度、渲染耗时存在差异,实际帧间隔不固定,会导致移动速度出现波动。

修复方案

  1. 删除多余的backgroundOneMove、backgroundTwoMove布尔变量,简化重置逻辑:每帧直接检查两张背景的位置,某张背景完全移出左边缘时,直接将其x坐标设置为另一张背景的x坐标加背景宽度,保证两张图永远首尾相接,不需要依赖互相的状态标记。
  2. 修正属性赋值错误:重置背景2位置时,给background2.x赋值,不要误修改y坐标。
  3. 修正缩放系数:水平位移计算使用screenRatioX做适配。
  4. (可选优化)后续可替换固定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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 07:06:19