要在 Android 中使用 SurfaceView 绘制 2D 游戏,我在主要 Activity 的 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()
中,我将所有内容绘制到 Canvas 上:
@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);
...
}
感谢您的帮助!
最佳答案
如果可以的话,直接用时间来计算你所有的物理。这通常效果最好。
如果您无法根据时间进行计算,因为您所做的只是基于步骤,并且您知道生成下一步不会花费太多时间,那么您还有另一种选择。
您创建了两个线程。第一个以固定速率推进状态(并且您必须确保它也以该速率在慢速设备上工作)。第二个采用当前状态是看到并绘制它。现在第二个线程可以随心所欲地慢,因为它只是跳过一些状态(或者如果速度更快,则多次绘制相同的状态)。
下面的小例子有一个线程每次都推进一些状态对象并替换引用,所以消费线程不需要担心它的状态对象被修改
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();
}
}
如果生产线程无法以所需的速率运行,那仍然无法为您提供与速度无关的结果。
关于android - 在 Android 的 SurfaceView 中使计算和绘图独立于 CPU 速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10363346/