0

I have an OpenGL project that has a collection of GameObjects. I have a thread which is the game loop that iterates over the objects and calls the update method. Additionally, OpenGL has the drawFrame method that iterates over the objects and renders the objects. Finally, I have the onTouchEvent method that is in a third thread that iterates over the objects and calls each objects version of the onTouchEvent method.

That said, when one of my object moves position based off the screen touch, I am getting a ghost image of the object in it's previous location. It is a simple flicker, but very annoying and very obvious. Other objects moving freely (not based off the onTouchEvent) do not have the ghost image of itself trailing, but the object who's position changes when the onTouchEvent method is called, creates a ghost image.

How do I prevent this from happening? I've wrapped both my methods in "synchronized(this)" and it still does not work.

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);

            }
        }
    }
}
genpfault
  • 51,148
  • 11
  • 85
  • 139
Matthew
  • 3,886
  • 7
  • 47
  • 84
  • Does the `update` method of the object you're moving around via touch also change the position? – Aert Jul 31 '13 at 03:06
  • yes, it changes it's position based off of it's velocity. – Matthew Jul 31 '13 at 09:49
  • The camera object is designed to follow a particular object. When I make the camera stationary and I control the main object, I don't get any ghost, meaning that it has to do with the camera following the object. – Matthew Jul 31 '13 at 11:41
  • BTW, I would suggest avoiding [`synchronized(this)`](http://stackoverflow.com/questions/442564/avoid-synchronizedthis-in-java). – Aert Aug 02 '13 at 01:51

2 Answers2

1

I use a very similar gameobject system with a little difference. I have an Input Class which has 3 arrays:

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

I update these variables in the onTouch event of the UI Thread.

And instead of calling an onTouch for the GameObject when it occurs I use these variables to determine if it has been touched in the GameObject's update event. It also makes it more clean if you want to switch to Multitouch sometime.

You can also make the variables static if you want I just use multiple Input methods so it's cleaner for me.

Also I recommend sleeping for 16 ms when onTouch event occurs. Otherwise you will experience huge lag spikes when you hold your finger on the screen.

I will also post my approach to this problem. My Input class looks like:

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

And when you want to access it from your GameObject's update method:

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

I hope what I said makes some sense and it works fo you too.

Daniel Sharp
  • 169
  • 1
  • 2
  • 12
  • This approach relies on the update to be done in between touch events. Isn't it better to buffer the touch events in a producer-consumer queue pattern? – Aert Aug 01 '13 at 07:39
  • It produces it a lot of errors but if you can handle them it's better performance. It's just a workaround, an another option to be considered. – Daniel Sharp Aug 01 '13 at 10:51
  • Ok, just worried about missing (overwriting) e.g. a down event because it's immediately followed by a move event. – Aert Aug 01 '13 at 23:54
1

The ghosting is probably a result of the render thread sometimes coming in after an update, sometimes before an update.

Consider the following scenario:

  1. Starting situation: objpos = campos = 0.
  2. You move your finger, fires a touch event and the UI thread updates the position of the object: objpos = 1, campos = 0.
  3. Render: objpos = 1, campos = 0.
  4. The update thread comes in and updates the camera position: campos = objpos = 1.
  5. Render: objpos = 1, campos = 1. etc.

So depending on the exact timing of the threads, you will get subsequent rendering using a different camera position relative to the object you're tracking (sometimes correct, sometimes lagging).

Simply putting in a synchronized won't help to resolve the timing issue. The way to solve it is to process the touch events on the update thread. To do that, you can:

  • Store the events for processing in the update thread. For example the way Daniel Sharp suggests, although there are some potential pitfalls there.
  • Post Runnables and handle those in the update thread. Much like Android's post(runnable) / runOnUIThread(runnable).
Aert
  • 1,989
  • 2
  • 15
  • 17