Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (132)
games submitted by our members
Games in WIP (603)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2
  ignore  |  Print  
  When to reset input  (Read 5342 times)
0 Members and 1 Guest are viewing this topic.
Offline Dream Of Sleeping
« Posted 2012-03-12 16:17:32 »

Hello everyone. Other than a comment on someone else's game this is my fist post. It seems like a really nice forum with nice people.

I've made a few simple games, like tetris and snake, but I always made an input class for each game. (Edit:What I mean is, now I'm trying to write an input class that is a bit more profesional and can be reused) I've never really been that sure on the best way to do it. The thing I'm a bit confused about is mouse input and when to reset it in the input class. The game I am making is a simple (although extremely violent and it bad taste) Whack A Mole type game. So the only control is pressing the mouse. The problem was, even on the menu screens, sometimes it would not register that my mouse had been clicked. I worked out the problem.

In sudo code my game loop went a bit like this.

update game
render
reset input (reseted mouseWasPushed variable)

I reallised what was happening was if I pushed the mouse button after update game, but before or during render, the mouse input variable was reset before upate game could read it. The game uses some pretty large image files as backgrounds. I will learn later about image compression and such.

Anyway I switched it round to this.

update game
reset input
render

So I even if I pushed the mouse button during rendering it would still work. It works fine now. I have not noticed one time when the mouse was unresponsive so it solved the bug. But what would happen with games with complex long calcuations in the update? I would have the same problem.

So how am I supposed to handle reseting such varables as mouseWasPushed? Am I not supposed to reset them and there is some other way? Or do I leave it inside the game logic and out of the loop, so I can reset it as soon as it was used. Any advice would be great.






Offline evilfrenchguy

Junior Devvie


Medals: 3



« Reply #1 - Posted 2012-03-12 16:25:48 »

Well, for simple games, I have a Controls class that handles all mouse/keyboard input, but for a more complex game with menus and stuff you will probably want game states.

Basically though, Controls.java implements KeyListener, MouseListener, and MouseMotionListener and overrides all their respective methods. It also gets passed a reference to your game in the constructor so it can access things.

So, I can check in keyPressed method of Controls.java if the spacebar was pressed, if it was I can simply say game.getPlayer().jump(). Easy as pie!

The listeners run on a separate thread in the background. So while you do your normal update, render, sleep it waits in the background for an event to happen. When something does get pressed, Controls.java will catch it and handle it.
Offline Dream Of Sleeping
« Reply #2 - Posted 2012-03-12 16:37:33 »

Hi and thanks for the reply. I have my game states all sorted out. That's not a problem.

"So, I can check in keyPressed method of Controls.java if the spacebar was pressed, if it was I can simply say game.getPlayer().jump(). Easy as pie!"

I understand this. But say if you were using the mouse to jump. You click to jump. Your game reads it and jumps. That mousePressed variable still says mousePressed, so you will keep jumping repeatedly until you reset it. So when do you reset it?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline evilfrenchguy

Junior Devvie


Medals: 3



« Reply #3 - Posted 2012-03-12 16:54:24 »

You don't need variables ^_^

Controls is a key listener, a mouse listener, and a mouse motion listener. Once you set it as the default listener with your component's addKeyListener, addMouseListener, and addMouseMotionListener methods, anytime any of those events happen, the respective method in Controls will be called 1 time.

So, after setting it as a listener, let's say you click the left mouse button. The Dispatch thread will be like "Oh! He pressed something on the mouse. Let's see who he told us was the class that should be notified. Controls? Ok, I'll call Controls' mousePressed method and pack the event details with it (MouseEvent)."

From there you can get the details on which exact mouse button it was with MouseEvent and make the player jump.

Does that make sense?
Offline Dream Of Sleeping
« Reply #4 - Posted 2012-03-12 17:03:12 »

I did make sense, because it's kind of like what I'm doing. But I must be misunderstanding you, because i'm still thinking I need to reset it some place. Let me show you what I have so far in my input class.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.Component;

public class InputManager implements Input, MouseListener, MouseMotionListener {
   
    private boolean mouseWasPressed;
    private boolean mouseWasReleased;
    private int mouseX;
    private int mouseY;
    private int mousePressX;
    private int mousePressY;
    private int mouseReleaseX;
    private int mouseReleaseY;
   
    public InputManager(Component component) {
        component.addMouseListener(this);
        component.addMouseMotionListener(this);
    }
   
    public void update() {
        reset();
    }
   
    public void reset() {
        mouseWasPressed = false;
        mouseWasReleased = false;
    }
    public boolean mousePressed() {
        return mouseWasPressed;
    }
   
    public boolean mouseReleased() {
        return mouseWasReleased;
    }
   
    public int getMouseX() {
        return mouseX;
    }
   
    public int getMouseY() {
        return mouseY;
    }
   
    public int getMousePressX() {
        return mousePressX;
    }
   
    public int getMousePressY() {
        return mousePressY;
    }
   
    public int getMouseReleaseX() {
        return mouseReleaseX;
    }
   
    public int getMouseReleaseY() {
        return mouseReleaseY;
    }
       
    @Override
    public void mouseClicked(MouseEvent event) {
       
    }
   
    @Override
    public void mousePressed(MouseEvent event) {
        mouseWasPressed = true;
        mousePressX = event.getX();
        mousePressY = event.getY();
    }
   
    @Override
    public void mouseReleased(MouseEvent event) {
        mouseWasReleased = true;
        mouseReleaseX = event.getX();
        mouseReleaseY = event.getY();
    }
   
    @Override
    public void mouseEntered(MouseEvent event) {}
   
    @Override
    public void mouseExited(MouseEvent event) {}
   
    @Override
    public void mouseMoved(MouseEvent event){
        mouseX = event.getX();
        mouseY = event.getY();
    }
   
    @Override
    public void mouseDragged(MouseEvent event) {
        mouseX = event.getX();
        mouseY = event.getY();
    }
}


And here is a snippet of code that uses that input class.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
 public void checkInput() {
       
        if (input.mousePressed()) {
           
            int x = input.getMousePressX();
            int y = input.getMousePressY();
           
            for (MoleHole moleHole :  moleHoles) {
               
                if (moleHole.hit(x, y) ) {
                   
                    if (!moleHole.moleIsDead()) {
                        moleHole.killMole();
                        updateScore(moleHole);
                        moleGenerator.newMole();
                    }
                 }
            }
        }
     }


So the Input class is the class that handles all the events. But it still stores the info. And that info needs to be reset. I thought that the input should be checked in the same time of every loop, not just react as soon as the user clicks?

Offline Regenuluz
« Reply #5 - Posted 2012-03-12 17:08:27 »

The place to reset any "key pressed" variable is on the "key release" event. Smiley Same goes with the mouse.
Offline Dream Of Sleeping
« Reply #6 - Posted 2012-03-12 17:15:26 »

The place to reset any "key pressed" variable is on the "key release" event. Smiley Same goes with the mouse.

But I need to find out if the mouse WAS pressed. Not just if it is pressed right now. I need to know if it has been pressed this loop. What if someone presses the mouse and releases before before my game can read that it was pressed at all?

Offline Dream Of Sleeping
« Reply #7 - Posted 2012-03-12 17:53:40 »

Well I hope I haven't scared you all away with my stupidity. haha

So I tried doing as Regenuluz said and it still worked. I think maybe I just wasn't compehending how fast the loop goes. No matter how fast I clicked and released the game upate stil caught the click. So I guess that solves it then.

Thanks both of you for your help. If anyone has anything to add, though, please feel free.

Offline evilfrenchguy

Junior Devvie


Medals: 3



« Reply #8 - Posted 2012-03-12 18:36:41 »

Oh, I see. You're having concurrency issues.

It shouldn't really make a difference if it was pressed this loop or not. The game loops upwards of 60x/second. I don't use variables, I just have the dispatch thread do my dirty work. =p

What exactly is it doing? Is there lag between a press and an action?

Offline Dream Of Sleeping
« Reply #9 - Posted 2012-03-12 19:14:30 »

No, in my first post I explained the problem I was having but since moving my code around it works. It's just i could imagine there could be a problem if my game  update methods take too long.




Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline 65K
« Reply #10 - Posted 2012-03-12 20:31:25 »

This is not thread safe.
You need to either synchronize methods you call from your game loop or at least declare the boolean variables as volatile.
Otherwise you are not guranteed to see the same values as the AWT event thread does.

Offline cylab

JGO Ninja


Medals: 55



« Reply #11 - Posted 2012-03-12 21:21:35 »

Usually you have two approaches to handling input:

- polling
- event processing

The first is how most action oriented games handle input, the latter is how Swing/AWT does it. Both have usecases in games.

For polling you usually have state variables like your mousePressed variable. You usually don't reset them in the game loop, but set it to false in the mouseReleased() method. This obviously has the upside that you encupsulate the mouse state completely in your listener, but the downside of missing clicks (rather rare with decent FPS) and "continuous repeat", meaning that as long as the user presses the mouse the action will be repeated every frame. But there are enough usecases (like movement control) where this behavious is exactly what you want.

The event approach is for actions that should happen on a state change (like changing from mouse pressed to mouse released). The easiest is to utilize the already existing Listener framework of Swing/AWT, but this has the downside of not playing too well with active rendering and gameloops in general due to threading problematics like 65K already explained. So you usually "convert" the awt events to your own events and put these in a thread safe queue (like ConcurrentLinkedQueue. In your update phase you can then pop events from this queue and process them to update your game state. This approach is great for selections and other non-repeating interactions.

Your code tries to get away with the polling approach for an event based action, which can only result in a fragile solution and will eventually break for other use-cases introduced later to your game. Best would be to support both in your InputManager and use the right approach for the individual use-case


Mathias - I Know What [you] Did Last Summer!
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #12 - Posted 2012-03-12 21:45:17 »

To make this thread safe you can create a GamePanel class like this
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
class GamePanel extends JPanel {
    ConcurrentLinkedQueue<InputEvent> eventQueue = new ConcurrentLinkedQueue<InputEvent>();
    @Override
    protected void processEvent(AWTEvent e) {
        if(e instanceof InputEvent) eventQueue.add((InputEvent)e);
        else super.processEvent(e);
    }
    {
        enableEvents(  AWTEvent.MOUSE_EVENT_MASK
                     | AWTEvent.MOUSE_MOTION_EVENT_MASK
                     | AWTEvent.KEY_EVENT_MASK);
        addHierarchyListener(new HierarchyListener() {
            public void hierarchyChanged(HierarchyEvent e) {
                if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) > 0) {
                    requestFocusInWindow();
                }
            }
        });
    }
    public void handleEvents() {
        while(!eventQueue.isEmpty()) super.processEvent(eventQueue.poll());
    }
}


This uses a thread safe queue to store all input events so you can then handle them in your game loop safely.
You have 2 options here. You can add listeners to the game panel and call handleEvents from your game loop or you don't use listeners and instead poll the events from the queue directly.
e.g.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
GamePanel panel = new GamePanel();
panel.addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        System.out.println(e);
    }
});
panel.addKeyListener(new KeyAdapter() {
    public void keyPressed(KeyEvent e) {
        System.out.println(e);
    }
});
final JFrame frame = new JFrame();
frame.add(panel);
panel.setPreferredSize(new Dimension(800,600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
while(true) { // game loop
    panel.handleEvents();
    // do other stuff
}


or with direct polling
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
GamePanel panel = new GamePanel();
final JFrame frame = new JFrame();
frame.add(panel);
panel.setPreferredSize(new Dimension(800,600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
while(true) { // game loop
    while(!panel.eventQueue.isEmpty()) {
        InputEvent e = panel.eventQueue.poll();
        System.out.println(e);
    }
    // do other stuff
}
Offline Dream Of Sleeping
« Reply #13 - Posted 2012-03-12 22:20:27 »

Usually you have two approaches to handling input:

- polling
- event processing

The first is how most action oriented games handle input, the latter is how Swing/AWT does it. Both have usecases in games.

For polling you usually have state variables like your mousePressed variable. You usually don't reset them in the game loop, but set it to false in the mouseReleased() method. This obviously has the upside that you encupsulate the mouse state completely in your listener, but the downside of missing clicks (rather rare with decent FPS) and "continuous repeat", meaning that as long as the user presses the mouse the action will be repeated every frame. But there are enough usecases (like movement control) where this behavious is exactly what you want.

Hi and thanks for the detailed reply. There is just one major thing that is confusing me about your post. It seems that at the beginning you are suggesting that I either use polling or event processing, then later you mention the mouseReleased() method. So if were to use polling, and not event processing, what would call the mouseRelease method? How would I know the mouse was released? Are you a fan of doing it the way DrZoidberg suggests, by checking the actual eventQue?



Offline Dream Of Sleeping
« Reply #14 - Posted 2012-03-12 22:23:22 »

Thanks DrZoidberg, I was completely unaware that it could be done that way. So I will have a look at the API.

And thanks 65K, I will make what I have thread safe,  if I do stick with that way of doing things. I  was just learning about volaltile variables today.

Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #15 - Posted 2012-03-12 23:19:39 »

So if were to use polling, and not event processing, what would call the mouseRelease method? How would I know the mouse was released?

You could do this

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
while(true) {
    while(!panel.eventQueue.isEmpty()) {
        InputEvent e = panel.eventQueue.poll();
        if((e.getModifiers & MouseEvent.MOUSE_RELEASED) > 0) { // mouse was released
            MouseEvent me = (MouseEvent)e;
            ...
        }
    }
   
}


But I would just use a mouse listener. If you need them you can also have state variables that you set from within the listeners.
e.g.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
boolean leftMouseDown = false;
...
panel.addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        if(e.getButton() == MouseEvent.BUTTON1) leftMouseDown = true;
    }
    public void mouseReleased(MouseEvent e) {
        if(e.getButton() == MouseEvent.BUTTON1) leftMouseDown = false;
    }
});


you could also add a mouse/key listener to each character in the game
and then do something like this
1  
2  
3  
4  
panel.addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        for(GameCharacter gc: listOfGameCharacters) gc.getMouseListener().mousePressed(e);
    }
Offline Dream Of Sleeping
« Reply #16 - Posted 2012-03-12 23:31:14 »



But I would just use a mouse listener. If you need them you can also have state variables that you set from within the listeners.
e.g.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
boolean leftMouseDown = false;
...
panel.addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        if(e.getButton() == MouseEvent.BUTTON1) leftMouseDown = true;
    }
    public void mouseReleased(MouseEvent e) {
        if(e.getButton() == MouseEvent.BUTTON1) leftMouseDown = false;
    }
});


Now I'm starting to feel like I'm going in circles cause that's pretty much what I had. And I was told that the methods need to be synchronized, and the variables volatile. I was also told I was mixing polling and event based , and that it was a fragile solution.

I'd love to see code for an input class that a few people could agree that was a good solution. If I don't, I think I will just have to go ahead with something like was already doing but with synchronized methods.


Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #17 - Posted 2012-03-12 23:38:06 »

If you use my GamePanel class you don't need to synchronize your methods and you don't need volatile variables either. The ConcurrentLinkedQueue in my class does all the work of synchronizing the threads.
There is not just one correct solution. There is an infinite number of possible correct ways of doing this. How about you post your code and we tell you if it's ok.
Offline Abuse

JGO Knight


Medals: 15


falling into the abyss of reality


« Reply #18 - Posted 2012-03-13 00:04:55 »



But I would just use a mouse listener. If you need them you can also have state variables that you set from within the listeners.
e.g.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
boolean leftMouseDown = false;
...
panel.addMouseListener(new MouseAdapter() {
    public void mousePressed(MouseEvent e) {
        if(e.getButton() == MouseEvent.BUTTON1) leftMouseDown = true;
    }
    public void mouseReleased(MouseEvent e) {
        if(e.getButton() == MouseEvent.BUTTON1) leftMouseDown = false;
    }
});


Now I'm starting to feel like I'm going in circles cause that's pretty much what I had. And I was told that the methods need to be synchronized, and the variables volatile. I was also told I was mixing polling and event based , and that it was a fragile solution.

I'd love to see code for an input class that a few people could agree that was a good solution. If I don't, I think I will just have to go ahead with something like was already doing but with synchronized methods.



Yes, that is fragile code.
Your game code will undoubtably be assuming that the mouse/keyboard state remains the same throughout the duration of 1 game loop iteration.
This would not be the case using the above code.

If you were to implement your keylistener like that you'd need to take a copy of the mouse/keyboard state @ the start of each game loop so the value seen by your game code remained constant for the duration.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline DrZoidberg

Senior Devvie


Medals: 17



« Reply #19 - Posted 2012-03-13 00:35:15 »

Yes, that is fragile code.
Your game code will undoubtably be assuming that the mouse/keyboard state remains the same throughout the duration of 1 game loop iteration.
This would not be the case using the above code.

If you were to implement your keylistener like that you'd need to take a copy of the mouse/keyboard state @ the start of each game loop so the value seen by your game code remained constant for the duration.

My code was intended to be used in combination with the GamePanel class I posted earlier. If used like this, the state will remain the same throughout 1 game loop iteration.
Offline Dream Of Sleeping
« Reply #20 - Posted 2012-03-13 10:36:08 »

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.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
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;
       
   }
}


Offline 65K
« Reply #21 - Posted 2012-03-13 14:25:24 »

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.

Offline Dream Of Sleeping
« Reply #22 - Posted 2012-03-13 14:35:41 »

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.


Offline 65K
« Reply #23 - Posted 2012-03-13 15:24:16 »

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();
      }
      ...
   }

Offline Dream Of Sleeping
« Reply #24 - Posted 2012-03-13 15:55:30 »

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. Smiley Thanks again.

Offline Dream Of Sleeping
« Reply #25 - Posted 2012-03-13 16:51:25 »

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?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
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();
                    }
                 }
            }
        }
      }

Offline 65K
« Reply #26 - Posted 2012-03-13 17:09:43 »

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 ...

Offline Dream Of Sleeping
« Reply #27 - Posted 2012-03-13 17:29:46 »

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.

Offline 65K
« Reply #28 - Posted 2012-03-13 18:45:44 »

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.

Offline Dream Of Sleeping
« Reply #29 - Posted 2012-03-13 19:35:38 »

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? 

Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (37 views)
2014-12-15 09:26:44

Mr.CodeIt (30 views)
2014-12-14 19:50:38

BurntPizza (62 views)
2014-12-09 22:41:13

BurntPizza (99 views)
2014-12-08 04:46:31

JscottyBieshaar (59 views)
2014-12-05 12:39:02

SHC (74 views)
2014-12-03 16:27:13

CopyableCougar4 (77 views)
2014-11-29 21:32:03

toopeicgaming1999 (138 views)
2014-11-26 15:22:04

toopeicgaming1999 (127 views)
2014-11-26 15:20:36

toopeicgaming1999 (38 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!