When to reset input

I started writing another input class based on everyones feedback.

So I wanted to know if the mouse is pressed just once or if it is held down. I wanted it to be thread safe. I also wanted it to use a method called poll, as that seems to be the convention, and it seems that it should be called before the game as updated. Only one method is synchronized, as that is the only chance that variables could be manipulated by two threads at the same time. But maybe I’m getting confused so let me know.

Here is the code. I just wrote it out quickly so it may contain a stupid mistake, but I’ve tested it and it seems to work so far.

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Component;

public class InputManager extends MouseAdapter {
    
    private volatile MouseState previousMouseState = new MouseState();
    private volatile MouseState nextMouseState = new MouseState();
    private volatile MouseState currentMouseState = new MouseState();   
    
    public InputManager(Component component) {
        component.addMouseListener(this);
        component.addMouseMotionListener(this);
    }
    
    public synchronized void poll() {
        // I find this less confusing then swapping refrences, but may change it later.
        previousMouseState.x = currentMouseState.x;
        previousMouseState.y = currentMouseState.y;
        previousMouseState.down = currentMouseState.down;
        currentMouseState.x = nextMouseState.x;
        currentMouseState.y = nextMouseState.y;
        currentMouseState.down = nextMouseState.down;
     }
    
   
    // returns whether mouse was pressed since last frame
    public boolean mousePressed() {
        return !previousMouseState.down && currentMouseState.down ;
    }
    
  
    public boolean mouseDown() {
        return currentMouseState.down;
    }
    
    // returns whether mouse was released last frame
    public boolean mouseReleased() {
        return previousMouseState.down && !currentMouseState.down;
    }
    
    public boolean mouseUp() {
        return !currentMouseState.down;
    }
    public int getMouseX() {
        return currentMouseState.x;
    }
    
    public int getMouseY() {
        return currentMouseState.y;
    }
    
    public boolean mouseMoved() {
        return currentMouseState.x != previousMouseState.x 
                || currentMouseState.y != previousMouseState.y;
    }
    
    public boolean mouseDragged() {
        return (currentMouseState.down && previousMouseState.down)
                && mouseMoved();
    }
    @Override
    public void mousePressed(MouseEvent event) {
        nextMouseState.down = true;
        
    }
    
    @Override
    public void mouseReleased(MouseEvent event) {
        nextMouseState.down = false;
    }
    
    @Override 
    public void mouseMoved(MouseEvent event) {
        nextMouseState.x = event.getX();
        nextMouseState.y = event.getY();
    }
   
    @Override
    public void mouseDragged(MouseEvent event) {
        nextMouseState.x = event.getX();
        nextMouseState.y = event.getY();
    }
    
    private class MouseState {
        private volatile boolean down;
        private volatile int x;
        private volatile int y;
        
   }
}

There is some confusion about thread safety here:
Synchronizing only the one method your game loop calls has no effect at all, the AWT thread would have to lock/sync the same methods/variables as well.
Mixing volatile and synchronize is not necessary. Volatile guarantees only visibility across threads barriers but applies no locking or atomic access.

My approach is a separate input queue for the main game loop which is fed from AWT listeners. Either with a thread safe queue or by locking when adding and removing events. No struggling with mouse states, no lost input events.

Would you be willing to copy and paste your code? I find it hard to follow what people are saying unless I can see an example.

My stuff is part of a larger framework, so hard to extract, but here is a rough sketch of a possible solution:

-> Create an interface
public interface InputListener {
void onMouseEvent(MouseEvent event);
void onKeyEvent(…);
}

-> Let your game loop class implement the interface
public class GameLoop implements InputListener {
private ConcurrentLinkedQueue mouseQueue;

void onMouseEvent(MouseEvent event) {
mouseQueue.add(event);
}

void loop() {

MouseEvent me = mouseQueue.poll()

}

-> Give your InputManager the reference to your game class as InputListener
-> For each received mouse event call onMouseEvent on InputListener
-> In the main game loop just poll the next input event: MouseEvent me = mouseQueue.poll()

… or with synchronization and a LinkedList:

public class GameLoop implements InputListener {
private LinkedList mouseQueue;

void onMouseEvent(MouseEvent event) {
synchronized (mouseQueue) {
mouseQueue.add(event);
}
}

void loop() {

synchronized (mouseQueue) {
MouseEvent me = mouseQueue.poll();
}

}

Thanks so for much for doing all that. I just want to take a while to think about what you have written, and then I will have to some questions. So I really hope you check back here later. :slight_smile: Thanks again.

65K, I wrote another input class. I thought I had kept all the fundamental pinciples of your example but I must be doing something wrong, because I am getting lag. The longer I play the longer it takes to respond to input. What I think must be happening is that I am adding events to the queue quicker than I am polling them. I was only polling the queue once per loop. Is that right?

I tried to write my own input class to do what you said but I also wanted it to be easy to work with the way I have organised my code.

So what is happening is this. My game loop calls the poll method in the input class just before it updates and then renders the current game scene. When the poll method is called the input class calls poll on the event queue and stores that event in the lastEvent variable. Then in my game scene class, they ask the input class if the mouse has been pressed. This then checks if the last event is not null and has not been pressed.

So should this work? As I say I quickly start getting lag. Should I poll the queue until it’s empty, each loop?

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Component;
import java.util.concurrent.ConcurrentLinkedQueue;

public class InputManager extends MouseAdapter {
    private MouseEvent lastEvent;
    
    private ConcurrentLinkedQueue<MouseEvent> eventQueue = 
            new ConcurrentLinkedQueue<MouseEvent>();
    
    public InputManager(Component comp) {
        comp.addMouseListener(this);
        comp.addMouseMotionListener(this);
    }
    
    public void poll() {
        lastEvent = eventQueue.poll();
    }
    
    public boolean mousePressed() {
        return isEventID(MouseEvent.MOUSE_PRESSED);
    }
    
    public boolean mouseReleased() {
        return isEventID(MouseEvent.MOUSE_RELEASED);
    }
    
    public boolean mouseMoved() {
        return isEventID(MouseEvent.MOUSE_MOVED);
    }
    
    public boolean mouseDragged() {
        return isEventID(MouseEvent.MOUSE_DRAGGED);
    }
    
    public boolean isEventID(int id) {
        return lastEvent != null && lastEvent.getID() == id;
    }
    
    public int getMouseX() {
       return lastEvent == null ? 0 : lastEvent.getX();
    }
    
    public int getMouseY() {
        return lastEvent == null ? 0 : lastEvent.getY();
    }
    
    @Override
    public void mousePressed(MouseEvent event) {
        eventQueue.add(event);
    }
    
    @Override
    public void mouseReleased(MouseEvent event) {
        eventQueue.add(event);
    }
    
    public void mouseMoved(MouseEvent event) {
        eventQueue.add(event);
    }
    
    public void mouseDragged(MouseEvent event) {
        eventQueue.add(event);
    }
}

This is my game play scene class. It has a method to check the input.

[quote] public void checkInput() {

    if (input.mousePressed()) {
       
         int x = input.getMouseX();
         int y = input.getMouseY();
       
        
        for (MoleHole moleHole :  moleHoles) {
            
            if (moleHole.hit(x, y) ) {
                
                if (!moleHole.moleIsDead()) {
                    moleHole.killMole();
                    updateScore(moleHole);
                    moleGenerator.newMole();
                }
             }
        }
    }
  }

[/quote]

No, the event queue should become property of your game class.

InputManager does nothing but eagerly listening to incoming events. For each event, it is so kind to hand the event over to your game class. Then forgets about it. No storing of last events. Thus, your game class is decoupled from the AWT event thread.

Your game loop looks out for new events, removes them from the queue and processes them.
I bet you are not able to produce events faster than your machine handles them, so polling one event per frame should be enough.

Oooh, I am bad in explaining …

No I think it’s just I’m bad at understanding. lol I still don’t understand why I was getting lag. It seemed to me that I was adding to the queue faster than I was polling.

So when you say this.

“Your game loop looks out for new events, removes them from the queue and processes them.”

What do you mean by processes. I mean my game loop is in a State Manager class. That updates the current state. Should I pass the MouseEvent to that state? Because my game loop wouldn’t know how to handle the event.

I mean just “do whatever you need to do when mouse events are received”.
I don’t know your game, can’t tell you. Lag can have many courses, hard to tell without seeing and debugging the game.

I’ve tried doing it the queue way and for some reason it doesn’t work well for me. If I don’t add the mouse moved events to the queue then it’s fine. But when I do add the mouse moved events to the queue, after moving the mouse around there is a very long delay from when I click the mouse to when it does what it is supposed to. This must mean that by moving the mouse, it is adding events faster than I am polling them.

But I need to add the mouse moved events because I need the x, y coordinates, even when the mouse has not been clicked. I wonder why this happens to my game and not to yours?

What about ignoring zillions of move events but querying the mouse location directly:
MouseInfo#getPointerInfo()

Wow I wish I had known that you could get the mouse info before I erased and rewrote my input class for the million time today. lol I should have checked that.

You’ve been a great help. I have really learned a lot from you.