线程中对象的速度不准确

时间:2017-06-20 16:03:17

标签: java android multithreading game-loop

我有一个MainThread类,可以作为游戏循环使用。然后在我的面板中,我有这些矩形,它们是音符表的音符和线条(我解析音乐XML文件并根据解析的数据绘制这些),我也解析速度,我希望这些被移动根据节奏。我有一个函数updateX(float x),它更新了对象的x位置。通过线程的开始,我设置一个变量startTime = System.CurrentTimeMillis()然后我计算elapsedTime = System.CurrentTimeMillis() - startTime。那么delta x就是velocity * elapsedTime其中velocity =(一个小节的长度/节拍数)*(60000 millis / tempo)。但问题是,当我用节拍器测试它时,它是不准确的。此外,音符(矩形)往往比线条移动得快一点。这是mainthread:

public class MainThread extends Thread{
public static final int MAX_FPS = 30;
private double averageFPS;
private SurfaceHolder surfaceHolder;
private PlayPanel playPanel;
private boolean running;
public static Canvas canvas;

public MainThread(SurfaceHolder surfaceHolder, PlayPanel playPanel) {
    super();
    this.surfaceHolder = surfaceHolder;
    this.playPanel = playPanel;
}
ScheduledExecutorService scheduler =
        Executors.newScheduledThreadPool(1);
ScheduledFuture scheduledFuture;

long tick = 0;
int count = 1;
public void start() {

    final Runnable runnable = new Runnable() {
        @Override
        public void run() {

            canvas = null;

            if (metronomeOn) {
                if (tick == 0) {
                   // System.out.println(TAG + " Running " + tick + " " + 60000/tempo);
                    if (count % beat == 1) {
                        sendMidi(0x99, 77, 64);
                      //  System.out.println("TICK");
                    } else {
                        sendMidi(0x99, 76, 64);
                      //  System.out.println("TACK");
                    }
                    count++;
                    tick++;

                } else if (tick >= 600/tempo - 1) {
                    tick = 0;
                } else {
                   // System.out.println(TAG + " Running " + tick + " " + 60000/tempo);
                    tick++;
                }
            } else if (!metronomeOn) {
                tick = 0;
                count = 1;
            }

            try {
                canvas = surfaceHolder.lockCanvas();
                playPanel.update();
                playPanel.draw(canvas);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (canvas != null) {
                    try {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    };

   scheduledFuture =
            scheduler.scheduleAtFixedRate(runnable, 0, 100, TimeUnit.MILLISECONDS);
}


public void cancel() {
    scheduledFuture.cancel(true);
}

这是updateX函数:

// updates the x_position of the notes
public void updateX(float x) {
    rectangle.set(x, rectangle.top, x+rectangle.width(), rectangle.bottom);
    rectangle2.set(x, rectangle2.top, x+rectangle2.width(), rectangle2.bottom);
    if (ledger != null) {
        ledger.updateX(x);
    }
}

// updates the x position of the lines
public void updateX(float x) {
    startX = x;
    stopX = x;
}

和更新功能:

 // updates the notes with the given speed
public void update(float s, long startTime) {
    float elapsedTime = (float)(System.currentTimeMillis() - startTime);
    //System.out.println(TAG + "elapsed time float: " + elapsedTime + " elapsed time long " + (System.currentTimeMillis() - startTime));
    for (Notes n : notes) {
        n.decrementX(s * elapsedTime);
    }

任何人都知道为什么会发生这种情况或有任何解决方案? 提前谢谢。

要添加,我已经使用countDownTimer实现了节拍器,并且根据节奏准确,所以我想知道我是否可以使用countDownTimer实现游戏循环?!

2 个答案:

答案 0 :(得分:0)

Thread.sleep不够准确,无法达到您的目的。当线程退出睡眠时,可能无法立即执行。从睡眠结束到恢复执行之间的时间是不确定的,例如,取决于系统目前的繁忙程度。

我建议您查看java.util.concurrent.ScheduledExecutorService.scheduleAtFixedRate()。您可以将其配置为以固定间隔运行更新代码。它可能比你自己的while循环更好地测量时间。

答案 1 :(得分:0)

这是因为你计算距离的“数学”是错误的,你不应该添加一些时间,而只是计算从开始经过的时间。你需要设置确切的位置,删除任何添加或减少位置的代码 - 它需要大量的重构,但会简化你的代码,我不能这样做,因为你没有发布整个代码,它也是很多工作,你可以自己做:)这是一个代码示例,显示了如何以正确的方式做到这一点:

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExactPositionBasedOnTime {

    public static void main(String[] args) {

        final long speed = 100; // in pixels per second

        final long startTime = System.currentTimeMillis();

        Runnable updater = new Runnable() {
            @Override
            public void run() {
                long elapsedTime = System.currentTimeMillis() - startTime;
                int xPosition = (int) ((elapsedTime * speed) / 1000);
                updateXPosition(xPosition);

            }
        };

        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(updater, 0, 1000 / 30, TimeUnit.MILLISECONDS);

    }

    private static void updateXPosition(int xPosition) {
        // set the position, not add anything, just set it !!!
        System.out.println("the xPosition is: " + xPosition);
    }
}