I'm making a game, and I have a Main Menu that works perfectly. When I select one of the options, it brings up another Menu in a new window. However in this new window, the KeyListener is not responding. If I click back to the Main Menu window, the KeyListener is still working there. Here is the code:
MainMenu:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
public class DisplayMainMenu extends JFrame implements KeyListener{
static int width = 799, height = 463;
int arrowPos = 310;
boolean clear = true;
BufferedImage menu = null;
BufferedImage arrow = null;
LevelSkip test = new LevelSkip();
boolean done = false;
static DisplayMainMenu main;
public static void main(String[] args){
main = new DisplayMainMenu();
main.setResizable(false);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
main.init();
}
public void init() {
try{
menu = ImageIO.read(new File("Main Menu.png"));
arrow = ImageIO.read(new File("arrow.png"));
}catch(IOException ie) {
System.out.println(ie.getMessage());
}
this.setSize(width, height);
this.addKeyListener(this);
clear = true;
paint(getGraphics());
}
public void paint (Graphics g){
if(clear==true){
g.drawImage(menu,0,0,null);
clear = false;
}
g.drawImage(arrow,275,arrowPos,null);
}
public void keyPressed(KeyEvent e){
String key = e.getKeyText(e.getKeyCode());
if(key == "Up"){
clear = true;
if (arrowPos > 310)
arrowPos -= 30;
else
arrowPos = 370;
paint(getGraphics());
}
if(key == "Down"){
clear = true;
if (arrowPos < 370)
arrowPos += 30;
else
arrowPos = 310;
paint(getGraphics());
}
if(key == "Space"){
done = true;
switch(arrowPos){
case 310: System.out.println("RUN NEW GAME"); test.init();
break;
case 340: System.out.println("RUN HIGH SCORES");
break;
case 370: System.exit(0);
}
}
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
}
LevelSkip:
import java.awt.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
public class LevelSkip extends JFrame implements KeyListener {
static int width = 799, height = 463;
int arrowPos = 109;
boolean clear = true;
BufferedImage menu = null;
BufferedImage arrow = null;
public void init() {
LevelSkip main = new LevelSkip();
main.setSize(width, height);
main.requestFocusInWindow();
main.addKeyListener(main);
main.setResizable(false);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
try{
menu = ImageIO.read(new File("level skip.png"));
arrow = ImageIO.read(new File("arrow2.png"));
}catch(IOException ie) {
System.out.println(ie.getMessage());
}
clear = true;
paint(main.getGraphics());
}
public void paint (Graphics g){
if(clear==true){
g.drawImage(menu,0,0,null);
clear = false;
}
g.drawImage(arrow,arrowPos,355,null);
}
public void keyPressed(KeyEvent e){
String key = e.getKeyText(e.getKeyCode());
if(key == "Left"){
clear = true;
if (arrowPos > 109)
arrowPos -= 260;
else
arrowPos = 629;
paint(getGraphics());
}
if(key == "Right"){
clear = true;
if (arrowPos < 629)
arrowPos += 260;
else
arrowPos = 109;
paint(getGraphics());
}
if(key == "Space"){
switch(arrowPos){
case 109: System.out.println("ADD 1 TO LEVEL AND RUN BATTLE");
break;
case 369: System.out.println("ADD 5 TO LEVEL AND RUN BATTLE");
break;
case 629: System.out.println("ADD 10 TO LEVEL AND RUN BATTLE");
}
}
}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
}
I'm not exactly sure what the problem is, the Level Skip window displays fine, it just doesn't register any key presses.
If you've searched on this problem at all, you'll see that it almost always means that the component being listened to doesn't have focus. 90% of the time the solution is to use Key Bindings.
Your other problem is that you're comparing Strings ==. You don't want to do this. Use the equals or the equalsIgnoreCase(...) method instead. Understand that == checks if the two objects are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here. So instead of
if (fu == "bar") {
// do something
}
do,
if (fu.equals("bar")) {
// do something
}
or,
if (fu.equalsIgnoreCase("bar")) {
// do something
}
You're also
calling paint(...) directly, something you should almost never do.
Drawing in a top level window's paint(...) method which you also should avoid instead of drawing in a JPanel's (or other JComponent) paintComponent(...) method.
Not calling the paint or paintComponent's super method at the start of the method
Putting program logic in a paint or paintComponent method.
etc...
You will want to go through the Swing tutorials before going much further to learn from the pros.
Related
I'm new to Java and I'm learning by myself. I got stuck at this point. I created a very basic paint application, but my friend told me that as I put everything in one *.java file that you can tell that I'm a beginning programmer, since I'm not really using object-oriented programming :D So I decided to divide my code into various files. But now the code is not working (obviously).
So I have the Main.java file which creates a window for the program (JFrame) and inside of that JFrame there are 2 panels, one on the BorderLayout.WEST, and secong in the CENTER. The one on the .WEST is the SideBar (with buttons etc.) and the one in the .CENTER is the main drawing board:
The Main.java file (public class Main):
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.*;
public class Main {
private Paint myPaint;
private menuSideBar sidePanel;
Main(){
JFrame ramka = new JFrame("Paint Application");
sidePanel = new menuSideBar(); //this is the sidebar
myPaint = new Paint(); // this is the drawing board
ramka.getContentPane().add(sidePanel, BorderLayout.WEST);
ramka.getContentPane().add(myPaint, BorderLayout.CENTER);
ListenForWindow lForWindow = new ListenForWindow();
ramka.addWindowListener(lForWindow);
ramka.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
ramka.setSize(1400, 800);
ramka.setLocationRelativeTo(null);
ramka.setVisible(true);
}
private class ListenForWindow implements WindowListener{
public void windowOpened(WindowEvent e) {
}
public void windowClosing(WindowEvent e) {
int closing = JOptionPane.showConfirmDialog(null, "Exit program?", "Exit", JOptionPane.YES_NO_OPTION);
if (closing == JOptionPane.YES_OPTION){
System.exit(0);
}
}
public void windowClosed(WindowEvent e) {
}
public void windowIconified(WindowEvent e) {
}
public void windowDeiconified(WindowEvent e) {
}
public void windowActivated(WindowEvent e) {
}
public void windowDeactivated(WindowEvent e) {
}
}
public static void main(String[] args){
new Main();
}
}
Then I have the drawing board, in file Paint.java (public class Paint):
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Paint extends JComponent {
private static final long serialVersionUID = 1L;
private BufferedImage bimage;
private Graphics2D g2;
private int whichShape;
private int oldX, oldY, newX, newY;
private int w, h;
private Color cColor;
Paint() {
ListenForMouse lForMouse = new ListenForMouse();
this.addMouseListener(lForMouse);
this.addMouseMotionListener(lForMouse);
}
public void paintComponent(Graphics g){
if (bimage == null){
bimage = new BufferedImage(getSize().width, getSize().height, BufferedImage.TYPE_INT_ARGB);
g2 = (Graphics2D) bimage.getGraphics();
g2.setBackground(Color.WHITE);
g2.setColor(Color.BLACK);
clear();
}
g.drawImage(bimage, 0, 0, null);
}
public void clear(){
g2.clearRect(0, 0, getSize().width, getSize().height);
repaint();
}
public void draw(){
w = newX - oldX;
h = newY - oldY;
if (w<0){
w = w * (-1);
}
if (h<0){
h = h * (-1);
}
switch (whichShape) {
case 1:
check();
g2.drawRect(oldX, oldY, w, h);
repaint();
break;
case 2:
check();
g2.drawRoundRect(oldX, oldY, w, h, 40, 40);
repaint();
break;
case 3:
check();
g2.drawOval(oldX, oldY, w, h);
repaint();
break;
case 4:
check();
g2.fillRect(oldX, oldY, w, h);
repaint();
break;
case 5:
check();
g2.fillRoundRect(oldX, oldY, w, h, 20, 20);
repaint();
break;
case 6:
check();
g2.fillOval(oldX, oldY, w, h);
repaint();
break;
}
}
public void rectangle(){
whichShape = 1;
}
public void roundedrectangle(){
whichShape = 2;
}
public void oval(){
whichShape = 3;
}
public void filledrectangle(){
whichShape = 4;
}
public void filledroundedrectangle(){
whichShape = 5;
}
public void filledoval(){
whichShape = 6;
}
public void colorChooser(){
cColor = JColorChooser.showDialog(null, "Choose color", Color.black);
g2.setColor(cColor);
}
public void check(){
if (oldX > newX){
int z;
z = oldX;
oldX = newX;
newX = z;
}
if (oldY > newY){
int z;
z = oldY;
oldY = newY;
newY = z;
}
}
public class ListenForMouse implements MouseListener, MouseMotionListener{
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
if (whichShape == 1 || whichShape == 2 || whichShape == 3 || whichShape == 4 || whichShape == 5 || whichShape == 6){
oldX = e.getX();
oldY = e.getY();
}
}
public void mouseReleased(MouseEvent e) {
if (whichShape == 1 || whichShape == 2 || whichShape == 3 || whichShape == 4 || whichShape == 5 || whichShape == 6){
newX = e.getX();
newY = e.getY();
draw();
}
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
}
}
Later there is sidebar, which is in the menuSideBar.java file (public class menuSideBar), this class reads in 4 other classes (there are 4 different menus I want to add, here I will just show one example):
import javax.swing.*;
public class menuSideBar extends JPanel {
private static final long serialVersionUID = 1L;
sideBar1 sb1;
//sideBar2 sb2;
//sideBar3 sb3;
//sideBar4 sb4;
menuSideBar(){
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
sb1 = new sideBar1();
//sb2 = new sideBar2();
//sb3 = new sideBar3();
//sb4 = new sideBar4();
this.add(sb1);
//this.add(sb2);
//this.add(sb3);
//this.add(sb4);
}
}
And this sideBar1.java file (public class sideBar1) contains JButtons with ActionListeners referring to methods in Paint.java file:
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class sideBar1 extends JPanel {
private static final long serialVersionUID = 1L;
Paint myPaint;
JButton pencilBut, brushBtn, bckpaintBtn, eraserBtn, textBtn, rectBtn, roundedrectBtn, ovalBtn, frectBtn, fovalBtn, froundedrectBtn, lineBtn;
sideBar1(){
Border border = BorderFactory.createTitledBorder("Paint");
this.setBorder(border);
this.setLayout(new GridLayout(0,3));
myPaint = new Paint();
ListenForButton lForButton = new ListenForButton();
pencilBut = new JButton("pencil");
pencilBut.addActionListener(lForButton);
brushBtn = new JButton("brush");
brushBtn.addActionListener(lForButton);
bckpaintBtn = new JButton("background paint");
bckpaintBtn.addActionListener(lForButton);
eraserBtn = new JButton("eraser");
eraserBtn.setIcon(icon_eraser);
eraserBtn.addActionListener(lForButton);
textBtn = new JButton("text");
textBtn.addActionListener(lForButton);
lineBtn = new JButton("line");
lineBtn.addActionListener(lForButton);
rectBtn = new JButton("rectangle");
rectBtn.addActionListener(lForButton);
roundedrectBtn = new JButton("rounded rectangle");
roundedrectBtn.addActionListener(lForButton);
ovalBtn = new JButton("oval");
ovalBtn.addActionListener(lForButton);
frectBtn = new JButton("filled rectangle");
frectBtn.addActionListener(lForButton);
froundedrectBtn = new JButton("filled rounded rectangle");
froundedrectBtn.addActionListener(lForButton);
fovalBtn = new JButton("filled oval");
fovalBtn.addActionListener(lForButton);
this.add(pencilBut);
this.add(brushBtn);
this.add(bckpaintBtn);
this.add(eraserBtn);
this.add(textBtn);
this.add(lineBtn);
this.add(rectBtn);
this.add(roundedrectBtn);
this.add(ovalBtn);
this.add(frectBtn);
this.add(froundedrectBtn);
this.add(fovalBtn);
}
public class ListenForButton implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == brushBtn){
} else if (e.getSource() == bckpaintBtn){
} else if (e.getSource() == eraserBtn){
} else if (e.getSource() == textBtn){
} else if (e.getSource() == lineBtn){
} else if (e.getSource() == rectBtn){
System.out.println("rectangle");
myPaint.rectangle();
} else if (e.getSource() == roundedrectBtn){
myPaint.roundedrectangle();
} else if (e.getSource() == ovalBtn){
myPaint.oval();
} else if (e.getSource() == frectBtn){
myPaint.filledrectangle();
} else if (e.getSource() == froundedrectBtn){
myPaint.filledroundedrectangle();
} else if (e.getSource() == fovalBtn){
myPaint.filledoval();
}
}
}
}
Now, when I execute the code everything loads in smoothly, I can see all the panels and and all the components inside, but when I click the buttons nothing happens. I'm guessing it has to do with the inheritance, or actually I have a few questions:
Did I understand my friend correctly? Should I divide my code into various *.java files and have every class in a different file, in the same package? Or should I include those various classes in one *.java file? (if the latter option is ok, can somebody modify at least a fragment of my code, so I would know how to properly incorporate inheritance?, some of the classes extend JPanels and JComponents, and I've read that you should not mix up extending JPanels etc. (views) with models(?)
Is it ok to read into Main.java file JPanels which are in another file menuSideBar.java and then reading into menuSideBar.java panels that are inside of sideBar1.java? Or should I define which panel goes into which panel in the Main class?
Why is the Paint.java not reading the ActionListener of the JButtons of sideBar1.java? What am I doing wrong?
Thank you! Sorry if that seems obvious to you, but all the examples in the books or tutorials have very short examples of inheritance and very simple ones and somehow I cannot transfer those simple examples into my code. Thank you!
Your main problem is that you're creating two Paint objects when you only need and should have one. To check this for yourself, search this page for how many times you see new Paint(). You should only see it once in the code, and you're seeing it twice.
Why does this matter? Well one Paint object is displayed in the GUI, and the other is not displayed, but its methods are being called when buttons are pressed in the ActionListener. Calling methods on the non-displayed object will not translate into a visible response in the displayed one.
An easy solution that is wrong is to make your paint variable static and share it everywhere. This is wrong as you lose the benefits of OOP and increase the risk of bugs. Better is to pass a reference from the visualized Paint object into the ActionListener where it is needed, so that only one Paint object is created, and methods are called in the listener on the same Paint object as the one that's displayed.
More concrete, change this:
sidePanel = new menuSideBar(); //this is the sidebar
myPaint = new Paint(); // this is the drawing board
to this:
myPaint = new Paint(); // call this first
sidePanel = new menuSideBar(myPaint); // and pass it in
In the sideBar constructor (the class should be renamed SideBar), use the parameter:
public sideBar(Paint myPaint) {
this.myPaint = myPaint;
// .... all other constructor code
}
and get rid of new Paint() from sideBar.
I'm trying to make a circle capable of moving when the keys "i, j, k, l" are pressed (as arrow keys) and stop when released. Tried creating a Timer in order to wait a second before moving again so the animation is appreciable, but since I created the 'while(!quit)' loop, no graphics move or show. Could you please indicate my errors please?
Code:
import java.awt.*;
import java.awt.Color.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Timer;
import java.util.TimerTask;
public class Event_test{
public static void main(String args[])
{
boolean quit = false;
JFrame Window = new JFrame("Event_test");
MyCanvas WCanvas = new MyCanvas();
KeyCatcher k = new KeyCatcher();
WCanvas.addKeyListener(k);
Window.getContentPane().add(WCanvas);
Window.setSize(640, 360);
Window.setVisible(true);
Window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new Notification(1);
while(!quit)
{
WCanvas.update(WCanvas.getGraphics());
if(!Notification.flag)
{
continue;
}
else
{
Notification.flag = false;
System.out.println("tic");
/*
CONTROLS:
*/
if(KeyCatcher.KEYS[0])
{
MyCanvas.x--;
}
if(KeyCatcher.KEYS[1])
{
MyCanvas.y++;
}
if(KeyCatcher.KEYS[2])
{
MyCanvas.x++;
}
if(KeyCatcher.KEYS[3])
{
MyCanvas.y--;
}
if(KeyCatcher.KEYS[4])
{
quit = true;
}
new Notification(1);
}
}
}
}
class MyCanvas extends Canvas
{
static int x, y;
public void paint(Graphics g)
{
g = this.getGraphics();
MyClass.drawSomething(g, x, y);
}
}
class MyClass
{
public static void drawSomething(Graphics g, int x, int y)
{
g.setColor(Color.RED);
g.drawOval(x, y, 10, 10);
}
}
class KeyCatcher implements KeyListener
{
static boolean KEYS[] = new boolean[5];
public void keyPressed(KeyEvent e)
{
if(e.getKeyChar() == 'j'/*IZQ*/)
{
KEYS[0] = true;
}
if(e.getKeyChar() == 'i'/*ARR*/)
{
KEYS[1] = true;
}
if(e.getKeyChar() == 'l'/*DER*/)
{
KEYS[2] = true;
}
if(e.getKeyChar() == 'k'/*ABA*/)
{
KEYS[3] = true;
}
if(e.getKeyChar() == 'q'/*QUIT*/)
{
KEYS[4] = true;
}
}
public void keyReleased(KeyEvent e)
{
if(e.getKeyChar() == 'j'/*IZQ*/)
{
KEYS[0] = false;
}
if(e.getKeyChar() == 'i'/*ARR*/)
{
KEYS[1] = false;
}
if(e.getKeyChar() == 'l'/*DER*/)
{
KEYS[2] = false;
}
if(e.getKeyChar() == 'k'/*ABA*/)
{
KEYS[3] = false;
}
if(e.getKeyChar() == 'q'/*QUIT*/)
{
KEYS[4] = false;
}
}
public void keyTyped(KeyEvent e){
}
}
class Notification{
static boolean flag = false;
Timer timer;
Notification(int seconds)
{
timer = new Timer();
timer.schedule(new Task(), seconds*1000);
}
class Task extends TimerTask
{
public void run()
{
//What task does:
flag = true;
timer.cancel();
}
}
}
Your question is about Swing (not AWT) so:
Don't extend Canvas. Instead custom painting is done by extending a JPanel
Don't override paint(...). Custom painting is done by overrding paintComponent(...)
Don't use a TimerTask. Instead use a Swing Timer.
Don't use the getGraphics() method. See point 2.
Don't invoke update(...). You just invoke repaint() on the component and it will paint iself.
Don't use a while loop. That is the reason for using the Swing Timer.
Don't start variable names with an upper case character. Follow Java naming conventions.
Don't use a KeyListener. Swing was designed to be used with Key Bindings.
Check out the Swing tutorial on Custom Painting for basic painting examples to get you started.
Check out Motion Using the Keyboard. The Keyboard Animation example addresses many of these issues.
I got the snake running down, now I try to get the keypressed() method to work. I don't think it's listening from the keyboard. My idea is for example if the the down key is pressed, the variable direction is set to 1 and the switch case statement would handle that. I doubt that the direction variable was't read by the switch case.
My main class:
package com.bin.snake;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Game extends JPanel{
boolean playingSnake = true;
Snake snake = new Snake();
public Game() {
addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
snake.keyPressed(e);
}
});
setFocusable(true);
}
public void paint(Graphics g) {// inherit paint method of JPanel class with
// parameter g type Graphics
// parameter g is the object to paint things
super.paint(g); // erase latest image
Graphics2D g2d = (Graphics2D) g; // casting g to Graphics2D type
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// rendering
// image
snake.paint(g2d);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Snake!");
frame.setVisible(true);
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Game game = new Game();
frame.add(game);
while (true) {
game.updateGame();
game.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void updateGame() {
snake.moveSnake();
}
}
My Snake Class:
package com.bin.snake;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
public class Snake {
private Game game;
int iSnakeLength = 10;
int direction = 1;
final int SIDE = 13;
int[] snakeY = new int[2000];
int[] snakeX = new int[2000];
int xs = 0;
int ys = 0;
public void moveSnake() {
switch (direction) {
case 0:// up
snakeY[0] -=1.5;
break;
case 1:// down
snakeY[0] += 1.5;
break;
case 2:// right
snakeX[0] += 1.5;
break;
case 3:// left
snakeX[0] -=1.5;
break;
}
for (int i = iSnakeLength; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
}
public void paint(Graphics2D g) {
g.fillRect(snakeX[0], snakeY[0], SIDE, SIDE);
for (int i = 0; i < iSnakeLength; i++) {
g.fillRect(snakeX[i + 1], snakeY[i + 1], SIDE, SIDE);
}
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_DOWN){
direction = 1;
}
if(e.getKeyCode() == KeyEvent.VK_UP){
direction = 0;
}
if(e.getKeyCode() == KeyEvent.VK_LEFT){
direction = 3;
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
direction = 2;
}
}
}
I don't think it's listening from the keyboard. My idea
KeyEvents are only dispatched to the component with focus. A JPanel is not focusable by default.
To make a panel focusable you use:
panel.setFocusable( true );
Other comments:
Custom painting is done by overriding the paintComponent(...) method, not the paint() method. Read the Swing tutorial on Custom Painting for more information and examples.
You should NOT be using a KeyListener. Swing was designed to be used with Key Bindings. Read the Swing tutorial on How to Use Key Bindings. You can also check out Motion Using the Keyboard which contains working examples of moving a component using Key Bindings.
For animation you should be using a Swing Timer to schedule the animation. Check the table of contents from the tutorial links I have already provided. There is a section on How to Use Swing Timers.
(The game is not complete yet but nevertheless)
Using the keyboard input does not do anything, I believe this is due to KeyListener not being defined properly. Here is my code:
mainClass:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class mainClass extends JComponent{
private static ballAndGameplay ball;
private static calculateDrawPos blocks;
public static void main(String[] a) {
JFrame window = new JFrame("PONG");
window.setSize(1280,720);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainClass mc = new mainClass();
window.getContentPane().add(new mainClass());
window.setVisible(true);
ball = new ballAndGameplay(630,300,5,5);
blocks = new calculateDrawPos(false,false,false,false);
mc.addKeyListener(blocks);
while (true){
blocks.moveBlocks();
ball.moveBall();
mc.removeAll();
mc.paint(window.getGraphics());
try{
Thread.sleep(10);
}
catch (Exception e){
}
}
}
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect (-10, -10, 1300, 740);
g.setColor(Color.WHITE);
g.fillRect (10, blocks.PlayerOneY, 25, 125); //left player
g.fillRect (1230, blocks.PlayerTwoY, 25, 125); //right player
g.fillRect (638, -10, 4, 740); //middle line
g.fillRect (ball.ballX, ball.ballY, 20, 20); //ball
}
}
calculateDrawPos:
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class calculateDrawPos extends KeyAdapter implements KeyListener {
int PlayerOneY=275;
int PlayerTwoY=275;
boolean wPressed;
boolean sPressed;
boolean upPressed;
boolean downPressed;
public calculateDrawPos (boolean a, boolean b, boolean c, boolean d) {
this.wPressed=a;
this.sPressed=b;
this.upPressed=c;
this.downPressed=d;
}
public void keyPressed(KeyEvent event) {
int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.VK_W);
{
wPressed=true;
}
if (keyCode == KeyEvent.VK_S);
{
sPressed=true;
}
if (keyCode == KeyEvent.VK_UP);
{
upPressed=true;
}
if (keyCode == KeyEvent.VK_DOWN);
{
downPressed=true;
}
}
public void keyReleased(KeyEvent event) {
int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.VK_W);
{
wPressed=false;
}
if (keyCode == KeyEvent.VK_S);
{
sPressed=false;
}
if (keyCode == KeyEvent.VK_UP);
{
upPressed=true;
}
if (keyCode == KeyEvent.VK_DOWN);
{
downPressed=true;
}
}
public void moveBlocks(){
if (wPressed==(true)){
PlayerOneY-=5;
}
if (sPressed==(true)){
PlayerOneY+=5;
}
if (upPressed==(true)){
PlayerTwoY-=5;
}
if (downPressed==(true)){
PlayerTwoY+=5;
}
}
}
ballAndGameplay:
import java.util.Random;
public class ballAndGameplay {
private static Random rand;
int ballX;
int ballY;
int ballXVelocity;
int ballYVelocity;
public ballAndGameplay (int a, int b, int c, int d) {
rand = new Random();
this.ballX=a;
this.ballY=b;
this.ballXVelocity=c;
this.ballYVelocity=d;
}
public void moveBall() {
boolean pointFinished = (ballX <= -20 || ballX >= 1280);
if (ballY <= 20 || ballY >= 700) {
ballYVelocity*=-1;
}
// if ((ballY >= PlayerOneY && ballY <= PlayerOneY+125 && ballX <= 35) || (ballY >= PlayerTwoY && ballY <= PlayerTwoY+125 && ballX >= 1245)){
// ballXVelocity*=-1.2;
// }
if (pointFinished==(true)){
try{
Thread.sleep(500);
}
catch (Exception e){
}
ballX=630;
ballY=300;
getBallX();
getBallY();
}
ballX+=ballXVelocity;
ballY+=ballYVelocity;
// System.out.println("ballX: "+ballX+" ballY: "+ballY+" ballXVelocity: "+ballXVelocity+" ballYVelocity: "+ballYVelocity);
}
public int getBallX(){
if (ballX != 630){
return ballXVelocity;
}
int side = rand.nextInt(2);
int number = rand.nextInt(5);
number+=4;
if (side==0){
ballXVelocity=-1*number;
}
else if (side==1){
ballXVelocity=1*number;
}
return ballXVelocity;
}
public int getBallY(){
if (ballY != 300){
return ballYVelocity;
}
int side = rand.nextInt(2);
int number = rand.nextInt(5);
number+=4;
if (side==0){
ballYVelocity=-1*number;
}
else if (side==1){
ballYVelocity=1*number;
}
return ballYVelocity;
}
}
You wrote
mainClass mc = new mainClass();
window.getContentPane().add(new mainClass());
That means that the mainClass that you added to your window isn't the same one that mc references. So all those operations you do to mc won't make any changes in your window.
You could try writing
mainClass mc = new mainClass();
window.getContentPane().add(mc);
instead.
Issues:
A KeyListener only works on a component that has current focus.
Your adding your KeyListener to a JComponent, a component that by default isn't even able to gain the focus (unlike a JButton, JTextField, and other components that typically allow keyboard interaction.)
One short term solution is to make your JComponent, the mc variable focusable by calling mc.setFocusable(true); and then giving it the system focus by calling mc.requestFocusInWindow();.
But having said this, you're still far better off using Key Bindings
Other issues:
Override and draw in the JComponent's paintComponent method not the paint method, if only to avoid the jerky graphics you'll get using paint.
Don't forget to call the super's paintComponent method in your override to erase the old ball image.
Never use a Graphics object obtained from a component by calling getGraphics() on it as the object thus obtained my become invalid or go null over time leading to invalid graphics or worse.
Never call the paint(...) or paintComponent(...) method directly as you're doing.
Instead use passive graphics as per the Swing graphics tutorials which I urge you to read.
Use a Swing Timer instead a while (true) loop since your while (true) loop risks tying up the Swing event thread, freezing your application.
I have asked a question regarding this same program before. I solved the issues I had then, and I now have rooted out a different problem. When I run the code it shows a small blue box on the screen. It is supposed to move when you press the arrow keys and I have discovered that if you click the character and hold an arrow key for a moment then it will move. What I need is to make it refreshes the screen automatically, I believe I need an update() method for that, but I am not sure. If anybody can help me figure this out or improve my code in some way that would be helpful. I made some changes based off of some comments I received on my previous question.
CharacterTest.java:
import javax.swing.JFrame;
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.Color;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import java.awt.BorderLayout;
import java.awt.Canvas;
//import java.awt.event.KeyEvent;
import java.awt.event.*;
public class CharacterTest extends JFrame{
public CharacterTest()
{
super("Character Test"); //instantiate a window to draw the character in
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //this will stop the program when it closes
setSize(800, 800); //create the window
MCharacter C = new MCharacter(); //call the box so that it can be affected
getContentPane().add(C); //draws the character on the window
setVisible(true); //show the window
while(C.determine(C.getX(), C.getY()) == false) //as long as the character is witin a particular area
{
if(C.getUpKey() == true) //if the up arrow key is pressed
{
C.up(); //set the y variable to a higher position
}
if(C.getDownKey() == true) //if the down arrow key is pressed
{
C.down(); //set the y variable to a lower positon
}
if(C.getRightKey() == true) //if the right arrow key is pressed
{
C.right(); //set the x variable ro a more right position
}
if(C.getLeftKey() == true) //if the left key is pressed
{
C.left(); //set the x variable to a more left position
}
repaint(); //repaint the character at a new position
try {
Thread.sleep(20);
}
catch (InterruptedException ex){
}
}
}
public static void main(String[] args) {
CharacterTest test = new CharacterTest(); //calls the method which creates the screeen, the character, and checks for key srokes
}
}
MCharacter.java:
import javax.swing.JFrame;
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.Color;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import java.awt.BorderLayout;
import java.awt.Canvas;
//import java.awt.event.KeyEvent;
import java.awt.event.*;
public class MCharacter extends Canvas{
//these will be the instance variables for the character's parameters- its size and its location
private int width;
private int height;
private int x;
private int y;
//these will be turned true if the corresponding key is pressed - defined in the private class line 132
private boolean leftKey = false;
private boolean rightKey = false;
private boolean upKey = false;
private boolean downKey = false;
//some constructors that would easily be called
public MCharacter()
{
setBackground(Color.WHITE);
setWidth(10);
setHeight(10);
setX(400);
setY(400);
addKeyListener(new MyKeyListener()); //adds a key listener as the private class below (line 152)
}
public MCharacter(int xPos, int yPos)
{
setBackground(Color.WHITE);
setWidth(10);
setHeight(10);
setX(xPos);
setY(yPos);
addKeyListener(new MyKeyListener()); //adds a key listener as the private class below (line 152)
}
public MCharacter(int wth, int hgt, int xPos, int yPos)
{
setBackground(Color.WHITE);
setWidth(wth);
setHeight(hgt);
setX(xPos);
setY(yPos);
addKeyListener(new MyKeyListener()); //adds a key listener as the private class below (line 152)
}
//setters for each of the variables
public void setWidth(int wth)
{
width = wth;
}
public void setHeight(int hgt)
{
height = hgt;
}
public void setX(int xPos)
{
x = xPos;
}
public void setY(int yPos)
{
y = yPos;
}
//getters for each of the varaibles
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
//used to determine if the character should move
public void setUpKey(boolean setUp)
{
upKey = setUp;
}
public void setDownKey(boolean setDown)
{
downKey = setDown;
}
public void setRightKey(boolean setRight)
{
rightKey = setRight;
}
public void setLeftKey(boolean setLeft)
{
leftKey = setLeft;
}
public boolean getUpKey()
{
if(upKey == true)
{
return true;
}
return false;
}
public boolean getDownKey()
{
if(downKey == true)
{
return true;
}
return false;
}
public boolean getRightKey()
{
if(rightKey == true)
{
return true;
}
return false;
}
public boolean getLeftKey()
{
if(leftKey == true)
{
return true;
}
return false;
}
//the following class is goign to be used to determine if an arrow key is being pressed
private class MyKeyListener extends KeyAdapter{
public void keyPressed(KeyEvent e){
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT: //if left key is pressed
setLeftKey(true); //set this boolean to true
break;
case KeyEvent.VK_RIGHT: //if right key is pressed
setRightKey(true); //set this boolean to true
break;
case KeyEvent.VK_UP: //if the up key is pressed
setUpKey(true); //set this boolean to true
break;
case KeyEvent.VK_DOWN: //if the down key is pressed
setDownKey(true); //set this boolean to true
break;
}
}
public void keyReleased(KeyEvent e){
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT: //if left key is released
setLeftKey(false); //set this boolean to false
break;
case KeyEvent.VK_RIGHT: //if right key is released
setRightKey(false); //set this boolean to false
break;
case KeyEvent.VK_UP: //if up key is released
setUpKey(false); //set this boolean to false
break;
case KeyEvent.VK_DOWN: //if down key is released
setDownKey(false); //set this boolean to false
break;
}
}
}
//I am going to call the paint method here and create a small box that I will use as the character
public void paint(Graphics window)
{
window.setColor(Color.BLUE); //sets the color of the character
window.fillRect(x, y, width, height); //sets the dimensions of the character
}
//this method will be used to keep checking for a key pressed: while this is false check for a keytyped
public boolean determine(int x, int y)
{
if( x >= 10 && x <= 790 && y >= 10 && y <= 790)
{
return false;
}
return true;
}
//the following methods will be to move the character
public void up()
{
setY(getY()-2);
}
public void down()
{
setY(getY()+2);
}
public void right()
{
setX(getX()+2);
}
public void left()
{
setX(getX()-2);
}
}
To do this smoothly, you will have to move the while-loop out of the CharacterTest constructor and execute this on a separate Thread. Also define your leftKey, rightKey, upKey and downKey fields as volatile to prevent synchronization issues between the AWT/Swing Event Thread and your rendering Thread (since the event thread and your rendering thread may be accessing these fields at the same time).
If you execute the Canvas' repaint() from your own separate Thread, you should also setIgnoreRepaint(true) in MCharacter's constructor to prevent Swing from repainting your Canvas as well.
Also, to make your code more readable, I suggest you define a method in MCharacter called move() and execute the contents of your while loop there (it makes sense to 'move' a character). the run() method with the loop will then look like this:
#Override public void run() {
while (character.determine()) {
character.move();
}
}