JFrame KeyInput & Graphics

Well, after searching through this forum and practically the entire net, I figured out how to do graphics and KeyInput inside of a JFrame. Unfortunantely, the speed at which I recieve keyboard input is unaceptable for a game. I figured that the initial delay is due to the OS’s repeat delay for the keyboard (under control panel for windows). Well, what I’m asking is if there is some way to ignore the repeat delay or query the keybuffer directly, any help on this would be greatly appreciated.

code:
`import java.awt.;
import java.awt.event.
;

import javax.swing.JFrame;

public class Pong extends Canvas {

private int lastX;
private int lastY;
private Graphics context;

  public static void main(String args[]) {
        JFrame frame = new JFrame("Pong");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(0,0,500,500);
        frame.setVisible(true);
        frame.getContentPane().add(new Pong(500, 500));
  }

public Pong ( int width, int height) {
super();
this.setSize( width, height);
this. enableEvents( AWTEvent.MOUSE_MOTION_EVENT_MASK |
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.KEY_EVENT_MASK);
}

public void addNotify() {
super.addNotify();
context = this.getGraphics().create();
}

public void processKeyEvent(KeyEvent event) {
System.out.println(event.getID());
}

public void processMouseMotionEvent(MouseEvent event) {

  if ( event.getID() == MouseEvent.MOUSE_DRAGGED) { 
  int currentX = event.getX();
  int currentY = event.getY();

     context.drawLine( lastX, lastY, currentX, currentY); 
     lastX = currentX;
     lastY = currentY;                     
  }         

}

}`

Thanks!

ps. I’m sorry if this is the wrong forum to post in, but I couldn’t find a better one, and this is kinda about 2d graphics

on KeyDown events, set a flag,
on KeyUp events, clear the flag,
in your game loop check the state of the flag each iteration.

You can ignore keyTyped events, as the rate at which they are sent is dependent on the platform/OS.

I don’t think that you understand exactly what I want.

Here’s the program re-written the way that you want it:

`import javax.swing.;
import java.awt.
;
import java.awt.event.*;

public class Pong extends JPanel implements KeyListener {
int y=0;
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {up=down=false;}
boolean up, down;
static JFrame frame = new JFrame(“Pong”);

  public void keyPressed(KeyEvent e) {
        switch(e.getKeyCode()) {
        case KeyEvent.VK_UP:
              up=true;
              repaint();
              break;
        case KeyEvent.VK_DOWN:
              down=true;
              repaint();
              break;
        default:
              break;
        }
  }
  
  public Dimension getPreferredSize() {
        return new Dimension(300, 300);
  }

  public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (up) y++;
        if (down) y--;
        g.drawString("Y: " + y, 50, 50);
  }
  
  public Pong() {
        frame.addKeyListener(this);
  }
  
  
  public static void main(String argv[]) {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new Pong(), BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
  }

}`

Notice the lag time between pressing the up/down button and the number changing, this would be horrible inside a game, so how do I fix it to be quicker?

You’re probably need an active rendering loop. A quick fix is to add “repaint();” at the end of your paintComponent() function. This will cause the frame to be rendered in a infinite loop. Instead of just when it get an event from the keyboard.

Very nice! :smiley:
Thanks a ton.

Just two more questions then, you made this seem like a crude way to do an “active rendering loop”, so what is the best way to do this? Not in another thread, right?

Also, the way I had my program before puting the paintcomponent() loop into an infinite loop, the interval between touching the key and it repeating was the same as my system delay for the keyboard, I even tried changing it and I saw the effects. So why does adding this active rendering loop ignore that setting?

Thanks a ton guys, you have impressed me a lot.

When you hold down a key, keyPressed is called once immidiatly, then at the key repeat interval until it is released. Because you only repainted when a key was pressed, you get the same frame rate as the key repeat.

In a active rendering loop you update the screen indendent from the keybaord input. The keyrepeat dosn’t mather because the second time keyPressed is called the down flag is already set and setting it again has no effect.

The best way to do an active rendering loop is in your own thread. You will update the screen activly, not threw repaint() and paint(). Basicly you grab the component Graphics object using getGraphics() and use it to paint you gfx. Although it works the way your doing it now too. By calling repaint() in paint()

The way you are doing it now is more of a plea then a command.

Calling repaint says “gee I’d like this repainted soon” but when the repaint actually happens is up to AWT.

In active screen painting you actually MAKE the system paint a frame when you say so and fli pit to screen when its ready.

Its a totally different way to structure your app and rthe way almost all real games work. (Applet games are stuck doign it the crappy AWT way and crossing their fingers)

Look at Scroller or one of the other examples to see how its done.

Sure, I’d love to do it the “right” way, but where do I get this scroller program (or any of the other programs?)

This should make vague sense, and is the fundamental fondation of a game loop.

import java.awt.*;
import java.awt.event.*;
class Pong extends Frame implements Runnable
{
   public Pong()
   {
      super("Controls=arrowKeys & mouse, Quit=Escape");
      setBounds(0,0,400,400);
      enableEvents(AWTEvent.MOUSE_EVENT_MASK|AWTEvent.MOUSE_MOTION_EVENT_MASK|AWTEvent.KEY_EVENT_MASK);
      show();
      Thread me = new Thread(this);
      me.start();
   }
   
   int [] controls = new int[256];
   MouseEvent mouse;

   //In the key handler, set and clear the flag[s]
   public void processEvent(AWTEvent e)
   {
      if(e instanceof KeyEvent)
      {
         KeyEvent ke = (KeyEvent)e;
         controls[ke.getKeyCode()&0xFF] = (ke.getID()==KeyEvent.KEY_RELEASED)?0:1;
      }
      else
      {
         mouse = (MouseEvent)e;
      }
   }
   
   
   //in the game loop, check the flags.
   public void run()
   {
      int x = 200,y = 200;
      while(controls[KeyEvent.VK_ESCAPE]==0)
      {
         int leftRight = controls[KeyEvent.VK_RIGHT] - controls[KeyEvent.VK_LEFT];
         int upDown = controls[KeyEvent.VK_DOWN] - controls[KeyEvent.VK_UP];
         
         if(mouse!=null)
         {
            x = mouse.getX();
            y = mouse.getY();
            mouse=null;
         }
         
         x+=leftRight;
         y+=upDown;
         
         Graphics g = getGraphics();
         g.setColor(Color.blue);
         g.fillOval(x-10,y-10,20,20);
         g.dispose();
         try
         {
            Thread.sleep(10);
         }
         catch(Exception e){}
      }
      dispose();
   }
   
   public static void main(String [] args)
   {
      new Pong();
   }
}

Thanks a ton guys. This is just what I’ve been looking for, now I will finally be able to make my own games in java.

Just two more questions, where are a bunch of good examples that I can look at to get learning faster?

And is it at all possible to make a game inside the Java console? Like with key input and color…

Thanks again!!!

Hello again, I’ve been having a lot of fun making games in JFrames, but I now want to “graduate” on to the Full Screen API. After searching around this forum a bit, I put to gether some code, but one again, I’m having a key input problem when using BufferStrategy to have two buffers, but when I just use the getgraphics() method off of my full Frame, it works fine. So my question is, how do I use the double buffering quickly.

Here’s some code
public void run() { Graphics g = bs.getDrawGraphics(); // Graphics g = fr.getGraphics(); //with this alone, it works fine... BufferedImage mage=null; try { mage = ImageIO.read(new File("Images\\dg_classm32.gif")).getSubimage(0, 0, 32, 32); } catch (IOException e) { e.printStackTrace(); } BufferedImage buffer = new BufferedImage(fr.getWidth(), fr.getHeight(), BufferedImage.SCALE_SMOOTH); int frames=0; double fps=0, start; while (true) { start = System.currentTimeMillis(); g.clearRect(0, 0, fr.getWidth(), fr.getHeight()); if (up) y--; if (down) y++; if (left) x--; if (right) x++; g.drawImage(mage, x, y, null); g.setColor(Color.red); g.drawString("FPS: " + (1000.0/(System.currentTimeMillis()-start)), 50, 50); bs.show(); }

Why does it run so slowly?! ???

What OS and Java version are you using?

I’m using WinXP Home with JRE v 1.4.2.

ok, several things :-

  1. By default BufferStrategy uses page flipping when in fullscreen, so you shouldn’t hold the graphics context you obtain from bs.getDrawGraphics(); inbetween calls to bs.show();
    Move your bs.getDrawGraphics(); line into your render loop so you get a new instance each render loop.
    Also, you should call g.dispose(); once youve finished rendering to it.

  2. pre-Java 1.5, Images returned from ImageIO are not accelerated, therefor, you have to copy them into an acceleratable image before they will be cached in vram.
    Note in 1.5, all BufferedImages are acceleratable (supposedly, though i doubt they will be…).

You will need to copy the image to an managed Image returned from graphicsConfiguration.createCompatibleImage(…)

  1. Timer resolution of currentTimeMillis() on WinXP is only 10ms, therefor that FPS code you have there will be terribly inaccurate. (it will likely flick between 100fps and NaN [due to div/0])

p.s.

With regard to managed images and all that stuff, this is a good read :-

http://weblogs.java.net/pub/wlg/385

All right, thanks for the tips, I think I can figure it out from there.
;D