Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (538)
Games in Android Showcase (132)
games submitted by our members
Games in WIP (600)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  REQUEST: Portable key handler  (Read 3032 times)
0 Members and 1 Guest are viewing this topic.
Offline genepi

Senior Newbie




azerty


« Posted 2003-05-27 22:52:34 »

Hi,

On linux, key management in games is not easy due to a bug (or is it a linux feature?): http://developer.java.sun.com/developer/bugParade/bugs/4153069.html

On linux, the keyReleased() event is fired, when the user holds a key down, even if the key is not released. This makes it difficult to determine when a key is actually released.
On Windows, the keyReleased event is sent only at the end of the keyPressed() / keyTyped() sequence, as documented.

I think it would be good to have a bit of portable code to handle both configurations. Who has already had troubles with this situation?
Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #1 - Posted 2003-05-28 00:29:38 »

This apparently has to do with how linux handles repeating keys..  there is already a bug filed (don't feel like searching for the #)... This inconsistency which basically amounts to false information is a major pain in the butt...

Offline princec

« JGO Spiffy Duke »


Medals: 429
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #2 - Posted 2003-05-28 08:48:17 »

LWJGL seems to handle keys properly Wink

Cas Smiley

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

Senior Newbie




azerty


« Reply #3 - Posted 2003-05-28 12:52:18 »

Hi,

@swpalmer,
Yes, the bug numbers in Sun Database are 4153069 and 4839127. And Sun does not know how to solve it easily...

@princec
Can you confirm that LWJGL handles the sequence of events correctly, on both platforms Windows and linux (the bug is visible on linux; Windows is correct according to the doc). Press and hold a key and look at the KeyEvent generated when the typomatic key-repeat runs.

On Windows you get:
pressed
typed
pressed
typed
pressed
typed
pressed
typed
pressed
typed
released

On linux, you get:
pressed
typed
released
pressed
typed
released
pressed
typed
released
pressed
typed
released
pressed
typed
released

You can use the code from http://developer.java.sun.com/developer/bugParade/bugs/4839127.html

If you confirm that it works in LWJGL, I'll look at the code. But I want a Java-only solution to use on a J2ME platform...
Offline princec

« JGO Spiffy Duke »


Medals: 429
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #4 - Posted 2003-05-28 13:19:03 »

We don't use Java events in LWJGL, and we do indeed see correct key behaviour. But you won't see it on any devices in the near future because we've got no OpenGL for them yet.

Not sure why you're worried about Linux if you're doing J2ME work...?

Cas Smiley

Offline genepi

Senior Newbie




azerty


« Reply #5 - Posted 2003-05-28 13:26:34 »

Hi,

Because the Sharp Zaurus is running linux Grin. And the bug occurs with Esmertec/Insignia Jeode and Sun Personal Profile JVMs.

It makes me wonder if there are people writing games on linux workstations in Java Huh
Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #6 - Posted 2003-05-28 16:00:40 »

Notice that even the Windows implementation is technically not reporting what is really happening.. there should only be one 'pressed', one or more 'typed', then one 'release'.

At least I can't imagine any other reasonable sequence.  I find it mind boggling that something like this remains unfixed after so long.  It is so fundamental.

Offline elias

Senior Devvie





« Reply #7 - Posted 2003-05-28 18:29:37 »

In LWJGL however there's one subtle difference - the keyboard repeat  rate is off. That's a consequence of DirectInput on win32 and the disabling of the key repeat on linux (the only solution at hand). So it might not be the best solution after all, if you need the repeat rate of the OS.

- elias

Offline genepi

Senior Newbie




azerty


« Reply #8 - Posted 2003-05-28 18:55:47 »

Thinking more of it, that's not a trivial problem...
What sequence of events (and value) should you obtain if you press and hold two keys? Shocked

Under Windows, I see a KeyPressed and KeyTyped for both keys, at the begining of the sequence; then KeyPressed + KeyTyped for only one; and KeyReleased for both.

The key repeat mecanism does not make it simple to go up right when the user presses at the same time [^]+[>] Tongue
Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #9 - Posted 2003-05-28 22:21:47 »

I don't think the repeating need do anything special for two keys.. if it wants to repeat only one or none it would make no difference to me.. so long as there is only ONE pressed and ONE released that happen only when the key or keys are actually pressed and released.  Wether or not you get multiple key typed events is usually not relevant for games.

Perhaps ignoring the OS repeat and handling it internally with a timer is a better way to go?  (When you say it is turned off for Linux in LWJGL does that affect the whole system or just the LWJGL window?)  If the repeat rate can be queried from the OS then that seems like a good solution.

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

Senior Devvie





« Reply #10 - Posted 2003-05-29 05:00:10 »

It's for the whole systme unfortunately - and is only enabled again at Keyboard.destroy(). Better solutions are welcome.

- elias

Offline genepi

Senior Newbie




azerty


« Reply #11 - Posted 2003-05-31 19:49:33 »

Hi,

As a start, here is some code to filter the surnumerous KeyReleased events under linux.

The ONE advantage:
- It's portable code using JDK 1.1.8 API.

The disadvantages:
- It creates on thread by KeyListener it manages.
- The filtering is based on a delay. If no more event is received during a delay, then the KeyReleased must be the last of the sequence and is sent to the KeyListener. If the delay is chosen too big, there is a lag before the last event is sent; if too short, some KeyReleased events can escape the filter...
- The choice of the delay depends on the platform. 100 ms seems to be OK on the Zaurus, but creates problems when accessing it remotely with VNC.

TODO:
- Define exactly what we need (in another post) for cross-platform key processing.
- Support dumb keys like [Shift] which repeat under Windows but don't under linux.
- Put the delay in a system property, or at least in a parameter of the constructor...

The begining of the code:
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  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
264  
265  
266  
267  
268  
269  
270  
271  
272  
273  
274  
275  
276  
277  
278  
279  
280  
281  
282  
283  
284  
285  
286  
287  
288  
289  
290  
291  
292  
293  
294  
295  
296  
297  
298  
299  
300  
301  
302  
303  
304  
305  
306  
307  
308  
309  
310  
311  
312  
313  
314  
315  
316  
317  
318  
319  
320  
321  
322  
323  
324  
325  
326  
327  
328  
329  
330  
331  
332  
333  
334  
335  
336  
337  
338  
339  
340  
341  
342  
343  
344  
345  
346  
347  
348  
349  
350  
351  
352  
353  
354  
355  
356  
357  
358  
359  
360  
361  
362  
363  
364  
365  
366  
367  
368  
369  
370  
371  
372  
373  
374  
375  
376  
377  
378  
379  
380  
381  
382  
import java.awt.event.*;
import java.awt.*;

/**
 * Test class for the KeyHandler code
 *
 * @author genepi
 * @created  May 29, 2003
 */

public class Test
{
      /**
       * @param  args the command line arguments
       */

      public static void main(String[] args)
      {
            Frame fr = new Frame();
            TextField l = new TextField("Enter keys");
            fr.add(l);
            fr.pack();
            fr.show();
            l.requestFocus();

            fr.addWindowListener(new WindowAdapter()
                  {
                        public void windowClosing(final WindowEvent evt)
                        {
                              System.exit(0);
                        }
                  });
           
            if (args.length > 0)
            {
                  l.addKeyListener(new KL());
            }
            else
            {
                  l.addKeyListener(new KeyHandler(new KL()));
            }
      }


      /**
       * Simple KeyListener that prints events received
       *
       * @author genepi
       * @created  May 29, 2003
       */

      private static class KL
            implements KeyListener
      {
            /**
             *  Description of the Method
             *
             * @param  e Description of Parameter
             */

            public void keyPressed(KeyEvent e)
            {
                  System.out.println(e);
            }


            /**
             *  Description of the Method
             *
             * @param  e Description of Parameter
             */

            public void keyReleased(KeyEvent e)
            {
                  System.out.println(e);
            }


            /**
             *  Description of the Method
             *
             * @param  e Description of Parameter
             */

            public void keyTyped(KeyEvent e)
            {
                  System.out.println(e);
            }
      }
}


/**
 * The <code>KeyHandler</code> filters <code>KeyEvent</code>
 * sequences to make linux and Windows behave in the same way.
 *
 * On linux, the key-repeat is different from Windows:
 * <b>Character key pressed and held</b>
 * <pre>
 * Windows:
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_RELEASED
 *
 * Linux:
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_RELEASED (*)
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_RELEASED (*)
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_RELEASED (*)
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_RELEASED (*)
 * KEY_PRESSED
 * KEY_TYPED
 * KEY_RELEASED
 * </pre>
 * The KeyHandler filters for the extraneous KEY_RELEASED events received
 * on the linux platform.
 *
 * TODO:
 * - Manage dumb keys like [Shift] or [Control]
 * - Check behavior with simultaneous keys pressed.
 * - The lag/delay of the filter must be read form a property or
 * passed as a parameter.
 *
 * This class is a fa├žade to the real <code>KeyListener</code>.
 *
 * @author genepi
 * @created  May 29, 2003
 */

class KeyHandler
       implements KeyListener
{
      /**
       * The <code>KeyListener</code> protected by the <code>KeyHandler</code>.
       */

      private final KeyListener _listener;
     
     
      /**
       * The thread that filters the keys.
       */

      private final KeyHandlerThread _thread;
     
     
      /**
       * Create the key handler that will dispatch the events to the key listener.
       *
       * @param listener The key listener that will receive key events.
       */

      public KeyHandler(final KeyListener listener)
      {
            _listener = listener;
            // TODO: $$PME
            // The 100ms delay must be adapted to the plaform
            _thread = new KeyHandlerThread(100L);
            _thread.start();
      }
     
     
      /**
       * When the <code>KeyHandler</code> is garbage collected, the companion
       * thread is no more necessary and can stop running.
       */

      protected void finalize()
            throws Throwable
      {
            // KeyEventThread can kill itself now
            _thread.stopHandler();
      }


      /**
       * Add the key pressed event to the queue
       *
       * @param  evt The key event
       */

      public void keyPressed(final KeyEvent evt)
      {
            _thread.addEvent(evt);
      }


      /**
       * Add the key released event to the queue
       *
       * @param  evt The key event
       */

      public void keyReleased(final KeyEvent evt)
      {
            _thread.addEvent(evt);
      }


      /**
       * Add the key typed event to the queue
       *
       * @param  evt The key event
       */

      public void keyTyped(final KeyEvent evt)
      {
            _thread.addEvent(evt);
      }


      /**
       * Send a key event to the listener.
       *
       * @param evt The key event to send
       */

      private void dispatchKeyEvent(final KeyEvent evt)
      {
            switch (evt.getID())
            {
            case KeyEvent.KEY_PRESSED:
                  _listener.keyPressed(evt);
                  break;
                 
            case KeyEvent.KEY_RELEASED:
                  _listener.keyReleased(evt);
                  break;
                 
            case KeyEvent.KEY_TYPED:
                  _listener.keyTyped(evt);
                  break;
                 
            default:
                  throw new RuntimeException("Unhandled key event: " + evt);
            }
      }
     
     
      /**
       * The <code>KeyHandlerThread</code> filters the key events
       * generated by the key-repeat controller to get a platform
       * neutral sequence of events.
       *
       * @author genepi
       * @date May 30, 2003
       */

      private class KeyHandlerThread
            extends Thread
      {
            /**
             * The queue of key events.
             */

            private final FlexibleQueue _events = new FlexibleQueue();
           
           
            /**
             * Handle events while this variable is <code>true</code>
             */

            private volatile boolean _handleKeys;
           
           
            /**
             * The delay we have to wait to accept a <code>KeyReleased</code>
             * event.
             */

            final private long _delay;
           
           
            /**
             * Constructor of the KeyHandler thread.
             *
             * @param delay The delay while no new event must be received before
             * we accept a <code>KeyReleased</code> event.
             */

            KeyHandlerThread(final long delay)
            {
                  super("KeyHandlerThread");
                  setDaemon(true);
                  _delay = delay;
                  _handleKeys = true;
            }
           
           
            /**
             * The key event queue processing loop.
             */

            public void run()
            {
                  while (_handleKeys)
                  {
                        synchronized (this)
                        {
                              try
                              {
                                    // Sleep only if no more events to process
                                    if (_events.size() == 0)
                                    {
                                          wait();
                                    }
                              }
                              catch (InterruptedException ie)
                              {
                                    // A new key should be available in the queue...
                              }
                             
                              if (_events.size() > 0)
                              {
                                    // Time to wake up: a key has been detected
                                    KeyEvent event = (KeyEvent) _events.removeElement();
                                   
                                    if (event.getID() == KeyEvent.KEY_RELEASED)
                                    {
                                          // We have to wait for next key to determine what to do
                                          try
                                          {
                                                wait(_delay);
                                          }
                                          catch (InterruptedException ie2)
                                          {
                                                // Could be a new key in the queue or delay expired.
                                          }
                                         
                                          // There is another key waiting in the queue
                                          if (_events.size() > 0)
                                          {
                                                KeyEvent event2 = (KeyEvent) _events.removeElement();
                                                // If it's a KeyPressed on the same key...
                                                if (event2.getID() == KeyEvent.KEY_PRESSED && event.getKeyChar() == event2.getKeyChar())
                                                {
                                                      // We can drop the KeyReleased event
                                                      dispatchKeyEvent(event2);
                                                }
                                                else
                                                {
                                                      // Both events (KeyReleased + other) must be re-sent
                                                      dispatchKeyEvent(event);
                                                      dispatchKeyEvent(event2);
                                                }
                                          }
                                          else
                                          {
                                                // No new event after the KeyReleased (we are there
                                                // because the delay expired), we send the KeyReleased
                                                dispatchKeyEvent(event);
                                          }
                                    }
                                    else
                                    {
                                          // Non filtered event
                                          dispatchKeyEvent(event);
                                    }
                              }
                        }
                  }
            }
           
           
            /**
             * Change the condition in the thread loop: the thread dies
             */

            void stopHandler()
            {
                  _handleKeys = false;
            }
           
           
            /**
             * Add a new key event in the thread queue for processing.
             *
             * @param evt The key event
             */

            void addEvent(final KeyEvent evt)
            {
                  synchronized (this)
                  {
                        _events.addElement(evt);
                        notifyAll();
                  }
            }
      }
}
Offline genepi

Senior Newbie




azerty


« Reply #12 - Posted 2003-05-31 19:50:40 »

The end of the code:
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  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
/**
 * A flexible queue backed with an array, that resizes if needed.
 * This implementation is synchronized.
 *
 * Let capacity = _content.length.
 * Let _endIndex = (_end - 1 + capacity) % capacity
 *
 * _content[_start] is the start
 * _content[_endIndex] is the end
 *
 * if _start <= _end,
 *      _content[_start .. _endIndex] contains the Objects in the order they were
 *      inserted, and _size = _end - _start.
 * if _start > _end,
 *       _content[_start .. _content.length - 1, 0 .. _endIndex] contains the Objects in
 *      the order they were inserted, and _size = _end - _start + capacity.
 *
 * Adapted from http://www.cs.toronto.edu/~jepson/148/general/exercises/src/RubberBandQueue/RubberBandQueue.java
 *
 * @author genepi
 * @created  May 29, 2003
 */

class FlexibleQueue
{
      /**
       * The initial number of elements this queue can hold,
       */

      private final int _initialSize;

      /**
       * The increment.
       */

      private final int _increment;

      /**
       * The index of the start of the queue.
       */

      private int _start = 0;

      /**
       * The index of the end of the queue.
       */

      private int _end = 0;

      /**
       * The number of elements in me.
       */

      private int _size = 0;

      /**
       * The items stored in _content[_start .. _end-1],
       * with wraparound.
       */

      private Object[] _content;


      /**
       * Constructor
       */

      public FlexibleQueue()
      {
            this(10, 5);
      }


      /**
       * Constructor with parameters
       *
       * @param size The initial size
       * @param increment The size increment when the queue is full
       */

      public FlexibleQueue(final int size, final int increment)
      {
            _content = new Object[size];
            _initialSize = size;
            _increment = increment;
            _start = 0;
            _end = 0;
            _size = 0;
      }


      /**
       * Add the given element to this queue.
       *
       * @param object the element to be added.
       * @exception  QueueFullException the queue cannot be expanded
       *             this only occurs when the machine is nearly out
       *             of memory.
       */

      public void addElement(final Object object)
      {
            synchronized (_content)
            {
                  if (_size == _content.length)
                  {
                        expandFullQueue();
                  }
     
                  _content[_end] = object;
                  _end = (_end + 1) % _content.length;
                  _size += 1;
            }
      }


      /**
       * Dequeue the first object in the queue.
       * Precondition: The queue cannot be empty.
       *
       * @return  the first object in the queue.
       */

      public Object removeElement()
      {
            synchronized (_content)
            {
                  Object result = _content[_start];
                  _start = (_start + 1) % _content.length;
                  _size -= 1;
                  return result;
            }
      }


      /**
       * Reead the first object in the queue.
       * Precondition: The queue cannot be empty.
       *
       * @return  the first object in the queue.
       */

      public Object readElement()
      {
            synchronized (_content)
            {
                  return _content[_start];
            }
      }


      /**
       * Get the size of the queue (the number of elements queued)
       *
       * @return  the size of the queue.
       */

      public int size()
      {
            synchronized (_content)
            {
                  return _size;
            }
      }


      /**
       * Returns the current capacity of the queue.
       *
       * @return  the capacity of the queue.
       */

      public int capacity()
      {
            synchronized (_content)
            {
                  return _content.length;
            }
      }


      /**
       * Resets the queue to be empty and of minimal capacity.
       */

      public void reset()
      {
            synchronized (_content)
            {
                  _content = new Object[_initialSize];
                  _start = 0;
                  _end = 0;
                  _size = 0;
            }
      }


      /**
       * Returns a string representation of this queue.
       *
       * @return  a string representation of this queue.
       */

      public String toString()
      {
            final StringBuffer buf = new StringBuffer();
            buf.append(getClass().getName());
            buf.append("[size=");
            buf.append(_size);
            buf.append(", content={");
           
            if (_start > _end || _size == _content.length)
            {
                  for (int i = _start, size = _content.length; i < size; i++)
                  {
                        buf.append(_content[i]);
                        if (i != _size - 1)
                        {
                              buf.append(", ");
                        }
                  }
                  for (int i = 0; i < _end; i++)
                  {
                        buf.append(_content[i]);
                        if (i != _end - 1)
                        {
                              buf.append(", ");
                        }
                  }
            }
            else
            {
                  for (int i = _start; i < _end; i++)
                  {
                        buf.append(_content[i]);
                        if (i != _end - 1)
                        {
                              buf.append(", ");
                        }
                  }
            }
           
            buf.append("}]@");
            buf.append(hashCode());
           
            return buf.toString();
      }



      /**
       * Make the _content array _increment items larger.
       * Precondition: The queue must currently be full!
       */

      private void expandFullQueue()
      {
            // New array for copying queue into.
            final Object[] tmpContent;
            tmpContent = new Object[_content.length + _increment];

            // Copy _content to new array, using wrap around.
            if (_start > _end || _size == _content.length)
            {
                  System.arraycopy(_content, _start, tmpContent, 0, _content.length - _start);
                  System.arraycopy(_content, 0, tmpContent, _content.length - _start, _end);
            }
            else
            {
                  System.arraycopy(_content, _start, tmpContent, 0, _end - _start + 1);
            }

            _start = 0;
            _end = _content.length;
            // one past the last element in the queue.

            // Finally, replace old _content array with the new one.
            _content = tmpContent;
      }
}
Offline endolf

JGO Coder


Medals: 7
Exp: 15 years


Current project release date: sometime in 3003


« Reply #13 - Posted 2003-08-28 17:00:29 »

Jinput should cover this, if you don't mind polling the keybaord that is

HTH

Endolf

Pages: [1]
  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 (29 views)
2014-12-15 09:26:44

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

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

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

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

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

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

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

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

toopeicgaming1999 (30 views)
2014-11-26 15:20:08
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

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
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!