Applet Graphic Bug? - java

I got this really annoying error. It's really hard to explain, but basically whenever my snake tiles (I'm coding the game "Snake") leaves the screen, I set it so it returns to the same y, but the x as 0, as the x = 0 is the leftmost part of the screen...
So yeah, hard to explain. Here's the full code:
Main Class:
package com.Code0.Snake.Main;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.LinkedList;
import java.util.Random;
import com.Code0.Snake.Listeners.KeyListenerS;
import com.Code0.Snake.Threads.EventS;
public class MainS extends Applet{
//Defining vars.
public final int BOX_HEIGHT = 15;
public final int BOX_WIDTH = 15;
public final int GRID_HEIGHT = 30;
public final int GRID_WIDTH = 30;
public static final int NORTH = 1;
public static final int SOUTH = 2;
public static final int EAST = 3;
public static final int WEST = 4;
public static final int NONE = 0;
public static int direction;
public LinkedList<Point> snakeParts = new LinkedList<Point>();
public Point itemLocation = new Point();
public Graphics graphics;
int randomX;
int randomY;
int runCount = 0;
EventS events = new EventS(this);
KeyListenerS keylistener = new KeyListenerS(this);
//On Initialization
public void init(){
//Adding "Snakeparts" to the linked list.
snakeParts.add(new Point(10, 10));
snakeParts.add(new Point(10, 11));
snakeParts.add(new Point(10, 12));
this.addKeyListener(keylistener);
}
//Startup paint method.
public void paint(Graphics paramGraphics){
graphics = paramGraphics.create();
this.customInit();
this.setBackground(Color.WHITE);
this.drawAll();
}
public void customInit(){
this.runCount++;
this.setSize(new Dimension(451,451));
if(this.runCount == 2){
Thread eventThread = new Thread(events);
eventThread.start();
}
}
//Used to call all draw methods.
public void drawAll(){
Random random = new Random();
//Calling all draw methods.
this.drawGrid(graphics);
this.drawFruit(graphics,random.nextInt(GRID_WIDTH),random.nextInt(GRID_HEIGHT));
this.drawSnake(graphics);
}
public void drawGrid(Graphics paramGraphics){
paramGraphics.drawRect(0,0,BOX_WIDTH * GRID_WIDTH,BOX_HEIGHT * GRID_HEIGHT);
//Drawing horizontal lines.
for(int x = BOX_WIDTH; x < BOX_WIDTH * GRID_WIDTH; x+=BOX_WIDTH){
paramGraphics.drawLine(x,0,x,BOX_HEIGHT * GRID_HEIGHT);
}
//Drawing vertical lines.
for(int y = BOX_HEIGHT; y < BOX_HEIGHT * GRID_HEIGHT; y+=BOX_HEIGHT){
paramGraphics.drawLine(0, y, GRID_WIDTH * BOX_WIDTH,y);
}
}
public void drawSnake(Graphics paramGraphics){
paramGraphics.setColor(Color.GREEN);
for(Point point : this.snakeParts){
paramGraphics.fillRect(point.x * 15, point.y * 15, BOX_WIDTH, BOX_HEIGHT);
}
paramGraphics.setColor(Color.BLACK);
}
public void drawFruit(Graphics paramGraphics, int paramX, int paramY){
paramGraphics.setColor(Color.RED);
int tempX = paramX * 15;
int tempY = paramY * 15;
paramGraphics.fillOval(tempX, tempY, BOX_WIDTH,BOX_HEIGHT);
paramGraphics.setColor(Color.BLACK);
}
public void drawEmpty(Graphics paramGraphics, int x, int y){
paramGraphics.clearRect(x * 15, y * 15, 15, 15);
this.drawGrid(graphics);
}
}
Event Class (run on seperate thread):
package com.Code0.Snake.Threads;
import java.awt.Point;
import com.Code0.Snake.Main.MainS;
public class EventS implements Runnable{
MainS main;
int CURRENT_DIRECTION;
public EventS(MainS paramMain){
this.main = paramMain;
}
#Override
public void run() {
//Infinite loop checking the game and updating it.
while(true){
main.drawGrid(main.graphics);
Point head;
Point tail;
Point finalPoint = new Point();
switch(MainS.direction){
//If direction = north
case(MainS.NORTH):
head = main.snakeParts.getFirst();
finalPoint = new Point(head.x, head.y - 1);
main.snakeParts.push(finalPoint);
tail = main.snakeParts.getLast();
main.snakeParts.remove(tail);
main.drawSnake(main.graphics);
main.drawEmpty(main.graphics, tail.x, tail.y);
break;
//If direction = south
case(MainS.SOUTH):
head = main.snakeParts.getFirst();
finalPoint = new Point(head.x, head.y + 1);
main.snakeParts.push(finalPoint);
tail = main.snakeParts.getLast();
main.snakeParts.remove(tail);
main.drawSnake(main.graphics);
main.drawEmpty(main.graphics, tail.x , tail.y);
break;
case(MainS.WEST):
head = main.snakeParts.getFirst();
finalPoint = new Point(head.x - 1, head.y);
main.snakeParts.push(finalPoint);
tail = main.snakeParts.getLast();
main.snakeParts.remove(tail);
main.drawSnake(main.graphics);
main.drawEmpty(main.graphics, tail.x , tail.y);
break;
case(MainS.EAST):
head = main.snakeParts.getFirst();
finalPoint = new Point(head.x + 1, head.y);
main.snakeParts.push(finalPoint);
tail = main.snakeParts.getLast();
main.snakeParts.remove(tail);
main.drawSnake(main.graphics);
main.drawEmpty(main.graphics, tail.x , tail.y);
break;
case(MainS.NONE):
break;
}
if(finalPoint.x > main.GRID_WIDTH){
int totalSnakeParts = main.snakeParts.size();
main.snakeParts.clear();
for(int i = totalSnakeParts; i > 0; i--){
Point tempPoint = new Point(i - 2, finalPoint.y);
main.snakeParts.add(tempPoint);
main.drawGrid(main.graphics);
System.out.println(tempPoint);
}
main.drawSnake(main.graphics);
}
try {
Thread.currentThread().sleep((long)100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
KeyListener Class:
package com.Code0.Snake.Listeners;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import com.Code0.Snake.Main.MainS;
public class KeyListenerS implements KeyListener{
MainS main;
public KeyListenerS(MainS paramMain){
this.main = paramMain;
}
#Override
public void keyPressed(KeyEvent event) {
if(event.getKeyCode() == KeyEvent.VK_UP){
if(main.direction == main.SOUTH){
return;
}
main.direction = main.NORTH;
}
if(event.getKeyCode() == KeyEvent.VK_DOWN){
if(main.direction == main.NORTH){
return;
}
main.direction = main.SOUTH;
}
if(event.getKeyCode() == KeyEvent.VK_LEFT){
if(main.direction == main.EAST){
return;
}
main.direction = main.WEST;
}
if(event.getKeyCode() == KeyEvent.VK_RIGHT){
if(main.direction == main.WEST){
return;
}
main.direction = main.EAST;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
}
EDIT
Here's the link to what it looks like (the bug):
http://s15.postimg.org/8z62dy7az/Bildschirmfoto_2014_06_03_um_16_47_48.png

OK, I'll try to go through your mistakes one-by-one, but really, this is a huge mess.
You save the graphics state and call paint from a separate Thread. Now, I'm not sure about applets and AWT, but in Swing that is a huge no-no. There is a convenient repaint() method available...
You do not have any synchronization on direction. This means the model can ignore the users commands if it feels like it. Try volatile. In fact, having both all variables and your drawing code in one class is fairly bad. MVC is the way to go.
You suffer from a large amount of off-by-1 errors. If you don't know what it is - google will help you. Just to name a few places where you have them: finalPoint.x > main.GRID_WIDTH will fire one cell too late since the grid starts with 0, for(int i = totalSnakeParts; i > 0; i--){Point tempPoint = new Point(i - 2, finalPoint.y); means the first point is in a negative coordinate (when i == 1).
In your border checking logic you reset the position of all the snake points, yet you do not paint empty squares on the previous positions. You do that when processing a move, why aren't you doing it now? That is the residual squares you have left on the right.
Your border checks are doing something strange anyway. What they do is straighten out the snake and teleport it to the left edge completely instead of just moving the head there. It looks the same while the snake is only 3 squares long because of a combination of bugs in number 3, but will be apparent with a longer one (Try it!). A classic approach would be to simply set the x coordinate of the head to 0, instead of resetting everything. That will also make number 4 obsolete, since you won't have to repaint the remainders at all (there won't be any, snake doesn't teleport).

Related

Snake game in Java but my restart button does not work

My restart button of the game does not work and it multiples when it is clicked. I do not understand Java perfectly I am considering myself good.
Main of the game
package snake_game;
public class snake {
public static void main(String arg[]) {
new GameFrame();
// is exacly the same as frame f = new frame();
// this is shorter and does the same job
}
}
GamePanel
package snake_game;
// import java.awt.event.ActionEvent;
// import java.awt.event.ActionListener;
// import java.awt.Graphics;
// import java.awt.event.KeyAdapter;
// import java.awt.event.KeyEvent;
// or I could write simply
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements ActionListener {
// panal dimentions
static final int SCREEN_WIDTH = 600;
static final int SCREEN_HEIGHT = 600;
// panal dimentions
// size
static final int UNIT_SIZE = 25;
// size to make 600 * 600 = 1200 px equel between 25 px
static final int GAME_UNITS = (SCREEN_WIDTH * SCREEN_HEIGHT) / UNIT_SIZE;
// size
// delay how fast the game will be
static final int DELAY = 75;
// delay
// dimentions
final int x[] = new int[GAME_UNITS];
final int y[] = new int[GAME_UNITS];
// dimentions
// snake
int bodyParts = 6;
// snake
// apple
int appleEaten;
int appleX;
int appleY;
// apple
char direction = 'R';
boolean running = false;
Timer timer;
Random random;
GamePanel game;
JButton resetButton;
GamePanel() {
random = new Random();
this.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
this.setBackground(Color.black);
this.setFocusable(true);
this.addKeyListener(new MyKeyAdapter());
startGame();
// when we want to make the program to continie we must say what the programm
// must execute next
}
public void startGame() {
newApple();
running = true;
timer = new Timer(DELAY, this);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g) {
if (running) {
for (int i = 0; i < SCREEN_HEIGHT / UNIT_SIZE; i++) {
g.drawLine(i * UNIT_SIZE, 0, i * UNIT_SIZE, SCREEN_HEIGHT);
g.drawLine(0, i * UNIT_SIZE, i * SCREEN_WIDTH, i * UNIT_SIZE);
}
g.setColor(Color.red);
g.fillOval(appleX, appleY, UNIT_SIZE, UNIT_SIZE);
for (int i = 0; i < bodyParts; i++) {
if (i == 0) {
g.setColor(Color.green);
g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
} else {
g.setColor(new Color(45, 180, 0));
// random color
g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
// random color
g.fillRect(x[i], y[i], UNIT_SIZE, UNIT_SIZE);
}
}
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 30));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("SCORE:" + appleEaten, (SCREEN_WIDTH - metrics.stringWidth("SCORE:" + appleEaten)) / 2,
g.getFont().getSize());
} else {
gameOver(g);
}
}
public void newApple() {
appleX = random.nextInt((int) (SCREEN_WIDTH / UNIT_SIZE)) * UNIT_SIZE;
appleY = random.nextInt((int) (SCREEN_HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
}
public void move() {
for (int i = bodyParts; i > 0; i--) {
x[i] = x[i - 1];
y[i] = y[i - 1];
}
switch (direction) {
case 'U':
y[0] = y[0] - UNIT_SIZE;
break;
case 'D':
y[0] = y[0] + UNIT_SIZE;
break;
case 'L':
x[0] = x[0] - UNIT_SIZE;
break;
case 'R':
x[0] = x[0] + UNIT_SIZE;
break;
}
}
public void checkApple() {
if ((x[0] == appleX) && (y[0] == appleY)) {
bodyParts++;
appleEaten++;
newApple();
}
}
public void checkCollisions() {
// check if head collides with body
for (int i = bodyParts; i > 0; i--) {
if ((x[0] == x[i]) && (y[0] == y[i])) {
running = false;
}
}
// check if head touches left border
if (x[0] < 0) {
running = false;
}
// check if head touches right border
if (x[0] > SCREEN_WIDTH) {
running = false;
}
// check if head touches top border
if (y[0] < 0) {
running = false;
}
// check if head touches bottom border
if (y[0] > SCREEN_HEIGHT) {
running = false;
}
if (!running) {
timer.stop();
}
}
public void gameOver(Graphics g) {
// score
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 30));
FontMetrics metrics1 = getFontMetrics(g.getFont());
g.drawString("SCORE:" + appleEaten, (SCREEN_WIDTH - metrics1.stringWidth("SCORE:" + appleEaten)) / 2,
g.getFont().getSize());
// game over text
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 75));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("Game Over", (SCREEN_WIDTH - metrics2.stringWidth("Game Over")) / 2, SCREEN_HEIGHT / 2);
// restart button
resetButton = new JButton();
resetButton.setText("Restart");
resetButton.setSize(100, 50);
resetButton.setLocation(150, 150);
resetButton.addActionListener(this);
game = new GamePanel();
this.add(resetButton);
this.add(game);
this.setVisible(true);
// restart button
}
#Override
public void actionPerformed(ActionEvent e) {
if (running) {
move();
checkApple();
checkCollisions();
}
repaint();
// restart button
if (e.getSource() == resetButton) {
this.remove(game);
game = new GamePanel();
this.add(game);
resetButton.setVisible(false);
SwingUtilities.updateComponentTreeUI(this);
// restart button
}
}
public class MyKeyAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if (direction != 'R') {
direction = 'L';
}
break;
case KeyEvent.VK_RIGHT:
if (direction != 'L') {
direction = 'R';
}
break;
case KeyEvent.VK_UP:
if (direction != 'D') {
direction = 'U';
}
break;
case KeyEvent.VK_DOWN:
if (direction != 'U') {
direction = 'D';
}
break;
}
}
}
}
GameFrame
package snake_game;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
GameFrame() {
GamePanel panel = new GamePanel();
this.add(panel);
this.setTitle("Snake");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setVisible(true);
this.setLocationRelativeTo(null);
this.setUndecorated(false);
}
}
Introduction
I copied your code into my Eclipse IDE and ran it as is. I received the following runtime error.
Exception in thread "main" java.awt.IllegalComponentStateException: The frame is displayable.
at java.desktop/java.awt.Frame.setUndecorated(Frame.java:926)
at com.ggl.testing.SnakeGame$GameFrame.<init>(SnakeGame.java:41)
at com.ggl.testing.SnakeGame.main(SnakeGame.java:24)
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay close attention to the Concurrency in Swing section.
I've been writing Swing code for over 10 years, and I have the Oracle website bookmarked in my browser. I still look up how to use certain components to make sure I'm using them correctly.
The first thing I did was to slow down your snake so I could test the game. I changed the delay of 75 to a delay of 750. Here's a screenshot of your current GUI.
Looking over your code, you extend a JFrame. You don't need to extend a JFrame. You're not changing any JFrame functionality. It's much simpler to use a JFrame. This leads to one of my Java rules.
Don't extend a Swing component, or any Java class, unless you intend
to override one or more of the class methods.
You do extend a JPanel. That's fine because you override the paintComponent method.
Finally, your JPanel class is doing too much work. You also make heavy use of static fields. Even though you'll only create one JPanel, it's a good habit to treat every class as if you will create multiple instances of the class. This creates fewer problems for yourself down the road.
You have the right idea, creating three classes.
So let's rework your code, using some basic patterns and Swing best practices. At this time, I don't know how many classes we'll wind up creating.
Explanation
When I write a Swing GUI, I use the model–view–controller (MVC) pattern. The name implies that you create a model first, then the view, then the controller(s).
An application model consists of one or more plain Java getter/setter classes.
A view consists of a JFrame, one or more JPanels, and any other necessary Swing components.
The controller consists of one or more Actions or ActionListeners. In Swing, there's usually not one controller to "rule them all".
To summarize:
The view reads information from the model
The view does not update the model
The controllers update the model and repaint/revalidate your view.
Model
I created two model classes, SnakeModel and Snake.
The SnakeModel class is a plain Java getter/setter class that holds one Snake instance, the apple eaten count, the apple location, the size of the game area, and a couple of booleans. One boolean indicates whether or not the game loop is running and the other boolean indicates whether or not the game is over.
The game area uses a java.awt.Dimension to hold the width and height of the game area. The width and the height do not have to have the same value. The game area can be rectangular.
The game area is measured in units. In the view, I convert the units into pixels. That's the opposite of what you did. If you want to change the game area, all you have to do is change the dimensions in the SnakeModel class. Everything in the view is based on the game area dimension.
The Snake class holds a java.util.List of java.awt.Point objects and a char direction. A java.awt.Point object holds an X and Y value. Since we're dealing with objects, rather than int values, we have to be careful to clone the object when we want a new Point.
View
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a JFrame, a drawing JPanel, and a separate button JPanel. Generally, it's not a good idea to add Swing components to a drawing JPanel. By creating a separate button JPanel, I get the added feature of a "Start Game" button at almost no extra cost. The button is disabled while the game is running.
The JFrame methods must be called in a specific order. The setVisible method must be called last.
I made the drawing JPanel more complicated by adding a separate area for the score.
I made the drawing JPanel less complicated by only drawing the state of the game, based on the application model. Period. Nothing else.
I limited the random colors to the white end of the color spectrum to maintain the contrast between the snake and the drawing JPanel background.
I used key bindings instead of a key listener. One advantage is that the drawing JPanel doesn't have to be in focus. Since I have a separate button JPanel, the drawing JPanel doesn't have focus.
Another advantage is that I can add the WASD keys with four lines of additional code.
One disadvantage is that the key bindings code looks more complicated than a key listener. Once you've coded a few key bindings, you'll appreciate the advantages.
Controller
I created three controller classes, ButtonListener, TimerListener, and MovementAction.
The ButtonListener class implements ActionListener. The ButtonListener class initializes the game model and restarts the timer.
The TimerListener class implements ActionListener. The TimerListener class is the game loop. This class moves the snake, checks to see if the apple is eaten, checks to see if the snake has moved outside the game area or touched itself, and repaints the drawing JPanel. I used your code as a model for the code in this class.
The MovementAction class extends AbstractAction. The AbstractAction class implements Action. This class changes the direction of the snake, based on the key presses.
I create four instances of the MovementAction class, one for each direction. This makes the actionPerformed method of the class much simpler.
Images
Here's what the revised GUI looks like when you start the game.
Here's the revised GUI during the game.
Here's the revised GUI when the game is over.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post this code as one block.
You should put the separate classes in separate files.
When setting up a Swing GUI project, I create separate packages for the model, view, and controller. This helps me keep the code organized.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SnakeGame implements Runnable {
public static void main(String arg[]) {
SwingUtilities.invokeLater(new SnakeGame());
}
private final GamePanel gamePanel;
private final JButton restartButton;
private final SnakeModel model;
public SnakeGame() {
this.model = new SnakeModel();
this.restartButton = new JButton("Start Game");
this.gamePanel = new GamePanel(model);
}
#Override
public void run() {
JFrame frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(gamePanel, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.setBackground(Color.black);
restartButton.addActionListener(new ButtonListener(this, model));
panel.add(restartButton);
return panel;
}
public JButton getRestartButton() {
return restartButton;
}
public void repaint() {
gamePanel.repaint();
}
public class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
private final int margin, scoreAreaHeight, unitSize;
private final Random random;
private final SnakeModel model;
public GamePanel(SnakeModel model) {
this.model = model;
this.margin = 10;
this.unitSize = 25;
this.scoreAreaHeight = 36 + margin;
this.random = new Random();
this.setBackground(Color.black);
Dimension gameArea = model.getGameArea();
int width = gameArea.width * unitSize + 2 * margin;
int height = gameArea.height * unitSize + 2 * margin + scoreAreaHeight;
this.setPreferredSize(new Dimension(width, height));
setKeyBindings();
}
private void setKeyBindings() {
InputMap inputMap = this.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = this.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "down");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "right");
actionMap.put("up", new MovementAction(model, 'U', 'D'));
actionMap.put("down", new MovementAction(model, 'D', 'U'));
actionMap.put("left", new MovementAction(model, 'L', 'R'));
actionMap.put("right", new MovementAction(model, 'R', 'L'));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension gameArea = model.getGameArea();
drawHorizontalGridLines(g, gameArea);
drawVerticalGridLines(g, gameArea);
drawSnake(g);
drawScore(g, gameArea);
if (model.isGameOver) {
drawGameOver(g, gameArea);
} else {
drawApple(g);
}
}
private void drawHorizontalGridLines(Graphics g, Dimension gameArea) {
int y1 = scoreAreaHeight + margin;
int y2 = y1 + gameArea.height * unitSize;
int x = margin;
for (int index = 0; index <= gameArea.width; index++) {
g.drawLine(x, y1, x, y2);
x += unitSize;
}
}
private void drawVerticalGridLines(Graphics g, Dimension gameArea) {
int x1 = margin;
int x2 = x1 + gameArea.width * unitSize;
int y = margin + scoreAreaHeight;
for (int index = 0; index <= gameArea.height; index++) {
g.drawLine(x1, y, x2, y);
y += unitSize;
}
}
private void drawApple(Graphics g) {
// Draw apple
g.setColor(Color.red);
Point point = model.getAppleLocation();
if (point != null) {
int a = point.x * unitSize + margin + 1;
int b = point.y * unitSize + margin + scoreAreaHeight + 1;
g.fillOval(a, b, unitSize - 2, unitSize - 2);
}
}
private void drawScore(Graphics g, Dimension gameArea) {
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 36));
FontMetrics metrics = getFontMetrics(g.getFont());
int width = 2 * margin + gameArea.width * unitSize;
String text = "SCORE: " + model.getApplesEaten();
int textWidth = metrics.stringWidth(text);
g.drawString(text, (width - textWidth) / 2, g.getFont().getSize());
}
private void drawSnake(Graphics g) {
// Draw snake
Snake snake = model.getSnake();
List<Point> cells = snake.getCells();
Point cell = cells.get(0);
drawSnakeCell(g, cell, Color.green);
for (int index = 1; index < cells.size(); index++) {
// Color color = new Color(45, 180, 0);
// random color
Color color = new Color(getColorValue(), getColorValue(),
getColorValue());
cell = cells.get(index);
drawSnakeCell(g, cell, color);
}
}
private void drawSnakeCell(Graphics g, Point point, Color color) {
int x = margin + point.x * unitSize;
int y = margin + scoreAreaHeight + point.y * unitSize;
if (point.y >= 0) {
g.setColor(color);
g.fillRect(x, y, unitSize, unitSize);
}
}
private int getColorValue() {
// White has color values of 255
return random.nextInt(64) + 191;
}
private void drawGameOver(Graphics g, Dimension gameArea) {
g.setColor(Color.red);
g.setFont(new Font("Ink Free", Font.BOLD, 72));
FontMetrics metrics = getFontMetrics(g.getFont());
String text = "Game Over";
int textWidth = metrics.stringWidth(text);
g.drawString(text, (getWidth() - textWidth) / 2, getHeight() / 2);
}
}
public class ButtonListener implements ActionListener {
private final int delay;
private final SnakeGame view;
private final SnakeModel model;
private final Timer timer;
public ButtonListener(SnakeGame view, SnakeModel model) {
this.view = view;
this.model = model;
this.delay = 750;
this.timer = new Timer(delay, new TimerListener(view, model));
}
#Override
public void actionPerformed(ActionEvent event) {
JButton button = (JButton) event.getSource();
String text = button.getText();
if (text.equals("Start Game")) {
button.setText("Restart Game");
}
button.setEnabled(false);
model.initialize();
timer.restart();
}
}
public class TimerListener implements ActionListener {
private final SnakeGame view;
private final SnakeModel model;
public TimerListener(SnakeGame view, SnakeModel model) {
this.view = view;
this.model = model;
}
#Override
public void actionPerformed(ActionEvent event) {
moveSnake();
checkApple();
model.checkCollisions();
if (model.isGameOver()) {
Timer timer = (Timer) event.getSource();
timer.stop();
model.setRunning(false);
view.getRestartButton().setEnabled(true);
}
view.repaint();
}
private void moveSnake() {
Snake snake = model.getSnake();
Point head = (Point) snake.getHead().clone();
switch (snake.getDirection()) {
case 'U':
head.y--;
break;
case 'D':
head.y++;
break;
case 'L':
head.x--;
break;
case 'R':
head.x++;
break;
}
snake.removeTail();
snake.addHead(head);
// System.out.println(Arrays.toString(cells.toArray()));
}
private void checkApple() {
Point appleLocation = model.getAppleLocation();
Snake snake = model.getSnake();
Point head = snake.getHead();
Point tail = (Point) snake.getTail().clone();
if (head.x == appleLocation.x && head.y == appleLocation.y) {
model.incrementApplesEaten();
snake.addTail(tail);
model.generateRandomAppleLocation();
}
}
}
public class MovementAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private final char newDirection, oppositeDirection;
private final SnakeModel model;
public MovementAction(SnakeModel model, char newDirection,
char oppositeDirection) {
this.model = model;
this.newDirection = newDirection;
this.oppositeDirection = oppositeDirection;
}
#Override
public void actionPerformed(ActionEvent event) {
if (model.isRunning()) {
Snake snake = model.getSnake();
char direction = snake.getDirection();
if (direction != oppositeDirection && direction != newDirection) {
snake.setDirection(newDirection);
// System.out.println("New direction: " + newDirection);
}
}
}
}
public class SnakeModel {
private boolean isGameOver, isRunning;
private int applesEaten;
private Dimension gameArea;
private Point appleLocation;
private Random random;
private Snake snake;
public SnakeModel() {
this.random = new Random();
this.snake = new Snake();
this.gameArea = new Dimension(24, 24);
}
public void initialize() {
this.isRunning = true;
this.isGameOver = false;
this.snake.initialize();
this.applesEaten = 0;
Point point = generateRandomAppleLocation();
// Make sure first apple isn't under snake
int y = (point.y == 0) ? 1 : point.y;
this.appleLocation = new Point(point.x, y);
}
public void checkCollisions() {
Point head = snake.getHead();
// Check for snake going out of the game area
if (head.x < 0 || head.x > gameArea.width) {
isGameOver = true;
return;
}
if (head.y < 0 || head.y > gameArea.height) {
isGameOver = true;
return;
}
// Check for snake touching itself
List<Point> cells = snake.getCells();
for (int index = 1; index < cells.size(); index++) {
Point cell = cells.get(index);
if (head.x == cell.x && head.y == cell.y) {
isGameOver = true;
return;
}
}
}
public Point generateRandomAppleLocation() {
int x = random.nextInt(gameArea.width);
int y = random.nextInt(gameArea.height);
this.appleLocation = new Point(x, y);
return getAppleLocation();
}
public void incrementApplesEaten() {
this.applesEaten++;
}
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
public boolean isGameOver() {
return isGameOver;
}
public void setGameOver(boolean isGameOver) {
this.isGameOver = isGameOver;
}
public Dimension getGameArea() {
return gameArea;
}
public int getApplesEaten() {
return applesEaten;
}
public Point getAppleLocation() {
return appleLocation;
}
public Snake getSnake() {
return snake;
}
}
public class Snake {
private char direction;
private List<Point> cells;
public Snake() {
this.cells = new ArrayList<>();
initialize();
}
public void initialize() {
this.direction = 'R';
cells.clear();
for (int x = 5; x >= 0; x--) {
cells.add(new Point(x, 0));
}
}
public void addHead(Point head) {
cells.add(0, head);
}
public void addTail(Point tail) {
cells.add(tail);
}
public void removeTail() {
cells.remove(cells.size() - 1);
}
public Point getHead() {
return cells.get(0);
}
public Point getTail() {
return cells.get(cells.size() - 1);
}
public char getDirection() {
return direction;
}
public void setDirection(char direction) {
this.direction = direction;
}
public List<Point> getCells() {
return cells;
}
}
}

How to detect collisions between objects in LibGDX

This is my first post on stack overflow so I apologize in advance if I'm breaking any rules about posting, etc. I have been working on a an asteroids-esque shooting game and I can't figure out how to get the collision detection working between the rocks and the laser.
The source code can be found here. I had to make some changes to the update method of LevelScreen because the original code is dependent on using the BlueJ IDE. I found a fix in this post and got the collision working between the spaceship and the rocks.
The LevelScreen class
package com.mygdx.game;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import java.util.ArrayList;
public class LevelScreen extends BaseScreen {
private Spaceship spaceship;
private boolean gameOver;
private boolean rocksNeedRemoved;
private Rock toRemove;
private ArrayList<Rock> rocks;
private ArrayList<Laser> lasers;
public void initialize() {
gameOver = false;
toRemove = null;
rocksNeedRemoved = false;
BaseActor space = new BaseActor(0, 0, mainStage);
space.loadTexture("space.png");
space.setSize(800, 600);
BaseActor.setWorldBounds(space);
spaceship = new Spaceship(400, 300, mainStage);
rocks = new ArrayList<Rock>();
lasers = new ArrayList<Laser>();
rocks.add(new Rock(600, 500, mainStage));
rocks.add(new Rock(600, 300, mainStage));
rocks.add(new Rock(600, 100, mainStage));
rocks.add(new Rock(400, 100, mainStage));
rocks.add(new Rock(200, 100, mainStage));
rocks.add(new Rock(200, 300, mainStage));
rocks.add(new Rock(200, 500, mainStage));
rocks.add(new Rock(400, 500, mainStage));
lasers.add(new Laser(400, 500, mainStage));
}
public void update(float dt) {
//Code from book(Throws class not found error)
/*
for (BaseActor rockActor : BaseActor.getList(mainStage,
"Rock")) {
if (rockActor.overlaps(spaceship)) {
if (spaceship.shieldPower <= 0) {
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(spaceship);
spaceship.remove();
spaceship.setPosition(-1000, -1000);
BaseActor messageLose = new BaseActor(0, 0,
uiStage);
messageLose.loadTexture("message-
lose.png");
messageLose.centerAtPosition(400, 300);
messageLose.setOpacity(0);
messageLose.addAction(Actions.fadeIn(1));
gameOver = true;
}
else {
spaceship.shieldPower -= 34;
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(rockActor);
rockActor.remove();
}
}
for (BaseActor laserActor :
BaseActor.getList(mainStage, "Laser")) {
if (laserActor.overlaps(rockActor)) {
}
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(rockActor);
laserActor.remove();
rockActor.remove();
}
}
if (!gameOver && BaseActor.count(mainStage, "Rock") ==
0) {
BaseActor messageWin = new BaseActor(0, 0,
uiStage);
messageWin.loadTexture("message-win.png");
messageWin.centerAtPosition(400, 300);
messageWin.setOpacity(0);
messageWin.addAction(Actions.fadeIn(1));
gameOver = true;
}
}
*/
// loop I used to get collision working between rocks
and spaceship
for (Rock each : rocks)
if (spaceship.overlaps(each) && !each.crashed &&
spaceship.shieldPower <= 0) {
Explosion boom = new Explosion(0, 0,
mainStage);
Explosion boom2 = new Explosion(0, 0,
mainStage);
boom.centerAtActor(spaceship);
boom2.centerAtActor(each);
spaceship.remove();
spaceship.setPosition(-1000, -1000);
each.crashed = true;
each.clearActions();
each.addAction(Actions.fadeOut(1));
each.addAction(Actions.after(Actions.removeActor()));
rocksNeedRemoved = true;
toRemove = each;
} else if (spaceship.overlaps(each) &&
!each.crashed) {
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(each);
spaceship.shieldPower -= 34;
each.crashed = true;
each.clearActions();
each.addAction(Actions.fadeOut(1));
each.addAction(Actions.after(Actions.removeActor()));
rocksNeedRemoved = true;
toRemove = each;
}
//check for collision between rocks and lasers (Not
working correctly)
for (int i = rocks.size() - 1; i >= 0; i--) {
Rock rock = rocks.get(i);
for (int j = lasers.size() - 1; j >= 0; j--) {
Laser laser = lasers.get(j);
if(rock.getBounds().overlaps(laser.getBounds())) {
Explosion boom = new Explosion(0, 0,
mainStage);
boom.centerAtActor(rock);
rock.crashed = true;
rock.clearActions();
rock.addAction(Actions.fadeOut(1));
rock.addAction(Actions.after(Actions.removeActor()));
rocksNeedRemoved = true;
toRemove = rock;
}
}
}
}
//override default InputProcessor method
public boolean keyDown(int keycode) {
if (keycode == Keys.X)
spaceship.warp();
if (keycode == Keys.SPACE)
spaceship.shoot();
return false;
}
}
The Spaceship class.
package com.mygdx.game;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.math.MathUtils;
public class Spaceship extends BaseActor {
private Thrusters thrusters;
private Shield shield;
public int shieldPower;
public Spaceship(float x, float y, Stage s) {
super(x, y, s);
loadTexture("spaceship.png");
setBoundaryPolygon(8);
setAcceleration(100);
setMaxSpeed(100);
setDeceleration(10);
thrusters = new Thrusters(0, 0, s);
addActor(thrusters);
thrusters.setPosition(-thrusters.getWidth(),
getHeight() / 2 - thrusters.getHeight() / 2);
shield = new Shield(0, 0, s);
addActor(shield);
shield.centerAtPosition(getWidth() / 2, getHeight() /
2);
shieldPower = 100;
}
public void act(float dt) {
super.act(dt);
float degreesPerSecond = 160; //rotation speed
if (Gdx.input.isKeyPressed(Keys.LEFT))
rotateBy(degreesPerSecond * dt);
if (Gdx.input.isKeyPressed(Keys.RIGHT))
rotateBy(-degreesPerSecond * dt);
if (Gdx.input.isKeyPressed(Keys.UP)) {
accelerateAtAngle(getRotation());
thrusters.setVisible(true);
}
else {
thrusters.setVisible(false);
}
shield.setOpacity(shieldPower / 100f);
if (shieldPower <= 0)
shield.setVisible(false);
applyPhysics(dt);
wrapAroundWorld();
}
public void warp() {
if(getStage() == null)
return;
Warp warp1 = new Warp(0, 0, this.getStage());
warp1.centerAtActor(this);
setPosition(MathUtils.random(800),
MathUtils.random(600));
Warp warp2 = new Warp(0, 0, this.getStage());
warp2.centerAtActor(this);
}
public void shoot() {
if (getStage() == null)
return;
Laser laser = new Laser(0, 0, this.getStage());
laser.centerAtActor(this);
laser.setRotation(this.getRotation());
laser.setMotionAngle(this.getRotation());
}
}
The Laser class.
package com.mygdx.game;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
public class Laser extends BaseActor {
Rectangle bounds;
public Laser(float x, float y, Stage s) {
super(x, y, s);
bounds = new Rectangle((int)getX(), (int)getY(),
(int)getWidth(), (int)getHeight());
loadTexture("laser.png");
addAction(Actions.delay(1));
addAction(Actions.after(Actions.fadeOut(0.5f)));
addAction(Actions.after(Actions.removeActor()));
setSpeed(400);
setMaxSpeed(400);
setDeceleration(0);
}
public void act(float dt) {
super.act(dt);
applyPhysics(dt);
wrapAroundWorld();
}
public Rectangle getBounds() {
return bounds;
}
private void setXY(float pX,float pY) {
setPosition(pX, pY);
bounds.setX((int)pX);
bounds.setY((int)pY);
}
}
The Rock class
package com.mygdx.game;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.math.MathUtils;
import com.mygdx.game.BaseActor;
public class Rock extends BaseActor {
public boolean crashed;
Rectangle bounds;
public Rock(float x, float y, Stage s) {
super(x, y, s);
bounds = new Rectangle((int)getX(), (int)getY(),
(int)getWidth(), (int)getHeight());
loadTexture("rock.png");
float random = MathUtils.random(30);
addAction(Actions.forever(Actions.rotateBy(30 + random,
1)));
setSpeed(50 + random);
setMaxSpeed(50 + random);
setDeceleration(0);
setMotionAngle(MathUtils.random(360));
crashed = false;
}
public void act(float dt) {
super.act(dt);
applyPhysics(dt);
wrapAroundWorld();
}
public boolean isCrashed() {
return crashed;
}
public void crashed() {
crashed = true;
clearActions();
addAction(Actions.fadeOut(1));
addAction(Actions.after(Actions.removeActor()));
}
public Rectangle getBounds() {
return bounds;
}
private void setXY(float pX,float pY) {
setPosition(pX, pY);
bounds.setX((int)pX);
bounds.setY((int)pY);
}
}
Boundary and overlap methods from BaseActor class
public void setBoundaryPolygon(int numSides) {
float w = getWidth();
float h = getHeight();
float[] vertices = new float[2 * numSides];
for(int i = 0; i < numSides; i ++) {
float angle = i * 6.28f / numSides;
//x coordinate
vertices[2 * i] = w / 2 * MathUtils.cos(angle) + w / 2;
//y coordinate
vertices[2 * i + 1] = h / 2 * MathUtils.sin(angle) + h / 2;
}
boundaryPolygon = new Polygon(vertices);
}
public Polygon getBoundaryPolygon() {
boundaryPolygon.setPosition(getX(), getY());
boundaryPolygon.setOrigin(getOriginX(), getOriginY());
boundaryPolygon.setRotation(getRotation());
boundaryPolygon.setScale(getScaleX(), getScaleY());
return boundaryPolygon;
}
public boolean overlaps(BaseActor other) {
Polygon poly1 = this.getBoundaryPolygon();
Polygon poly2 = other.getBoundaryPolygon();
//initial text to improve performance
if(!poly1.getBoundingRectangle().overlaps(poly2.getBoundingRectangle()))
return false;
return Intersector.overlapConvexPolygons(poly1, poly2);
}
So I guess the real question would be how would I go about checking for collisions between the rocks ArrayList and the lasers fired by the player? At this point I just want to finish the game even if it's not using best practices. I tried using the method described here and received no errors but also no collision between lasers and rock. Even if I manually add a laser to the lasers ArrayList. This last post I found leads me to believe I need something like a getAllLasers() but I'm not 100% sure how to go about that. Would it be easier to just learn Box2D or Quadtree?
I realize this is a complex problem and want to thank you in advance for taking taking the time to read it. I'm happy to provide any more information you need.
You already have Rectangle bounds for both of this entities, this is all you need. You can use Rectangle.overlaps()
for(Laser laser: lasers){
for(Rock rock: rocks){
if(laser.getBounds().overlaps(rock.getBounds())){
//collided!
}
}
}
Make sure you are getting a updated rectangle/bounds. Add this extra line in both Rock and Laser getBounds() method:
public Rectangle getBounds() {
bounds.set(getX(),getY(),getWidth(),getHeight());
return bounds;
}
if your actors scales or rotate you should update bounds accordingly
I think problem may be with update your actors bounds, i can't find where you update it. I wrote similiar game and i change Bounds of actors on each update step and all works well in some lines...
public void update() {
...
// changing bounds
bounds = new Rectangle(position.x,position.y,
actorWidth,
actorHeight);
}
Or if you use other approach check that your bounds changing in time

Swing 2D game low performance

I am making a clone of Flappy Bird. I was doing just fine performance-wise: 60 fps. This was when it had 1 pillar/obstacle only. As soon as I added 3 of them my fps dropped to 30 and below. Then game is unplayable now. I get that this has something to do with doing repaint() all the time.
Here is the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
/**
* Created by Lazar on 25/05/15.
*/
public class Environment extends JComponent implements ActionListener {
public static final Dimension dimension = new Dimension(800,600);
BufferedImage img;
BufferedImage ptica1;
BufferedImage ptica2;
double skokbrojac = 0;
int brzina = 4; // speed // MUST Background % brzina = 0
int dx;
int dx2;
int pad = 0; //drop
Timer timer;
boolean parno;
boolean skok = false;
//Stubovi // Pillars
Stub stub1 = new Stub();
Stub stub2 = new Stub();
Stub stub3 = new Stub();
ArrayList<Stub>stubovi = new ArrayList<Stub>();
int razmakStub; // Space between pillars
public Environment() {
setPreferredSize(dimension);
img = Util.openImage("pozadina.png");
ptica1 = Util.openImage("ptica1.png");
ptica2 = Util.openImage("ptica2.png");
stubovi.add(stub1);
stubovi.add(stub2);
stubovi.add(stub3);
dx = img.getWidth()/2;
timer = new Timer(1000/60,this);
timer.start();
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
skok = true; // start jump
skokbrojac = 0; //jump frame counter
}
});
}
protected void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
//g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
if(dx == img.getWidth()){ //image horizontal scroll
dx2 = 0;
}
if(dx2 == img.getWidth()/2){ //image horizontal scroll
dx = dimension.width;
}
g2d.drawImage(img,getWidth() - dx, 0, null); //draw background
if(dx >= img.getWidth()){
g2d.drawImage(img,getWidth() - dx2, 0, null);
}
if(parno){
g2d.drawImage(ptica1,dimension.width/2, 290 + pad, null); //draw bird
}
else{
g2d.drawImage(ptica2,dimension.width/2, 290 + pad, null); //draw bird
}
stub1.postoji = true; //pillar1 exists?
if(razmakStub > 240){
stub2.postoji = true;
}
if(razmakStub > 480){ //pillar1 exists?
stub3.postoji = true;
}
for(Stub i : stubovi){ //draw pillars if they exist
if(i.postoji)
i.crtaj(g2d);
}
}
#Override
public void actionPerformed(ActionEvent e) {
dx = dx + brzina;
dx2 = dx2 + brzina;
if(skokbrojac > 5) // jump frame lenght
skok = false;
if(skok){
pad -= 15; // jump height
}
else{
pad += 8; //rate of the fall
}
skokbrojac++;
parno ^= true; // for different bird images
if(290 + pad >= 536 || 290 + pad<= 3) //border hit detect
timer.stop();
razmakStub += brzina;
for(Stub i : stubovi){ //reset pillars and make them move
if(i.postoji){
if(i.getDx() < -50){
i.setDx(800);
i.randomDy();
}
i.setDx(i.getDx() - brzina);
}
}
repaint();
}
}
Complete project source
Also bear in mind this is really unpolished version so the code is ugly. I am looking for a solution to boost performance.
Main Class:
import javax.swing.*;
/**
* Created by Lazar on 25/05/15.
*/
public class Main {
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Frame(new Environment());
}
});
}
}
Frame class:
import javax.swing.*;
/**
* Created by Lazar on 25/05/15.
*/
public class Frame extends JFrame{
public Frame(JComponent content){
setContentPane(content);
setTitle("Flappy");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(getPreferredSize());
setResizable(false);
setVisible(true);
setLocationRelativeTo(null);
}
}
Stub/Pillar class:
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* Created by Lazar on 26/05/15.
*/
public class Stub {
BufferedImage dole;
BufferedImage gore;
Random r = new Random();
int dx = 700;
int dy = r.nextInt(250) + 250;
boolean postoji = false;
public void crtaj(Graphics2D g2d){
dole = Util.openImage("stub_dole.png");
gore = Util.openImage("stub_gore.png");
g2d.drawImage(dole, dx, dy, null);
g2d.drawImage(gore, dx, -(560-dy), null);
}
public void setDx(int dx) {
this.dx = dx;
}
public void randomDy(){
this.dy = r.nextInt(250) + 250;
}
public int getDx() {
return dx;
}
}
Ptica/Brid class:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
/**
* Created by Lazar on 26/05/15.
*/
public class Ptica {
BufferedImage ptica1;
BufferedImage ptica2;
boolean ptica;
boolean skok = false;
int pad = 0;
double skokBrojac = 0;
public Ptica(){
ptica1 = Util.openImage("/slike/ptica1.png");
ptica2 = Util.openImage("/slike/ptica2.png");
}
public void crtajPticu(Graphics g2d){
ptica ^= true;
if(ptica){
g2d.drawImage(ptica1, Environment.dimension.width/2, Environment.dimension.height/2-110 + pad, null);
}
else{
g2d.drawImage(ptica2, Environment.dimension.width/2, Environment.dimension.height/2-110 + pad, null);
}
System.out.println(pad);
}
public void setSkok(boolean skok) {
this.skok = skok;
}
public void setSkokBrojac(double skokBrojac) {
this.skokBrojac = skokBrojac;
}
public double getSkokBrojac() {
return skokBrojac;
}
public boolean isSkok() {
return skok;
}
public void setPad(int pad) {
this.pad = pad;
}
public int getPad() {
return pad;
}
}
Util class:
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* Created by Lazar on 25/05/15.
*/
public class Util {
public static BufferedImage openImage(String name){
try {
if(!name.startsWith("/slike/")){
name="/slike/"+name;
}
return ImageIO.read(Util.class.getResource(name));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
Avoid adding all you classes to the default package, this could cause issues with class loading on some versions of Java
Painting should paint the state and should not be making decisions or changing the state
Don't, repeatedly, load resources
For example, from you Stub class, which Environment's paintComponent calls crtaj, you do the following...
public void crtaj(Graphics2D g2d){
dole = Util.openImage("stub_dole.png");
gore = Util.openImage("stub_gore.png");
g2d.drawImage(dole, dx, dy, null);
g2d.drawImage(gore, dx, -(560-dy), null);
}
Loading the images can take time. You should either have a "cache" class which managers them (loading them once) or load them when the Stub class is created (I'd prefer the cache class, as if you create and destroy many Stubs, loading the resources within the Stub class (constructor for example) could become a bottle neck
For example, which was able to go from 200-300 objects moving simultaneously, to over 4000 through the use of a re-usable object cache (rather the re-creating the objects and re-loading their resources)
Use a profiler to determine where you code is actually spending time (Note that YourKit has a 15 day free trial license available).
Once you know what your bottleneck is then determine if there's an easy fix, if not consider better algorithms and data-structures to reduce the algorithmic complexity of your code.
Profiling, as suggested by #alex-fitzpatrick, is always good. Also:
Is the type of images created by your Util.openImage call compliant with the graphics2D object you paint on? You may be spending some with the conversions (image types).
eliminate calls to getWidth() etc. You know these values after object initialization, cache them.
If possible, don't call repaint on the entire component. Use the overloaded version that specifies the area to repaint.
... and consider using JavaFX for games :-)

drawing and JLabel on a Frame

I wanted to create a caterpillar game using Java Frame. It basically has two players trying to occupy a square to increase their scores. I've been using two classes:
the caterpillar class
package caterpillar;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
class Caterpillar {
private Color color;
private Point position;
private char direction;
private Queue<Point> body;
private Queue<Character> commands;
private int score;
public Caterpillar (Color c, Point p){
color = c;
direction = 'E';
body = new LinkedList<Point>();
score = 0;
commands = new LinkedList<Character>();
for (int i = 0; i < 10; i++){
position= new Point(p.x + i, p.y);
body.offer(position);
}
}
public void setDirection(char direction){
commands.offer(new Character(direction));
}
public void move(CaterpillarGame game){
if(commands.size()>0){
Character c = (Character)commands.peek();
commands.poll();
direction = c.charValue();
if(direction == 'Z')
return;
}
Point np = newPosition();
if(game.canMove(np)){
body.poll();
body.offer(np);
position = np;
}
score+=game.squareScore(np);
}
private Point newPosition(){
int x = position.x;
int y = position.y;
if(direction == 'E')
x++;
else if(direction == 'W')
x--;
else if(direction == 'N')
y--;
else if(direction == 'S')
y++;
return new Point(x, y);
}
public boolean inPosition(Point np){ //check if position has alredy been occupied
Iterator <Point>it = body.iterator();
while(it.hasNext()){
Point location = it.next();
if(np.equals(location))
return true;
}
return false;
}
public int getScore(){
return score;
}
public void paint(Graphics g){
g.setColor(color);
Iterator <Point>it = body.iterator();
while(it.hasNext()){
Point p = it.next();
g.fillOval(5 + CaterpillarGame.SegmentSize * p.x,
15 + CaterpillarGame.SegmentSize * p.y,
CaterpillarGame.SegmentSize,
CaterpillarGame.SegmentSize);
}
}
}
the game class:
package caterpillar;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;
import javax.swing.JLabel;
public class CaterpillarGame extends Frame{
final static int BoardWidth = 60;
final static int BoardHeight = 40;
final static int SegmentSize = 10;
private Caterpillar playerOne;
private Caterpillar playerTwo;
private Point square;
private int number; //points the players will get if occupy the square
private Random generator;
private JLabel score1, score2;// scores of two players
public static void main(String[] args){
CaterpillarGame game= new CaterpillarGame();
game.run();
}
public CaterpillarGame(){
setBackground(Color.GREEN);
setVisible(true);
setSize((BoardWidth+1)*SegmentSize, BoardHeight*SegmentSize + 30);
addKeyListener(new KeyReader());
playerOne = new Caterpillar(Color.blue, new Point(20, 10));
playerTwo = new Caterpillar(Color.red, new Point(20, 30));
addWindowListener(new CloseQuit());
generator = new Random();
number = 1;
score1 = new JLabel("Player One: "+playerOne.getScore());
score2 = new JLabel("Player Two: "+playerTwo.getScore());
this.add(score1);
this.add(score2);
square = new Point(newSquare());
}
public void run(){
while(true){
movePieces();
repaint();
try{
Thread.sleep(100);
}catch(Exception e){}
}
}
public void paint(Graphics g){
playerOne.paint(g);
playerTwo.paint(g);
g.setColor(Color.white);
g.fillRect(square.x, square.y, 10,10); //line 62, exception thrown
g.setColor(Color.BLACK);
g.drawString(Integer.toString(number), 10, 10);
}
public void movePieces(){
playerOne.move(this);
playerTwo.move(this);
}
public boolean canMove(Point np){
int x = np.x;
int y = np.y;
if((x<=0)||(y<=0))
return false;
if((x>=BoardWidth)||(y>=BoardHeight))
return false;
if(playerOne.inPosition(np))
return false;
if(playerTwo.inPosition(np))
return false;
return true;
}
private class KeyReader extends KeyAdapter{
public void keyPressed(KeyEvent e){
char c = e.getKeyChar();
switch(c){
case 'q': playerOne.setDirection('Z');
break;
case 'a': playerOne.setDirection('W');
break;
case 'd': playerOne.setDirection('E');
break;
case 'w': playerOne.setDirection('N');
break;
case 's': playerOne.setDirection('S');
break;
case 'p': playerTwo.setDirection('Z');
break;
case 'j': playerTwo.setDirection('W');
break;
case 'l': playerTwo.setDirection('E');
break;
case 'i': playerTwo.setDirection('N');
break;
case 'k': playerTwo.setDirection('S');
break;
}
}
}
public Point newSquare(){
Point p = new Point(generator.nextInt(51), generator.nextInt(31));
while(playerOne.inPosition(p)||playerTwo.inPosition(p)){
p = new Point(generator.nextInt(51), generator.nextInt(31));
}
number++;
return square = p;
}
public int squareScore(Point p){
if(p.equals(square)){
newSquare();
return number;
}
return 0;
}
private class CloseQuit extends WindowAdapter{
public void windowClosing(WindowEvent e){
System.exit(0);
}
}
}
this is what i have so far, but i got several problems when i ran the program:
the JLabels, square, and string didn't show up
it kept throwing java.lang.NullPointerException
how do i have the program stopped when the score of one player reaches a certain number?
how to check if a caterpillar touches the square?
Thanks in advance.
the JLabels, square, and string didn't show up
They are, you're just screwing with the paint process so badly that you're preventing them from been painted. Also, I "believe" that java.awt.Frame uses a BorderLayout.
This is one of the reasons we generally discourage people from overriding paint, it's far to easy to break the paint chain
Add a call to super.paint before you perform any custom painting.
#Override
public void paint(Graphics g) {
super.paint(g);
I'd prefer that you used paintComponent from a JComponent based class and add that object to your frame, but you seem to have restrictions from doing so...
Also, change the layout manager to something like FlowLayout to get both labels on the screen.
See Painting in AWT and Swing and Laying Out Components Within a Container for more details
it kept throwing java.lang.NullPointerException
This seems to be a race condition. Essentially, you are calling setVisible on the frame BEFORE you've finished initialising the requirements of the UI, which is causing paint to be called BEFORE the frame is fully initialized.
Call setVisible last. Better yet, don't call setVisible from within the constructor...
how do i have the program stopped when the score of one player reaches a certain number?
... In your "game loop" you need to be checking that state of the score and breaking out of the "game loop" when the required state is meet...
how to check if a caterpillar touches the square?
That's a much more complicated question. Essentially you could use the interests or contains method of a Rectangle (or other Shape object). You know where the Point of the square and "segments" of the Caterpillar are. You know the size of the square and the "segments".
You need to compare each of the segments to see if they intersect the are of the square. Take a closer look at java.awt.Rectangle for more details

Menu in a simple java game [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 9 years ago.
Improve this question
I created a GUI named Menu class for my Start, Help and Exit button. my problem is my game won't start, it hangs everytime I press the Start button. Can someone check my error and explain it to me because its my case study at school.
package spaceSip;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
public class Menu extends JFrame implements ActionListener
{
private JLabel titleL;
private JButton startB, helpB, exitB;
static JFrame frame1 = new JFrame();
public Menu()
{
frame1.setSize(500,250);
Container mainP = frame1.getContentPane();
mainP.setLayout(null);
titleL = new JLabel("WELCOME");
startB = new JButton("Start");
helpB = new JButton("Help");
exitB = new JButton("Exit");
mainP.add(titleL);
titleL.setFont(new Font("Chiller",Font.BOLD,50));
titleL.setBounds(100, 30, 200, 50);
mainP.add(startB);
startB.setMnemonic(KeyEvent.VK_S);
startB.setBounds(200, 80, 100, 20);
mainP.add(helpB);
helpB.setMnemonic(KeyEvent.VK_H);
helpB.setBounds(200, 100, 100, 20);
mainP.add(exitB);
exitB.setMnemonic(KeyEvent.VK_E);
exitB.setBounds(200, 120, 100, 20);
startB.addActionListener(this);
helpB.addActionListener(this);
exitB.addActionListener(this);
frame1.setVisible(true);
frame1.setResizable(false);
}
public void actionPerformed(ActionEvent e)
{
String key = e.getActionCommand();
if(key == "Start")
{
frame1.dispose();
new SpaceShipMain();
}
else if(key == "Help")
{
}
else
System.exit(0);
}
public static void main(String[]args)
{
new Menu();
}
}
package spaceSip;
import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class SpaceShipMain implements MouseListener
{
private JFrame background;
private SpaceShipPanel back;
public static boolean paused;
public static boolean crashed;
public static boolean started;
public static boolean playedOnce;
public boolean goingUp;
private double upCount;
public static int distance;
public static int maxDistance;
public final int XPOS;
public final int NUMRECS;
public final int RECHEIGHT;
public final int RECWIDTH;
private int moveIncrement;
private int numSmoke;
private ArrayList<SpaceShipImage> toprecs;
private ArrayList<SpaceShipImage> bottomrecs;
private ArrayList<SpaceShipImage> middlerecs;
private ArrayList<SpaceShipImage> smoke;
private SpaceShipImage helicopter;
public SpaceShipMain()
{
NUMRECS = 100;
RECHEIGHT = 70;
RECWIDTH = 30;
XPOS = 200;
playedOnce = false;
maxDistance = 0;
load(new File("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/Best.txt"));
initiate();
}
public void load(File file)
{
try
{
Scanner reader = new Scanner(file);
while(reader.hasNext())
{
int value = reader.nextInt();
if(value > maxDistance)
maxDistance = value;
}
}
catch(IOException i )
{
System.out.println("Error. "+i);
}
}
public void save()
{
FileWriter out;
try
{
out = new FileWriter("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/Best.txt");
out.write("" + maxDistance);
out.close();
}
catch(IOException i)
{
System.out.println("Error: "+i.getMessage());
}
}
public void initiate()
{
if(!playedOnce)
{
background = new JFrame("Space Ship Game");
background.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //closes the program when the window is closed
background.setResizable(false); //don't allow the user to resize the window
background.setSize(new Dimension(800,500));
background.setVisible(true);
back = new SpaceShipPanel("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/starfield.jpg");
background.add(back);
back.addMouseListener(this);
}
playedOnce = true;
goingUp = false;
paused = false;
crashed = false;
started = false;
distance = 0;
upCount = 0;
moveIncrement = 2;
numSmoke = 15;
toprecs = new ArrayList<SpaceShipImage>();
middlerecs = new ArrayList<SpaceShipImage>();
bottomrecs = new ArrayList<SpaceShipImage>();
smoke = new ArrayList<SpaceShipImage>();
helicopter = new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rocketship.GIF",XPOS,200);
for(int x = 0; x < NUMRECS; x++)
toprecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,5));
for(int x = 0; x < NUMRECS; x++)
bottomrecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,410));
middlerecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid2.jpg",1392,randomMidHeight()));
middlerecs.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid2.jpg",1972,randomMidHeight()));
drawRectangles();
}
public void drawRectangles()
{
long last = System.currentTimeMillis();
long lastCopter = System.currentTimeMillis();
long lastSmoke = System.currentTimeMillis();
long lastSound = System.currentTimeMillis();
int firstUpdates = 0;
double lastDistance = (double)System.currentTimeMillis();
while(true)
{
if(!paused && !crashed && started && (double)System.currentTimeMillis() - (double)(2900/40) > lastDistance)
{
lastDistance = System.currentTimeMillis();
distance++;
}
if(!paused && !crashed && started && System.currentTimeMillis() - 10 > lastCopter)
{
lastCopter = System.currentTimeMillis();
updateCopter();
updateMiddle();
}
if(!paused && !crashed && started && System.currentTimeMillis() - 100 > last)
{
last = System.currentTimeMillis();
updateRecs();
}
if(!paused && !crashed && started && System.currentTimeMillis() - 75 > lastSmoke)
{
lastSmoke = System.currentTimeMillis();
if (firstUpdates < numSmoke)
{
firstUpdates++;
smoke.add(new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/smoke.GIF",187,helicopter.getY()));
for(int x = 0; x < firstUpdates; x++)
smoke.set(x,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/smoke.GIF",smoke.get(x).getX() - 12, smoke.get(x).getY()));
}
else
{
for(int x = 0; x < numSmoke - 1; x++)
smoke.get(x).setY(smoke.get(x+1).getY());
smoke.set(numSmoke - 1,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/smoke.GIF",187,helicopter.getY()));
}
}
back.updateImages(middlerecs,helicopter,smoke);
}
}
public void updateRecs()
{
for(int x = 0; x < (NUMRECS - 1); x++) //move all but the last rectangle 1 spot to the left
{
toprecs.set(x,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,toprecs.get(x+1).getY()));
bottomrecs.set(x,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rec2.JPG",RECWIDTH*x,bottomrecs.get(x+1).getY()));
}
}
public void randomDrop()
{
toprecs.get(26).setY(toprecs.get(26).getY() + (463 - bottomrecs.get(26).getY()));
bottomrecs.get(26).setY(463);
}
public int randomMidHeight()
{
int max = 10000;
int min = 0;
for(int x = 0; x < NUMRECS; x++)
{
if(toprecs.get(x).getY() > min)
min = (int)toprecs.get(x).getY();
if(bottomrecs.get(x).getY() < max)
max = (int)bottomrecs.get(x).getY();
}
min += RECHEIGHT;
max -= (RECHEIGHT + min);
return min + (int)(Math.random() * max);
}
//moves the randomly generated middle rectangles
public void updateMiddle()
{
if(middlerecs.get(0).getX() > -1 * RECWIDTH)
{
middlerecs.set(0,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(0).getX() - (RECWIDTH/5), middlerecs.get(0).getY()));
middlerecs.set(1,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(1).getX() - (RECWIDTH/5), middlerecs.get(1).getY()));
}
else
{
middlerecs.set(0,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(1).getX() - (RECWIDTH/5), middlerecs.get(1).getY()));
middlerecs.set(1,new SpaceShipImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/asteroid.gif",middlerecs.get(0).getX() + 580,randomMidHeight()));
}
}
public boolean shoot()
{
for(int x = 3; x <= 7; x++)
if(helicopter.getY() >= bottomrecs.get(x).getY())
return true;
for(int y = 3; y <= 7; y++)
if(helicopter.getY() <= toprecs.get(y).getY())
return true;
for(int z = 0; z <= 1; z++)
if(isInMidRange(z))
return true;
return false;
}
public boolean isInMidRange(int num)
{
Rectangle middlecheck = new Rectangle((int)middlerecs.get(num).getX(),(int)middlerecs.get(num).getY(),RECWIDTH,RECHEIGHT);
Rectangle coptercheck = new Rectangle((int)helicopter.getX(),(int)helicopter.getY(),70,48); //asteroid X and y bump
return middlecheck.intersects(coptercheck);
}
public void crash()
{
crashed = true;
if(distance > maxDistance)
{
maxDistance = distance;
save();
}
int reply = JOptionPane.showConfirmDialog(null, " RESTART ?", "GAME OVER", JOptionPane.YES_NO_OPTION);
if(reply == JOptionPane.YES_OPTION)
initiate();
else
System.exit(0);
initiate();
}
//moves the spaceship
public void updateCopter()
{
upCount += .10;
if(goingUp)
{
if(upCount < 3.5)
helicopter.setPosition(XPOS,(double)(helicopter.getY() - (.3 + upCount)));
else
helicopter.setPosition(XPOS,(double)(helicopter.getY() - (1.2 + upCount)));
helicopter.setImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rocketship.GIF");
}
else
{
if(upCount < 1)
helicopter.setPosition(XPOS,(double)(helicopter.getY() + upCount));
else
helicopter.setPosition(XPOS,(double)(helicopter.getY() + (1.2 + upCount)));
helicopter.setImage("C:\\Users/Travelmate/workspace/GAME/src/spaceSip/rocketship.GIF");
}
if(shoot())
crash();
}
//Called when the mouse exits the game window
public void mouseExited(MouseEvent e)
{
paused = true;
}
//Called when the mouse enters the game window
public void mouseEntered(MouseEvent e)
{
}
//Called when the mouse is released
public void mouseReleased(MouseEvent e)
{
goingUp = false;
upCount = -1;
if(paused)
paused = false;
}
//Called when the mouse is pressed
public void mousePressed(MouseEvent e)
{
if (!started)
started = true;
goingUp = true;
upCount = 0;
}
//Called when the mouse is released
public void mouseClicked(MouseEvent e)
{
}
}
package spaceSip;
import java.awt.Image;
import javax.swing.ImageIcon;
public class SpaceShipImage
{
private Image image; //The picture
private double x; //X position
private double y; //Y position
//Construct a new Moving Image with image, x position, and y position given
public SpaceShipImage(Image img, double xPos, double yPos)
{
image = img;
x = xPos;
y = yPos;
}
//Construct a new Moving Image with image (from file path), x position, and y position given
public SpaceShipImage(String path, double xPos, double yPos)
{
this(new ImageIcon(path).getImage(), xPos, yPos);
//easiest way to make an image from a file path in Swing
}
//They are set methods. I don't feel like commenting them.
public void setPosition(double xPos, double yPos)
{
x = xPos;
y = yPos;
}
public void setImage(String path)
{
image = new ImageIcon(path).getImage();
}
public void setY(double newY)
{
y = newY;
}
public void setX(double newX)
{
x = newX;
}
//Get methods which I'm also not commenting
public double getX()
{
return x;
}
public double getY()
{
return y;
}
public Image getImage()
{
return image;
}
}
package spaceSip;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
class SpaceShipPanel extends JPanel
{
private Image background;
private ArrayList<SpaceShipImage> middle;
private SpaceShipImage copter;
private ArrayList<SpaceShipImage> smoke;
//Constructs a new ImagePanel with the background image specified by the file path given
public SpaceShipPanel(String img)
{
this(new ImageIcon(img).getImage());
//The easiest way to make images from file paths in Swing
}
//Constructs a new ImagePanel with the background image given
public SpaceShipPanel(Image img)
{
background = img;
Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
//Get the size of the image
//Thoroughly make the size of the panel equal to the size of the image
//(Various layout managers will try to mess with the size of things to fit everything)
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
middle = new ArrayList<SpaceShipImage>();
smoke = new ArrayList<SpaceShipImage>();
}
//This is called whenever the computer decides to repaint the window
//It's a method in JPanel that I've overwritten to paint the background and foreground images
public void paintComponent(Graphics g)
{
//Paint the background with its upper left corner at the upper left corner of the panel
g.drawImage(background, 0, 0, null);
//Paint each image in the foreground where it should go
for(SpaceShipImage img : middle)
g.drawImage(img.getImage(), (int)(img.getX()), (int)(img.getY()), null);
for(SpaceShipImage img : smoke)
g.drawImage(img.getImage(), (int)(img.getX()), (int)(img.getY()), null);
if(copter != null)
g.drawImage(copter.getImage(), (int)(copter.getX()), (int)(copter.getY()), null);
drawStrings(g);
}
public void drawStrings(Graphics g)
{
g.setColor(Color.WHITE);
g.setFont(new Font("Arial",Font.BOLD,20));
g.drawString("Distance: " + SpaceShipMain.distance,30,440);
g.setFont(new Font("Arial",Font.BOLD,20));
if (SpaceShipMain.distance > SpaceShipMain.maxDistance)
g.drawString("Best: " + SpaceShipMain.distance,650,440);
else
g.drawString("Best: " + SpaceShipMain.maxDistance,650,440);
if(SpaceShipMain.paused)
{
g.setFont(new Font("Chiller",Font.BOLD,72));
g.drawString("Paused",325,290);
g.setFont(new Font("Chiller",Font.BOLD,30));
g.drawString("Click to unpause.",320,340);
}
}
//Replaces the list of foreground images with the one given, and repaints the panel
public void updateImages(ArrayList<SpaceShipImage> newMiddle,SpaceShipImage newCopter,ArrayList<SpaceShipImage> newSmoke)
{
copter = newCopter;
middle = newMiddle;
smoke = newSmoke;
repaint(); //This repaints stuff... you don't need to know how it works
}
}
You compare Strings with equals(value equality) not with ==(reference equality). For more information read this previous question How do I compare strings in Java?
As #AndrewThompson always advice don't use NullLayout
Java GUIs might have to work on a number of platforms, on different
screen resolutions & using different PLAFs. As such they are not
conducive to exact placement of components. To organize the
components for a robust GUI, instead use layout managers, or
combinations of
them1, along
with layout padding & borders for white
space2.
As you say that your gui is blocked, that may be cause some of your execution code takes too more time and it's executed in the same thread as gui stuff (The Event Dispatch Thread). As you have a while(true) that's block the gui so use a SwingTimer for execute repeatidly task or if that code that takes long time execute it in a background thread using a Swing Worker. Here you have a complete example. When you perform custom painting you should override paintComponent and in first line you have to call super.paintComponent(..) to follow correct chaining. Read more in Painting in AWT-Swing.
Another error i see is that you are adding components after calling setVisible(true) without calling revalidate() repaint(). So i recommend to first add components to container then call setVisible(true) at final step.

Categories