在Android'中独立于CPU速度进行计算和绘图;s SurfaceView
为了使用SurfaceView在Android中绘制2D游戏,我在主要活动的在Android'中独立于CPU速度进行计算和绘图;s SurfaceView,android,2d,surfaceview,Android,2d,Surfaceview,为了使用SurfaceView在Android中绘制2D游戏,我在主要活动的onCreate()中使用了它: 这是对此类的引用: public class GameView extends SurfaceView implements SurfaceHolder.Callback 此外,我还有一个线程,它的run()函数: public void run() { Canvas c; while (_run) { c = null; try {
onCreate()
中使用了它:
这是对此类的引用:
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);
...
}
谢谢你的帮助 当我开始渲染帧时,我会节省时间(在你的例子中是updatePhysics()),然后下次我到达这一点时,我知道要花多少时间,如果时间过得太快,你可以使用如果可以,直接使用时间来计算你所有的物理量。这通常效果最好 如果您无法基于时间进行计算,因为您所做的只是基于步骤的,并且您知道生成下一步不会花费太多时间,那么您有另一个选择 创建两个线程。第一种方法是以固定速率推进状态(您必须确保这也能以该速率在低速设备上运行)。第二种方法获取当前状态,即查看并绘制该状态。现在,第二个线程可以像它想要的那样慢,因为它只是跳过一些状态(或者如果更快,则多次绘制相同的状态) 下面的小示例有一个线程,它推进某个状态对象并每次替换引用,因此使用线程不必担心它的状态对象被修改
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();
}
}
如果生产线程不能以所需的速率运行,那么仍然无法提供与速度无关的结果。与您的建议完全相同。您需要一些系统时钟的参考。要么以固定的时间间隔更新(这会限制帧速率,因此可能不好),要么根据时间计算,使其直接依赖于时间(2),而不是依赖于帧。帧率改变,因此,不是每一帧都意味着相同的时间跨度。谢谢你,扎普和古斯塔沃!所以我需要用类似于
*milliu seconds\u passed/40
?通过常数40
我可以确定一切的速度,对吗?睡眠
只有在你想要一个上限(例如60fps)的情况下才起作用,但是如果设备比你的上限慢,你仍然会遇到同样的问题。当它慢的时候你不会睡觉,只有当它快的时候,当它慢的时候你除了尝试改进你的设计之外别无选择,也许可以尝试OpenGL,但这意味着如果你在一两帧内降到35fps,那么你的物理就会变得一团糟。根据经过的时间更新您的物理是标准和最佳解决方案。感谢您提供此替代解决方案!你的介绍让我很容易做出决定,而我选择的解决方案将取决于时间。
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();
}
}