I'm making Arkanoid game in Java Swing. When I run my game KeyEvent not working.
Why is the game not detecting key events?
Main class in my program make a frame (JFrame). Also I have Manage class which managing all classes from my game.
This is the code: Player class is class of paddle in my game.
//imports
public class Player extends JPanel implements ActionListener, KeyListener
{
private int x, y, width, height;
private Timer timer;
private int fps = 60;
private int delay = 1000/fps;
public Player(int x, int y, int width, int height)
{
//setFocusable(true);
addKeyListener(this);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
timer = new Timer(delay, this);
timer.start();
}
#Override
public void actionPerformed(ActionEvent e)
{
timer.start();
repaint();
}
#Override
public void keyPressed(KeyEvent e)
{
switch (e.getKeyCode())
{
case KeyEvent.VK_A:
if (x <= 0) {
x = 0;
} else {
x -= 3;
}
break;
case KeyEvent.VK_D:
if (x >= 600) {
x = 600;
} else {
x += 3;
}
break;
}
}
public void paint(Graphics g)
{
g.setColor(Color.BLUE);
g.fillRect(x, y, width, height);
}
#Override
public void keyTyped(KeyEvent e) { }
#Override
public void keyReleased(KeyEvent e) { }
}
The main problem with that code was that the custom painted component was neither focusable nor focused (so could not receive key events).
This code fixes those problems as well as a couple more (check code comments for details).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Player extends JPanel implements ActionListener, KeyListener {
private int x, y, width, height;
private final Timer timer;
private final int fps = 60;
private final int delay = 1000 / fps;
public Player(int x, int y, int width, int height) {
addKeyListener(this);
this.x = x;
this.y = y;
this.width = width;
this.height = height;
timer = new Timer(delay, this);
timer.start();
// a component must be focusable to get key events!
setFocusable(true);
}
#Override
public void actionPerformed(ActionEvent e) {
//timer.start(); // Timer should already be started!
repaint();
}
#Override
// correct method for custom painting a JComponent is paintComponent!
public void paintComponent(Graphics g) {
// call the super method to ensure previous paints are erased!
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(x, y, width, height);
}
#Override
// a custom painted component should suggest a size to be used by the
// layout manager
public Dimension getPreferredSize() {
return new Dimension(400,100);
}
#Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_A:
if (x <= 0) {
x = 0;
} else {
x -= 3;
}
break;
case KeyEvent.VK_D:
if (x >= 600) {
x = 600;
} else {
x += 3;
}
break;
}
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
public static void main(String[] args) {
Runnable r = () -> {
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationByPlatform(true);
Player player = new Player(2, 2, 5, 20);
f.setContentPane(player);
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
// a component must be focused to detect key events!
player.requestFocusInWindow();
};
SwingUtilities.invokeLater(r);
}
}
Related
So I created a simple simple simulation where squares are spawned randomly with random vectors and bounce of the edges of the window.
I wanted it to take into account the window being resized. So that if I change the dimensions of the window from 600x600 to 1200x600 the squares will bounce of the new border rather than 600x600.
I tried doing getWidth() getHeight() but it would return 0.
So I put it in the pain() (since it gets called on window resize) method and saved the return values as local variables. But I cannot call getjpWidth() from the Rect class.
So basically what I need is to get new window dimension into the move() method in the Rect class.
Please feel free to point out any other mistakes and things that can be done better. I'm new to 2D programming (studying Computer Science)
Application
import javax.swing.*;
public class Application {
private Application(){
//create a JFrame window
JFrame frame = new JFrame("Moving Squares");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//add a JPanel
GamePanel gamePanel = new GamePanel();
frame.add(gamePanel);
//pack the window around the content
frame.pack();
//center
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String args[]){
new Application();
}
}
GamePanel
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
public class GamePanel extends JPanel implements Runnable{
private int jpWidth=0, jpHeight=0;
//set JPanel size
private static final Dimension DESIRED_SIZE = new Dimension(600,600);
#Override
public Dimension getPreferredSize(){
return DESIRED_SIZE;
}
//constructor
GamePanel(){
Thread t = new Thread(this);
t.start();
}
private ArrayList <Rect> rect=new ArrayList<>();
public void run(){
for(int i=0; i<15; i++){
rect.add(new Rect());
}
while(true){
for(Rect rect:rect){
rect.move();
}
//repaint still image for better frames
//should be 100fps instead it's >144fps
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
}
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g.create();
jpWidth=getWidth();
jpHeight=getHeight();
g2d.setColor(Color.white);
g2d.fillRect(0,0,jpWidth,jpHeight);
for(Rect rect:rect) {
g2d.setColor(Color.black);
g2d.fillRect(rect.getXcord()-1, rect.getYcord()-1, rect.getWidth()+2, rect.getHeight()+2);
g2d.setColor(Color.getHSBColor(rect.getR(), rect.getG(), rect.getB()));
g2d.fillRect(rect.getXcord(), rect.getYcord(), rect.getWidth(), rect.getHeight());
}
}
public int getJpWidth() {
return jpWidth;
}
public int getJpHeight() {
return jpHeight;
}
}
Rect
import java.util.Random;
public class Rect {
//properties
private int width=30, height=30;
private int R, G, B;
//movement
private int xCord, yCord;
private int xVector, yVector;
private int xSlope, ySlope;
public Rect(){
Random rand = new Random();
//random color
R=rand.nextInt(255);
G=rand.nextInt(255);
B=rand.nextInt(255);
//random spawn position
xCord=rand.nextInt(600-width);
yCord=rand.nextInt(600-height);
//direction
do{
xVector=rand.nextInt(3) - 1;
yVector=rand.nextInt(3) - 1;
}while(xVector==0 || yVector==0);
//slope
do{
xSlope=rand.nextInt(3);
ySlope=rand.nextInt(3);
}while(xSlope==0 || ySlope==0);
xVector*=xSlope;
yVector*=ySlope;
}
public void move(){
//if(xCord>=//how to get screen width ? ){}
if((xCord>=600-width) || (xCord<=0)){
bounceX();
}
if((yCord>=600-height) || (yCord<=0)) {
bounceY();
}
xCord+=xVector;
yCord+=yVector;
}
public void bounceX(){
xVector*=-1;
}
public void bounceY(){
yVector*=-1;
}
public int getR() {
return R;
}
public int getG() {
return G;
}
public int getB() {
return B;
}
public int getXcord() {
return xCord;
}
public int getYcord() {
return yCord;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
}
So basically what I need is to get new window dimension into the move() method in the Rect class.
Don't know if it is the best design but I pass the "panel" as a parameter to the "move()" method so its width/height can be used.
Here is some old code I have lying around that shows this approach:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class BallAnimation4
{
private static void createAndShowUI()
{
BallPanel panel = new BallPanel();
JFrame frame = new JFrame("BallAnimation4");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.pack();
frame.setLocationRelativeTo( null );
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible( true );
panel.addBalls(5);
panel.startAnimation();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
class BallPanel extends JPanel implements ActionListener
{
private ArrayList<Ball> balls = new ArrayList<Ball>();
public BallPanel()
{
setLayout( null );
// setBackground( Color.BLACK );
}
public void addBalls(int ballCount)
{
Random random = new Random();
for (int i = 0; i < ballCount; i++)
{
Ball ball = new Ball();
ball.setRandomColor(true);
ball.setLocation(random.nextInt(getWidth()), random.nextInt(getHeight()));
// ball.setMoveRate(32, 32, 1, 1, true);
ball.setMoveRate(16, 16, 1, 1, true);
// ball.setSize(32, 32);
ball.setSize(64, 64);
balls.add( ball );
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball ball: balls)
{
ball.draw(g);
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(800, 600);
}
public void startAnimation()
{
Timer timer = new Timer(1000/60, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
private void move()
{
for (Ball ball : balls)
{
ball.move(this);
}
}
class Ball
{
public Color color = Color.BLACK;
public int x = 0;
public int y = 0;
public int width = 1;
public int height = 1;
private int moveX = 1;
private int moveY = 1;
private int directionX = 1;
private int directionY = 1;
private int xScale = moveX;
private int yScale = moveY;
private boolean randomMove = false;
private boolean randomColor = false;
private Random myRand = null;
public Ball()
{
myRand = new Random();
setRandomColor(randomColor);
}
public void move(JPanel parent)
{
int iRight = parent.getSize().width;
int iBottom = parent.getSize().height;
x += 5 + (xScale * directionX);
y += 5 + (yScale * directionY);
if (x <= 0)
{
x = 0;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (x >= iRight - width)
{
x = iRight - width;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (y <= 0)
{
y = 0;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
if (y >= iBottom - height)
{
y = iBottom - height;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, width, height);
}
public void setColor(Color c)
{
color = c;
}
public void setLocation(int x, int y)
{
this.x = x;
this.y = y;
}
public void setMoveRate(int xMove, int yMove, int xDir, int yDir, boolean randMove)
{
this.moveX = xMove;
this.moveY = yMove;
directionX = xDir;
directionY = yDir;
randomMove = randMove;
}
public void setRandomColor(boolean randomColor)
{
this.randomColor = randomColor;
switch (myRand.nextInt(3))
{
case 0: color = Color.BLUE;
break;
case 1: color = Color.GREEN;
break;
case 2: color = Color.RED;
break;
default: color = Color.BLACK;
break;
}
}
public void setSize(int width, int height)
{
this.width = width;
this.height = height;
}
}
}
Also, note that for animation you should be using a Swing Timer to schedule the animation. Updates to Swing components should be done on the Event Dispatch Thread (EDT). While not likely to cause a problem with this simple application it is a good habit to make sure this basic rule is followed otherwise you can have random problems and it is never easy to debug a random problem.
I am attempting to create a Big Bang world which contains a circle whose initial state should have it moving one pixel diagonally to the bottom-right. Every time the user presses the four arrow keys, the circle should move one pixel towards that direction and keep moving. For example, if I keep pressing the right arrow key, the circle should move towards the right and keep moving faster and faster each time I press it. The problem I am having is that I can't get the circle to move at all! I thought changing x and y coordinates oft he circle for each method should move it accordingly. What am I doing wrong? What can I do to have the circle move as I have described it? Here is the file Game.java that I need to fix:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Game implements World {
public Game() { }
int x, y = 200;
int width, height = 100;
void Circle(int x, int y, int width, int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void draw(Graphics g) {
int x = 200;
int y = 200;
int width = 100;
int height = 100;
g.setColor(Color.red);
g.fillOval(x,
y,
width,
height);
}
public void teh() {
this.x++;
this.y++;
}
public void meh(MouseEvent e) {
int x = e.getX(), y = e.getY();
System.out.println("Mouse event detected.");
}
public void keh(KeyEvent e) {
int x = e.getKeyLocation(), y = e.getKeyLocation();
int key = e.getKeyCode();
if (key == KeyEvent.VK_UP){
this.y--;
System.out.println("Up.");
}
else if (key == KeyEvent.VK_DOWN){
this.y++;
System.out.println("Down.");
}
else if (key == KeyEvent.VK_RIGHT){
this.x++;
System.out.println("Right.");
}
else if (key == KeyEvent.VK_LEFT){
this.x--;
System.out.println("Left.");
}
else{}
// switch( keyCode ) {
// case KeyEvent.VK_UP:
// // handle up
// break;
// case KeyEvent.VK_DOWN:
// // handle down
// break;
// case KeyEvent.VK_LEFT:
// // handle left
// break;
// case KeyEvent.VK_RIGHT :
// // handle right
// break;
// }
}
public boolean hasEnded() {
return false;
}
public void sayBye() {
System.out.println("BYE!");
}
public static void main(String[] args) {
BigBang b = new BigBang(new Game());
b.start( 50, // delay
400 // size
);
}
}
These are the supplementary files that are used but should remain untouched. I am including them for your reference:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class BigBang extends JComponent implements ActionListener, MouseListener, MouseMotionListener, KeyListener {
Timer timer;
World world;
public BigBang(World world) {
this.world = world;
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.addKeyListener(this);
this.setFocusable(true);
this.requestFocus();
}
public void start(int delay, int size) {
JFrame a = new JFrame();
a.add( this );
a.setVisible(true);
a.setSize(size, size);
this.timer = new Timer(delay, this);
this.timer.start();
}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) {
this.world.meh(e);
this.repaint();
}
public void mouseDragged(MouseEvent e) {
this.world.meh(e);
this.repaint();
}
public void mouseMoved(MouseEvent e) { }
public void mouseReleased(MouseEvent u) {
this.world.meh(u);
this.repaint();
}
public void mouseClicked(MouseEvent e) { }
public void keyPressed(KeyEvent e) {
this.world.keh(e);
this.repaint();
}
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
// int count;
public void actionPerformed(ActionEvent e) {
// this.count += 1;
// System.out.println("Ouch" + this.count);
if (this.world.hasEnded()) {
this.timer.stop();
this.world.sayBye();
} else {
this.world.teh();
}
this.repaint();
}
public void paintComponent(Graphics g) {
this.world.draw(g);
}
}
And the interface World.java:
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
interface World {
void draw(Graphics g);
void teh();
void meh(MouseEvent e);
void keh(KeyEvent e);
boolean hasEnded();
void sayBye();
}
Where does my mistake lie? Why am I not able to move the circle when the arrow keys are pressed? How can I continue to add on to the velocity of the object each time the arrow key is pressed? Any modifications or advice will be helpful.
The issue is in your draw method:
public void draw(Graphics g)
{
int x = 200;
int y = 200;
int width = 100;
int height = 100;
g.setColor(Color.red);
g.fillOval(x, y, width, height);
}
You are constantly making new local variables, x, y, width, and height, and drawing with those
Instead you should be using the variables your class already has designed:
public void draw(Graphics g)
{
g.setColor(Color.red);
g.fillOval(x, y, width, height);
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingCircleGUI
{
JFrame frame;
public int x,y;
public int vx = 30,vy=20;
public int width = 500,height = 500;
public int diameter=100;
CircleDrawPanel drawPanel;
Color color = Color.magenta.darker();
JButton button;
Timer timer2 = new Timer(10, new AnimateCircleListener());
boolean isRunning = false;
public static void main (String[] args)
{
MovingCircleGUI gui = new MovingCircleGUI();
gui.go();
}
//this method sets up the JFrame and adds the draw panel to the frame
public void go()
{
frame = new JFrame("MovingCircleGUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new CircleDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
button = new JButton("Click me to start the animation");
drawPanel.add(button);
frame.getContentPane().add(BorderLayout.SOUTH , button);
button.addActionListener(new AnimateCircleListener());
frame.setSize(width,height);
frame.setVisible(true);
}
class CircleDrawPanel extends JPanel
{
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
g2.setColor(color);
g2.fillOval(x,y,diameter,diameter);
}
}
public void MovingBall()
{
x = x + vx;
y = y + vy;
if( y >= height)
{
y=0;
boolean xIsSame = true;
int randomX = 0;
do
{
randomX = Math.round((float)Math.random()*width);
if (randomX != x)
{
x = randomX;
}
}
while(!xIsSame);
}
if(x <= 0)
{
x = width+x;
}
if (x >= width)
{
x = x-width;
}
timer2.start();
frame.repaint();
}
class AnimateCircleListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()== button)
{
if(isRunning)
{
isRunning = false;
button.setText("Click me to start the animation");
button.revalidate();
}
else
{
isRunning = true;
MovingBall();
button.setText("Click me to stop the animation");
button.revalidate();
}
}
}
}
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
}
I am trying to construct a one button which handles two events mainly to start and stop the animation. What I am trying to do is when the user clicks the button the animation of the bouncing ball will start, and the text of the button will change to "Click me to stop", and when the user clicks the button again the animation will stop. I am using the timer.
The animation is okay, I worked that out, and that when the user clicks the button, the animation start that's okay too. The only problem I've got is how am I going to handle another event to the same button?
You could do something similar to this. This way you only need one Action Listener and a boolean to tell it which action to do.
boolean isRunning = false
button.addActionListener(new YourActionListener());
public class YourActionListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==button)
{
if(isRunning)
{
isRunning = false;
button.setText("Click me to start");
button.revalidate();
}
else
{
isRunning = true;
button.setText("Click me to stop");
button.revalidate();
}
}
}
}
Edit
Your code now works. What you wanted to do was call the MoveBall method when timer2 fired the Action Listener.
This can be done by
if(e.getSource()==timer2)
{
MovingBall();
}
so in your code it would look like.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingCircleGUI
{
JFrame frame;
public int x,y;
public int vx = 10,vy=5;
public int width = 500,height = 500;
public int diameter=50;
CircleDrawPanel drawPanel;
Color color = Color.magenta.darker();
JButton button;
Timer timer2 = new Timer(25, new AnimateCircleListener());
boolean isRunning = false;
public static void main (String[] args)
{
MovingCircleGUI gui = new MovingCircleGUI();
gui.go();
}
//this method sets up the JFrame and adds the draw panel to the frame
public void go()
{
frame = new JFrame("MovingCircleGUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawPanel = new CircleDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
button = new JButton("Click me to start the animation");
drawPanel.add(button);
frame.getContentPane().add(BorderLayout.SOUTH , button);
button.addActionListener(new AnimateCircleListener());
frame.setSize(width,height);
frame.setVisible(true);
}
class CircleDrawPanel extends JPanel
{
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
g2.setColor(color);
g2.fillOval(x,y,diameter,diameter);
}
}
public void MovingBall()
{
x = x + vx;
y = y + vy;
if( y >= height)
{
y=0;
boolean xIsSame = true;
int randomX = 0;
do
{
randomX = Math.round((float)Math.random()*width);
if (randomX != x)
{
x = randomX;
}
}
while(!xIsSame);
}
if(x <= 0)
{
x = width+x;
}
if (x >= width)
{
x = x-width;
}
frame.repaint();
}
class AnimateCircleListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()== button)
{
if(timer2.isRunning())
{
button.setText("Click me to start the animation");
button.revalidate();
timer2.stop();
}
else
{
button.setText("Click me to stop the animation");
button.revalidate();
timer2.start();
}
}
if(e.getSource()==timer2)
{
MovingBall();
}
}
}
public int getX()
{
return x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int y)
{
this.y = y;
}
}
You can remove the previous ActionListener on the JButton using button.removeActionListener(<your first action listener>) and then add the second action listener using button.addActionListener(<your second action listener>). Then, to change the text on the button, just use button.setText("Your Text Here").
I'm all new to this site and to Java, so please be lenient.
I'm writing a program that allows to draw different type of shapes with a button click and after hitting another button move/stop/reset them.
I've made already the most part I think (the shapes are correctly creating and storing in an arraylist, the same with the reset, which clear the screen), but I can't figure out how to make them move.I got a function for movement but can't find a way to make the shapes form the arraylist to move. Can anyone give me a little advise.
Thanks
P.S. If there is something wrong/bad coding and needs to be fixed I'll be grateful if you will point at them.
Here is my code:
MyShape class is for creating different shapes.
import java.awt.*;
import java.util.Random;
public abstract class MyShape extends Component {
protected Color color;
private int x, y, dimX, dimY;
public Random random = new Random();
public MyShape(int x, int y, int dimX, int dimY){
this.x = x;
this.y = y;
this.dimX = dimX;
this.dimY = dimY;
color = new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));
}
public abstract void draw(Graphics g);
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getDimX() {
return dimX;
}
public void setDimX(int dimX) {
this.dimX = dimX;
}
public int getDimY() {
return dimY;
}
public void setDimY(int dimY) {
this.dimY = dimY;
}
}
CircleShape - creating circles.
import java.awt.*;
public class CircleShape extends MyShape {
public CircleShape(int x, int y, int dimX, int dimY) {
super(x, y, dimX, dimY);
}
#Override
public void draw(Graphics g) {
g.setColor(color);
g.fillOval(getX(), getY(), getDimX(), getDimY());
}
}
RectangleShape - rectangles
import java.awt.*;
public class RectangleShape extends MyShape {
public RectangleShape(int x, int y, int dimX, int dimY) {
super(x, y, dimX, dimY);
}
#Override
public void draw(Graphics g) {
g.setColor(color);
g.fillRect(getX(), getY(), getDimX(), getDimY());
}
}
and the DrawShape class which handles pretty much everything
public class DrawShapes extends JPanel {
private JButton addButton, resumeAllButton, stopAllButton, resetButton;
private final int FRAME_WIDTH = 800;
private final int FRAME_HEIGHT = 530;
private int x, y, dimX, dimY;
private Random random = new Random();
public List<MyShape> myShapeList = new CopyOnWriteArrayList<MyShape>();
private Timer timer = null;
public boolean move = false;
public DrawShapes() {
this.setLayout(null);
addButton = new JButton("Add Shape");
resumeAllButton = new JButton("Resume Shapes");
stopAllButton = new JButton("Stop All Shapes");
resetButton = new JButton("Reset");
addButton.setBounds(40, 20, 150, 30);
resumeAllButton.setBounds(230, 20, 150, 30);
stopAllButton.setBounds(420, 20, 150, 30);
resetButton.setBounds(610, 20, 150, 30);
this.add(addButton);
this.add(resumeAllButton);
this.add(stopAllButton);
this.add(resetButton);
stopAllButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
move = false;
}
});
resumeAllButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
move = true;
}
});
addButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Thread thread = new Thread() {
public void run() {
init();
}
};
thread.start();
}
});
resetButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < myShapeList.size(); i++) {
myShapeList.clear();
repaint();
}
}
});
}
public void moveIt() {
boolean directionUp = random.nextBoolean();
boolean directionLeft = random.nextBoolean();
boolean directionDown = !directionUp;
boolean directionRight = !directionLeft;
while (move) {
if (x <= 0) {
directionRight = true;
directionLeft = false;
}
if (x >= FRAME_WIDTH - dimX) {
directionRight = false;
directionLeft = true;
}
if (y <= 70) {
directionUp = false;
directionDown = true;
}
if (y >= FRAME_HEIGHT + 50 - dimY) {
directionUp = true;
directionDown = false;
}
if (directionUp)
y--;
if (directionDown)
y++;
if (directionLeft)
x--;
if (directionRight)
x++;
}
}
public void init() {
x = 0;
y = 0;
dimX = (random.nextInt(FRAME_WIDTH) + 100) / 2;
dimY = (random.nextInt(FRAME_HEIGHT) + 100) / 2;
while (x <= 0)
x = (random.nextInt(FRAME_WIDTH) - dimX);
while (y <= 70)
y = (random.nextInt(FRAME_HEIGHT) - dimY);
int choice = 0;
choice = random.nextInt(2) + 1;
switch (choice) {
case 1:
RectangleShape rectangleShape = new RectangleShape(x, y, dimX, dimY);
myShapeList.add(rectangleShape);
timer.start();
repaint();
break;
case 2:
CircleShape circleShape = new CircleShape(x, y, dimX, dimY);
myShapeList.add(circleShape);
repaint();
break;
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 70, 800, 530);
for (MyShape aMyShapeList : myShapeList) {
aMyShapeList.draw(g);
}
}
public static void main(String args[]) {
JFrame jFrame = new JFrame();
jFrame.add(new DrawShapes());
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setSize(800, 600);
jFrame.setResizable(false);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
jFrame.setLocation(dim.width / 2 - jFrame.getSize().width / 2, dim.height / 2 - jFrame.getSize().height / 2);
jFrame.setLocationRelativeTo(null);
jFrame.setVisible(true);
}
}
From the code you posted I can see that you are not calling your moveIt() method anywhere.
You have the right idea of how to move things around. The basic algorithm is:
Calculate new positions
Repaint the view
I can recommend you do the following:
You are currently calling your init method in a thread. I am not sure this is needed. Remove the thread logic and just call the method on the main thread.
Introduce another button tho start the actual animation. When clicking, create a thread that will call your moveIt() method.
I am currently trying to learn a little bit about programming games.
I got myself a player class extending a sprite class i wrote by myself.
Now i want to know, how i can change the image of my player object in the middle of the running game. imagine a spaceship, when pressing the right arrow key it should be a different image, inclined to the right.
now i am trying to do this:
when a button is pressed (e.g. space) setImage(the new image)
But now, when i call this method my image just disappears and the new one won't appear?
Any ideas?
My code of the Main class:
public class Game extends JPanel implements Runnable{
private static final long serialVersionUID = 1L;
public boolean isRunning;
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
private Player player;
public Game() {
addKeyListener(new TAdapter());
setFocusable(true);
requestFocus();
start();
}
public void start() {
isRunning = true;
new Thread(this).start();
}
public void stop() {
isRunning = false;
}
public void run() {
init();
while(isRunning) {
update();
repaint();
try {
Thread.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void init() {
player = new Player("/ship_blue", WIDTH - 32/2, 400);
}
public void update() {
player.update();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
drawPlayer(g2d);
}
public void drawPlayer(Graphics2D g2d) {
if(player.isVisible)g2d.drawImage(player.getImage(),(int) player.getX(), (int) player.getY(), null);
}
public static void main(String[] args) {
Game gameComponent = new Game();
Dimension size = new Dimension(WIDTH*SCALE, HEIGHT*SCALE);
JFrame frame = new JFrame("Invaders");
frame.setVisible(true);
frame.setSize(size);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(gameComponent);
}
public class TAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e) {
player.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
player.keyReleased(e);
}
}
}
That's the main class, now the player class in which i am trying to change the image in the onPress() method:
public class Player extends Sprite{
private double glideSpeed = .125;
private int fixY;
private int xDirection = 0;
private int yDirection = 1;
public Player(String source, int x, int y) {
this.x = x;
this.y = y;
ImageIcon ii = new ImageIcon(this.getClass().getResource(source));
setImage(ii.getImage());
setTileSize(ii.getIconWidth());
setSpeed(0.2);
fixY = y;
}
public void update() {
x += xDirection * 1.4;
glide(10);
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT) {
xDirection = 1;
}
if(key == KeyEvent.VK_LEFT) {
xDirection = -1;
}
if(key == KeyEvent.VK_SPACE) {
ImageIcon ii2 = new ImageIcon("/player_blue_negativ");
setImage(ii2.getImage()); //<-----Here I am trying to change the image
}
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT || key == KeyEvent.VK_LEFT) {
xDirection = 0;
}
}
public void glide(int span) {
y += yDirection * glideSpeed;
if(y - fixY > span || fixY - y > span) {
yDirection = -yDirection;
}
}
}
And to complete everything here's my sprite class:
public class Sprite {
protected double x;
protected double y;
protected int tileSize;
protected double speed;
protected Image img;
protected boolean isVisible;
public Sprite() {
isVisible = true;
}
public double getSpeed() {
return speed;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int getTileSize() {
return tileSize;
}
public Image getImage() {
return img;
}
public boolean getVisible() {
return isVisible;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
public void setTileSize(int tileSize) {
this.tileSize = tileSize;
}
public void setImage(Image img) {
this.img = img;
}
public void die() {
isVisible = false;
}
}
if(key == KeyEvent.VK_SPACE) {
ImageIcon ii2 = new ImageIcon("/player_blue_negativ");
There is the basic problem. All images should be loaded and cached prior to an event happening. When the event occurs, use the cached image and the GUI should render immediately.
As mentioned by #camickr, the use of an ImageIcon also subtly slips from an URL to a String (presumed to represent a
File path). Stick with the URL obtained from getResource(..).
On ImageObserver.
If an ImageObserver is used to paint the image, the observer will be informed of updates to images that are asynchronously loaded. All components (e.g. JPanel) implement ImageObserver, so..
g2d.drawImage(player.getImage(),(int) player.getX(), (int) player.getY(), null);
Should be:
g2d.drawImage(player.getImage(),(int) player.getX(), (int) player.getY(), this);
Free the EDT!
class Game extends JPanel implements Runnable ..
Thread.sleep(5);
Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens.
Also don't attempt to update the GUI from anything other than the EDT.
Instead of calling Thread.sleep(n) in a Runnable, implement a Swing Timer for repeating tasks or a SwingWorker for long running tasks. See Concurrency in Swing for more details.
Use paintComponent(Graphics) for Swing components!
public void paint(Graphics g) {
Should be:
#Override
public void paintComponent(Graphics g) {
When you load the initial image you use the following without a problem:
ImageIcon ii = new ImageIcon(this.getClass().getResource(source));
When you change the image you use:
ImageIcon ii2 = new ImageIcon("/player_blue_negativ");
I'm guessing you don't find the image, probably because you don't need the "/". But why not use the same method that you know works?