hi everyone im using netbeans 7.2.1
and was following a tutorial for keyboard input
this code should draw a circle in JFrame, which it does
but then should take arrow key input to move it, which it does not
thank you for your help :)
package gamefirstclass;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class GameFirstClass extends JFrame {
//Variables
int y, x;
//Double Buffer
private Image dbImage;
private Graphics dbg;
//Window Basics
public GameFirstClass() {
addKeyListener(new AL());
setTitle("Add window title");
setSize(800, 700);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 150;
y = 150;
}
// main
public static void main(String[] args) {
new GameFirstClass();
}
//Controls
public class AL extends KeyAdapter {
#Override
public void keyPressed(KeyEvent event) {
int keyCode = event.getKeyCode();
if (keyCode == event.VK_LEFT)
{
x--;
}
if (keyCode == event.VK_RIGHT)
{
x++;
}
if (keyCode == event.VK_UP)
{
y--;
}
if (keyCode == event.VK_DOWN)
{
y++;
}
}
#Override
public void keyReleased(KeyEvent event) {
}
}
//Double Buffer
#Override
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.fillOval(x, y, 15, 15);
repaint();
}
}
Okay, so there is one problem: your if statements!.
Basically, in your keyListener, the if statements don't execute because of the semicolon after them.
The blocks are all that remain, and the modifications to x are the same. Just remove those semicolons and I believe it will work.
Also, don't call repaint() from paintComponent(), because you are generating a lot of repaints for every time. I don't know if this matters, but you should call setFocusable(true) on your JFrame to make sure that the KeyListener works.
Good Luck!
Related
So here's my code. I implemented keyListener and actionListener. I was able to change the coordinates for the panel so It could be able to move left or right. But I have noticed that keyListener doesn't focus very well. I Have to close and rerun the app again and again for it to work and I am able to control it. I have heard of keyBidings but I don't really get it as much. How can I implement keyBindings to make the keyboard responses more focusable?
package brickBreaker;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.Timer;
import javax.swing.JPanel;
public class game extends JPanel implements KeyListener, ActionListener {
private Timer timer;
private boolean play = false;
private int playerx = 650;
private int ballx=900, bally=500,ballxdir=-1,ballydir=-2;
int delay =8;
public game() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer = new Timer(delay, this);
timer.start();
}
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(1, 1, 1500 ,950);
// user panel
g.setColor(Color.CYAN);
g.fillRect(playerx, 900, 250, 15);
//ball
g.setColor(Color.GREEN);
g.fillOval(ballx, bally, 30, 30);
g.dispose();
}
public void right() {
play = true;
playerx += 20;
}
public void left() {
play = true;
playerx -=20;
}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key ==KeyEvent.VK_LEFT) {
System.out.print("Left\n");
left();
}if (key == KeyEvent.VK_RIGHT) {
System.out.print("Right\n");
right();
}
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
if (play) {
ballx +=ballxdir;
bally +=ballydir;
if (ballx <0) {
ballxdir =-ballxdir;
}
if (bally <0) {
ballydir =-ballydir;
}
if (ballx <1000) {
ballxdir =-ballxdir;
}
}
repaint();
}
}
Sorry for being extremely late to answer, but I think I've fixed your problem.
You're right - You need to open and close the JPanel again and again before it works. But the issue is this: The JPanel keeps losing focus. So all you have to do is add:
requestFocus(true);
to the paint() method, like so:
public void paint(Graphics g) {
requestFocus(true);
g.setColor(Color.BLACK);
g.fillRect(1, 1, 1500 ,950);
// user panel
g.setColor(Color.CYAN);
g.fillRect(playerx, 900, 250, 15);
//ball
g.setColor(Color.GREEN);
g.fillOval(ballx, bally, 30, 30);
g.dispose();
}
and the program works!
I have been following some java game development tutorial , and I was able to create an oval shaped object that can move with my keyboard input and created boundaries , however I attempted to replace the oval with an image but for some reason it is not showing up, I'm pretty sure the image I chose is not large , it is more like a small icon and thanks in advance.
package javagame;
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.ImageIcon;
import javax.swing.JFrame;
public class JavaGame extends JFrame{
int x,y;
private Graphics dbg;
private Image dbImage;
Font font = new Font("Arial" , Font.BOLD, 30);
Image face;
public class AL extends KeyAdapter{
public void keyPressed(KeyEvent e){
int KeyCode = e.getKeyCode();
if(KeyCode == e.VK_LEFT) {
if(x<=0)
x=0;
else
x -=15;
}
if(KeyCode == e.VK_DOWN) {
if(y>=480)
y=480;
else
y+=5;
}
if(KeyCode == e.VK_UP) {
if(y<=20)
y=20;
else
y-=5;
}
if(KeyCode == e.VK_RIGHT) {
if(x>=480)
x=480;
else
x+=5;
}
}
public void keyReleased(KeyEvent e){
}
}
public JavaGame() {
//game images
ImageIcon i = new ImageIcon("C:/Users/Sheref/Documents/NetBeansProjects/avaGame/src/javagame/type-of-solder-001-512.gif");
face = i.getImage();
//game properties
addKeyListener(new AL());
setTitle(" Strategy Game");
setSize(500,500);
setResizable(true);
setVisible(true);
setBackground(Color.CYAN);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 20 ;
y = 30;
}
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.drawImage(face, x, y, this);
repaint();
}
public static void main(String[] args) {
new JavaGame();
}
}
In your paint method, instead of
this.dbImage = this.createImage(this.getWidth(), this.getHeight());
put:
this.dbImage = this.createImage((int) this.getSize().getWidth(), (int) this.getSize().getHeight());
I was trying to understand the difference between JFrame and JPanel. I tend to use subclasses of JFrame instead of JPanel, but people always tell me that it's better to use a subclass of JPanel instead. Here is an example of me using JFrame:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Game extends JFrame implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
public void move() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public class AL extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(+1);
}
if (keyCode == e.VK_UP) {
setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
setYCoord(+1);
}
Game.this.repaint();
}
#Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(0);
}
if (keyCode == e.VK_UP) {
setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
setYCoord(0);
}
Game.this.repaint();
}
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
addKeyListener(new AL());
setTitle("Game");
setSize(500, 500);
setResizable(true);
setVisible(true);
setBackground(Color.BLACK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 250;
y = 250;
}
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
#Override
public void run() {
try {
while (true) {
move();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
This works fine (except for there being a small delay when I hold down one of the buttons), but when I try to change my code by implementing JPanel instead of JFrame, nothing shows up... Here's the code for the JPanel subclass:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable {
int x, y, xCoord, yCoord;
private Image dbImage;
private Graphics dbg;
JFrame frame;
public void move() {
x += xCoord;
y += yCoord;
if (x <= 20) {
x = 20;
}
if (x >= 480) {
x = 480;
}
if (y <= 40) {
y = 40;
}
if (y >= 480) {
y = 480;
}
}
public void setXCoord(int xcoord) {
xCoord = xcoord;
}
public void setYCoord(int ycoord) {
yCoord = ycoord;
}
public class AL extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(-1);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(+1);
}
if (keyCode == e.VK_UP) {
setYCoord(-1);
}
if (keyCode == e.VK_DOWN) {
setYCoord(+1);
}
Game.this.repaint();
}
#Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
setXCoord(0);
}
if (keyCode == e.VK_RIGHT) {
setXCoord(0);
}
if (keyCode == e.VK_UP) {
setYCoord(0);
}
if (keyCode == e.VK_DOWN) {
setYCoord(0);
}
Game.this.repaint();
}
}
public static void main(String[] args) {
Game game = new Game();
Thread t = new Thread(game);
t.start();
}
public Game() {
frame = new JFrame();
frame.addKeyListener(new AL());
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.setVisible(true);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 250;
y = 250;
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, 15, 15);
}
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
#Override
public void run() {
try {
while (true) {
move();
Thread.sleep(30);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
As the code shows, you need a custom JPanel, because you want to change the behavior of some methods like paintComponent.
However, you don't need a custom JFrame, so no need to create a class extending it.
Finally, your main class has no need to be your panel class.
Here is an example class, I moved the frame stuff from Game's constructor to this main class.
public class MainClass {
public static void main(String[] args) {
Game game = new Game();
JFrame frame = new JFrame();
frame.addKeyListener(new AL());
frame.setTitle("Game");
frame.setSize(500, 500);
frame.setResizable(true);
frame.getContentPane().add(game);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread(game);
t.start();
}
}
A JFrame is a far more complex component then you might think, for starters, it has a JRootPane as it's primary container, which contains the contentPane, JMenuBar and controls the glassPane
see How to Use Root Panes for more details.
The JFrame also has decorations (borders), these borders are painted within the confines of the window itself, the contents are then laid out within these, so the borders don't paint over then.
When you override paint of a top level container, like JFrame, there are a number of issues which you run into:
It's not double buffered, which can cause flickering as the window is updated
The other components can be painted independently of the frame (so the frame's paint method is not called), which can cause no end of issues
You can now paint beneath the frame's decorations, see Java graphic image, How to get the EXACT middle of a screen, even when re-sized and How can I set in the midst? for more details.
You're locking your self into a single use case, you can't add windows to other containers, which reduces your components re-use value
Generally speaking, from a OOP point of view, you're not actually adding any new functionality to the class (or least none which can't be generated through better approaches).
When you use something like JPanel, all other the above are no longer of concern:
They are double buffered by default
If you use a layout manager (on the content pane), the component will be laid out within the frame's decorations
The width and height of the component represent the whole viewable area
You can add this component to what ever container you want
You should also override the JPanel's getPreferredSize method and return the preferred size you want your panel to generally be, then you can use JFrame#pack to "pack" the window around it, this will make the window larger then the content area, but means you're not scratching your head wondering why you set the window to a certain size, but your component is smaller
I'm writing a simple drawing program that uses keyListeners. It works, but every time it needs to draw another circle, I have to use the repaint() method or it won't automatically repaint the screen after using one of the arrow keys. It would be fine except that it uses up way too much CPU (around 50%) for such a simple program. Any ideas on how to NOT use the repaint() method so that it can do whatever it needs without eating up all my CPU? Here is the source code:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JComboBox;
import javax.swing.JFrame;
public class Game extends JFrame {
int x, y;
public class AL extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
x--;
}
if (keyCode == e.VK_RIGHT) {
x++;
}
if (keyCode == e.VK_UP) {
y--;
}
if (keyCode == e.VK_DOWN) {
y++;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
public static void main(String[] args) {
Game game = new Game();
}
public Game() {
addKeyListener(new AL());
setTitle("Game");
setSize(500, 500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 150;
y = 150;
}
#Override
public void paint(Graphics g) {
g.fillOval(x, y, 15, 15);
repaint();
}
}
You are doing a few things wrong when it comes to painting:
Don't paint on a top level component like JFrame, instead add a JPanel to it and paint on it instead.
Don't override paint, override paintComponent instead.
Don't call repaint inside methods that paint (like paint and paintComponent), it will cause a recursion.
Also, use key bindings instead of key listeners. Here is an example of everything coming together:
class Example extends JPanel {
int x = 0;
int y = 0;
Example() {
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "right");
getActionMap().put("right", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
x++;
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
g.clearRect(0, 0, getWidth(), getHeight());
g.drawRect(x, y, 30, 30);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new Example());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
Don't call repaint(); inside paint(). Repaint schedules a paint(), so no wonder your CPU is having a hard time.
Like Kayaman said you should never call repaint() from within paint().
You can call the Frames repaint() Method in keyPressed() so the Frame will be repainted every time you press a key.
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == e.VK_LEFT) {
x--;
}
if (keyCode == e.VK_RIGHT) {
x++;
}
if (keyCode == e.VK_UP) {
y--;
}
if (keyCode == e.VK_DOWN) {
y++;
}
Game.this.repaint();
}
/*...*/
#Override
public void paint(Graphics g) {
g.fillOval(x, y, 15, 15);
}
Call to repaint() will cause RepaintManager to call paint() method. So if you call repaint() inside paint() it will loop infinitely. Instead you can repaint your JFrame once the key-pressed action performed.
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT) {
x--;
}
if (keyCode == KeyEvent.VK_RIGHT) {
x++;
}
if (keyCode == KeyEvent.VK_UP) {
y--;
}
if (keyCode == KeyEvent.VK_DOWN) {
y++;
}
repaint();
}
Remove repaint() call from paint() method and add it as above.
But if there are different places which you want the JFrame to be repainted and you choose the above method, it will be messy again. So, you can use a Timer to call repaint().
private final javax.swing.Timer timer;
private final int REFRESH_TIME = 100;
public Game() {
addKeyListener(new AL());
setTitle("Game");
setSize(500, 500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 150;
y = 150;
timer = new javax.swing.Timer(REFRESH_TIME, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
If you want, you can use another way to call repaint() once in a period. The calling Thread need not to be an EDT.
I have a java program which displays an ellipse on the screen and changes its direction through the use of the arrow keys. I constantly call repaint() on the ellipse using a while loop.
The ellipse moves, but the problem is that it leaves a trail of ellipses on its path. How do I make it so that it removes the old ellipses and then repaint the new one?
Code:
public void run(){
while (animator != null)
{
setBackground(Color.GREEN);
repaint();
// The direction works and the rest works fine.
player1.move(player1, player1.direction);
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
break;
}
}
}
// The paintComponent of my player1 is fine.
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D)g;
player1.paintComponent(g2, player1);
}
The problem is likely that your paintComponent(Graphics) overriden method is not calling super.paintComponent(g), which clears the canvas, among other things:
// Bad:
#Override
public void paintComponent(Graphics g) {
// paint the ellipse, etc.
}
// Good:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// paint the ellipse, etc.
}
Here is some basic code which does the very basics (without the use of a continuous while loop):
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class Ellipses extends JFrame {
public static void main(String[] args){
//Ensures application is not run on the main thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Ellipses myEllipses = new Ellipses();
myEllipses.init();
}
});
}
public Ellipses(){
//Set up the frame
this.setTitle("Ellipses Example");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.setSize(400, 400);
}
private Ellipse ellipse1;
public void init(){
Container contentPane = this.getContentPane();
//Create a new ellipse and add to the content pane
ellipse1 = new Ellipse();
contentPane.add(ellipse1);
//Add the keyListener to the contentPane
contentPane.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP){
ellipse1.decreaseY();
}
if(e.getKeyCode() == KeyEvent.VK_DOWN){
ellipse1.increaseY();
}
if(e.getKeyCode() == KeyEvent.VK_LEFT){
ellipse1.decreaseX();
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
ellipse1.increaseX();
}
//Repaint the ellipse
ellipse1.repaint();
}
});
//Request the focus so key presses can be detected
contentPane.setFocusable(true);
contentPane.requestFocus();
}
//Create an ellipse which can be drawn to the screen
public class Ellipse extends JComponent{
private int x , y; //Coordinates of the oval
public Ellipse(){
setCoordinates(100, 100);
}
public void setCoordinates(int x, int y){
this.x = x;
this.y = y;
}
public void increaseY(){
y+=10;
}
public void increaseX(){
x+=10;
}
public void decreaseY(){
y-=10;
}
public void decreaseX(){
x-=10;
}
public void paint(Graphics g){
//Ensures previous paint is cleared
super.paintComponents(g);
g.setColor(Color.RED);
g.fillOval(x, y, 100, 100);
}
}
}
You should draw the background Color before drawing the ellipse in the game loop :)
Also you can call repaint() , it actually calls update() to do
the dirty work of clearing the screen and calling paint().
When you use repaint the update method inherit
public void update(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, width, height);
g.setColor(getForeground());
paint(g)
}
so the background return to it's original color and the trail of ellipses will be removed