Related
I am trying to create a snake clone just as a practice. ive drawn the snake and added the movement patterns but the snake eats on it self when I press any key to move. but its not moving. the array retracts the reactacles on the starting point and does nothing.
here is my snake class I have removed my comments as they where more than the code and the posting system was not allowing me to post
Edit
If you need anything from the other classes please let me know. but I think my error is somewhere in here
EDIT 2
Added the entire code, you can just copy paste in inside a new project and you will reproduce my error.
public class Snake {
List<Point> sPoints;
int xDir,yDir;
boolean isMoving,addTail;
final int sSize = 20, startX = 150 , startY = 150;
public Snake(){
sPoints = new ArrayList<Point>();
xDir = 0;
yDir = 0;
isMoving = false;
addTail = false;
sPoints.add(new Point(startX,startY));
for(int i=1; i<sSize; i++) {
sPoints.add(new Point(startX - i * 4,startY));
}
}
public void draw(Graphics g){
g.setColor(Color.white);
for(Point p : sPoints) {
g.fillRect(p.getX(),p.getY(),4,4);
}
}
public void move(){
if (isMoving) {
Point temp = sPoints.get(0);
Point last = sPoints.get(sPoints.size() - 1);
Point newstart = new Point(temp.getX() + xDir * 4, temp.getY() + yDir * 4);
for (int i = sPoints.size() - 1; i >= 1; i--) {
sPoints.set(i, sPoints.get(i - 1));
}
sPoints.set(0, newstart);
}
}
public int getxDir() {
return xDir;
}
public void setxDir(int x) {
this.xDir = xDir;
}
public int getyDir() {
return yDir;
}
public void setyDir(int y) {
this.yDir = yDir;
}
public int getX(){
return sPoints.get(0).getX();
}
public int getY(){
return sPoints.get(0).getY();
}
public boolean isMoving() {
return isMoving;
}
public void setIsMoving(boolean b) {
isMoving = b;
}
}
The following is the point class. just some getters setters for the points ,for those i used the IntelliJ to auto generate them.. (again i removed comments )
public class Point {
private int x,y;
public Point() {
x = 0;
y = 0;
}
public Point(int x, int y) {
this.x =x;
this.y =y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
and finally my main class called game.
in here what I do is create my applet give it background color. create my threat for the runnable. and also add the movement patterns for up/right/down/left...
and use several classes to update my drawing patterns so it can simulate movement by updating each of state of my rect list.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Game extends Applet implements Runnable, KeyListener {
//setting up double buffering.
Graphics graphics;
Image img;
Thread thread;
Snake snake;
public void init() {
//setting the size of our Applet
this.resize(400,400);
//we gonna create the image just the same size as our applet.
img = createImage(400,400);
//this represents our offscreen image that we will draw
graphics = img.getGraphics();
this.addKeyListener(this);
snake = new Snake();
thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
//Setting the background of our applet to black
graphics.setColor(Color.black);
//Fill rectangle 0 , 0 (starts from) for top left corner and then 400,400 to fill our entire background to black
graphics.fillRect(0,0,400,400);
snake.draw(graphics);
//painting the entire image
g.drawImage(img,0,0,null);
}
//Update will call on Paint(g)
public void update(Graphics g){
paint(g);
}
//Repaint will call on Paint(g)
public void repaint(Graphics g){
paint(g);
}
public void run() {
//infinite loop
for(;;) {
snake.move();
//drawing snake
this.repaint();
//Creating a time delay
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void keyTyped(KeyEvent keyEvent) {
}
public void keyPressed(KeyEvent keyEvent) {
if(!snake.isMoving()){ //this will allow the snake to start moving, but will disable LEFT for just the 1st move
if(keyEvent.getKeyCode() == KeyEvent.VK_UP || keyEvent.getKeyCode() == KeyEvent.VK_RIGHT ||
keyEvent.getKeyCode() == KeyEvent.VK_DOWN ) {
snake.setIsMoving(true);
}
}
//setting up Key mapping so when the user presses UP,RIGHT,DOWN,LEFT. the Snake will move accordingly
if(keyEvent.getKeyCode() == KeyEvent.VK_UP) {
if (snake.getyDir() != 1) {
snake.setyDir(-1);
snake.setxDir(0);
}
}
if(keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
if (snake.getxDir() != -1) {
snake.setxDir(1);
snake.setyDir(0);
}
}
if(keyEvent.getKeyCode() == KeyEvent.VK_DOWN) {
if (snake.getyDir() != -1) {
snake.setyDir(1);
snake.setxDir(0);
}
}
if(keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
if (snake.getxDir() != 1) {
snake.setxDir(-1);
snake.setyDir(0);
}
}
}
public void keyReleased(KeyEvent keyEvent) {
}
}
Here is some opinion I have reading your code.
The reason your snake won't move is because your snake.setyDir() and
snake.setxDir() didn't take the input to overwrite xDir and yDir. They are assigning to itself.
There is a Point2D class ready for you in JDK
When moving the snake, you just need to remove the tail and add one
more block before the head. You can keep the body tight (according
to my common knowledge to snake).
Consider a L shape snake on the left, the bottom end is the head and it is currently heading right. To move the snake, remove the tail (green block) and add one more to the head according to its direction (red block). It final state become the snake on the right. LinkedList suit the needs.
If using two int (xDir and yDir) to control the snake direction
is confusing, you can help your self by creating a enum. Those -1,
0, 1 with x and y may confuse you.
Declare constant instead of magic number. e.g. the width of block 4,
image size 400
Is Snake.addTail unnecessary?
Attribute should has accessibility modifier
End result:
Game.java
import java.applet.Applet;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Arrays;
public class Game extends Applet implements Runnable, KeyListener {
private final int GAMEBOARD_WIDTH = 400;
// setting up double buffering.
private Graphics graphics;
private Image img;
private Thread thread;
private Snake snake;
public void init() {
// setting the size of our Applet
this.resize(GAMEBOARD_WIDTH, GAMEBOARD_WIDTH);
// we gonna create the image just the same size as our applet.
img = createImage(GAMEBOARD_WIDTH, GAMEBOARD_WIDTH);
// this represents our offscreen image that we will draw
graphics = img.getGraphics();
this.addKeyListener(this);
snake = new Snake();
thread = new Thread(this);
thread.start();
}
public void paint(Graphics g) {
// Setting the background of our applet to black
graphics.setColor(Color.BLACK);
// Fill rectangle 0 , 0 (starts from) for top left corner and then 400,400 to
// fill our entire background to black
graphics.fillRect(0, 0, GAMEBOARD_WIDTH, GAMEBOARD_WIDTH);
snake.draw(graphics);
// painting the entire image
g.drawImage(img, 0, 0, null);
}
// Update will call on Paint(g)
public void update(Graphics g) {
paint(g);
}
// Repaint will call on Paint(g)
public void repaint(Graphics g) {
paint(g);
}
public void run() {
// infinite loop
for (;;) {
snake.move();
// drawing snake
this.repaint();
// Creating a time delay
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void keyTyped(KeyEvent keyEvent) {
}
public void keyPressed(KeyEvent keyEvent) {
int keyCode = keyEvent.getKeyCode();
if (!snake.isMoving()) {
// this will allow the snake to start moving, but will disable LEFT for just the
// 1st move
if (matchKey(keyCode, KeyEvent.VK_UP, KeyEvent.VK_RIGHT, KeyEvent.VK_DOWN)) {
snake.setIsMoving(true);
}
}
// setting up Key mapping so when the user presses UP,RIGHT,DOWN,LEFT. the Snake
// will move accordingly
if (matchKey(keyCode, KeyEvent.VK_UP)) {
snake.setDirection(Direction.UP);
}
if (matchKey(keyCode, KeyEvent.VK_RIGHT)) {
snake.setDirection(Direction.RIGHT);
}
if (matchKey(keyCode, KeyEvent.VK_DOWN)) {
snake.setDirection(Direction.DOWN);
}
if (matchKey(keyCode, KeyEvent.VK_LEFT)) {
snake.setDirection(Direction.LEFT);
}
}
// return true if targetKey contains the provided keyCode
private boolean matchKey(int keyCode, int... targetKey) {
return Arrays.stream(targetKey).anyMatch(i -> i == keyCode);
}
public void keyReleased(KeyEvent keyEvent) {
}
}
Snake.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import java.util.LinkedList;
public class Snake {
private final int sSize = 20, startX = 150, startY = 150;
private final int BLOCK_WIDTH = 4;
private LinkedList<Point2D.Float> sPoints;
private boolean isMoving;
private Direction direction;
public Snake() {
sPoints = new LinkedList<Point2D.Float>();
isMoving = false;
sPoints.add(new Point2D.Float(startX, startY));
for (int i = 1; i < sSize; i++) {
sPoints.add(new Point2D.Float(startX - i * BLOCK_WIDTH, startY));
}
}
public void draw(Graphics g) {
g.setColor(Color.white);
for (Point2D p : sPoints) {
g.fillRect((int) p.getX(), (int) p.getY(), BLOCK_WIDTH, BLOCK_WIDTH);
}
}
public void move() {
if (isMoving) {
sPoints.removeLast();
steer(sPoints.getFirst());
}
}
private void steer(Point2D head) {
Point2D.Float newHead = new Point2D.Float();
switch (this.getDirection()) {
case UP:
newHead.setLocation(head.getX(), head.getY() - BLOCK_WIDTH);
break;
case DOWN:
newHead.setLocation(head.getX(), head.getY() + BLOCK_WIDTH);
break;
case LEFT:
newHead.setLocation(head.getX() - BLOCK_WIDTH, head.getY());
break;
case RIGHT:
newHead.setLocation(head.getX() + BLOCK_WIDTH, head.getY());
break;
}
this.sPoints.addFirst(newHead);
}
public int getX() {
return (int) sPoints.get(0).getX();
}
public int getY() {
return (int) sPoints.get(0).getY();
}
public boolean isMoving() {
return isMoving;
}
public void setIsMoving(boolean b) {
isMoving = b;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction d) {
if (this.getDirection() == null) {
this.direction = d;
} else if (!this.getDirection().isOpposite(d)) {
this.direction = d;
}
}
}
Direction.java
public enum Direction {
UP(-1), DOWN(1), LEFT(-2), RIGHT(2);
int vector;
Direction(int i) {
this.vector = i;
}
public boolean isOpposite(Direction d) {
return this.vector + d.vector == 0;
}
}
Snack.java
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Snake extends JFrame {
public Snake() {
initUI();
}
private void initUI() {
add(new Board());
setResizable(false);
pack();
setTitle("Snake");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame ex = new Snake();
ex.setVisible(true);
});
}
}
Board.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Board extends JPanel implements ActionListener {
private final int B_WIDTH = 300;
private final int B_HEIGHT = 300;
private final int DOT_SIZE = 10;
private final int ALL_DOTS = 900;
private final int RAND_POS = 29;
private final int DELAY = 140;
private final int x\[\] = new int\[ALL_DOTS\];
private final int y\[\] = new int\[ALL_DOTS\];
private int dots;
private int apple_x;
private int apple_y;
private boolean leftDirection = false;
private boolean rightDirection = true;
private boolean upDirection = false;
private boolean downDirection = false;
private boolean inGame = true;
private Timer timer;
private Image ball;
private Image apple;
private Image head;
public Board() {
initBoard();
}
private void initBoard() {
addKeyListener(new TAdapter());
setBackground(Color.black);
setFocusable(true);
setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT));
loadImages();
initGame();
}
private void loadImages() {
ImageIcon iid = new ImageIcon("src/resources/dot.png");
ball = iid.getImage();
ImageIcon iia = new ImageIcon("src/resources/apple.png");
apple = iia.getImage();
ImageIcon iih = new ImageIcon("src/resources/head.png");
head = iih.getImage();
}
private void initGame() {
dots = 3;
for (int z = 0; z < dots; z++) {
x\[z\] = 50 - z * 10;
y\[z\] = 50;
}
locateApple();
timer = new Timer(DELAY, this);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
if (inGame) {
g.drawImage(apple, apple_x, apple_y, this);
for (int z = 0; z < dots; z++) {
if (z == 0) {
g.drawImage(head, x\[z\], y\[z\], this);
} else {
g.drawImage(ball, x\[z\], y\[z\], this);
}
}
Toolkit.getDefaultToolkit().sync();
} else {
gameOver(g);
}
}
private void gameOver(Graphics g) {
String msg = "Game Over";
Font small = new Font("Helvetica", Font.BOLD, 14);
FontMetrics metr = getFontMetrics(small);
g.setColor(Color.white);
g.setFont(small);
g.drawString(msg, (B_WIDTH - metr.stringWidth(msg)) / 2, B_HEIGHT / 2);
}
private void checkApple() {
if ((x\[0\] == apple_x) && (y\[0\] == apple_y)) {
dots++;
locateApple();
}
}
private void move() {
for (int z = dots; z > 0; z--) {
x\[z\] = x\[(z - 1)\];
y\[z\] = y\[(z - 1)\];
}
if (leftDirection) {
x\[0\] -= DOT_SIZE;
}
if (rightDirection) {
x\[0\] += DOT_SIZE;
}
if (upDirection) {
y\[0\] -= DOT_SIZE;
}
if (downDirection) {
y\[0\] += DOT_SIZE;
}
}
private void checkCollision() {
for (int z = dots; z > 0; z--) {
if ((z > 4) && (x\[0\] == x\[z\]) && (y\[0\] == y\[z\])) {
inGame = false;
}
}
if (y\[0\] >= B_HEIGHT) {
inGame = false;
}
if (y\[0\] < 0) {
inGame = false;
}
if (x\[0\] >= B_WIDTH) {
inGame = false;
}
if (x\[0\] < 0) {
inGame = false;
}
if (!inGame) {
timer.stop();
}
}
private void locateApple() {
int r = (int) (Math.random() * RAND_POS);
apple_x = ((r * DOT_SIZE));
r = (int) (Math.random() * RAND_POS);
apple_y = ((r * DOT_SIZE));
}
#Override
public void actionPerformed(ActionEvent e) {
if (inGame) {
checkApple();
checkCollision();
move();
}
repaint();
}
private class TAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) {
leftDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) {
rightDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_UP) && (!downDirection)) {
upDirection = true;
rightDirection = false;
leftDirection = false;
}
if ((key == KeyEvent.VK_DOWN) && (!upDirection)) {
downDirection = true;
rightDirection = false;
leftDirection = false;
}
}
}
}
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
When I try to export as a Runnable JAR file, I have no options for 'Launch configuration'. Why is this? I am new to programming, can anyone help me?
It's a code for the game Snake. Do I need a main()?
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.JOptionPane;
#SuppressWarnings("serial")
public class snakeCanvas extends Canvas implements Runnable, KeyListener
{
private final int BOX_HEIGHT = 15;
private final int BOX_WIDTH = 15;
private final int GRID_HEIGHT = 25;
private final int GRID_WIDTH = 25;
private LinkedList<Point> snake;
private Point fruit;
private int direction = Direction.NO_DIRECTION;
private Thread runThread; //allows us to run in the background
private int score = 0;
private String highScore = "";
private Image menuImage = null;
private boolean isInMenu = true;
public void paint(Graphics g)
{
if (runThread == null)
{
this.setPreferredSize(new Dimension(640, 480));
this.addKeyListener(this);
runThread = new Thread(this);
runThread.start();
}
if (isInMenu)
{
DrawMenu(g);
//draw menu
}
else
{
if (snake == null)
{
snake = new LinkedList<Point>();
GenerateDefaultSnake();
PlaceFruit();
}
if (highScore.equals(""))
{
highScore = this.GetHighScore();
System.out.println(highScore);
}
DrawSnake(g);
DrawFruit(g);
DrawGrid(g);
DrawScore(g);
//draw everything else
}
}
public void DrawMenu(Graphics g)
{
if (this.menuImage == null)
{
try
{
URL imagePath = snakeCanvas.class.getResource("snakeMenu.png");
this.menuImage = Toolkit.getDefaultToolkit().getImage(imagePath);
}
catch (Exception e)
{
e.printStackTrace();
}
}
g.drawImage(menuImage, 0, 0, 640, 480, this);
}
public void update(Graphics g)
{
Graphics offScreenGraphics;
BufferedImage offscreen = null;
Dimension d= this.getSize();
offscreen = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
offScreenGraphics = offscreen.getGraphics();
offScreenGraphics.setColor(this.getBackground());
offScreenGraphics.fillRect(0, 0, d.width, d.height);
offScreenGraphics.setColor(this.getForeground());
paint(offScreenGraphics);
g.drawImage(offscreen, 0, 0, this);
}
public void GenerateDefaultSnake()
{
score = 0;
snake.clear();
snake.add(new Point (0,2));
snake.add(new Point(0,1));
snake.add(new Point(0,0));
direction = Direction.NO_DIRECTION;
}
public void Move() //adds a new 'block' at front of snake, deleting the back 'block' to create movement
{
Point head = snake.peekFirst(); //grab the first item with no problems
Point newPoint = head;
switch (direction) {
case Direction.NORTH:
newPoint = new Point(head.x, head.y - 1); //vector of -j
break;
case Direction.SOUTH:
newPoint = new Point(head.x, head.y + 1);
break;
case Direction.WEST:
newPoint = new Point(head.x - 1, head.y);
break;
case Direction.EAST:
newPoint = new Point (head.x + 1, head.y);
break;
}
snake.remove(snake.peekLast()); //delete the last block
if (newPoint.equals(fruit))
{
//the snake hits the fruit
score+=10;
Point addPoint = (Point) newPoint.clone(); //creating the end piece upon eating fruit
switch (direction) {
case Direction.NORTH:
newPoint = new Point(head.x, head.y - 1); //vector of -j
break;
case Direction.SOUTH:
newPoint = new Point(head.x, head.y + 1);
break;
case Direction.WEST:
newPoint = new Point(head.x - 1, head.y);
break;
case Direction.EAST:
newPoint = new Point (head.x + 1, head.y);
break;
}
//the movement upon eating fruit
snake.push(addPoint);
PlaceFruit();
}
else if (newPoint.x < 0 || newPoint.x > (GRID_WIDTH - 1))
{
//snake has gone out of bounds - reset game
CheckScore();
GenerateDefaultSnake();
return;
}
else if (newPoint.y < 0 || newPoint.y > (GRID_HEIGHT - 1))
{
//snake has gone out of bounds - reset game
CheckScore();
GenerateDefaultSnake();
return;
}
else if (snake.contains(newPoint))
{
//running into your tail - reset game
CheckScore();
GenerateDefaultSnake();
return;
}
snake.push(newPoint);
}
public void DrawScore(Graphics g)
{
g.drawString("Score: " + score, 0, BOX_HEIGHT * GRID_HEIGHT + 15);
g.drawString("Highscore:" + highScore, 0, BOX_HEIGHT * GRID_HEIGHT + 30);
}
public void CheckScore()
{
if (highScore.equals(""))
return;
if (score > Integer.parseInt((highScore.split(":")[1])))
{
String name = JOptionPane.showInputDialog("New highscore. Enter your name!");
highScore = name + ":" + score;
File scoreFile = new File("highscore.dat");
if (!scoreFile.exists())
{
try {
scoreFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileWriter writeFile = null;
BufferedWriter writer = null;
try
{
writeFile = new FileWriter(scoreFile);
writer = new BufferedWriter(writeFile);
writer.write(this.highScore);
}
catch (Exception e)
{
}
finally
{
try
{
if (writer != null)
writer.close();
}
catch (Exception e) {}
}
}
}
public void DrawGrid(Graphics g)
{
//drawing the outside rectangle
g.drawRect(0, 0, GRID_WIDTH * BOX_WIDTH, GRID_HEIGHT * BOX_HEIGHT);
//drawing the vertical lines
for (int x = BOX_WIDTH; x < GRID_WIDTH * BOX_WIDTH; x+=BOX_WIDTH)
{
g.drawLine(x, 0, x, BOX_HEIGHT * GRID_HEIGHT);
}
//drawing the horizontal lines
for (int y = BOX_HEIGHT; y < GRID_HEIGHT * BOX_HEIGHT; y+=BOX_HEIGHT)
{
g.drawLine(0, y, GRID_WIDTH * BOX_WIDTH, y);
}
}
public void DrawSnake(Graphics g)
{
g.setColor(Color.ORANGE);
for (Point p : snake)
{
g.fillRect(p.x * BOX_WIDTH, p.y * BOX_HEIGHT, BOX_WIDTH, BOX_HEIGHT);
}
g.setColor(Color.BLACK);
}
public void DrawFruit(Graphics g)
{
g.setColor(Color.RED);
g.fillOval(fruit.x * BOX_WIDTH, fruit.y * BOX_HEIGHT, BOX_WIDTH, BOX_HEIGHT);
g.setColor(Color.BLACK);
}
public void PlaceFruit()
{
Random rand = new Random();
int randomX = rand.nextInt(GRID_WIDTH);
int randomY = rand.nextInt(GRID_HEIGHT);
Point randomPoint = new Point(randomX, randomY);
while (snake.contains(randomPoint)) //If the fruit happens to spawn on the snake, it will change position
{
randomX= rand.nextInt(GRID_WIDTH);
randomY= rand.nextInt(GRID_HEIGHT);
randomPoint = new Point(randomX, randomY);
}
fruit = randomPoint;
}
#Override
public void run() {
while (true)
{
repaint();
//runs indefinitely (CONSTANTLY LOOPS)
if (!isInMenu)
Move();
//Draws grid, snake and fruit
//buffer to slow down the snake
try
{
Thread.currentThread();
Thread.sleep(100);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public String GetHighScore()
{
FileReader readFile = null;
BufferedReader reader = null;
try
{
readFile = new FileReader("highscore.dat");
reader = new BufferedReader(readFile);
return reader.readLine();
}
catch (Exception e)
{
return "Nobody:0";
}
finally
{
try{
if (reader != null)
reader.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
#Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode())
{
case KeyEvent.VK_UP:
if (direction != Direction.SOUTH)
direction = Direction.NORTH;
break;
case KeyEvent.VK_DOWN:
if (direction != Direction.NORTH)
direction = Direction.SOUTH;
break;
case KeyEvent.VK_RIGHT:
if (direction != Direction.WEST)
direction = Direction.EAST;
break;
case KeyEvent.VK_LEFT:
if (direction != Direction.EAST)
direction = Direction.WEST;
break;
case KeyEvent.VK_ENTER:
if (isInMenu)
{
isInMenu = false;
repaint();
}
break;
case KeyEvent.VK_ESCAPE:
isInMenu = true;
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Basically, you need a Run configuration that was already used to execute your Java application to have a runnable jar file. This is used to select the correct starting point of the program.
If you have executed your Java application using the Run command (either from the Run menu or the toolbar), a Run configuration was created that executes. However, judging from your question, you have not done so, as you have no entrypoint defined for your application.
For that java uses a static main method with predefined parameters, and any Java class that has such a method can be executed. After you have successfully executed your application, it can be started, and then the created run configuration can be used to export a jar file.
I am making a snake game, and I am stuck at where making the tails follow the head. And I heard using an add and remove on the head and tails could make that happen, but I have no idea where to start with that.
Here's my code so far:
Screen.java
public class Screen extends JPanel implements ActionListener, KeyListener {
public static final JLabel statusbar = new JLabel("Default");
public static final int WIDTH = 800, HEIGHT = 800;
Timer t = new Timer(100, this);
int x = 400;
int y = 400;
int size = 5; //increase size if eat
private boolean right = false, left = false, up = false, down = false;
int head = 0;
private LinkedList<BodyPart> snake = new LinkedList<BodyPart>();
private BodyPart b;
public Screen(){
initSnake();
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void update(){
}
public void direction(){
if(right) x+=10;
if(left) x-=10;
if(up) y-=10;
if(down) y+=10;
}
public void trackOutBound(){
if(x < 0 || x > 800 || y < 0 || y > 800) {
x = 400;
y = 400;
}
}
public void initSnake(){
if(snake.size() == 0){
b = new BodyPart(x, y);
for(int i = 0; i < size; i++) {
snake.add(b);
}
System.out.println(snake);
}
}
public static void main(String[] args) {
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(new Color(10, 50, 0));
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.BLACK);
for(int i = 0; i < WIDTH / 10; i++) {
g.drawLine(i * 10, 0, i * 10, HEIGHT);
}
for(int i = 0; i < HEIGHT / 10; i++) {
g.drawLine(0, i * 10, WIDTH, i * 10);
}
int tempx = 0, tempy = 0;
int temp = 0;
for(int i = 0; i < size; i++){
if(i == head) {
snake.get(i).x = x;
snake.get(i).y = y;
snake.get(i).draw(g);
g.setColor(Color.blue);
g.fillRect(x, y, 10, 10);
g.setColor(Color.white);
g.drawRect(x, y, 10, 10);
} else if(i > 0 && up) {
snake.get(i).x = x;
snake.get(i).y = y + temp;
snake.get(i).draw(g);
} else if(i > 0 && down) {
snake.get(i).x = x;
snake.get(i).y = y - temp;
snake.get(i).draw(g);
} else if(i > 0 && left) {
snake.get(i).x = x + temp;
snake.get(i).y = y;
snake.get(i).draw(g);
} else if(i > 0 && right) {
snake.get(i).x = x - temp;
snake.get(i).y = y;
snake.get(i).draw(g);
}
temp += 10;
}
/*
if(snake.size() == 5){
snake.add(b);
size += 1;
}
*/
}
#Override
public void actionPerformed(ActionEvent e) {
direction();
trackOutBound();
repaint();
// System.out.println(snake);
statusbar.setText("(" + x + " , " + y + ")");
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT && !left) {
up = false;
down = false;
right = true;
}
if(key == KeyEvent.VK_LEFT && !right) {
up = false;
down = false;
left = true;
}
if(key == KeyEvent.VK_UP && !down) {
left = false;
right = false;
up = true;
}
if(key == KeyEvent.VK_DOWN && !up) {
left = false;
right = false;
down = true;
}
}
#Override
public void keyReleased(KeyEvent e) {}
}
BodyPart.java
public class BodyPart {
int x;
int y;
public BodyPart(int x, int y) {
this.x = x;
this.y = y;
}
public void draw(Graphics g) {
this.x = x;
this.y = y;
g.setColor(Color.red);
g.fillRect(x, y, 10, 10);
g.setColor(Color.white);
g.drawRect(x, y, 10, 10);
}
}
Frame.java
public class Frame extends JPanel {
private static JLabel statusbar = new JLabel("Default");
public void statusbar(){
statusbar = Screen.statusbar;
}
public static void main(String[] args) {
JFrame f = new JFrame();
Screen s = new Screen();
f.add(s);
f.add(statusbar, BorderLayout.SOUTH);
f.setSize(800, 800);
f.setVisible(true);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Now this code would only make the tails flip to the horizontal or vertical, is it possible to make the tails follow the head by using this code? or I need to change my code?
Thank you
The basic idea is, you need some kind of List which contains ALL the points of the snake. Conceptually, the List would contain virtual coordinates, that is 1x1 would represent a coordinate in virtual space, which presented a place on a virtual board (which would have some wide and height).
You could then translate that to the screen, so this would allow each part of the snake to be larger then a single pixel. So, if each part was 5x5 pixels, then 1x1 would actually be 5x5 in the screen.
Each time the snake moves, you add a new value to the head and remove the last value from tail (assuming it's not growing). When you needed to paint the snake, you would simply iterate over the List, painting each point of the snake.
The following is a simple example, which uses a LinkedList, which pushes a new Point onto the List, making a new head, and removing the last element (the tail) on each cycle.
Which basically boils down to...
snakeBody.removeLast();
snakeBody.push(new Point(xPos, yPos));
As a runnable concept
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Snake {
public static void main(String[] args) {
new Snake();
}
public Snake() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public enum Direction {
UP, DOWN, LEFT, RIGHT
}
private int xPos, yPos;
private Direction direction = Direction.UP;
private LinkedList<Point> snakeBody = new LinkedList<>();
public TestPane() {
xPos = 100;
yPos = 100;
for (int index = 0; index < 50; index++) {
snakeBody.add(new Point(xPos, yPos));
}
bindKeyStrokeTo("up.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), new MoveAction(Direction.UP));
bindKeyStrokeTo("down.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), new MoveAction(Direction.DOWN));
bindKeyStrokeTo("left.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), new MoveAction(Direction.LEFT));
bindKeyStrokeTo("right.pressed", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), new MoveAction(Direction.RIGHT));
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
yPos--;
break;
case DOWN:
yPos++;
break;
case LEFT:
xPos--;
break;
case RIGHT:
xPos++;
break;
}
if (yPos < 0) {
yPos--;
} else if (yPos > getHeight() - 1) {
yPos = getHeight() - 1;
}
if (xPos < 0) {
xPos--;
} else if (xPos > getWidth() - 1) {
xPos = getWidth() - 1;
}
snakeBody.removeLast();
snakeBody.push(new Point(xPos, yPos));
repaint();
}
});
timer.start();
}
public void bindKeyStrokeTo(String name, KeyStroke keyStroke, Action action) {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(keyStroke, name);
am.put(name, action);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (Point p : snakeBody) {
g2d.drawLine(p.x, p.y, p.x, p.y);
}
g2d.dispose();
}
public class MoveAction extends AbstractAction {
private Direction moveIn;
public MoveAction(Direction direction) {
this.moveIn = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
direction = this.moveIn;
}
}
}
}
Now, this has no collision detection or other functionality, but you can move the snake around and it will follow itself
For snake style movement, you can, from the tail to the head, move each BodyPart position to the position of the BodyPart ahead of it. For the head there is no part ahead so you have to write decision code whether to simply move the same direction as the part before it or a new direction based on input. Then update the screen.
I understand the logic behind it, but don't know how to translate that to code. Could someone please show me on this example that I wrote?
All the applet does is that the center rectangle moves up, down, right and left of its own accord. I want to get rid of the annoying flicker with a double buffer, but I don't know what change/add to make that happen.
import java.applet.*;
import java.awt.*;
public class Broadway extends Applet implements Runnable {
Thread animation;
int locx,locy; // location of rectangle
int width, height; // dimensions of rectangle
// direction of motion
static final byte UP = 0;
static final byte DOWN = 1;
static final byte LEFT = 2;
static final byte RIGHT = 3;
byte state; // state the rect is in
// length of pausing interval in ms
static final int REFRESH_RATE = 100;
public void init() {
setBackground(Color.black);
locx = 80; // parameters of center rect
locy = 100;
width = 110;
height = 90;
state = UP;
}
public void start() {
animation = new Thread(this);
if (animation != null) {
animation.start();
}
}
public void paint(Graphics g) {
g.setColor(Color.yellow);
g.fillRect(0,0,90,90);
g.fillRect(250,0,40,190);
g.fillRect(80,110,100,20);
g.setColor(Color.blue);
g.fillRect(80,200,220,90);
g.fillRect(100,10,90,80);
g.setColor(Color.lightGray);
g.fillRect(locx,locy,width,height);
g.setColor(Color.red);
g.fillRect(200,0,45,45);
g.fillRect(0,100,70,200);
g.setColor(Color.magenta);
g.fillRect(200,55,60,135);
}
//update the center rectangle
void updateRectangle() {
switch (state) {
case DOWN:
locy += 2;
if (locy >= 110) {
state = UP;
}
break;
case UP:
locy -= 2;
if (locy <= 90) {
state = RIGHT;
}
break;
case RIGHT:
locx += 2;
if (locx >= 90) {
state = LEFT;
}
break;
case LEFT:
locx -= 2;
if (locx <= 70) {
state = DOWN;
}
break;
}
}
public void run() {
while (true) {
repaint();
updateRectangle();
try {
Thread.sleep (REFRESH_RATE);
} catch (Exception exc) { };
}
}
public void stop() {
if (animation != null) {
animation.stop();
animation = null;
}
}
}
import java.awt.*;
Change that to
import javax.swing.*;
public class Broadway extends Applet ..
Change that to
public class Broadway extends JApplet ..
Move all the custom painting into a JPanel. Override paintComponent(Graphics) rather than paint(Graphics). A JPanel is double buffered by default.
Do animation using a Swing Timer.
As a general tip
Code a frame & launch it from a link using Java Web Start before considering using an applet.
Update
This code implements most of the advice above, and displays the resulting smooth, buffered animation in an option pane. It could instead be displayed in a JFrame or JApplet.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Broadway extends JPanel {
int locx, locy; // location of rectangle
int width, height; // dimensions of rectangle
Timer timer;
// direction of motion
static final byte UP = 0;
static final byte DOWN = 1;
static final byte LEFT = 2;
static final byte RIGHT = 3;
byte state; // state the rect is in
// length of pausing interval in ms
static final int REFRESH_RATE = 100;
public Broadway() {
setBackground(Color.black);
locx = 80; // parameters of center rect
locy = 100;
width = 110;
height = 90;
state = UP;
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
updateRectangle();
}
};
timer = new Timer(this.REFRESH_RATE, listener);
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.yellow);
g.fillRect(0, 0, 90, 90);
g.fillRect(250, 0, 40, 190);
g.fillRect(80, 110, 100, 20);
g.setColor(Color.blue);
g.fillRect(80, 200, 220, 90);
g.fillRect(100, 10, 90, 80);
g.setColor(Color.lightGray);
g.fillRect(locx, locy, width, height);
g.setColor(Color.red);
g.fillRect(200, 0, 45, 45);
g.fillRect(0, 100, 70, 200);
g.setColor(Color.magenta);
g.fillRect(200, 55, 60, 135);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
//update the center rectangle
void updateRectangle() {
switch (state) {
case DOWN:
locy += 2;
if (locy >= 110) {
state = UP;
}
break;
case UP:
locy -= 2;
if (locy <= 90) {
state = RIGHT;
}
break;
case RIGHT:
locx += 2;
if (locx >= 90) {
state = LEFT;
}
break;
case LEFT:
locx -= 2;
if (locx <= 70) {
state = DOWN;
}
break;
}
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
Broadway bw = new Broadway();
bw.start();
JOptionPane.showMessageDialog(null, bw);
bw.stop();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
Basically you replace the paint method so that it paints to an Image the same size as the Applet, then draw that image on Graphics g.
There's plenty of implementations out there.
Related questions:
Java Double Buffering
Double Buffering in Java
How do you double buffer in java for a game?
Java: how to do double-buffering in Swing?
Implementing Double Buffering in Java
stop applet flickering with double buffering Java Applet