I'm trying to make Pong with Java and Swing. However, I have two problems - one, the rectangle on-screen doesn't move at all, and two, a NullPointerException is happening even though the code still runs. Here are my two files:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Pong extends JFrame implements KeyListener {
private static final long serialVersionUID = -5782301423436L;
JPanel panel;
Paddle paddle1;
public Pong() {
super("Pong");
panel = new JPanel();
this.add(panel);
super.setPreferredSize(new Dimension(800, 600));
super.setVisible(true);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.pack();
addKeyListener(this);
paddle1 = new Paddle(100, 300);
}
public static void main(String[] args) {
#SuppressWarnings("unused")
Pong game = new Pong();
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, 800, 600);
g.setColor(Color.BLACK);
this.paddle1.draw(g);
}
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
// I've tested this part and the w and s key presses
// are still detected, but nothing happens. Why not?
public void keyPressed(KeyEvent keyEvent) {
int key = keyEvent.getKeyCode();
if (key == KeyEvent.VK_ESCAPE) {
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
} else if (key == KeyEvent.VK_W) {
this.paddle1.up();
} else if (key == KeyEvent.VK_S) {
this.paddle1.down();
}
}
}
import java.awt.Graphics;
public class Paddle {
// Position of the paddle's center
int x;
int y;
public Paddle(int x, int y) {
this.x = x;
this.y = y;
}
public void draw(Graphics g) {
g.fillRect(this.x - 10, this.y - 40, 20, 80);
}
// Up is negative because Java coordinates
public void up() {
this.y -= 30;
}
public void down() {
this.y += 30;
}
}
I've tested the keyPressed method in the Pong file, and when the w and s keys are pressed, it does, in fact, detect them (it will print a line out to the console). I'm very new to graphics in Java, and this is the first thing I've tried creating that's not copy-pasted from a tutorial on YouTube. I've been looking at my code for about an hour and can't find where the error could possibly be. Any and all help would be greatly appreciated.
I've checked the code and can help you solve the errors.
First, here you've shown the Paddle using paint() method of the JFrame Pong. You're detecting the 'W' and 'S' keys and according to the key you've written the code to update the y co-ordinate in the Paddle class.
But, the problem here is, you're just updating the y co-ordinate. You're not drawing the paddle with new co-ordinates again. So, after setting the new y value, you should draw the paddle again.
So, in the keyPressed() method, after
this.paddle1.up();
and
this.paddle1.down();
you should again call the draw() method of the Paddle class that will draw the Paddle at the modified location.
In this draw() method you're passing a Graphics object which is the graphics configuration of the current JFrame Pong. So, this can be get using getGraphics() method of the JFrame.
So, the line to add after up() or down() will be
this.paddle1.draw(getGraphics());
Since we are dealing with graphics, it is also the proper thing to repaint the JFrame once you change something in its graphics. If you have added only the above line after up() and down() method in the keyPressed() method, you will see the Paddle is not moving. Its previous drawing will be as it is and as per the key pressed, new Paddle will be drawn upwards or downwards. So, instead of a moving paddle you will see a vertical bar. This is happening because we are modifying the graphics but not painting the JFrame again. So, it will just add the new rectangle in the JFrame keeping previous as it is.
So, to get rid of this, you should use repaint() method of the JFrame so that it draws only the latest screen.
So, the complete keyPressed() method would be like :
#Override
public void keyPressed(KeyEvent keyEvent) {
int key = keyEvent.getKeyCode();
if (key == KeyEvent.VK_ESCAPE) {
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
} else if (key == KeyEvent.VK_W) {
this.paddle1.up();
this.paddle1.draw(getGraphics());
repaint();
} else if (key == KeyEvent.VK_S) {
this.paddle1.down();
this.paddle1.draw(getGraphics());
repaint();
}
}
So, now after detecting correct keys, it is drawing the paddle again and
after that discards the previous ones as well so that while you will run this code, you will be able to see the paddle is moving as per the key pressed.
Now, about the NullPointerException. I tried to run this code but I could not reproduce such exception. So, after trying the suggested code, if you encounter NPE, you can update the stack trace to get it solved.
I have read the comments and they are very useful. Do check the link that explains better way for listening to the key events.
Thank you.
Related
I am trying to do a PAC-MAN clone in Java. I am using swing and awt for the GUI.
I implemented motion into my main character and it responds to keys pressed, but when the character moves the "old image" stays there. So, rather than it looking like pacman moves through the screen he leaves a trail. It is my understanding that when I use the repaint() function the image should be cleared and painted again.
This is my code:
//PACMAN CLASS
import Entities.Ghost;
import Entities.Player;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Pacman extends JPanel implements KeyListener {
Player player = new Player("Pacman.png", 0, 0);
Ghost[] ghosts = new Ghost[4];
public Pacman(){
addKeyListener(this);
setFocusable(true);
ghosts[0] = new Ghost("Ghost_Red.png", 200, 200);
ghosts[1] = new Ghost("Ghost_Red.png", 150, 150);
ghosts[2] = new Ghost("Ghost_Red.png", 300, 100);
ghosts[3] = new Ghost("Ghost_Red.png", 50, 300);
}
public void paintComponent(Graphics g){
player.draw(g, this);
for(Ghost ghost: ghosts){
ghost.draw(g, this);
}
}
public void update() {
repaint();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER){
new Thread( () -> {
while (true){
update();
try{
Thread.sleep(10);
}catch(InterruptedException err){
err.printStackTrace();
}
}
}).start();
}
int SPEED = 4;
if(e.getKeyCode() == KeyEvent.VK_RIGHT && player.x < (getWidth() - player.width)){
player.x += SPEED;
}
if(e.getKeyCode() == KeyEvent.VK_LEFT && player.x > 0){
player.x -= SPEED;
}
if(e.getKeyCode() == KeyEvent.VK_UP && player.y > 0){
player.y -= SPEED;
}
if(e.getKeyCode() == KeyEvent.VK_DOWN && player.y < (getHeight() - player.height)){
player.y += SPEED;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
//MY MAIN
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Pacman");
Pacman panel = new Pacman();
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(224*2,288*2);
frame.setResizable(false);
}
}
I followed a tutorial to get here and the guy's graphic do work properly. Thoug he is using JAva 10 and I am using Java 16
When doing custom painting, and especially when doing some "animations", it's really important to call super.paintComponent(g); as the first line in your paintComponent(...) method
This will repaint all the things you're not painting on that "frame", and thus this is what you need to do to solve your problem.
If you want to know more in detail what super.paintComponent() does, then read this answer.
So your code should end up looking like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
player.draw(g, this);
for(Ghost ghost : ghosts) {
ghost.draw(g, this);
}
//Any extra painting do it here
}
Also, this line:
frame.setVisible(true);
Should be the last one on your program
And about this line:
frame.setSize(224*2,288*2);
Better override your JPanel's getPreferredSize, and then call frame.pack() this will make your pane to have that size and then add the frame decorations, otherwise your panel will be smaller than you think it is; for more information take a look at this question and answers: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
And as a tip, don't use "magic numbers", instead declare constants as of what those 224 and 288 means and why you multiply them by 2.
And forgot to mention that when programming games, it's better to use KeyBindings rather than infinite loops (while(true)) with KeyListeners; here's an excellent answer from #HovercraftFullOfEels that shows how to do it.
I am creating a small Java Jpanel game in which I am supposed to have a rocket that moves up and down via arrows and fires via space.
The firing method should work like this: Space bar pressed, thing fires and moves across screen , and then when it hits a certain x, it disappears. Also, you can only fire once until the other bullet disappears.
I do not know what I am doing wrong. For one, as soon as my code starts you can see a bullet flying across the screen.
2nd, the bullet is not disappearing.
3rd, even though the other bullet is still visible, it allows me to fire again.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class SpaceGame extends JPanel implements ActionListener{
Timer t = new Timer(2, this);
private ImageIcon rocket,asteroid,bullet;
private JLabel rocketlabel,ast1,ast2,ast3,bulletLabel;
public static int y=90,dy=0,bulletX=110,bulletY,i=0,canFire;
//public sound sound;
static boolean bulletFired=false;;
static JFrame f = new JFrame();
SpaceGame(){
this.setBackground(Color.black);
rocket = new ImageIcon(getClass().getResource("rocketFinal.png"));
rocketlabel= new JLabel(rocket);
this.add(rocketlabel);
asteroid = new ImageIcon(getClass().getResource("asteroid.png"));
ast1=new JLabel(asteroid);
ast2=new JLabel(asteroid);
ast3=new JLabel(asteroid);
bullet = new ImageIcon(getClass().getResource("bulletReal.png"));
bulletLabel = new JLabel(bullet);
canFire=1;
bulletLabel.setVisible(false);
this.add(ast1);this.add(ast2);this.add(ast3);this.add(bulletLabel);
f.addKeyListener(new controller());
this.setLayout(null);
this.setVisible(true);
}
public class controller implements KeyListener{
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode== KeyEvent.VK_UP) {
dy=-1;
}
if(keyCode== KeyEvent.VK_DOWN) {
dy=1;
}
if(keyCode== KeyEvent.VK_SPACE) {
if(canFire==0) {
System.out.println(String.valueOf(canFire));
bulletFired = true;
bulletY = y;
bulletX=110;
}canFire=1;
}
}
#Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch(key) {
case KeyEvent.VK_UP: dy=0; break;
case KeyEvent.VK_DOWN: dy=0; break;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
rocketlabel.setBounds(45,y,rocket.getIconWidth(),80);
fireBullet();
paintStars(g);
t.start();
}
public void paintStars(Graphics g) {
g.setColor(Color.yellow);
for(int i=0; i<5;i++) {
Random rand = new Random();
int o = rand.nextInt(500);
int p = rand.nextInt(300);
g.fillOval(o, p, 3, 3);
}
}
public void actionPerformed(ActionEvent e) {
if(y==-20) y=249;
if(y==250)y=-20;
y+=dy;
if(bulletFired=true) {
bulletX++;
if(bulletX==455)bulletFired=false;bulletLabel.setVisible(false);System.out.println(String.valueOf(bulletX)); canFire=0;
}
repaint();
}
public void fireBullet(){
if(bulletFired=true) {
bulletLabel.setVisible(true);
bulletLabel.setBounds(bulletX,bulletY+25,bullet.getIconHeight(),bullet.getIconWidth());
}
}
public static void main(String[] args) {
String filepath = "SpaceGameMusic.wav";
musicStuff musicPlayer = new musicStuff();
musicPlayer.playMusic(filepath);
SpaceGame t = new SpaceGame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(t);
f.setSize(500,335);
f.setVisible(true);
f.setResizable(false);
}
}
For one, as soon as my code starts you can see a bullet flying across the screen.
The paintComponent() method is for painting only. You can't control when Swing will determine a component needs to be repainted.
So, for example:
t.start();
should NOT be in the painting method. As soon as the frame is made visible the panel will be painted and the Timer will be started.
You application code should control when the Timer is started.
Other issues:
you should not be using static variables. The variable should simply be instances of your class.
the paintStars() method should not generate random locations. Again. a painting method should only paint the current state of the class. So if you want to change the location of the stars you should have a method like randomizeStars(). In this method you would update an ArrayList of Point objects. Each Point instance would represent the location of a star. Then the paintStars() method would simply iterate through the ArrayList and paint each star.
you should not be using a KeyListener. A KeyListener only works if a component has focus. You can't guarantee that your component will lose focus. Instead you should be using Key Bindings. Key bindings allow you to handle a KeyEvent even if the component doesn't have focus. See Motion Using the Keyboard for more information and a working example.
you can only fire once until the other bullet disappears
Your canFire variable should be a boolean variable so it only has true/false values. Again you have a method that sets the state. Your game logic will then check the state before firing the bullet again.
if(y==-20) y=249;
if(y==250)y=-20;
Don't hardcode values. The number should be based on the size of your panel. So you use methods like getWidth() and getHeight() to determine the current size of the panel.
The problem was quite simply that I had forgotten to use == in my if(boolean) statements.
I am making a classic space shooter type game for a GUI project, using JPanels and swing etc. I have a multi-layered main menu with a button for play and exit etc, however, the issue I am having is in my DrawPanel class which paints all the graphics for the game. This class has one giant paint component method that activates on a timer every 500 ms, and renders blinking stars on a background, renders the spaceship, and renders falling enemies. The stars are blinking, the enemies are falling but the spaceship is not moving at all and I have to use the "a" and "d' keys. The question: how can I move the ship? Below is an extremely summarized version of that rendering class to keep it simple, the paint component class is full of other things however including generating blinking stars.
public DrawPanel(int x, int y)
{
t = new Timer(500, new ActionListener(){#Override public void actionPerformed (ActionEvent event){repaint();}});
this.addKeyListener(new KeyAdapter()
{
#Override public void keyPressed(KeyEvent e) { ship.keyPressed(e);System.out.println("keypressed");}
#Override public void keyReleased(KeyEvent e) { ship.keyReleased(e);System.out.println("keyreleased");}
});
t.start();
}
public void paintComponent(Graphics Graphic)
{
super.paintComponent(Graphic);
drawShip(Graphic);
drawEnemy(Graphic,10);
}
private void drawShip(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(ship.getImage(), ship.getX(),
ship.getY(), this);
}
This is the spaceship class that is called by the drawship method, and that explains the calls by variable "ship" in the previous code.(Note: there are no compiler errors or runtime errors in anything)
public class Spaceship {
private int x = 780;
private int y = 850;
private Image image;
public Spaceship()
{
ImageIcon ii = new ImageIcon("spaceship.png");
image = ii.getImage();
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key ==KeyEvent.VK_A) {
x += -10;
}
if (key == KeyEvent.VK_D) {
x+= 10;
}
}
public void keyReleased(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_A) {
x += 0;
}
if (key == KeyEvent.VK_D) {
x += 0;
}
}
}
My thought process on this was that the keyboard would be active at all times and anytime it registered a press it would modify an x so that the next time the ship would be drawn it would be in a different position. However it would be bound by the same time that the enemies and background are bound by and wouldn't fluidly move side to side now that I think about it, but I have no clue how to make a separate paint component, timer, and repaint as that's the only way I know to paint. Also
I do realize I haven't done bound checking on this for the ship movement but that shouldn't be an issue right now as it's not even moving a centimeter yet, much less out of bounds, and it is not even printing the debug statements in the listener.
All of my classes are added to panels which have been bound to buttons and tabs in my main menu by adding it all to default constructor of the main method, so this is how the entire game executes:
public static void main(String[] args) throws IOException
{
GalagaRipOff main = new GalagaRipOff();
}
I created this java program and I wanted an output of which if int x and int y are above 100, it would draw a rectangle. But it doesn't. How can I make it work?Is there another line of code I need to add?
Here's my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameSetup extends JPanel implements MouseMotionListener{
public static JFrame njf = new JFrame("Test");
public static int x, y;
public static void main(String[] args){
GameSetup gs = new GameSetup();
njf.add(gs);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.BLACK);
g.setColor(Color.GREEN);
g.fillRect(150, 75, 200, 100);
g.setColor(Color.ORANGE);
g.drawString("Play", 239, 123);
njf.addMouseListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
x = e.getX();
y = e.getY();
}
});
if(x > 100 && y > 100){
g.drawRect(10, 10, 100, 100);
}
}
public GameSetup(){
njf.setSize(500,500);
njf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
njf.setResizable(false);
njf.setLocationRelativeTo(null);
njf.setVisible(true);
}
#Override
public void mouseDragged(MouseEvent arg0) {
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
Okay, there are several things wrong with the code that you included above.
The first that stood out to me was the way that you are adding the mouse action listener to frame. There are multiple things that are wrong with this.
First of all, you are doing this in the paintComponent method, which, if it works, is still considered to be bad practice because the paintComponent method may be called multiple times. Do that, as pointed out by the comments, in the constructor of the panel.
The second is that you are adding the mouse listener to the frame, not the panel, which doesn't work because the panel is "above" the frame, so the mouse event will only be recognized within the panel. Your best bet here is to add a MouseMotionListener directly to the panel itself.
The third is that you are implementing the MouseMotionListenerinterface in the GameSetup class, but never actually doing anything with this implementation. So what I did was that I got rid of the inner class, and just had the panel be its own MouseMotionListnener
The second thing that is wrong with the code is that the paintComponent method is only called at certain points in time (see this). That means that even though the mouse might have moved within the zone, your paintComponent method isn't being called to update the screen accordingly. For this, you need to call the repaint method for your panel.
The third is that you didn't set a size for your panel, and the default one is 0x0, so you need to set a size of your panel (which should be the same as the frame itself) (if you want to keep the default layout).
All of that being said, here is your code that I fixed up. I added a variable called enteredZone to keep track of if the mouse had previously entered the zone, so that the rectangle would stay up even if the mouse left the zone after entering it (its your choice if you want to keep it). Note that there are other things with this code that might be considered bad practice, but this is enough to get you started:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameSetup extends JPanel implements MouseMotionListener {
public static JFrame njf = new JFrame("Test");
public static int x = 0, y = 0;
public static boolean enteredZone = false;
public static void main(String[] args) {
GameSetup gs = new GameSetup();
gs.addMouseMotionListener(gs);
njf.add(gs);
njf.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.BLACK);
g.setColor(Color.GREEN);
g.fillRect(150, 75, 200, 100);
g.setColor(Color.ORANGE);
g.drawString("Play", 239, 123);
if (x > 100 && y > 100 || enteredZone){
g.drawRect(10, 10, 100, 100);
enteredZone = true;
}
}
public GameSetup() {
super();
setSize(500, 500);
njf.setSize(500,500);
njf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
njf.setResizable(false);
njf.setLocationRelativeTo(null);
}
#Override
public void mouseDragged(MouseEvent arg0) {
}
#Override
public void mouseMoved(MouseEvent e) {
x = e.getX();
y = e.getY();
if (x > 100 && y > 100) repaint();
}
}
There's no errors, but when I press any of the buttons, my oval/circle doesn't move at all? Can anyone help? I've been looking up and down the code for about 20 minutes seeing if I typed anything wrong or put something in the wrong place. I can't tell if this is with the way I'm moving it or my thread.
package com.badfitz66.mainpackage;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Main extends JFrame implements Runnable
{
int x, y, xDirection, yDirection;
private Image dbImage;
private Graphics dbG;
Font font = new Font("Black Caps", Font.ITALIC | Font.BOLD, 30);
public void run()
{
try
{
while(true)
{
Move();
Thread.sleep(5);
}
}
catch(Exception e){
System.out.println("Error");
}
}
public void Move()
{
x += xDirection;
y += yDirection;
if (x <= 0)
x = 0;
if(x >= 500)
x = 500;
if (y <= 50)
y = 50;
if (y >= 250)
y = 250;
}
public void setXDir(int xdir)
{
xDirection = xdir;
}
public void setYDir(int ydir)
{
yDirection = ydir;
}
public class AL extends KeyAdapter
{
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
if(keyCode == e.VK_D)
{
setXDir(+1);
}
if(keyCode == e.VK_A)
{
setXDir(-1);
}
if(keyCode == e.VK_W)
{
setYDir(-1);
}
if(keyCode == e.VK_S)
{
setYDir(+1);
}
}
public void keyReleased(KeyEvent e)
{
int keyCode = e.getKeyCode();
if(keyCode == e.VK_D)
{
setXDir(0);
}
if(keyCode == e.VK_A)
{
setXDir(0);
}
if(keyCode == e.VK_W)
{
setYDir(0);
}
if(keyCode == e.VK_S)
{
setYDir(0);
}
}
}
public Main()
{
addKeyListener(new AL());
setTitle("Java game testing");
setResizable(false);
setVisible(true);
setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.green);
x = 150;
y = 150;
}
public void paint(Graphics g)
{
dbImage = createImage(getWidth(),getHeight());
dbG = dbImage.getGraphics();
paintComponent(dbG);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g)
{
g.setFont(font);
g.drawString("Hello world", 125, 50);
g.setColor(Color.cyan);
g.fillOval(x, y, 15, 15);
repaint();
}
public static void main(String[] args)
{
Main jg = new Main();
//Threads
Thread t1 = new Thread();
t1.start();
}
}
You never call repaint from within your Move method
Thread t1 = new Thread(); won't do much, as it will never call any runnable code, in fact it will start and terminate almost immediately, how ever...
Swing is not thread safe and you should never modify the UI or anything the UI relies on from outside the Event Dispatching Thread, this especially important, as a paint cycle could occur at any time. See Concurrency in Swing for more details
You override the paint method of a top level container (JFrame) and then break the paint chain...paint is complex series of method calls chained together to generate the final result, you should always call super.paint first, but as you probably know, JFrame is not double buffered. So instead, you should create another class that extends from JPanel and override it's paintComponent method to perform the actual painting (in fact, for the most part, it should pretty much replace the functionality that the current JFrame is doing)...Swing components are double buffered by default...
Calling repaint from within a paint method...this is bad news and this will immediately schedule another paint cycle, this becomes so fast that it consume all your CPU cycles till you computer stands still
Using KeyListener. KeyListener is notorious for having issues, in particular, it will only ever trigger a key event if the component it is registered to IS FOCUSABLE and HAS FOCUS. A JFrame is made up of the physical window, the JRootPane, which holds the content pane (and a few other components), all of which can get in the way of the frame actually getting focus. Instead, using the previously mentioned JPanel, use the key bindings API, which will allow to control the level of focus required for the key events to be triggered. See How to Use Key Bindings for more details
You should also have a look at...
Performing Custom Painting
Painting in AWT and Swing
How to use Swing Timers