13 Replies - 1676 Views - Last Post: 06 October 2013 - 01:28 PM Rate Topic: -----

#1 novakasss  Icon User is offline

  • D.I.C Regular

Reputation: 4
  • View blog
  • Posts: 352
  • Joined: 11-July 12

Delay between key presses.

Posted 06 October 2013 - 03:06 AM

I am creating a game, and I faced off with one problem. The delays between key presses. For example, player moves right, then, if you want to move down, it stops, waits for half of second and then starts to move down. It makes game difficult to play. I have read that this is related with OS and etc. But for example why other games, have this smooth movement? Is it possible to fix it?
Is This A Good Question/Topic? 0
  • +

Replies To: Delay between key presses.

#2 Flukeshot  Icon User is offline

  • A little too OCD
  • member icon

Reputation: 416
  • View blog
  • Posts: 1,030
  • Joined: 14-November 12

Re: Delay between key presses.

Posted 06 October 2013 - 03:12 AM

I usually set up a timer that checks for a key being pressed, rather than relying on an action listener and OS based keypresses. That's one possible fix.
Was This Post Helpful? 1
  • +
  • -

#3 GregBrannon  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2198
  • View blog
  • Posts: 5,226
  • Joined: 10-September 10

Re: Delay between key presses.

Posted 06 October 2013 - 03:20 AM

Show your game loop and/or the design of the loop that controls the game's timing. I suspect you're using a while( true ) / Thread.sleep() construction which is not advised.
Was This Post Helpful? 0
  • +
  • -

#4 novakasss  Icon User is offline

  • D.I.C Regular

Reputation: 4
  • View blog
  • Posts: 352
  • Joined: 11-July 12

Re: Delay between key presses.

Posted 06 October 2013 - 03:35 AM

View PostGregBrannon, on 06 October 2013 - 03:20 AM, said:

Show your game loop and/or the design of the loop that controls the game's timing. I suspect you're using a while( true ) / Thread.sleep() construction which is not advised.


I don't have game loop.(For now) I just have a Timer, that repaints the game. This is very beginning, I wanted to test how the player moves, and saw this.

View PostFlukeshot, on 06 October 2013 - 03:12 AM, said:

I usually set up a timer that checks for a key being pressed, rather than relying on an action listener and OS based keypresses. That's one possible fix.


Can you tell me more? What you put inside keyPressed() ? Set some boolean value which is used by that timer?

This post has been edited by novakasss: 06 October 2013 - 03:38 AM

Was This Post Helpful? 0
  • +
  • -

#5 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 841
  • View blog
  • Posts: 2,576
  • Joined: 29-July 11

Re: Delay between key presses.

Posted 06 October 2013 - 04:45 AM

*
POPULAR

There's no need for boolean, timers outside your game loop timer, etc. Whether you are using a while loop and sleeping with a thread is irrelevant to the problem. It's all unnecessary advice.

You have an object that moves, a Player. A player can move in all directions. A player has a speed a which it travels. A Player has a method it calls with which to update its state.

When you press and hold a key it registers like so: key......key, key, key, key, etc... There is a pause between the initial key press and each additional time the key event is fired. You avoid it like so. When you press the key, the player's speed is set to a value. As long as you do not release the key, the value is still whatever you set it to via the key press, so it does not matter if you have a key event fired, then a delay, then a succession of key events fired.

public void update() {
    player.x += player.xSpeed;
    player.y += player.ySpeed;
}



In your key listener
public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode;
    if (key = KeyEvent.VK_UP) {
        player.ySpeed = -2;
    }
    ...etc
}

public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode;
    if (key = KeyEvent.VK_UP) {
        player.ySpeed = 0;
    }
    ...etc
}


This post has been edited by farrell2k: 07 October 2013 - 11:02 PM

Was This Post Helpful? 5
  • +
  • -

#6 Flukeshot  Icon User is offline

  • A little too OCD
  • member icon

Reputation: 416
  • View blog
  • Posts: 1,030
  • Joined: 14-November 12

Re: Delay between key presses.

Posted 06 October 2013 - 04:48 AM

The basic principle is that you have a Timer object that's refreshing the graphics and updating the game data.

More complex programs can have more than 1 of these timers, (I generally use 2, separating game update and graphic update) but for simplicity, let's just look at game updates.

So the timer will be set up to fire an ActionListener every x seconds:
int x = 20;
Timer gameUpdate = new Timer(x, this);



The timer above will refresh every 20 milliseconds. This is also equal to 50 refreshes per second. Again, the value is up to you, smaller value == faster game.

'this' is telling the compiler to use the containing class as an ActionListener for the timer. I use the 'this' keyword so that I can override the actionPerformed() method directly from my class code. It's possible to do this with an anonymous inner class, or a separate class file, or a regular inner class, it's just a design choice.

So what do we do with the actionPerformed method? Consolidate everything your game has to iterate, so let's be specific. Should our main character move on this tick?
public void actionPerformed(ActionEvent e) {
   if(!stationary)
      movePlayer();
}



Lastly you would set up a KeyListener with keybinds to VK_LEFT and VK_RIGHT, and use an enum to determine direction.

When left or right is held, movePlayer() should increment or decrement the player's horizontal position depending on the key held.

We can gain this effect by using the keyPressed and keyReleased methods in the KeyListener: keyPressed will set stationary to false and set the direction, keyReleased will set stationary to true.


Try it out and let me know how you get on.

Edit: Farrell beat me to the punch, and he's a veteran game developer, so his method is probably better than mine. :)

This post has been edited by Flukeshot: 06 October 2013 - 04:49 AM

Was This Post Helpful? 3
  • +
  • -

#7 novakasss  Icon User is offline

  • D.I.C Regular

Reputation: 4
  • View blog
  • Posts: 352
  • Joined: 11-July 12

Re: Delay between key presses.

Posted 06 October 2013 - 05:39 AM

View Postfarrell2k, on 06 October 2013 - 04:45 AM, said:

There's no need for boolean, timers outside your game loop timer, etc. Whether you are using a while loop and sleeping with a thread is irrelevant to the problem. It's all unnecessary advice.

You have an object that moves, a Player. A player can move in all directions. A player has a speed a which it travels. A Player has a method it calls with which to update its state.

When you press and hold a key it registers like so: key......key, key, key, key, etc... There is a pause between the initial key press and each additional time the key event is fired. You avoid it like so. When you press the key, the player's speed is set to a value. As long as you do not release the key, the value is still whatever you set it to via the key press, so it does not matter if you have a key event fired, then a delay, then a succession of key events fired.

public void update() {
    player.x += player.speed;
    player.y += player.speed;
}



In your key listener
public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode;
    if (key = KeyEvent.VK_UP) {
        player.speed = -2;
    }
    ...etc
}

public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode;
    if (key = KeyEvent.VK_UP) {
        player.speed = 0;
    }
    ...etc
}


Thank you, it works. I have one question, is this solution also protects this game from bug, when people with faster keypad(I don't know how to call this) can move faster than other with slower?

View PostFlukeshot, on 06 October 2013 - 04:48 AM, said:

Edit: Farrell beat me to the punch, and he's a veteran game developer, so his method is probably better than mine. :)/>

Still, thank you for your time. :)
Was This Post Helpful? 0
  • +
  • -

#8 Flukeshot  Icon User is offline

  • A little too OCD
  • member icon

Reputation: 416
  • View blog
  • Posts: 1,030
  • Joined: 14-November 12

Re: Delay between key presses.

Posted 06 October 2013 - 06:46 AM

Doesn't matter how fast the key repeats are, this method depends only on the fact that the key is held down, that's why you don't get a delay between first movement and consecutive movements.
Was This Post Helpful? 1
  • +
  • -

#9 novakasss  Icon User is offline

  • D.I.C Regular

Reputation: 4
  • View blog
  • Posts: 352
  • Joined: 11-July 12

Re: Delay between key presses.

Posted 06 October 2013 - 07:17 AM

I have another problem, the player seems moves not very constantly. I put some println statements which showed the coordinates. And here is the fragment from the output:
1018
1022
1028
1032
1032
1080
1086

Space between 1030-1080.... It makes impossible to set collision detection. What's wrong here?

			@Override
			public void keyPressed(KeyEvent e) {
				pressed.add(e.getKeyChar());
				if(isKeyDown == false){
					for(Character c : pressed){
						if(c == 'w' && player.getY()-1*SPEED > 30){
							speedY = -1*SPEED;
						}if(c == 's' && player.getY()+SPEED < 635){
							speedY = SPEED;
						}if(c == 'a' && player.getX()+-1*SPEED > 15){
							speedX = -1*SPEED;
						}if(c == 'd' && player.getX()+SPEED < 1185){
							speedX = SPEED;
						}
						isKeyDown = true;
					}
				}
			}

			@Override
			public void keyReleased(KeyEvent e) {
				pressed.remove(e.getKeyChar());
				int key = e.getKeyCode();
				if(key == KeyEvent.VK_W || key == KeyEvent.VK_S){
					speedY = 0;
				}
				if(key == KeyEvent.VK_A || key == KeyEvent.VK_D){
					speedX = 0;
				}
			}


	@Override
	public void actionPerformed(ActionEvent arg0) {
		update();
		repaint();
		isKeyDown = false;
	}



Timer set 20ms.
Was This Post Helpful? 0
  • +
  • -

#10 CasiOo  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1398
  • View blog
  • Posts: 3,097
  • Joined: 05-April 11

Re: Delay between key presses.

Posted 06 October 2013 - 07:20 AM

If you hold down a key, the OS will repeatedly fire a keyPressed with the keys being hold down. There is a delay before the repeating begins, and that may be what you are experiencing
On Windows you can go to Control Panel and Keyboard settings. You will see that you can modify how fast the repeating should be

The way I have been handling it, is by keeping track of all the pressed keys
I've implemented a Keyboard class for this purpose. You then ask the Keyboard class if a certain key is pressed when you need it
Run the following example program that I just made. It makes use of my Keyboard class
The program shows the pressed keys in a JTable
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;


public class KeyboardTest extends JFrame implements KeyListener, ActionListener {

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				new KeyboardTest();
			}
		});
	}
	
	private JTable table;
	
	public KeyboardTest() {
		table = new JTable(0, 1);
		add(table, BorderLayout.CENTER);
		
		table.addKeyListener(this);
		setSize(400, 400);
		setVisible(true);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		
		Timer timer = new Timer(16, this);
		timer.start();
		
		table.requestFocus();
	}
	
	/*
	 * KeyListener implementation
	 */
	@Override
	public void keyPressed(KeyEvent e) {
		Keyboard.getInstance().setKeyPressed(e);
		e.consume();
	}

	@Override
	public void keyReleased(KeyEvent e) {
		Keyboard.getInstance().setKeyReleased(e);
		e.consume();
	}

	@Override
	public void keyTyped(KeyEvent e) {}

	
	/*
	 * ActionListener implementation for the timer
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
		tableModel.setRowCount(0); //Discard all the current rows
		Keyboard keyboard = Keyboard.getInstance();
		
		for (int i=0; i<0xFFFF; i++) {
			if (keyboard.isKeyPressed(i)) {
				tableModel.addRow(new String[] { KeyEvent.getKeyText(i) });
				tableModel.fireTableDataChanged();
			}
		}
	}
}


import java.awt.event.KeyEvent;
import java.util.BitSet;

public class Keyboard {
	//The KeyEvent class uses unicode characters
	//FFFF is the last unicode character
	//A boolean element uses around 1 byte, while a BitSet only uses a bit for each element
	private BitSet keys = new BitSet(0xFFFF);
	
	public void clear() {
		keys.clear();
	}

	public void setKeyReleased(KeyEvent key) {
		if (key.getKeyCode() != KeyEvent.VK_UNDEFINED)
			if (key.getKeyCode() < keys.size() && key.getKeyCode() >= 0)
				keys.set(key.getKeyCode(), false);
	}

	public void setKeyPressed(KeyEvent key) {
		if (key.getKeyCode() != KeyEvent.VK_UNDEFINED)
			if (key.getKeyCode() < keys.size() && key.getKeyCode() >= 0) {
				keys.set(key.getKeyCode());
			}
	}
	
	public boolean isKeyPressed(int key) {
		if (key < keys.size() && key >= 0) 
			return keys.get(key);
		return false;
	}

	/*
	 * Singleton
	 */
	private static Keyboard instance = null;

	private Keyboard() {
	}

	public static Keyboard getInstance() {
		if (instance == null)
			instance = new Keyboard();
		return instance;
	}
}


Was This Post Helpful? 1
  • +
  • -

#11 novakasss  Icon User is offline

  • D.I.C Regular

Reputation: 4
  • View blog
  • Posts: 352
  • Joined: 11-July 12

Re: Delay between key presses.

Posted 06 October 2013 - 07:48 AM

I noticed that coordinates turbo is at the beginning of the movement, later it gets pretty much constant.
Was This Post Helpful? 0
  • +
  • -

#12 novakasss  Icon User is offline

  • D.I.C Regular

Reputation: 4
  • View blog
  • Posts: 352
  • Joined: 11-July 12

Re: Delay between key presses.

Posted 06 October 2013 - 11:24 AM

---

This post has been edited by novakasss: 06 October 2013 - 11:28 AM

Was This Post Helpful? 0
  • +
  • -

#13 GregBrannon  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2198
  • View blog
  • Posts: 5,226
  • Joined: 10-September 10

Re: Delay between key presses.

Posted 06 October 2013 - 11:30 AM

What happened?
Was This Post Helpful? 0
  • +
  • -

#14 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 841
  • View blog
  • Posts: 2,576
  • Joined: 29-July 11

Re: Delay between key presses.

Posted 06 October 2013 - 01:28 PM

Your keyPressed is a disgusting mess. :)

This is clearly not homework, so I am going top post some simple code for you to study. Sometimes the best way to learn is to see it in action.

Main.java
public class Main {
	
	public Main() {
		JFrame frame = new JFrame("Game Test");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(new GamePanel());
		frame.pack();
		frame.setVisible(true);
		frame.setLocationRelativeTo(null);
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new Main();
			}
		});
	}
}



GamePanel.java
public class GamePanel extends JPanel {
	private Player player;
	private Timer gameTimer;
	
	public GamePanel() {
		setBackground(Color.white);
		setPreferredSize(new Dimension(300,300));
		setFocusable(true);
	}
	
	public void addNotify() {
		super.addNotify();
		init();
		addKeyListener(new KeyMonitor(player));
		start();
	}
	
	private void init() { 
		gameTimer = new Timer(30, gameLoop);
		player = new Player();
	}
	
	public void start() {
		if (gameTimer != null && !gameTimer.isRunning()) {
			gameTimer.start();
		}
	}
	
	public void stop() {
		if (gameTimer != null && gameTimer.isRunning()) {
			gameTimer.stop();
		}
	}
	
	private void update() {
		player.update();
	}
	
	
	private ActionListener gameLoop = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			update();
			repaint();
		}
	};
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		player.draw((Graphics2D)g);
	}
}



KeyMonitor.java
public class KeyMonitor extends KeyAdapter {
	private Player player;
	
	public KeyMonitor(Player p) {
		player = p;
	}
	
	public void keyPressed(KeyEvent e) {
		player.keyPressed(e);
	}
	
	public void keyReleased(KeyEvent e) {
		player.keyReleased(e);
	}
}



Player.java
public class Player {
	private Rectangle bounds;
	private int ySpeed, xSpeed;
	
	public Player() {
		bounds = new Rectangle(20, 20, 20, 20);
	}

	public void update() {
		bounds.x += xSpeed;
		bounds.y += ySpeed;
		lockBounds();
	}
	
	public void draw(Graphics2D g) {
		g.setColor(Color.GREEN);
		g.fill(bounds);
	}

	private void lockBounds() {
		//you may consider passing the screen size to player object via constructor.
		if (bounds.x < 0) {
			bounds.x = 0;
		}
		if (bounds.x > 300 - bounds.width) {
			bounds.x = 300 - bounds.width;
		}
		if (bounds.y < 0) {
			bounds.y = 0;
		}
		if (bounds.y > 300 - bounds.height) {
			bounds.y = 300 - bounds.height;
		}
	}

	public void keyPressed(KeyEvent e) {
		int key = e.getKeyCode();
		if (key == KeyEvent.VK_W) {
			ySpeed = -5;
		}
		if (key == KeyEvent.VK_A) {
			xSpeed = -5;
		}
		if (key == KeyEvent.VK_S) {
			ySpeed = 5;
		}
		if (key == KeyEvent.VK_D) {
			xSpeed = 5;
		}
	}
	
	public void keyReleased(KeyEvent e) {
		int key = e.getKeyCode();
		if (key == KeyEvent.VK_W) {
			ySpeed = 0;
		}
		if (key == KeyEvent.VK_A) {
			xSpeed = 0;
		}
		if (key == KeyEvent.VK_S) {
			ySpeed = 0;
		}
		if (key == KeyEvent.VK_D) {
			xSpeed = 0;
		}
	}
}


This post has been edited by farrell2k: 06 October 2013 - 01:34 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1