I'm building a project for college and I have several shapes in my frame and I need to do some operations with those objects. for example:
Figure fig = figs.get(figs.size() -1);
fig.mov(dx,dy);
figs.set(figs.size() -1, fig);
repaint();
I have a array of figures and every time I drag/move 1 figure, I have to update that array and also do repaint() and this is going to happen when one of the keyboard arrows get pressed.
My problem here is that despite the repaint() function being fast, when I make several moves of 1 object, the screen goes white several times, making it difficult to see. I don't know a lot of java and I don't have any ideas of how to solve this. I was wondering if anyone has any ideas to help me out, please.
Example:
figurestest/Figure.java
package figurestest;
import java.awt.Graphics;
public abstract class Figure {
public int x, y;
public int w, h;
public Figure (int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public abstract void paint (Graphics g);
public abstract void mov (int dx, int dy);
}
figurestest/rect.java
package figurestest;
import java.awt.*;
public class Rect extends Figure {
public Rect (int x, int y, int w, int h) {
super(x, y, w, h);
}
public void paint (Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.fillRect(this.x, this.y, this.w, this.h);
g2d.drawRect(this.x,this.y, this.w,this.h);
}
public void mov (int dx, int dy){
this.x += dx;
this.y += dy;
}
}
testapp.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Random;
import figurestest.*;
class TestApp {
public static void main (String[] args) {
ListFrame frame = new ListFrame();
frame.setVisible(true);
}
}
class ListFrame extends JFrame {
ArrayList<Figure> figs = new ArrayList<Figure>();
Random rand = new Random();
ListFrame () {
this.addWindowListener (
new WindowAdapter() {
public void windowClosing (WindowEvent e) {
System.exit(0);
}
}
);
this.addKeyListener (
new KeyAdapter() {
public void keyPressed (KeyEvent evt) {
Dimension size = getContentPane().getSize();
int x = rand.nextInt(size.width);
int y = rand.nextInt(size.height);
int w = 5+ rand.nextInt(50);
int h = 5+ rand.nextInt(50);
if (evt.getKeyChar() == 'r') {
figs.add(new Rect(x,y, w,h));
}
else if (evt.getKeyCode() == KeyEvent.VK_DOWN || evt.getKeyCode() == KeyEvent.VK_LEFT || evt.getKeyCode() == KeyEvent.VK_RIGHT || evt.getKeyCode() == KeyEvent.VK_UP ) {
if (figs.size() > 0){
int dx = 0;
int dy = 0;
if(evt.getKeyCode() == KeyEvent.VK_DOWN) dy = 2;
else if (evt.getKeyCode() == KeyEvent.VK_UP){ dy = -2;}
else if (evt.getKeyCode() == KeyEvent.VK_RIGHT) dx = 2;
else if (evt.getKeyCode() == KeyEvent.VK_LEFT) dx = -2;
Figure fig = figs.get(figs.size() -1);
fig.mov(dx,dy);
figs.set(figs.size() -1, fig);
}
}
repaint();
}
}
);
this.setTitle("Figures");
this.setSize(350, 350);
}
public void paint (Graphics g) {
super.paint(g);
for (Figure fig: this.figs) {
fig.paint(g);
}
}
}
You're making some strange mistakes. Don't extend JFrame it is pointless in this case.
Here is a start. You should probably use something other than a key listener, and make an action instead for the input map instead.
class ListFrame {
ArrayList<Figure> figs = new ArrayList<Figure>();
Random rand = new Random();
ListFrame () {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel custom = new JPanel(){
#Override
public void paintComponent(Graphics g) {
super.paint(g);
for (Figure fig: figs) {
fig.paint(g);
}
}
#Override
public Dimension getPreferredSize(){
//you custom component should know it's preferred size.
return new Dimension(350, 350);
}
};
frame.addKeyListener (
new KeyAdapter() {
public void keyPressed (KeyEvent evt) {
Dimension size = custom.getSize();
int x = rand.nextInt(size.width);
int y = rand.nextInt(size.height);
int w = 5+ rand.nextInt(50);
int h = 5+ rand.nextInt(50);
if (evt.getKeyChar() == 'r') {
figs.add(new Rect(x,y, w,h));
}
else if (evt.getKeyCode() == KeyEvent.VK_DOWN || evt.getKeyCode() == KeyEvent.VK_LEFT || evt.getKeyCode() == KeyEvent.VK_RIGHT || evt.getKeyCode() == KeyEvent.VK_UP ) {
if (figs.size() > 0){
int dx = 0;
int dy = 0;
if(evt.getKeyCode() == KeyEvent.VK_DOWN) dy = 2;
else if (evt.getKeyCode() == KeyEvent.VK_UP){ dy = -2;}
else if (evt.getKeyCode() == KeyEvent.VK_RIGHT) dx = 2;
else if (evt.getKeyCode() == KeyEvent.VK_LEFT) dx = -2;
Figure fig = figs.get(figs.size() -1);
fig.mov(dx,dy);
//You don't need to set the value. You modified it.
//figs.set(figs.size() -1, fig);
}
}
custom.repaint();
}
}
);
frame.setContentPane(custom);
frame.pack();
frame.setTitle("Figures");
frame.setVisible(true);
}
}
Related
I have managed to push the other object but not the way I want it to, what I was trying to do is to push the other object towards the direction where I am pushing to other object like when you push a box or something. But in this case, I can't quite get how to push the other object to the same direction I am pushing it.
In this case: Box 1 pushing Box 2 from the -x(left) direction so the Box 2 will go towards the +x(right) direction and same with box 1 pushing box 2 from the -y(down) direction then box 2 will go towards +y(up) direction.
Here is the code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Main2 extends JFrame implements KeyListener, ActionListener {
public static int x = 0;
public static int y = 0;
public static int x2 = 100;
public static int y2 = 100;
public Main2() {
add(new panel());
}
public static void main(String[] args) {
Main2 test = new Main2();
test.setTitle("TEST");
test.setSize(Toolkit.getDefaultToolkit().getScreenSize());
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setVisible(true);
test.addKeyListener(test);
}
public class panel extends JPanel {
public panel() {
Container c = getContentPane();
c.setBackground(Color.white);
}
public void paint(Graphics g) {
super.paint(g);
object1(g, x, y);
g.setColor(Color.RED);
object2(g, x2, y2);
Rectangle object1 = new Rectangle(x, y, 25, 25);
Rectangle object2 = new Rectangle(x2, y2, 50, 50);
object1.contains(x, y);
object2.contains(x2, y2);
if (object1.intersects(object2.getX(), object2.getX(), object2.getX(), object2.getX())) {
x2 += 50;
y2 += 0;
}
pause(1);
repaint();
}
}
public static void pause(int time) {
try
{
Thread.sleep(time);
} catch (InterruptedException e) {
}
}
public void actionPerformed(ActionEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == e.VK_RIGHT) {
x += 20;
repaint();
}
if (e.getKeyCode() == e.VK_LEFT) {
x -= 20;
repaint();
}
if (e.getKeyCode() == e.VK_UP) {
y -= 20;
repaint();
}
if (e.getKeyCode() == e.VK_DOWN) {
y += 20;
repaint();
}
}
public void object1(Graphics g, int x, int y) {
g.fillRect(x, y, 30, 30);
}
public void object2(Graphics g, int x, int y) {
g.fillRect(x2, y2, 50, 50);
}
}
One option would be to keep track of the direction the box is moving:
enum Direction {NONE, LEFT, RIGHT, UP, DOWN};
Direction dir = Direction.NONE;
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == e.VK_RIGHT) {
x += 20;
dir = Direction.RIGHT;
repaint();
}
if (e.getKeyCode() == e.VK_LEFT) {
x -= 20;
dir = Direction.LEFT;
repaint();
}
if (e.getKeyCode() == e.VK_UP) {
y -= 20;
dir = Direction.UP;
repaint();
}
if (e.getKeyCode() == e.VK_DOWN) {
y += 20;
dir = Direction.DOWN;
repaint();
}
}
Then, when you intersect with the other rectangle, move accordingly:
if (object1.intersects(object2)) {
if (dir == Direction.RIGHT) x2 += 50;
else if (dir == Direction.LEFT) x2 -= 50;
else if (dir == Direction.DOWN) y2 += 50;
else if (dir == Direction.UP) y2 -= 50;
}
So basically im just playing around with movement systems for different purposes and having trouble making square stop right at the edge. Square moves off the side of screen by around 50% of the square it self and i cannot figure out why it is like that.
package SnakeMovement;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class SnakeMovement extends Canvas implements ActionListener, KeyListener {
Timer timer = new Timer(5, this);
int width, height;
int xSize = 50, ySize = 50;
int yPos = 0, xPos = 0, yVel = 0, xVel = 0;
public SnakeMovement(int w, int h) {
timer.start();
width = w;
height = h;
addKeyListener(this);
setFocusTraversalKeysEnabled(false);
setFocusable(true);
}
public void paint(Graphics g) {
super.paint(g);
g.fillRect(xPos, yPos, xSize, ySize);
}
public void actionPerformed(ActionEvent e) {
xPos += xVel;
yPos += yVel;
if (xPos >= width - xSize) {
xPos = width - xSize;
xVel = 0;
}
repaint();
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_W) {
yVel = -10;
xVel = 0;
}
if (key == KeyEvent.VK_S) {
yVel = 10;
xVel = 0;
}
if (key == KeyEvent.VK_A) {
xVel = -10;
yVel = 0;
}
if (key == KeyEvent.VK_D) {
xVel = 10;
yVel = 0;
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
I just want so square stops at each side of screen perfectly on the edge.
Try:
private static final int BORDER_SIZE = 1;
private static final Color RECT_COLOR = Color.BLUE, BORDER_COLOR = Color.RED;
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(BORDER_COLOR);
g.fillRect(xPos, yPos, xSize, ySize);
g.setColor(RECT_COLOR);
g.fillRect(xPos+BORDER_SIZE, yPos+BORDER_SIZE, xSize-2*BORDER_SIZE, ySize-2*BORDER_SIZE);
}
The idea is to paint a full size rectangular using the border color.
Then painting a smaller one (smaller by 2*BORDER_SIZE) using the rectangular color.
The following is mre of the above:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
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.JFrame;
import javax.swing.Timer;
public class SnakeMovement extends Canvas implements ActionListener, KeyListener {
private static final int BORDER_SIZE = 1, DELAY = 100;
private static final Color RECT_COLOR = Color.BLUE, BORDER_COLOR = Color.RED;
private final int xSize = 50, ySize = 50;
private int yPos = 0, xPos = 0, yVel = 5, xVel = 5;
private final Timer timer = new Timer(DELAY, this);
public SnakeMovement(int width, int height) {
timer.start();
addKeyListener(this);
setFocusTraversalKeysEnabled(false);
setFocusable(true);
setPreferredSize(new Dimension(width, height));
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(BORDER_COLOR);
g.fillRect(xPos, yPos, xSize, ySize);
g.setColor(RECT_COLOR);
g.fillRect(xPos+BORDER_SIZE, yPos+BORDER_SIZE, xSize-2*BORDER_SIZE, ySize-2*BORDER_SIZE);
}
#Override
public void actionPerformed(ActionEvent e) {
xPos += xVel;
yPos += yVel;
checkLimits();
repaint();
}
//when canvas edge is reached emerge from the opposite edge
void checkLimits(){
//check x and y limits
if (xPos >= getWidth()) {
xPos = -xSize;
}
if (xPos < -xSize ) {
xPos = getWidth();
}
if (yPos >= getHeight() ) {
yPos = -ySize;
}
if (yPos < -ySize ) {
yPos = getHeight();
}
}
//two other behaviors when hitting a limit. uncomment the desired behavior
/*
//when canvas edge is reached change direction
void checkLimits(){
//check x and y limits
if ( xPos >= getWidth() - xSize || xPos <= 0) {
xVel = - xVel;
}
if ( yPos >= getHeight() - ySize || yPos <= 0) {
yVel = - yVel;
}
}
*/
/*
//when canvas edge is reached stop movement
void checkLimits(){
//check x and y limits
if ( xPos >= getWidth() - xSize || xPos <= 0 || yPos >= getHeight() - ySize || yPos <= 0) {
timer.stop();
}
}
*/
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_W) {
yVel = -10;
xVel = 0;
}
if (key == KeyEvent.VK_S) {
yVel = 10;
xVel = 0;
}
if (key == KeyEvent.VK_A) {
xVel = -10;
yVel = 0;
}
if (key == KeyEvent.VK_D) {
xVel = 10;
yVel = 0;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
public static void main(String[] args0) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SnakeMovement(500, 500));
frame.pack();
frame.setVisible(true);
}
}
When gameover is true and I call the startGame method from the DOWN button KeyListener, it breaks my game and doesn't allow me to click the exit button on the JFrame and the paddle doesn't work anymore. Please help.
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Image;
public class AustinsBrickBreaker {
JFrame window;
DrawPanel panel;
public AustinsBrickBreaker() {
window = new JFrame("Brick Breaker");
panel = new DrawPanel();
window.setSize(800, 592);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(panel);
window.setLocationRelativeTo(null);
window.setVisible(true);
window.setResizable(false);
}
public void go() {
panel.startGame();
}
public static void main(String[] args) {
AustinsBrickBreaker game = new AustinsBrickBreaker();
game.go();
}
}
#SuppressWarnings("serial")
class DrawPanel extends JPanel implements KeyListener {
final int WIDTH = 800, HEIGHT = 592;
BufferedImage buffer;
public static Brick[][] bricks = new Brick[3][5];
Paddle paddle;
Ball ball;
int score = 0;
int lives = 3;
boolean gameover = false;
Image brickImage = Toolkit.getDefaultToolkit().getImage("brick.png");
Image brickImage2 = Toolkit.getDefaultToolkit().getImage("brick2.png");
Image brickImage3 = Toolkit.getDefaultToolkit().getImage("brick3.png");
Image brickImage4 = Toolkit.getDefaultToolkit().getImage("brick4.png");
Image brickImage5 = Toolkit.getDefaultToolkit().getImage("brick5.png");
Image paddleImage = Toolkit.getDefaultToolkit().getImage("paddle.png");
Image background = Toolkit.getDefaultToolkit().getImage("background.jpg");
Image ballImage = Toolkit.getDefaultToolkit().getImage("ball.png");
Image heartImage = Toolkit.getDefaultToolkit().getImage("heart.png");
public DrawPanel() {
setIgnoreRepaint(true);
addKeyListener(this);
setFocusable(true);
}
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) paddle.left = true;
if (key == KeyEvent.VK_RIGHT) paddle.right = true;
if (key == KeyEvent.VK_UP && gameover) {
gameover = false;
score = 0;
lives = 3;
startGame();
}
if (key == KeyEvent.VK_DOWN && gameover) System.exit(0);
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) paddle.left = false;
if (key == KeyEvent.VK_RIGHT) paddle.right = false;
}
public int count() {
int count = 0;
for (int r = 0; r < DrawPanel.bricks.length; r++)
for (int c = 0; c < DrawPanel.bricks[r].length; c++)
if (!bricks[r][c].visible) count++;
else
break;
int returner = 0;
if (count == 15) returner = 1;
return returner;
}
public void initialize() {
buffer = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
for (int r = 0; r < DrawPanel.bricks.length; r++)
for (int c = 0; c < DrawPanel.bricks[r].length; c++)
DrawPanel.bricks[r][c] = new Brick(c * 150 + 50, r * 60 + 30, 100, 50);
ball = new Ball(390, 200, 20, 20, 10);
paddle = new Paddle(350, 510, 100, 20, 8);
}
public void updateMovement() {
paddle.move();
ball.move();
}
public void checkCollisions() {
if (paddle.x <= 20) paddle.x = 20;
if (paddle.x >= 679) paddle.x = 679;
if (ball.x < 21) {
ball.left = false;
ball.right = true;
}
if (ball.x > 761) {
ball.left = true;
ball.right = false;
}
if (ball.y < 21) {
ball.up = false;
ball.down = true;
}
if (paddle.getBounds().intersects(ball.getBounds())) ball.swap();
for (int r = 0; r < DrawPanel.bricks.length; r++)
for (int c = 0; c < DrawPanel.bricks[r].length; c++) {
if (ball.getBounds().intersects(DrawPanel.bricks[r][c].getBounds()) && !DrawPanel.bricks[r][c].collision) {
ball.swap();
bricks[r][c].collide();
score += 10;
}
}
}
public void drawBuffer() {
Graphics2D b = buffer.createGraphics();
b.drawImage(background, 0, 0, null);
if (!gameover) {
for (int l = 0; l < lives; l++)
b.drawImage(heartImage, 20 * l + 620, 535, null);
b.drawString("Score: " + score, 700, 550);
b.drawImage(paddleImage, paddle.getX(), paddle.getY(), null);
b.drawImage(ballImage, ball.getX(), ball.getY(), null);
for (int r = 0; r < DrawPanel.bricks.length; r++)
for (int c = 0; c < DrawPanel.bricks[r].length; c++) {
if (bricks[r][c].visible == true)
if (bricks[r][c].colour == 1)
b.drawImage(brickImage, DrawPanel.bricks[r][c].getX(), DrawPanel.bricks[r][c].getY(), null);
else if (bricks[r][c].colour == 2)
b.drawImage(brickImage2, DrawPanel.bricks[r][c].getX(), DrawPanel.bricks[r][c].getY(), null);
else if (bricks[r][c].colour == 3)
b.drawImage(brickImage3, DrawPanel.bricks[r][c].getX(), DrawPanel.bricks[r][c].getY(), null);
else if (bricks[r][c].colour == 4)
b.drawImage(brickImage4, DrawPanel.bricks[r][c].getX(), DrawPanel.bricks[r][c].getY(), null);
else if (bricks[r][c].colour == 5)
b.drawImage(brickImage5, DrawPanel.bricks[r][c].getX(), DrawPanel.bricks[r][c].getY(), null);
}
b.dispose();
} else {
b.drawString("G A M E O V E R !", 340, 300);
b.drawString("G A M E O V E R !", 341, 300);
b.drawString("G A M E O V E R !", 342, 300);
b.drawString("Press ↑ To Play Again!", 332, 320);
b.drawString("Press ↓ To Exit :(", 342, 340);
}
}
public void drawScreen() {
Graphics2D g = (Graphics2D) this.getGraphics();
g.drawImage(buffer, 0, 0, this);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public void startGame() {
initialize();
while (!gameover) {
try {
updateMovement();
checkCollisions();
drawBuffer();
drawScreen();
Thread.sleep(15);
if (ball.y > 562 && lives != -1) {
Thread.sleep(500);
lives -= 1;
ball.x = 390;
ball.y = 200;
ball.left = false;
ball.right = false;
paddle.x = 350;
}
if (lives == -1) {
Thread.sleep(500);
gameover = true;
drawBuffer();
drawScreen();
}
//Replace Bricks
if (count() == 1) {
Thread.sleep(500);
startGame();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/*--|--|--|--| GAME CLASSES |--|--|--|--*/
class Brick {
int x, y, width, height, colour;
boolean collision, visible;
public Brick(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.collision = false;
this.visible = true;
this.colour = (int) Math.ceil(Math.random() * 5);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getBounds() {
return new Rectangle(getX(), getY(), getWidth(), getHeight());
}
public void collide() {
if (collision == false) {
visible = false;
collision = true;
}
}
}
class Paddle {
int x, y, width, height, speed;
boolean left, right;
public Paddle(int x, int y, int w, int h, int s) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.speed = s;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getBounds() {
return new Rectangle(getX(), getY(), getWidth(), getHeight());
}
public void move() {
if (left) x -= speed;
if (right) x += speed;
}
}
class Ball {
int x, y, width, height, speed;
boolean up, down, left, right;
public Ball(int x, int y, int w, int h, int s) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.speed = s;
this.up = false;
this.down = true;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getBounds() {
return new Rectangle(getX(), getY(), getWidth(), getHeight());
}
public void move() {
if (up) y -= speed;
if (down) y += speed;
if (left) x -= (float) Math.ceil(Math.random() * 5);
if (right) x += (float) Math.ceil(Math.random() * 5);
}
public void swap() {
if (up) {
down = true;
up = false;
} else if (down) {
up = true;
down = false;
}
double r = Math.random();
if (r <= 0.5) {
left = false;
right = true;
} else if (r > 0.5) {
left = true;
right = false;
} else left = true;
}
}
Your code completely ignores Swing threading rules, and this is somehow allowed when first run, since when first run, the startGame() method is called in the main thread off of the Swing event thread. But when it is called a second time, it is then called on the event thread, and this time, all those sleep calls put the Swing event thread and your application to sleep. The solution: learn about Swing threading rules, and have your application obey these rules, including not calling Thread.sleep, or having forever loops called on the event thread.
see: Lesson: Concurrency in Swing.
This is not an answer, but a very long comment
First, getGraphics is NOT how custom painting works in Swing and you should never use it.
Start by taking a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing.
Swing uses a passive rendering approaching, meaning that it's painting process can take place at any time, for any reason most without your interaction or knowledge, under your current approach, you could end up with intermediate flickering which be near impossible to diagnose or repeat.
If you want control over the painting (active painting), have a look at BufferStrategy and BufferStrategy and BufferCapabilities
Second, don't use KeyListener, there are a very limited number of circumstances I might consider using KeyListener, but this is not one of them and when you find yourself wanting to respond to key events, you should start with the key bindings API
Third, don't use Toolkit.getDefaultToolkit().getImage, but instead use ImageIO, it supports more images, it loads the image first before returning (rather than using a background thread) and throws an IOException when the image can't be loaded. See Reading/Loading an Image for more details
Fourth, you are violating the single thread rules of Swing. Basically, because the way the system works, main is called within what is called the "main" thread, but Swing runs in it's own thread (AKA The Event Dispatching Thread).
So when you first start, go is running in the "main" thread, but when you call start from your KeyListener, you're running within the EDT, meaning that you "game-loop" will block the EDT and nothing will ever paint again and the user won't be able to interact with your program.
See Concurrency in Swing for more details and How to Use Swing Timers for a possible solution
I have been working on a school assignment and the teacher wants us to make 7 circles appear on a JPanel and move downwards. Once a circle reaches the bottom a new circle should be made to replace the circle that reached the bottom of the JPanel. I decided to use an array to continue to make random circles but I cant get it to work right. I used a for loop to populate the array with circles that have a random radius and color. The code compiles but when I run it I get an exception. I am having a hard time getting the array to work properly. The other thing that I am not sure about is how to draw the circles so that they are space out across the JPanel.
The Code
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.Random;
public class keyExample extends JPanel implements ActionListener, KeyListener {
private Circle[] circles = new Circle[7];
Timer t = new Timer(5, this);
//current x and y
double x = 150, y = 200;
double changeX = 0, changeY = 0;
private Circle;
private int circlex = 0, circley = 0; // makes initial starting point of circles 0
private javax.swing.Timer timer2;
public keyExample() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
timer2 = new javax.swing.Timer(33, new MoveListener());
timer2.start();
}
public void NewCircle() {
Random colors = new Random();
Color color = new Color(colors.nextInt(256), colors.nextInt(256), colors.nextInt(256));
Random num = new Random();
int radius = num.nextInt(45);
for (int i = 0; i < circles.length; i++) {
circles[i] = new Circle(circlex, circley, radius, color);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.fill(new Rectangle2D.Double(x, y, 40, 40));
NewCircle();
for (int i = 0; i < circles.length; i++)
circles[i].fill(g);
}
public void actionPerformed(ActionEvent e) {
repaint();
x += changeX;
y += changeY;
changeX = 0;
changeY = 0;
}
public void up() {
if (y != 0) {
changeY = -3.5;
changeX = 0;
}
}
public void down() {
if (y <= 350) {
changeY = 3.5;
changeX = 0;
}
}
public void left() {
if (x >= 0) {
changeX = -3.5;
changeY = 0;
}
}
public void right() {
if (x <= 550) {
changeX = 3.5;
changeY = 0;
}
}
private class MoveListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
repaint();
Random speed = new Random();
int s = speed.nextInt(8);
circle.move(0, s);
}
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_UP) {
up();
}
if (code == KeyEvent.VK_DOWN) {
down();
}
if (code == KeyEvent.VK_RIGHT) {
right();
}
if (code == KeyEvent.VK_LEFT) {
left();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
Your problem is that you're trying to paint with the circle variable, a variable that you never assign a valid reference to.
One solution is to give it a valid reference via circle = new Circle(...), but having said that, I'll tell you to ignore it since you shouldn't even be using the variable circle. Just get rid of it. What you want to do is to use your circles array -- that's what you should be painting in your paintComponent method. Use a for loop inside of paintComponent and iterate through the array painting each circle item that the array holds.
I know I have asked a question like this before, but none of the answers in the old question worked for me. I am trying to make a little single-player pong game (It is in a Java applet). I already have a moveBall() function, as you can see below. But I don't know where to call it. I can't call it in the paint() method because it is double buffered.
public class Main extends Applet implements KeyListener, MouseListener {
private Rectangle paddle;
private Rectangle ball;
private ArrayList<Integer> keysDown;
private Image dbImage;
private Graphics dbg;
public int time = 300000;
Random randomGenerator = new Random();
int speed = 10;
int level = 1; // change to 0 once start menu works
int xpos, ypos;
int ballx, bally;
int width = 1024;
int height = 768;
int paddleWidth = 96;
int ballSize = 16;
String version = "0.0.1";
public static final int START_X_POS = 160;
public static final int START_Y_POS = 160;
public static final int START_WIDTH = 256;
public static final int START_HEIGHT = 64;
boolean startClicked;
boolean falling = true;
public void init() {
setSize(width, height);
addKeyListener(this);
addMouseListener(this);
setBackground(Color.black);
Frame c = (Frame)getParent().getParent();
c.setTitle("Asteroid Attack - Version " + version);
keysDown = new ArrayList<Integer>();
paddle = new Rectangle(getWidth()/2-paddleWidth, getHeight()-96, paddleWidth, 12);
ball = new Rectangle(getWidth()/2-ballSize, 96, ballSize, ballSize);
}
public void update(Graphics g) {
dbImage = createImage(getSize().width, getSize().height);
dbg = dbImage.getGraphics ();
if (dbImage == null) {}
dbg.setColor(getBackground ());
dbg.fillRect(0, 0, getSize().width, getSize().height);
dbg.setColor(getForeground());
paint(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
if (level != 0) {
g2.setPaint(Color.gray);
g2.fill(paddle);
g2.setPaint(Color.darkGray);
g2.fill(ball);
moveBall();
}
}
public void moveBall() {
bally = ball.y;
ballx = ball.x;
if (bally < paddle.y-32 && falling) {
bally += 12;
}
if (bally < paddle.y-32 && falling && paddle.x <= ballx && paddle.getMaxX() >= ball.x) { // collides with paddle
falling = false;
}
else { // does not collide with paddle
}
ball.setLocation(ballx, bally);
}
#Override
public void keyPressed(KeyEvent e) {
if (!keysDown.contains(e.getKeyCode()))
keysDown.add(new Integer(e.getKeyCode()));
key();
}
#Override
public void keyReleased(KeyEvent e) {
keysDown.remove(new Integer(e.getKeyCode()));
}
public void key() {
if (level != 0) {
int x = paddle.x;
int y = paddle.y;
if (keysDown.contains(KeyEvent.VK_ESCAPE)) {System.exit(0);}
if (x > 0 && x+paddleWidth < this.getWidth()) {
if (keysDown.contains(KeyEvent.VK_LEFT)) {x -= speed;}
if (keysDown.contains(KeyEvent.VK_RIGHT)) {x += speed;}
}
else { // so paddle doesn't exit room
if (x <= 0) {x += 4;}
else {x -= 4;}
}
paddle.setLocation(x, y);
repaint();
}
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void mouseClicked(MouseEvent me) {
if (level == 0) {
xpos = me.getX();
ypos = me.getY();
if (xpos >= START_X_POS && ypos >= START_Y_POS && xpos <= START_X_POS + START_WIDTH && ypos <= START_X_POS + START_HEIGHT ) {
level = 1;
}
}
}
#Override
public void mouseEntered(MouseEvent me) {}
#Override
public void mouseExited(MouseEvent me) {}
#Override
public void mouseReleased(MouseEvent me) {}
#Override
public void mousePressed(MouseEvent me) {}
}
Any help would be greatly appreciated!
You'll probably want a separate Thread to move the ball which keeps track of time while in a in a loop - that way if frame rates drop, or speed up, you can try ensure consistency in the ball movement speed.
e.g. here on using a thread in a applet http://www.realapplets.com/tutorial/threadexample.html