使用onTouchEvent在Android/Java/OpenGL中实现多线程

使用onTouchEvent在Android/Java/OpenGL中实现多线程,android,multithreading,opengl-es,touch-event,Android,Multithreading,Opengl Es,Touch Event,我有一个OpenGL项目,它有一组游戏对象。我有一个线程,它是在对象上迭代并调用update方法的游戏循环。此外,OpenGL具有drawFrame方法,该方法可以迭代对象并渲染对象。最后,我有第三个线程中的onTouchEvent方法,它迭代对象并调用onTouchEvent方法的每个对象版本 这就是说,当我的一个对象在屏幕触摸的基础上移动位置时,我会在它以前的位置上得到一个对象的鬼图像。这是一个简单的闪烁,但非常恼人,非常明显。其他自由移动的对象(不基于onTouchEvent)本身没有跟踪

我有一个OpenGL项目,它有一组游戏对象。我有一个线程,它是在对象上迭代并调用update方法的游戏循环。此外,OpenGL具有drawFrame方法,该方法可以迭代对象并渲染对象。最后,我有第三个线程中的onTouchEvent方法,它迭代对象并调用onTouchEvent方法的每个对象版本

这就是说,当我的一个对象在屏幕触摸的基础上移动位置时,我会在它以前的位置上得到一个对象的鬼图像。这是一个简单的闪烁,但非常恼人,非常明显。其他自由移动的对象(不基于onTouchEvent)本身没有跟踪的重影图像,但调用onTouchEvent方法时位置发生变化的对象会创建重影图像

如何防止这种情况发生?我已经用“synchronized(this)”包装了我的两个方法,但它仍然不起作用

public void drawFrame(GL10 gl)
    {
        if(renderType == RenderType.r2D)
            prepare2DDrawing(gl);
        else if(renderType == RenderType.r3D)
            prepare3DDrawing(gl);

    synchronized(this)
    {
        camera.draw(gl);
        for(GameObject obj:objects)
        {
            gl.glPushMatrix();
            obj.draw(gl);
            gl.glPopMatrix();
        }   
    }
}

public void update(float time)
{
    cds.testCollisions(objects);
    synchronized(this)
    {
        camera.update(time);
        for(GameObject obj:objects)
        {
            obj.update(time);
        }
        motions.clear();
    }
}
public void onTouchEvent(MotionEvent e)
{
    if(touchable == Touchable.TOUCHABLE)
    {
        synchronized(this)
        {
            for(GameObject obj:objects)
            {
                obj.onTouchEvent(e);

            }
        }
    }
}

我使用了一个非常相似的游戏对象系统,但有一点不同。我有一个输入类,它有3个数组:

boolean[] isTouched = new boolean[10];
int[] touchX = new int[10];
int[] touchY = new int[10];
我在UI线程的onTouch事件中更新这些变量

当游戏对象发生时,我使用这些变量来确定它是否在游戏对象的更新事件中被触摸过,而不是为游戏对象调用onTouch。如果你想在某个时候切换到多点触摸,这也会使它更干净

如果你想的话,你也可以把变量设置成静态的,我只需要使用多个输入法,这样对我来说就更干净了

同样我建议在发生此类事件时睡眠16 ms。否则,当你将手指放在屏幕上时,你会经历巨大的滞后峰值

我还将发布我对这个问题的处理方法。我的输入类看起来像:

public class MultiTouchHandler implements OnTouchListener {
    boolean[] isTouched = new boolean[10];
    int[] touchX = new int[10];
    int[] touchY = new int[10];
    float scaleX;
    float scaleY;
    public MultiTouchHandler(View view, float scaleX, float scaleY) {
        view.setOnTouchListener(this);
        this.scaleX = scaleX;
        this.scaleY = scaleY;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int pointerCount = event.getPointerCount();

        for (int i = 0; i < pointerCount; i++)
        {
            touchX[i] = (int) event.getX(i);
            touchY[i] = (int) event.getY(i);
            int action = event.getActionMasked();


            switch (action)
            {
                case MotionEvent.ACTION_DOWN:
                    isTouched[i] = true;
                    break;
                case MotionEvent.ACTION_UP:
                    isTouched[i] = false;
                    break;  
                case MotionEvent.ACTION_POINTER_DOWN:
                    isTouched[i] = true;
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    isTouched[i] = false;
                    break;
                case MotionEvent.ACTION_MOVE:
                    isTouched[i] = true;
                    break;
                default:
            }
        }

        try {
            Thread.sleep(16);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return true;
    }

    public boolean isTouchDown(int pointer) {
        return isTouched[pointer];
    }

    public int getTouchX(int pointer) {
        return touchX[pointer];
    }

    public int getTouchY(int pointer) {
        return touchY[pointer];
    }
}

我希望我所说的有一定的道理,它也适用于您。

重影可能是渲染线程有时在更新之后,有时在更新之前出现的结果

考虑以下场景:

  • 开始情况:objpos=campos=0
  • 移动手指,触发触摸事件,UI线程更新对象的位置:objpos=1,campos=0
  • 渲染:objpos=1,campos=0
  • 更新线程进入并更新相机位置:campos=objpos=1
  • 渲染:objpos=1,campos=1。 等等
  • 因此,根据线程的精确计时,您将使用与正在跟踪的对象相对的不同摄影机位置(有时正确,有时滞后)获得后续渲染

    简单地输入一个
    synchronized
    将无助于解决时间问题。解决方法是在更新线程上处理触摸事件。为此,您可以:

    • 将要处理的事件存储在更新线程中。例如,丹尼尔·夏普(Daniel Sharp)的建议,尽管存在一些潜在的陷阱
    • 发布
      可运行文件
      ,并在更新线程中处理这些文件。很像Android的
      post(runnable)
      /
      rununuithread(runnable)

    通过触摸移动的对象的
    更新方法是否也会改变位置?是的,它会根据其速度改变其位置。相机对象设计为跟随特定对象。当我使相机静止并且控制主对象时,我没有得到任何重影,这意味着它与跟随对象的相机有关。顺便说一句,我建议避免。这种方法依赖于在触摸事件之间进行的更新。在生产者-消费者队列模式中缓冲触摸事件不是更好吗?它会产生很多错误,但如果您能够处理它们,性能会更好。这只是一个解决方法,另一个需要考虑的选项。好吧,只是担心丢失(覆盖)例如一个关闭事件,因为它后面紧接着一个移动事件。
    
    boolean touched = YOURMULTITOUCHHANDLER.isTouchDown(0);
    float touchX = YOURMULTITOUCHHANDLER.getTouchX(0);
    float touchY = YOURMULTITOUCHHANDLER.getTouchY(0);