我一直在这个问题上绞尽脑汁。
当我正在运行我的应用程序(opengl游戏)时,传送很顺利但是当我触摸屏幕时,我的应用程序非常严重地减速(在强大的手机上没有明显的感觉,例如nexus,但是在htc魔术上它变得非常烦人)
我做了一个跟踪,发现触摸事件似乎是在不同的线程中处理的,即使它没有花费那么多的处理时间,我认为orroids在线程之间切换的能力也不是那么好...... / p>
当速度成为问题时,处理触摸的最佳方法是什么?
目前我正在使用: 在GLSurfaceView
中@Override
public boolean onTouchEvent(MotionEvent event) {
GameHandler.onTouchEvent(event);
return true;
}
欢迎任何想法
答案 0 :(得分:5)
我有一种感觉,下面的错误报告可能是相关的。可悲的是,它似乎只能在姜饼中修复。
刚刚看到SO更喜欢答案中的细节,以防链接消失等。上面提到了Adnroid 2.1中谷歌接受的错误,目标是在姜饼发布中修复。
问题7836:system_server消耗过多的CPU处理触摸事件
答案 1 :(得分:2)
我自己也不能担保,但是我的研究表明,触摸屏幕会引发很多事件,导致事件队列充斥,因此CPU会占用你的资源。
尝试放: -
try {
Thread.sleep(16);
} catch (InterruptedException e) {} //ignore
在onTouch方法中的任何返回之前(通常只有最后一个 - 但只是确保)。我知道睡眠通常是一件非常糟糕的事情,但它不在UI线程中,所以应该没问题。睡眠16应该将FPS限制在60。
答案 2 :(得分:1)
不要对onTouchEvent()进行繁重的计算。 OnTouchEvent每个手指每秒可以发射数十或数百次,你应该将繁重的计算推迟到游戏的其他部分(例如物理引擎或图形引擎)。特别要避免在onTouchEvent中绘图。
您最好使用onClickEvent或其他不太密集的鼠标事件,并且只有在真正需要跟踪触摸动作时才使用onTouchEvent。
答案 3 :(得分:0)
@Jason:
这将是一个评论,但它变得太大了,它与我刚才给出的答案不同[更好]。
我已使用此处所述的此方法更改了我的实现 http://obviam.net/index.php/the-android-game-loop/
通过使用上述方法意味着您不需要睡眠OnTouch事件。
另外,请不要忘记保护您的游戏循环线程,如http://wonton-games.blogspot.com/2010/06/lunar-lander-resume-game-workaround.html
所述另外请记住,Chris Pruett写Replica Island时说他使用了2个线程,一个用于update(),另一个用于render() - 两者都必须受到保护。
克里斯确实以16毫秒(60 fps)的速度睡眠他的OnTouch以减少事件 - 所以如果你仍然需要减少事件,我会说最好去试验 - 你只需触摸屏幕就会发生什么事情发生在看看它是否滞后,从而随后放手时加速。
对于我目前的需求,我使用相同的线程,因为我还没有进入OpenGL,我仍然使用画布。但是当我使用OpenGL时,它将是2个线程,并且每个线程都将是它自己的完整类。
最后,我没有通过计算帧来计算我的精灵,我用计时器包裹它们。我将分享我的课程并告诉你我是如何称呼它的。请记住,我仍然是Java新手,所以对代码不好道歉。
package com.yourname.yourapplication;
//Used for doing something after a set time
public class TimeDo {
private int mRepeat = 0; //Stores the last wait period for the reset() later
private long mTime = 0; //The goal time of when its due
private boolean mFlagged = false; //Stop them getting a second true on a subsequent check
public TimeDo(int milliseconds) {
reset(milliseconds);
}
public TimeDo() {
this(0);
}
public void reset(int milliseconds) {
mRepeat = milliseconds;
mTime = System.currentTimeMillis() + milliseconds;
mFlagged = mRepeat==0; //ignore if zero
}
public void reset() { //Set it back to the delay used last time
reset(mRepeat);
}
public boolean check() {
if (mFlagged) //Assert: shouldn't really happen
return false;
mFlagged = System.currentTimeMillis() > mTime;
return mFlagged;
}
public boolean checkAndReset() {
if (check()) {
reset();
return true;
}
return false; //note mFlagged could be true here, so don't use it
}
}
这样实施: -
public class Gem {
private TimeDo mMoveGem = new TimeDo(100); //move 10 times a second, 100ms
private int mX = 0;
private int mY = 0;
private int mMoveX = 3;
private int mMoveY = 4;
.
.
.
public void update() {
if (mMoveGem.checkAndReset()) {
mX += mMoveX;
mY += mMoveY;
.
.
}
}
public void render(Canvas canvas) {
//etc etc
.
.
}
}
希望任何有用的东西! 对不起,如果你必须重写你的大量应用程序 - 我做了。
编辑:那个TimeDo类不是postDelayed runnable,就像自动警报一样。如果您不检查,则不会触发“事件”。你可能会兴奋并让它创建一个runnable并传递一个驻留在你的类中的回调方法(想想一个OnClick方法) - 但是无法运行一个更新所有组件的独有的time-able update()线程。