在Android SurfaceView中进行独立于CPU速度的计算和绘图

时间:2012-04-28 12:08:28

标签: android 2d surfaceview

要使用SurfaceView在Android中绘制2D游戏,我在主要活动onCreate()中使用它:

setContentView(new GameView(this));

这是对这个类的引用:

public class GameView extends SurfaceView implements SurfaceHolder.Callback

此外,我有一个带有run()函数的帖子:

public void run() {
    Canvas c;
    while (_run) {
        c = null;
        try {
            c = _surfaceHolder.lockCanvas(null);
            synchronized (_surfaceHolder) {
                _panel.updatePhysics();
                _panel.onDraw(c);
            }
        }
        finally { // when exception is thrown above we may not leave the surface in an inconsistent state
            if (c != null) {
                _surfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

updatePhysics()我做了一些计算。当然,它们比这个简单的例子更复杂,但工作方式相同:

public void updatePhysics() {
    GraphicObject.Coordinates coord;
    GraphicObject.Speed speed;
    for (GraphicObject graphic : _allElements) {
        coord = graphic.getCoordinates();
        speed = graphic.getSpeed();
        coord.setX(coord.getX() + speed.getX());
        coord.setY(coord.getY() + speed.getY());
        ...
    }
}

onDraw()中,我将所有内容绘制到画布上:

@Override
public void onDraw(Canvas canvas) {
    canvas.drawBitmap(BITMAP, xPos, yPos, null);
    ...
}

这很好用 - 一切。当我在我的设备上测试它时,它看起来非常好。但是当我把它交给别人并且他做了一个测试游戏时,对象的移动速度要快得多!为什么会这样?因为线程尽可能频繁地调用updatePhysics(),这意味着快速设备更频繁地调用此函数?

如何防止这种情况并在所有设备上同样快速地制作游戏?像这样的东西?

private long lastRun = System.currentTimeMillis();

public void updatePhysics() {
    long millisPassed = System.currentTimeMillis()-lastRun;
    ...
    float newCoord = (coord.getX() + speed.getX()) * millisPassed / 33;
    coord.setX(newCoord);
    ...
}

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

如果可以,请直接使用时间计算所有物理。这通常效果最好。

如果您无法根据时间进行计算,因为您所做的只是基于步骤而且您知道生成下一步并不需要花费太多时间,那么您还有其他选择。

您创建两个线程。第一个以固定速率推进状态(你必须确保这也适用于那个速率慢的设备)。第二个是当前状态看到并绘制的。现在第二个线程可以像它想要的那样慢,因为它只是跳过某些状态(或者如果它更快则多次绘制相同的状态)。

下面的小例子有一个线程可以推进一些状态对象并每次替换引用,这样消费线程就不必担心它的状态对象被修改了

class GameState {
    private int state = 0;

    public GameState advanceState() {
        GameState result = new GameState();
        result.state = this.state + 1;
        return result;
    }
}

class SurfaceViewImplementation extends SurfaceView {

    // the current state
    volatile GameState mState = new GameState();

    void somewhere() {
        Thread fastProducer = new Thread(new Runnable() {
            private static final long MAX_WAIT = 1000 / 60; 
            @Override
            public void run() {
                while (!Thread.interrupted()) {
                    long timeBefore = SystemClock.currentThreadTimeMillis();
                    GameState newState = mState.advanceState();
                    mState = newState;
                    long timeAfter = SystemClock.currentThreadTimeMillis();
                    long timeSpent = timeAfter - timeBefore;
                    SystemClock.sleep(Math.max(0, MAX_WAIT - timeSpent));
                }
            }
        });
        fastProducer.start();
        Thread slowConsumer = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.interrupted()) {
                    GameState currentState = mState;
                    longRunningDraw(currentState);
                }
            }
        });
        slowConsumer.start();
    }
}

如果生成线程无法以所需的速率运行,那么仍然无法为您提供速度无关的结果。

答案 1 :(得分:-1)

我会节省开始渲染帧的时间(在你的情况下是updatePhysics()),然后下次我到达这一点,我知道要经过多少时间,如果要快,你可以使用{ {3}}