Help with KeyListener and graphics
I'm making a small pong game, in which there are 2 rackets - 1 of them is moved y the up and down arrows by the user.
Now, the problem is that when I'm pressing the up and down keys the racket won't move.
I've checked to see if the KeyEvent is executed, and it is, but the racket still refuses to move.
Here are the classes:
Main Class:
#2228610 - Pastie
Racket Class:
#2228525 - Pastie
thanks in advance.
Re: Help with KeyListener and graphics
Please post your code here in the thread. Be sure to wrap it in code tags. See: BB Code List - Java Programming Forums or press Go Advanced and use the # icon
Re: Help with KeyListener and graphics
Main:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.Color;
import java.awt.Graphics;
import java.util.logging.*;
import javax.swing.*;
/**
*
* @author User
*/
public class Main extends JFrame implements Runnable{
JPanel p = new JPanel();;
Racket p1,comp;
public Main(){
//Form info
super("Pong - made by Alex.");
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400,300);
setResizable(false);
//JPanel
p.setBackground(new Color(0,155,0));
this.add(p);
}
public static void main(String[] args) {
new Main();
}
public void paint(Graphics g){
super.paint(g);
//Players
p1 = new Racket(20, this.getHeight()/3, 20, 100, this.getHeight(), false, g, p.getBackground());
comp = new Racket(this.getWidth()-40, this.getHeight()/3, 20, 100, this.getHeight(), true, g, p.getBackground());
//KeyListeners
this.addKeyListener(p1);
this.addKeyListener(comp);
//draw Players
p1.drawMe();
comp.drawMe();
}
public void run() {
}
}
Racket:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
*
* @author User
*/
public class Racket implements KeyListener{
private int x,y,width,height;
private int formHeight;
private int defaultX, defaultY;
boolean auto;
Graphics g;
Color pColor;
public Racket(int x, int y, int width, int height, int formHeight, boolean auto, Graphics g, Color panelColor) {
this.x = x;
this.y = y;
defaultX = x;
defaultY = y;
this.width = width;
this.height = height;
this.formHeight = formHeight;
this.auto = auto;
this.g = g;
pColor = panelColor;
}
public boolean isAuto() {
return auto;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void moveUp(int speed){
if(y-speed>=0){
this.y -= speed;
drawMe();
}
}
public void moveDown(int speed){
if(y+speed <= formHeight){
this.y += speed;
drawMe();
}
}
public void drawMe(){
clear();
g.setColor(Color.green);
g.fillRect(x, y, width, height);
}
public void drawMeDefault(){
}
public void clear(){
g.setColor(pColor);
g.fillRect(x, y, width,height);
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if(!isAuto()){
if(e.getKeyCode() == KeyEvent.VK_UP)
moveUp(5);
else if(e.getKeyCode() == KeyEvent.VK_DOWN)
moveDown(5);
}
}
public void keyReleased(KeyEvent e) {
}
}
Re: Help with KeyListener and graphics
One comment so far, you should pass the Graphics object that the paint method receives to the drawing method each time the paint method is called. Don't save an old version and reuse it.
Don't create a new Racket object every time paint is called. You lose the contents of the old object.
Re: Help with KeyListener and graphics
I've changed the code as you said, but the rackets still refuse to move
this is the new codes(not a lot of difference)
Main:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.Color;
import java.awt.Graphics;
import java.util.logging.*;
import javax.swing.*;
/**
*
* @author User
*/
public class Main extends JFrame implements Runnable{
JPanel p = new JPanel();;
Racket p1,comp;
Graphics g;
public Main(){
//Form info
super("Pong - made by Alex.");
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400,300);
setResizable(false);
//JPanel
p.setBackground(new Color(0,155,0));
this.add(p);
//Players
p1 = new Racket(20, this.getHeight()/3, 20, 100, this.getHeight(), false, g, p.getBackground());
comp = new Racket(this.getWidth()-40, this.getHeight()/3, 20, 100, this.getHeight(), true, g, p.getBackground());
}
public static void main(String[] args) {
new Main();
}
public void paint(Graphics g){
super.paint(g);
this.g = g;
//renew graphics
p1.setGraphics(g);
comp.setGraphics(g);
//KeyListeners
this.addKeyListener(p1);
this.addKeyListener(comp);
//draw Players
p1.drawMe();
comp.drawMe();
}
public void run() {
}
}
Racket:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
*
* @author User
*/
public class Racket implements KeyListener{
private int x,y,width,height;
private int formHeight;
private int defaultX, defaultY;
boolean auto;
Graphics g;
Color pColor;
public Racket(int x, int y, int width, int height, int formHeight, boolean auto, Graphics g, Color panelColor) {
this.x = x;
this.y = y;
defaultX = x;
defaultY = y;
this.width = width;
this.height = height;
this.formHeight = formHeight;
this.auto = auto;
this.g = g;
pColor = panelColor;
}
public boolean isAuto() {
return auto;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setGraphics(Graphics g){
this.g = g;
}
public void moveUp(int speed){
if(y-speed>=0){
this.y -= speed;
drawMe();
}
}
public void moveDown(int speed){
if(y+speed <= formHeight){
this.y += speed;
drawMe();
}
}
public void drawMe(){
clear();
g.setColor(Color.green);
g.fillRect(x, y, width, height);
}
public void drawMeDefault(){
}
public void clear(){
g.setColor(pColor);
g.fillRect(x, y, width,height);
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if(!isAuto()){
if(e.getKeyCode() == KeyEvent.VK_UP)
moveUp(5);
else if(e.getKeyCode() == KeyEvent.VK_DOWN)
moveDown(5);
}
}
public void keyReleased(KeyEvent e) {
}
}
Re: Help with KeyListener and graphics
You still have some code in the paint() method that should be in the constructor. adding key listeners.
The Graphics object should be passed to the drawing methods directly, not via a setter.
What does this statement do in paint()?
this.g = g;
You should use it as needed for drawing, not save it. See above.
The move methods should call repaint() not the drawMe method. repaint() will cause a call to be made to paint() with a Graphics object which can be passed to the drawMe methods
Re: Help with KeyListener and graphics
ok thanks, but now I have another problem.
How can I use the reapint method through the Racket class without causing a recursion?
The problem is that I need to create a new Main object and then use this object to use the show method which shows the window.
but the problem is that it calls the main constructor, in which are the racket objects are created, and once they are created they call the main constructor again.
How can I avoid this loop?
Re: Help with KeyListener and graphics
never mind solved this bu sending the main object to the racket class through the racket's constructor.
But if you have a better way to solve this please share.
Re: Help with KeyListener and graphics
Perhaps if you posted the latest code we could make some suggestions?
Re: Help with KeyListener and graphics
Here is the code.
But I have a problem, the ball won't draw, although it moves.
Main Class:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.*;
import javax.swing.*;
/**
*
* @author User
*/
public class Main extends JFrame implements Runnable{
JPanel p = new JPanel();;
Racket p1,comp;
Ball ball;
Thread t;
public Main(){
//Form info
super("Pong - made by Alex.");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400,300);
setResizable(false);
setVisible(true);
//JPanel
p.setBackground(new Color(0,155,0));
this.add(p);
//Players
p1 = new Racket(20, this.getHeight()/3, 20, 100, this.getHeight(), false,this);
comp = new Racket(this.getWidth()-40, this.getHeight()/3, 20, 100, this.getHeight(), true,this);
//Ball
ball = new Ball(WIDTH/2, HEIGHT/2, 10, 3, HEIGHT, WIDTH, this, p1, comp);
//KeyListeners
this.addKeyListener(p1);
this.addKeyListener(comp);
}
public static void main(String[] args) {
new Main();
}
public void paint(Graphics g){
super.paint(g);
//set graphics
p1.setGraphics(g);
comp.setGraphics(g);
ball.setGraphics(g);
//draw Players
p1.drawMe();
comp.drawMe();
//draw ball
ball.drawMe();
//Thread
t = new Thread(this);
t.start();
}
public void run() {
while(!ball.didLose()){
try { t.sleep(50);}
catch (InterruptedException ex) {}
ball.move();
if(ball.getX() - (ball.getDiameter()/2)<p1.getX() + p1.getWidth() || ball.getX() + (ball.getDiameter()/2)> p1.getX()){
this.getContentPane().getGraphics().setColor(p.getBackground());
this.getContentPane().getGraphics().fillRect(0, 0, WIDTH, HEIGHT);
p1.restart();
comp.restart();
ball.restart();
ball.setLost(true);
try { t.sleep(1000);}
catch (InterruptedException ex) {}
}
}
}
}
Racket:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
*
* @author User
*/
public class Racket implements KeyListener{
private int x,y,width,height;
private int formHeight;
private int defaultX, defaultY;
boolean auto;
Graphics g;
Main m;
public Racket(int x, int y, int width, int height, int formHeight, boolean auto, Main m) {
this.x = x;
this.y = y;
defaultX = x;
defaultY = y;
this.width = width;
this.height = height;
this.formHeight = formHeight;
this.auto = auto;
this.m = m;
}
public boolean isAuto() {
return auto;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth(){
return width;
}
public void setGraphics(Graphics g){
this.g = g;
}
public void moveUp(int speed){
if(y-speed>=30){
this.y -= speed;
} else
this.y = 30;
}
public void moveDown(int speed){
if(y+speed+height <= formHeight){
this.y += speed;
} else
this.y = formHeight-height;
}
public void drawMe(){
g.setColor(Color.green);
g.fillRect(x, y, width, height);
}
public void restart(){
this.x = defaultX;
this.y = defaultY;
m.repaint();
}
public void think(){
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if(!isAuto()){
if(e.getKeyCode() == KeyEvent.VK_UP){
moveUp(5);
m.repaint();
}
else if(e.getKeyCode() == KeyEvent.VK_DOWN){
moveDown(5);
m.repaint();
}
} else
think();
}
public void keyReleased(KeyEvent e) {
}
}
Ball:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.*;
/**
*
* @author User
*/
public class Ball {
private int x,y,d,speed;
private int formHeight,formWidth;
private int defaultX, defaultY;
private int directionX, directionY;
private int ranX,ranY;
boolean lost;
Graphics g;
Main m;
Racket p1,p2;
public Ball(int x, int y, int d,int speed, int formHeight, int formWidth, Main m, Racket p1, Racket p2) {
this.x = x;
this.y = y;
this.defaultX = x;
this.defaultY = y;
this.d = d;
this.speed = speed;
this.formHeight = formHeight;
this.formWidth = formWidth;
this.m = m;
this.p1 = p1;
this.p2 = p2;
lost = false;
ranX = (int)(Math.random()*((speed+3)-speed+1)+speed);
ranY = (int)(Math.random()*((speed+3)-speed+1)+speed);
}
public void setGraphics(Graphics g) {
this.g = g;
}
public void setSpeed(int s){
speed = s;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setLost(boolean b){
lost = b;
}
public boolean didLose(){
return lost;
}
public void setDirectionX(int directionX) {//1-right, 2-left
this.directionX = directionX;
}
public void setDirectionY(int directionY) {//1-up, 2-down
this.directionY = directionY;
}
public int getDirectionX() {
return directionX;
}
public int getDirectionY() {
return directionY;
}
public int getDiameter(){
return d;
}
public void move(){
if(y-(d/2)<=30 || y+(d/2)>=formHeight)
ranY = -ranY;
if(x-(d/2)==p1.getX()+p1.getWidth() || x+(d/2)==p2.getX())
ranX = -ranX;
if(directionX==1)
x+=ranX;
else
x-=ranX;
if(directionY==1)
y-=ranY;
else
y+=ranY;
m.repaint();
}
public void drawMe(){
g.setColor(Color.red);
g.fillOval(x,y, d, d);
}
public void restart(){
this.x = defaultX;
this.y = defaultY;
m.repaint();
}
}
Re: Help with KeyListener and graphics
Several problems I see:
Passing a graphics with a setter method vs only to the draw methods.
Starting a new thread every time paint is called.
The paint method should be short and simple:
Code :
public void paint(Graphics g){
super.paint(g);
//draw Players
p1.drawMe(g);
comp.drawMe(g);
//draw ball
ball.drawMe(g);
}
Re: Help with KeyListener and graphics
But the problem is, that I'm also using the drawMe function inside the class itself, thus I'll need to send a Graphics object within the class itself also each time I'm using the drawMe() function.
But the problem is that I don't have a Graphics object if I wont send it to the class through a setter.
Re: Help with KeyListener and graphics
Quote:
he problem is, that I'm also using the drawMe function inside the class itself,
Where? I don't see where it is called from anywhere except the paint method.
Re: Help with KeyListener and graphics
Oh right, I was using it before I have modified the code.
Ill check it and I'll post the results.
Re: Help with KeyListener and graphics
You need to do some debugging to see what the code is doing. Add some printlns to show the execution flow and the values of variables as they change.
Re: Help with KeyListener and graphics
I'm always doing this, and when I did this all the ball methods execute normally till the end, but it just wont show on the screen.
And the results after modifying the code again were that the ball still wont show.
code:
Main:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.logging.*;
import javax.swing.*;
/**
*
* @author User
*/
public class Main extends JFrame implements Runnable{
JPanel p = new JPanel();;
Racket p1,comp;
Ball ball;
Thread t;
public Main(){
//Form info
super("Pong - made by Alex.");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400,300);
setResizable(false);
setVisible(true);
//JPanel
p.setBackground(new Color(0,155,0));
this.add(p);
//Players
p1 = new Racket(20, this.getHeight()/3, 20, 100, this.getHeight(), false,this);
comp = new Racket(this.getWidth()-40, this.getHeight()/3, 20, 100, this.getHeight(), true,this);
//Ball
ball = new Ball(WIDTH/2, HEIGHT/2, 10, 3, HEIGHT, WIDTH, this, p1, comp);
//KeyListeners
this.addKeyListener(p1);
this.addKeyListener(comp);
//Thread
t = new Thread(this);
t.start();
}
public static void main(String[] args) {
new Main();
}
public void paint(Graphics g){
super.paint(g);
//draw Players
p1.drawMe(g);
comp.drawMe(g);
//draw ball
ball.drawMe(g);
}
public void run() {
while(!ball.didLose()){
try { t.sleep(50);}
catch (InterruptedException ex) {}
ball.move();
if(ball.getX() - (ball.getDiameter()/2)<p1.getX() + p1.getWidth() || ball.getX() + (ball.getDiameter()/2)> p1.getX()){
this.getContentPane().getGraphics().setColor(p.getBackground());
this.getContentPane().getGraphics().fillRect(0, 0, WIDTH, HEIGHT);
p1.restart();
comp.restart();
ball.restart();
ball.setLost(true);
try { t.sleep(1000);}
catch (InterruptedException ex) {}
}
}
}
}
Racket:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
*
* @author User
*/
public class Racket implements KeyListener{
private int x,y,width,height;
private int formHeight;
private int defaultX, defaultY;
boolean auto;
Main m;
Ball ball;
public Racket(int x, int y, int width, int height, int formHeight, boolean auto, Main m) {
this.x = x;
this.y = y;
defaultX = x;
defaultY = y;
this.width = width;
this.height = height;
this.formHeight = formHeight;
this.auto = auto;
this.m = m;
}
public boolean isAuto() {
return auto;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth(){
return width;
}
public void moveUp(int speed){
if(y-speed>=30){
this.y -= speed;
} else
this.y = 30;
}
public void moveDown(int speed){
if(y+speed+height <= formHeight){
this.y += speed;
} else
this.y = formHeight-height;
}
public void drawMe(Graphics g){
g.setColor(Color.green);
g.fillRect(x, y, width, height);
}
public void restart(){
this.x = defaultX;
this.y = defaultY;
m.repaint();
}
public void think(){
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if(!isAuto()){
if(e.getKeyCode() == KeyEvent.VK_UP){
moveUp(5);
m.repaint();
}
else if(e.getKeyCode() == KeyEvent.VK_DOWN){
moveDown(5);
m.repaint();
}
} else
think();
}
public void keyReleased(KeyEvent e) {
}
}
Ball:
Code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package pongnew;
import java.awt.*;
/**
*
* @author User
*/
public class Ball {
private int x,y,d,speed;
private int formHeight,formWidth;
private int defaultX, defaultY;
private int directionX, directionY;
private int ranX,ranY;
boolean lost;
Main m;
Racket p1,p2;
public Ball(int x, int y, int d,int speed, int formHeight, int formWidth, Main m, Racket p1, Racket p2) {
this.x = x;
this.y = y;
this.defaultX = x;
this.defaultY = y;
this.d = d;
this.speed = speed;
this.formHeight = formHeight;
this.formWidth = formWidth;
this.m = m;
this.p1 = p1;
this.p2 = p2;
lost = false;
ranX = (int)(Math.random()*((speed+3)-speed+1)+speed);
ranY = (int)(Math.random()*((speed+3)-speed+1)+speed);
}
public void setSpeed(int s){
speed = s;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setLost(boolean b){
lost = b;
}
public boolean didLose(){
return lost;
}
public void setDirectionX(int directionX) {//1-right, 2-left
this.directionX = directionX;
}
public void setDirectionY(int directionY) {//1-up, 2-down
this.directionY = directionY;
}
public int getDirectionX() {
return directionX;
}
public int getDirectionY() {
return directionY;
}
public int getDiameter(){
return d;
}
public void move(){
if(y-(d/2)<=30 || y+(d/2)>=formHeight)
ranY = -ranY;
if(x-(d/2)==p1.getX()+p1.getWidth() || x+(d/2)==p2.getX())
ranX = -ranX;
if(directionX==1)
x+=ranX;
else
x-=ranX;
if(directionY==1)
y-=ranY;
else
y+=ranY;
m.repaint();
}
public void drawMe(Graphics g){
g.setColor(Color.red);
g.fillOval(x,y, d, d);
}
public void restart(){
this.x = defaultX;
this.y = defaultY;
m.repaint();
}
}
Re: Help with KeyListener and graphics
I don't see ANY debugging printlns in your code. You need to add some to show what is happening.
Add them at all the places decisions are made and values are changed that effect the problem you are hhaving.
Re: Help with KeyListener and graphics
From the debugging I found the problem.
It always draws the ball at x - 0, y-1 and it never changes although the other functions are executed till the end.
The problem was that I've sent to the racket class the WIDTH and HEIGHT parameters, instead of sending this.getWidth() and this.getHeight().
And i've also changed the ball size, so now I'm able to see him on the screen.
However it still wont move from his spot.
Re: Help with KeyListener and graphics
I think there are several math problems.
Re: Help with KeyListener and graphics
There is a problem with setting up the GUI. You set the frame visible before creating the rackets and ball. As soon as a component is made visible, its paint method will be called. This means that the frame paint method may try to call methods on the rackets and ball before they have been created - this will depend on whether the Swing event dispatching completes before the rackets and ball are created- it's pot luck, as the order and timing of Swing events is not guaranteed. Basically, you should never set your main frame visible until the GUI is complete and any components or objects that will be used in the paint methods have been created.
There is a similar problem with the form height and width used by the rackets and ball. If you correctly create the rackets & ball before setting the frame visible, the frame won't have a height or width yet, so you can't use it in the racket & ball constructors. You pass the Main object to them in any case, so you can grab its height and width whenever necessary by calling its height & width methods - i.e. you don't need to store those values in the rackets & ball. Removing those constructor parameters also simplifies things a fair bit.
Also, you're creating the Ball with constant values WIDTH and HEIGHT, which you haven't declared or initialized anywhere. The only reason you don't get a compiler error is that those constants happen to be defined in the completely unrelated ImageObserver interface which JFrame implements. If you want constant values for ball width and height, declare and initialize them and call them something like BALL_WIDTH and BALL_HEIGHT so it's clear what they're for, and they don't shadow pre-existing WIDTH & HEIGHT constants.
Re: Help with KeyListener and graphics
The problem was this line:
Code :
if(ball.getX() - (ball.getDiameter()/2)<p1.getX() + p1.getWidth() || ball.getX() + (ball.getDiameter()/2)> p1.getX()){
The expression was always true because i wrote
Code :
|| ball.getX() + (ball.getDiameter()/2)> p1.getX()
insted of
Code :
|| ball.getX() + (ball.getDiameter()/2)> comp.getX()
Re: Help with KeyListener and graphics
Thank you all for your help.
I've finally finished my project.
Thanks a lot for all your help!