0

我有一个包含 GameObjects 集合的 OpenGL 项目。我有一个线程,它是遍历对象并调用更新方法的游戏循环。此外,OpenGL 具有迭代对象并渲染对象的 drawFrame 方法。最后,我有 onTouchEvent 方法,它位于第三个线程中,它遍历对象并调用 onTouchEvent 方法的每个对象版本。

也就是说,当我的一个对象根据屏幕触摸移动位置时,我会在它之前的位置获得对象的重影图像。这是一个简单的闪烁,但非常烦人且非常明显。其他自由移动的物体(不是基于onTouchEvent)没有自身拖尾的重影,但是当调用onTouchEvent方法时位置发生变化的物体会产生重影。

我该如何防止这种情况发生?我已经将我的两种方法都包装在“同步(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);

            }
        }
    }
}
4

2 回答 2

1

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

boolean[] isTouched = new boolean[10];
int[] touchX = new int[10];
int[] touchY = new int[10];

我在 UI 线程的 onTouch 事件中更新了这些变量。

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

如果你想要我只使用多个输入法,你也可以将变量设为静态,这样对我来说更干净。

我还建议在 onTouch 事件发生时休眠 16 毫秒。否则,当您将手指放在屏幕上时,您会遇到巨大的延迟峰值。

我还将发布解决此问题的方法。我的输入类看起来像:

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];
    }
}

当您想从 GameObject 的更新方法中访问它时:

boolean touched = YOURMULTITOUCHHANDLER.isTouchDown(0);
float touchX = YOURMULTITOUCHHANDLER.getTouchX(0);
float touchY = YOURMULTITOUCHHANDLER.getTouchY(0);

我希望我说的有点道理,对你也有用。

于 2013-07-31T15:39:46.647 回答
1

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

考虑以下场景:

  1. 起始情况:objpos = campos = 0。
  2. 您移动手指,触发触摸事件,UI 线程更新对象的位置:objpos = 1,campos = 0。
  3. 渲染:objpos = 1,campos = 0。
  4. 更新线程进来并更新相机位置:campos = objpos = 1。
  5. 渲染:objpos = 1,campos = 1。等等。

因此,根据线程的确切时间,您将使用相对于您正在跟踪的对象的不同相机位置进行后续渲染(有时是正确的,有时是滞后的)。

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

  • 将要处理的事件存储在更新线程中。例如 Daniel Sharp 建议的方式,尽管其中存在一些潜在的陷阱。
  • Runnables在更新线程中发布和处理那些。很像安卓的post(runnable)/ runOnUIThread(runnable)
于 2013-08-02T01:44:16.123 回答