Related
I am trying to have each brick in my game have a random color, however when I try to do this the whole set of bricks become the same color. How do I make each individual brick a random color? Any help is appreciated.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Game extends JoeApplet implements KeyListener
{
String status;
int ballx = 294; // ball spawn x coordinate
int bally = 640; // ball spawn y coordinate
int batx = 294;
int baty = 654;
int brickx = 32;
int bricky = 50;
double movex = -16; // x speed of ball
double movey = -16; //y speed of ball
int count = 0;
int currentLevel=0;
int score=0; //starts score at 0
int lives=3; //lives start at 3
static boolean right = false;
static boolean left = false;
boolean ballFallDown = false;
boolean bricksOver = false;
Rectangle Ball = new Rectangle(ballx, bally, 14, 14); //creates ball
Rectangle Bat = new Rectangle(batx, baty, 100, 12); //creates bat(paddle)
Rectangle[] Brick = new Rectangle[49]; //creates desired number of bricks
public void paint(Graphics art)
{
switch(currentLevel)
{
case 0:
menuScreen(art);
break;
case 1:
game(art);
break;
}
}
public void menuScreen(Graphics art)
{
setSize(700, 700);
art.setColor(Color.BLACK);
art.fillRect(0, 0, 698, 698);
Color ballcolor=new Color(0,0,66);
art.setColor(ballcolor);
art.fillOval(Ball.x, Ball.y, Ball.width, Ball.height);
Color batcolor=new Color(0,0,66);
art.setColor(batcolor);
art.fill3DRect(Bat.x, Bat.y, Bat.width, Bat.height, true);
art.setColor(Color.green);
art.drawRect(0, 0, 698, 698);
art.setColor(Color.yellow);
Font menu = new Font("Arial", Font.BOLD, 20);
art.setFont(menu);
art.drawString("Brick Breaker", 100,400);
art.drawString("Press P to Play", 100,425);
art.drawString("Press Q to Quit game", 100,450);
for (int i = 0; i < Brick.length; i++)
{
if (Brick[i] != null)
{
Color mycolor=new Color(100,0,0);
art.setColor(mycolor);
art.fill3DRect(Brick[i].x, Brick[i].y, Brick[i].width,
Brick[i].height, true);
}
}
art.setColor(Color.YELLOW);
if (ballFallDown || bricksOver)
{
Font f = new Font("Arial", Font.BOLD, 20);
art.setFont(f);
art.drawString(status, 294, 349);
ballFallDown = false;
bricksOver = false;
}
}
public void game(Graphics art)
{
setSize(700, 700);
art.setColor(Color.BLACK);
art.fillRect(0, 0, 698, 698);
Color ballcolor=new Color(0,0,225);
art.setColor(ballcolor);
art.fillOval(Ball.x, Ball.y, Ball.width, Ball.height);
Color batcolor=new Color(0,0,139);
art.setColor(batcolor);
art.fill3DRect(Bat.x, Bat.y, Bat.width, Bat.height, true);
art.setColor(Color.green);
art.drawRect(0, 0, 698, 698);
for (int i = 0; i < Brick.length; i++)
{
if (Brick[i] != null)
{
Color mycolor=new Color(200,0,0);
art.setColor(mycolor);
art.fill3DRect(Brick[i].x, Brick[i].y, Brick[i].width,
Brick[i].height, true);
}
}
if (ballFallDown || bricksOver)
{
Font f = new Font("Arial", Font.BOLD, 20);
art.setFont(f);
art.drawString(status, 100,425);
ballFallDown = false;
bricksOver = false;
}
for (int i = 0; i < Brick.length; i++)
{
if (Brick[i] != null)
{
if (Brick[i].intersects(Ball))
{
score=score+10;
Brick[i] = null;
movey = -movey;
count++;
}
}
}
if (count == Brick.length)
{
bricksOver = true;
movex=0;
movey=0;
art.setColor(Color.green);
status = "YOU BEAT THE LEVEL!!";
art.drawString("Press E to Exit", 100,450);
art.drawString("Press N for Next Level", 100,475);
repaint();
}
repaint();
Font f = new Font("Arial", Font.BOLD, 20);
art.setFont(f);
art.setColor(Color.white);
art.drawString("Score:"+score, 600, 684);
Ball.x += movex;
Ball.y += movey;
if (left == true)
{
Bat.x -= 18;
right = false;
}
if (right == true)
{
Bat.x += 18;
left = false;
}
if (Bat.x <= 4)
{
Bat.x = 4;
}
else if (Bat.x >= 586)
{
Bat.x = 596;
}
if (Ball.intersects(Bat))
{
movey = -movey-.1;
}
if (Ball.x <= 0 || Ball.x + Ball.height >= 698)
{
movex = -movex;
}
if (Ball.y <= 0)
{
movey = -movey;
}
Font f1 = new Font("Arial", Font.BOLD, 20);
art.setFont(f1);
art.setColor(Color.white);
art.drawString("Lives:"+ lives, 5, 684);
if (Ball.y >= 698 && (bricksOver==false) && lives>0)
{
ballFallDown = true;
art.setColor(Color.red);
status = "";
art.drawString("", 100,450);
lives=lives-1;
ballx = 294;
bally = 640;
Ball = new Rectangle(ballx, bally, 14, 14);
movex = -16;
movey = -16;
repaint();
}
if(lives==0 && Ball.y >= 698)
{
art.setColor(Color.red);
art.drawString("You lost!!", 100,425);
art.drawString("Press E to Exit", 100,450);
}
}
public void init()
{
addKeyListener(this);
for (int i = 0; i < Brick.length; i++) //creates bricks
{
Brick[i] = new Rectangle(brickx, bricky, 40, 20);
if (i == 12) //1st row of bricks
{
brickx = 32;
bricky = 84;
}
if (i == 23) //2nd row of bricks
{
brickx = 82;
bricky = 118;
}
if (i == 32) //3rd row of bricks
{
brickx = 132;
bricky = 152;
}
if (i == 39) //4th row of bricks
{
brickx = 182;
bricky = 186;
}
if (i == 44) //5th row of bricks
{
brickx = 232;
bricky = 220;
}
if (i == 47) //6th row of bricks
{
brickx = 282;
bricky = 254;
}
if (i == 48) //7th row of bricks
{
brickx = 144;
bricky = 132;
}
brickx += 50; //spacing between each brick
}
}
public void restart()
{
ballx = 294;
bally = 640;
batx = 294;
baty = 654;
brickx = 32;
bricky = 50;
Ball = new Rectangle(ballx, bally, 14, 14);
Bat = new Rectangle(batx, baty, 100, 12);
movex = -16;
movey = -16;
ballFallDown = false;
bricksOver = false;
count = 0;
status = null;
for (int i = 0; i < Brick.length; i++) //recreates bricks
{
Brick[i] = new Rectangle(brickx, bricky, 40, 20);
if (i == 12)
{
brickx = 32;
bricky = 84;
}
if (i == 23)
{
brickx = 82;
bricky = 118;
}
if (i == 32)
{
brickx = 132;
bricky = 152;
}
if (i == 39)
{
brickx = 182;
bricky = 186;
}
if (i == 44)
{
brickx = 232;
bricky = 220;
}
if (i == 47)
{
brickx = 282;
bricky = 254;
}
if (i == 48)
{
brickx = 144;
bricky = 132;
}
brickx += 50;
}
repaint();
}
#Override
public void keyPressed(KeyEvent e) //allows each key to do desired action
{
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT)
{
left = true;
}
if (keyCode == KeyEvent.VK_RIGHT)
{
right = true;
}
if (keyCode == e.VK_P && currentLevel == 0)
{
currentLevel = 1;
}
else if (keyCode == e.VK_E && currentLevel == 1)
{
currentLevel = 0;
score=0;
lives=3;
restart();
}
else if(keyCode == e.VK_Q)
{
System.exit(0);
}
}
#Override
public void keyReleased(KeyEvent e)
{
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT)
{
left = false;
}
if (keyCode == KeyEvent.VK_RIGHT)
{
right = false;
}
}
#Override
public void keyTyped(KeyEvent e)
{
}
public static void main(String[] args)
{
Game prog = new Game();
prog.init();
}
}
I'd throw that code out and start over as you've got both program logic and repaints within your painting methods, neither of which will help you, and your code appears to be one big huge "God" class, all of which will leave you with code that's a horrific nightmare to debug. Recommendations:
Create at least two separate JPanels to display your program with, a GamePanel and a MenuPanel.
Swap these JPanels using a CardLayout.
Do all graphics within a JPanel's paintComponent method and not within a JFrame's or JApplet's paint method.
Don't forget to call the super's painting method, the same method as your override within your override. This is to clean up any dirty pixels.
Separate your program logic from your GUI
This means that you should have a logical non-GUI representation of a single Brick class as well as a collection of these non-GUI bricks.
You can always give your Brick class a Color field, one that the view or gui uses to paint it with.
Create a game-loop, one that you can control, one that doesn't involve calling repaint() within a painting method, since this leads to a completely uncontrollable loop.
Favor use of Key Bindings over KeyListeners.
Try to avoid use of "magic" numbers, such as hard-coding your brick width and spacing in the class itself. Better to use constants as this too makes debugging and enhancing much easier.
For example, some code that's just to demonstrate showing random colors:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import javax.swing.*;
public class BrickBreak {
private static void createAndShowGui() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("Brick Break");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
// JPanel that draws the game
class GamePanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final Color BACK_GRND = Color.BLACK;
private int prefW;
private int prefH;
private Bricks bricks = new Bricks();
public GamePanel() {
// wide enough to hold the complete top-row of Bricks
// using constants, so GUI automatically resizes if any sizes change
prefW = (Brick.WIDTH + Bricks.X_SPACING) * Bricks.ROW_COUNTS[0] + Bricks.X_SPACING;
prefH = prefW;
setBackground(BACK_GRND);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(prefW, prefH);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Brick brick : bricks) {
brick.draw(g2);
}
}
}
// non-GUI class that represents a logical Brick
class Brick {
public static final int WIDTH = 40;
public static final int HEIGHT = 20;
private int x;
private int y;
private Color color;
private Rectangle boundingRectangle;
public Brick(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
boundingRectangle = new Rectangle(x, y, WIDTH, HEIGHT);
}
// yeah, I'm mixing some view with model here.
public void draw(Graphics2D g2) {
g2.setColor(color);
g2.fill3DRect(x, y, WIDTH, HEIGHT, true);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return color;
}
// use this to test for collisions
public boolean contains(Point p) {
return boundingRectangle.contains(p);
}
#Override
public String toString() {
return "Brick [x=" + x + ", y=" + y + ", color=" + color + "]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Brick other = (Brick) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
// logical class that holds all Bricks
// Have class implement Iterable<Brick> so we can easily iterate through its containing
// Brick objects in a for-each loop
class Bricks implements Iterable<Brick> {
public static final int X_SPACING = 10;
public static final int Y_SPACING = X_SPACING;
public static final int[] ROW_COUNTS = {13, 11, 9, 7, 5, 3, 1};
private static final float MIN_SAT = 0.8f;
private List<Brick> brickList;
private Random random = new Random();
public Bricks() {
init(); // safe to call since it's final
}
public final void init() {
// recreate the brickList ArrayList
brickList = new ArrayList<>();
int y = Y_SPACING;
// for each row of bricks
for (int row = 0; row < ROW_COUNTS.length; row++) {
int x = X_SPACING + ((ROW_COUNTS[0] - ROW_COUNTS[row]) / 2) * (X_SPACING + Brick.WIDTH);
// for each column
for (int j = 0; j < ROW_COUNTS[row]; j++) {
// create a random color
float hue = random.nextFloat();
float saturation = MIN_SAT + random.nextFloat() * (1f - MIN_SAT);
float brightness = MIN_SAT + random.nextFloat() * (1f - MIN_SAT);
Color color = Color.getHSBColor(hue, saturation, brightness);
Brick brick = new Brick(x, y, color); // create a new Brick with this Color
brickList.add(brick);
x += X_SPACING + Brick.WIDTH;
}
y += Y_SPACING + Brick.HEIGHT;
}
}
// returns null if no collision
public Brick collision(Point p) {
for (Brick brick : brickList) {
if (brick.contains(p)) {
return brick;
}
}
return null;
}
#Override
public Iterator<Brick> iterator() {
return brickList.iterator();
}
public boolean remove(Brick brick) {
// because Brick implements equals and hashCode, we can do this
return brickList.remove(brick);
}
}
Note that I like using Color's static getHSBColor(float h, float s, float b) method for creating random Colors as this helps me to avoid creating dull Colors, since I can guarantee that the saturation and brightness are above minimum values. All three parameters must be float values between 0f and 1.0f
float hue = random.nextFloat();
float saturation = MIN_SAT + random.nextFloat() * (1f - MIN_SAT);
float brightness = MIN_SAT + random.nextFloat() * (1f - MIN_SAT);
Color color = Color.getHSBColor(hue, saturation, brightness);
Your code has quite a lot of issues, which #HovercaftFullOfEels answer already points out.
As for why your code doesn't work:
for (int i = 0; i < Brick.length; i++)
{
if (Brick[i] != null)
{
Color mycolor=new Color(100,0,0);
art.setColor(mycolor);
art.fill3DRect(Brick[i].x, Brick[i].y, Brick[i].width,
Brick[i].height, true);
}
}
This is the part that renders the bricks. You never create a random-number, but use the same Color for each brick (new Color(100, 0, 0);). Instead introduce a new variable into Brick that specifies the color of each brick and is initialized once with a random color.
The Brick-class would afterwards look like this:
public class Brick{
public int x;
public int y;
...
public Color color;
...
}
The ... are just placeholders for other code that may be content of the class. Regarding public access of Class-variables: Encapsulation is a fundamental concept of OOP and should be used (on encapsulation). E.g. instead of giving direct access to Brick.x consider introducing a method Brick#getX().
I have three classes. The frame for menu of Game from which when play button,
is pressed the class of frame containing Game should play.And the main class creates the object of Menu class.But the problem is that when I pressed play button it become white and after some times it says your score is 0 and Game is over. Please help me solve this problem I have tried every thing.
public class GameFrame extends JFrame implements KeyListener {
protected int x;
protected int y;
private int a;
private int b;
protected int c;
protected int d;
protected int f;
protected int k;
private int h;
private int i;
private int s;
GameFrame() {
/*
* setSize(760,700);
* setLocation(200,25);
* setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
* setResizable(false);
* setVisible(true);
*/
setLayout(null);
c = 500;
d = 530;
a = 1;
b = 1;
x = 50;
y = 250;
f = 50;
k = 700;
h = 1;
i = -1;
s = 1;
addKeyListener(this);
setFocusable(true);
}
public void paint(Graphics g) {
super.paint(g);
Graphics g1 = (Graphics) g;
g1.setColor(Color.DARK_GRAY);
g1.fillRect(0, 0, 1200, getHeight());
/*
* g1.setColor(Color.blue);
* g1.fillRect(0, 0, 850, getHeight());
* g1.setColor(Color.green);
* g1.fillRect(0, 0, 845, getHeight());
* g1.setColor(Color.yellow);
* g1.fillRect(0, 0, 840, getHeight());
* g1.setColor(Color.red);
*/
g1.fillRect(0, 0, 835, getHeight());
g1.setColor(Color.gray);
g1.fillRect(0, 0, 830, getHeight());
g1.setColor(Color.BLACK);
g1.fillRect(0, 0, 825, getHeight());
g1.setColor(Color.red);
g1.fillOval(x, y, 20, 20);
g1.setColor(Color.GREEN);
g1.fillOval(k, f, 20, 20);
g1.setColor(Color.blue);
g1.fillRect(c, d, 100, 15);
g1.setColor(Color.red);
g1.fillRect(300, 300, 90, 20);
g1.setColor(Color.pink);
g1.drawRect(0, 80, 760, 3);
g1.setColor(Color.orange);
g1.setFont(new Font(null, Font.BOLD, 20));
g1.drawString("Your Score", 500, 60);
g1.setFont(new Font(null, Font.BOLD, 25));
g1.drawString("Credit:", 50, 60);
g1.setFont(new Font(null, Font.BOLD, 20));
g1.drawString("Mohsin Hussain", 150, 60);
g1.drawString(String.valueOf(points()), 650, 60);
}
// Here is Run Method. From This Method Ball Will Move in Different Direction After Collision
public void Run() {
if (c < 0)
c = 1;
if (c > 740)
c = 675;
if (k < 0)
i = s;
if (k > 700)
i = -s;
k = k + i;
if (f < 80)
h = s;
if ((f + 20 > d && k + 20 > c && f < d + 6 && k < c + 60))
h = -s;
f = f + h;
if (x < 0)
a = s;
if (x > 700)
a = -s;
x = x + a;
if (y < 80)
b = s;
if ((x > 300 && x < 380) && (y == 300) && b < 0) {
b = s;
}
if ((x > 300 && x < 380) && (y == 300) && b > 0) {
b = -s;
}
System.out.println(x);
if ((y + 20 > d && x + 20 > c && y < d + 6 && x < c + 60))// Control the Direction as well
// as Collision of the Grey Ball
// From Rectangle
b = -s;
y = y + b;
finish();// Call the Method of Finish
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
repaint();
Run();
} // End Bracket of Run Method
// Method of Points that Control the Score as well as Increase the Speed
public int points() {
if ((y + 20 > d && x + 20 > c && y < d + 6 && x < c + 60)
|| (f + 20 > d && k + 20 > c && f < d + 6 && k < c + 60))
s += 1;
return s - 1;
} // /End Bracket of Points Method
// Method of Finish that Control Game Over Logic
private void finish() {
if (y > getHeight() - 20 || f > getHeight() - 20) {
// Logic of Set Time at the End of the Game
/*
* GregorianCalendar date = new GregorianCalendar();
*
* int day = date.get(Calendar.DAY_OF_MONTH); int month = date.get(Calendar.MONTH); int
* year = date.get(Calendar.YEAR);
*
* int second = date.get(Calendar.SECOND); int minute = date.get(Calendar.MINUTE); int
* hour = date.get(Calendar.HOUR);
*
* System.out.println("Ending Date of Game is "+day+"/"+(month+1)+"/"+year);
* System.out.println("Ending Time of Game is "+hour+" : "+minute+" : "+second);
*/
JOptionPane.showMessageDialog(this, "your Score is " + points(), "Game Over",
JOptionPane.YES_NO_OPTION);
System.exit(ABORT);
}// End Bracket of If Statement
}
public void keyPressed(KeyEvent arg0) {
if (arg0.getKeyCode() == KeyEvent.VK_LEFT)
c -= 25;
if (arg0.getKeyCode() == KeyEvent.VK_RIGHT)
c += 25;
} // End of KeyPressed
public void keyReleased(KeyEvent arg0) {} // End of KeyReleased
public void keyTyped(KeyEvent arg0) {}// End of KeyTyped
}
The Frame containing the menu for the Game from where when we press play button the Game should start.
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GameMenu extends JFrame {
private JButton play;
private JButton exit;
private JButton about;
private JButton credit;
private ImageIcon img;
private JLabel imgLabel;
private JPanel panel;
private float h = (float) 0.333;
private float b = (float) 0.600;
private float s = 1;
boolean runCheck = false;
public GameFrame frame;
GameMenu() {
setLayout(null);
setResizable(false);
panel = new JPanel();
panel.setLayout(null);
panel.setSize(760, 700);
panel.setBackground(Color.getHSBColor(h, s, b));
add(panel);
play = new JButton("PLAY");
play.setSize(100, 50);
play.setLocation(360, 400);
panel.add(play);
about = new JButton("ABOUT");
about.setSize(play.getSize());
about.setLocation(play.getX(), play.getY() + play.getHeight() + 50);
panel.add(about);
exit = new JButton("EXIT");
exit.setSize(play.getSize());
exit.setLocation(about.getX() + about.getWidth() + 50, about.getY());
panel.add(exit);
credit = new JButton("CREDIT");
credit.setSize(play.getSize());
credit.setLocation(about.getX() - about.getWidth() - 50, about.getY());
panel.add(credit);
img = new ImageIcon("C:\\Users\\Ghulam Haider\\Downloads\\b.jpg");
imgLabel = new JLabel(img);
imgLabel.setSize(imgLabel.getPreferredSize());
imgLabel.setLocation(230, 45);
panel.add(imgLabel);
mouseListener mouse = new mouseListener();
Action action = new Action();
play.addMouseListener(mouse);
}
private class Action implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {}
}
private class mouseListener implements MouseListener, Runnable {
#Override
public void mouseClicked(MouseEvent get) {}
#Override
public void mousePressed(MouseEvent e) {
if (e.getSource() == play) {
frame = new GameFrame();
frame.setSize(760, 700);
frame.setLocation(200, 25);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
frame.Run();
// end of while
}
}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void run() {
frame.Run();
// end of while
}
}
}
and at last the main class which calls menu frame.
import javax.swing.JFrame;
public class RunGame {
public static void main(String args[]) {
GameMenu menu = new GameMenu();
menu.setSize(760, 700);
menu.setLocation(200, 25);
menu.setVisible(true);
menu.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
But the problem is that when I pressed play button it become white and after some times it says your score is 0 and Game is over.
You are calling the GameFrame.Run method on the EDT - Swing is single threaded, and all painting, events, etc.. run on the EDT. If you have something which blocks it (for instance calling Thread.sleep on the EDT as the GameFrame.Run method does) then none of these can occur. Instead, create a Thread or using a Timer
Some other suggestions:
Don't paint to a JFrame - paint to a JPanel by overriding the paintComponent method.
Avoid using null layouts. Choose the appropriate LayoutManager for the layout you seek.
If looking for button clicks, use an ActionListener rather than MouseListener
Class names should start with an uppercase (eg mouselistener), and method names with lowercase. Avoid any potential naming conflicts with API names
Your if statement that contains finish() is not surrounded by brackets. Therefore after the it sees the condition it simply moves along and runs finish()
if((y+20>d&&x+20>c&&y<d+6&&x<c+60))//Control the Direction as well as Collision of the Grey Ball From Rectangle
b=-s;
y=y+b;
finish();//Call the Method of Finish
so everytime the run method is called it will terminate because finish() is also always called.
Try surrounding your if statement so that it only calls finish() if the statement is true. Or write a separate if statement where when the condition is met finish() is called.
I'm trying to write my first game in Java. I followed some tutorials and learned how to load and update a background using a Canvas and how to load and move a player sprite. I did these two separately and they worked fine, but when I put the two together and try to move the player, the game slows down to the point that it is unplayable. This only happens when I hold down an arrow key to move the player; the game actually runs "smoothly" if I rapidly tap the arrow key. After quite a bit of testing, I'm convinced that the problem occurs when the background is redrawn each frame. Any other improvements would also be appreciated.
Code (All of it):
Game.Java:
package Game;
import Level.Level;
import Player.Player;
import Sprites.SpriteSheetLoader;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
// Set dimensions of the game.
public static final int HEIGHT = 320;
public static final int WIDTH = 480;
public static final int SCALE = 2;
public static Dimension GAME_DIM = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
public SpriteSheetLoader loader;
public Screen screen;
public Level level;
public InputHandler input = new InputHandler(this);
public Player player = new Player();
private boolean running = false;
private boolean moving = true;
private int FPS = 60;
private long targetTime = 1000 / FPS;
// Set character's starting position at the center. I have no idea why I had to add the "- 50" to each value.
public int x = GAME_DIM.width / 2 - 50;
public int y = GAME_DIM.height / 2 - 50;
public int xScroll = 0;
public int yScroll = 0;
public int col = 0;
public int row = 0;
public int ticks = 0;
public int frame = 0;
public static void main(String[] args) {
Game game = new Game();
game.setPreferredSize(new Dimension(GAME_DIM));
game.setMaximumSize(new Dimension(GAME_DIM));
game.setMinimumSize(new Dimension(GAME_DIM));
JFrame frame = new JFrame("Valkyrie Game");
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
}
public void start() {
running = true;
new Thread(this).start();
}
public Game() {
}
public void init() {
loader = new SpriteSheetLoader();
screen = new Screen(WIDTH, HEIGHT);
level = new Level(16, 16);
}
public void run() {
init();
long start, elapsed, wait;
while (running) {
start = System.nanoTime();
render();
tick();
elapsed = System.nanoTime() - start;
//System.out.println("Elapsed: " + elapsed);
wait = targetTime - elapsed / 1000000;
if(wait < 0) {
wait = 5;
}
try {
Thread.sleep(wait);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop() {
running = false;
}
public void tick() {
// Movement
if (input.right) {
xScroll++;
player.setAnimation(player.walkRight);
//x++;
row = 2;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.left) {
xScroll--;
player.setAnimation(player.walkLeft);
//x--;
row = 1;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.up) {
yScroll--;
player.setAnimation(player.walkUp);
//y--;
row = 3;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
} else if (input.down) {
yScroll++;
player.setAnimation(player.walkDown);
//y++;
row = 0;
ticks++;
if(ticks < 10) {
frame = 1;
} else if(ticks == 10) {
frame = 2;
} else if(ticks == 20) {
frame = 3;
} else if(ticks == 30) {
frame = 2;
} else if(ticks == 40) {
frame = 1;
} else if(ticks == 50) {
ticks = 0;
frame = 0;
}
moving = true;
}
if (!input.down && !input.left && !input.right && !input.up) {
player.setAnimation(player.stand);
frame = 0;
ticks = 1;
moving = false;
}
//System.out.println("Tick: " + ticks);
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
requestFocus();
return;
}
do {
Graphics g = bs.getDrawGraphics();
try {
for (int i = 0; i < ticks; i++) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.drawImage(player.Player(frame, row), x, y, null);
level.renderBackground(xScroll, yScroll, screen);
for (int y = 0; y < this.screen.h; y++) {
for (int x = 0; x < screen.w; x++) {
pixels[x + (y * WIDTH)] = screen.pixels[x + (y * screen.w)];
}
}
}
} finally {
g.dispose();
}
bs.show();
this.update(bs.getDrawGraphics());
} while (bs.contentsLost());
// Graphics g = bs.getDrawGraphics();
//
// g.dispose();
// bs.show();
}
}
InputHandler.Java:
package Game;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class InputHandler implements KeyListener {
public boolean up = false;
public boolean down = false;
public boolean left = false;
public boolean right = false;
public InputHandler(Game game) {
game.addKeyListener(this);
}
public void toggle(KeyEvent ke, boolean pressed) {
int keyCode = ke.getKeyCode();
if(keyCode == KeyEvent.VK_UP) up = pressed;
if(keyCode == KeyEvent.VK_DOWN) down = pressed;
if(keyCode == KeyEvent.VK_LEFT) left = pressed;
if(keyCode == KeyEvent.VK_RIGHT) right = pressed;
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
toggle(e, true);
}
public void keyReleased(KeyEvent e) {
toggle(e, false);
}
}
Screen.Java:
package Game;
import Sprites.Sprite;
public class Screen {
public int w, h;
int xOffset = 0;
int yOffset = 0;
public int[] pixels;
public Screen(int w, int h) {
this.w = w; // 480
this.h = h; // 320
pixels = new int[w * h]; // 153600
}
public void renderSprite(int xPos, int yPos, Sprite sprite) {
int height = sprite.h;
int width = sprite.w;
xPos -= xOffset;
yPos -= yOffset;
for(int y = 0; y < height; y++) {
if(yPos + y < 0 || yPos + y >= h) continue;
for(int x = 0; x < width; x++) {
if(xPos + x < 0 || xPos + x >= w) continue;
int col = sprite.pixels[x + (y * height)];
if(col != -65281 && col < 0) pixels[(x + xPos) + (y + yPos) *w]= col;
}
}
}
public void setOffs(int xOffs, int yOffs) {
xOffset = xOffs;
yOffset = yOffs;
}
}
Level.Java:
package Level;
import Game.Screen;
import Sprites.Sprite;
import Sprites.Sprites;
import Tiles.Tile;
public class Level {
int w, h;
public int[] tiles;
public Level(int w, int h) {
this.w = w;
this.h = h;
tiles = new int[w * h];
loadMap(0, 0, 0, 0);
}
public void renderBackground(int xScroll, int yScroll, Screen screen) {
int xo = xScroll >> 4;
int yo = yScroll >> 4;
int w = (screen.w + 15) >> 4;
int h = (screen.h + 15) >> 4;
screen.setOffs(xScroll, yScroll);
for(int y = yo; y <= h + yo; y++) {
for(int x = xo; x <= w + xo; x++) {
getTile(x, y).render(x, y, screen);
}
}
screen.setOffs(0, 0);
}
public Tile getTile(int x, int y) {
if(x < 0 || y < 0 || x >= w || y >= h) return Tile.rockTile;
return Tile.tiles[tiles[x + y * w]];
}
public void loadMap(int x0, int y0, int x1, int y1) {
Sprite sprite = Sprites.level[x0][y0];
for(int y = 0; y < sprite.h; y++) {
for(int x = 0; x < sprite.w; x++) {
if(sprite.pixels[x + y * sprite.h] == -9276814) {
tiles[x + x1 + (y + y1) * h] = Tile.rockTile.id;
} else {
tiles[x + x1 + (y + y1) * h] = Tile.grassTile.id;
}
}
}
}
}
Player.Java:
package Player;
import Animation.Animation;
import Sprites.Sprite;
import java.awt.image.BufferedImage;
public class Player {
// Images for each animation
private BufferedImage[] walkingLeft = {Sprite.getSprite(0, 1), Sprite.getSprite(1, 1), Sprite.getSprite(2, 1)}; // Gets the upper left images of my sprite sheet
private BufferedImage[] walkingRight = {Sprite.getSprite(0, 2), Sprite.getSprite(1, 2), Sprite.getSprite(2, 2)};
private BufferedImage[] walkingUp = {Sprite.getSprite(0, 3), Sprite.getSprite(1, 3), Sprite.getSprite(2, 3)};
private BufferedImage[] walkingDown = {Sprite.getSprite(0, 0), Sprite.getSprite(1, 0), Sprite.getSprite(2, 0)};
private BufferedImage[] standing = {Sprite.getSprite(1, 0)};
// These are animation states.
public Animation walkLeft = new Animation(walkingLeft, 10);
public Animation walkRight = new Animation(walkingRight, 10);
public Animation walkUp = new Animation(walkingUp, 10);
public Animation walkDown = new Animation(walkingDown, 10);
public Animation stand = new Animation(standing, 10);
// This is the actual animation
public Animation animation = stand;
public BufferedImage Player(int x, int y) {
BufferedImage player = Sprite.getSprite(x, y);
return player;
}
public void update() {
animation.update();
}
public void render() {
}
public void setAnimation(Animation animation) {
this.animation = animation;
}
}
Sprite.Java:
package Sprites;
import Game.Game;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Sprite {
public int w, h;
public int[] pixels;
public static BufferedImage sprite = null;
public Sprite(int w, int h) {
this.w = w;
this.h = h;
pixels = new int[w * h];
}
public void clear(int color) {
for(int i = 0; i < pixels.length; i++) {
pixels[i] = color;
}
}
private static BufferedImage spriteSheet;
private static final int TILE_SIZE = 80;
public static BufferedImage loadSprite() {
try {
sprite = ImageIO.read(Game.class.getResource("/valkyrie.png"));
} catch (IOException e) {
e.printStackTrace();
}
return sprite;
}
public static BufferedImage getSprite(int xGrid, int yGrid) {
if(spriteSheet == null) {
spriteSheet = loadSprite();
}
// xGrid and yGrid refer to each individual sprite
return spriteSheet.getSubimage(xGrid * TILE_SIZE, yGrid * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
This is going to require double buffering. Any game with a lot going on needs double buffering.
How do you double buffer in java for a game?
Although I couldn't go through the code completely, it seems you do not do double buffering which affect performance drastically.
Try this in the relevant part of your program:
JFrame frame = new JFrame("Valkyrie Game");
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDoubleBuffered(true); //added line, rest is the same
game.start();
You really should use Timer. It will solve all your problems.
Every tick, you redraw all what you need.
And every tick, you should just check, which keys are pressed and which are not, instead of adding listeners. To keep tracking this, you always have to remember the keys pressed "before".
You can even create two Timers, one for graphic redraw and one for game logic.
Even timers can be delayed or something, the usual approach is to find out, how much time elapsed (System.nanoTime for example) and count how much of game logic you should forward to keep game always unlaggy and fluent.
It's the same game that I'm working on and I've come to a problem that I can't understand again. I'm using Key Bindings to move my sprite and it's working great! but since it's a snake game I need the sprite to keep moving without stopping and change direction and keep moving after the player types a key. I know it's possible with KeyListener, but I really don't want to have to change my program entirely. I just need to know what code needs to change, if possible.
On top of that I'm also working on two arrays for the x and y co-ordinates for the snake body so that squares will follow behind the head, but I can't get it to paint. and how to display an integer for the score.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
import java.util.ArrayList;
public class Snake2 extends JFrame {
/* Sprite: snake head co-ordinates */
int x = 400;
int y = 450;
int width = 10;
int height = 10;
/* Sprite: snake body */
int length = 0;
ArrayList <Integer> bodyX = new ArrayList <Integer>();
ArrayList <Integer> bodyY = new ArrayList <Integer>();
/* Score */
int point = 0;
/* Sprite: mouse co-ordinates */
Random rand = new Random();
int addx = (rand.nextInt(10))*10;
int addy = (rand.nextInt(10))*10;
int mx = ((rand.nextInt(5)+1)*100) + addx;
int my = ((rand.nextInt(6)+2)*100) + addy;
DrawPanel drawPanel = new DrawPanel();
public Snake2() {
addMouseListener(new MouseListener());
System.out.print(mx + " " + my);
/* move snake up */
Action upAction = new AbstractAction(){
public void actionPerformed(ActionEvent e) {
y -=10;
if (y >= my && y <= my+9 && x >= mx && x <= mx+9)
{
addx = (rand.nextInt(10))*10;
addy = (rand.nextInt(10))*10;
mx = ((rand.nextInt(5)+1)*100) + addx;
my = ((rand.nextInt(6)+1)*100) + addy;
point += 100;
length++;
bodyY.add(0, y);
}
if (y <99)
{
new GameOver();
dispose();
}
drawPanel.repaint();
}
};
/* move snake down */
Action downAction = new AbstractAction(){
public void actionPerformed(ActionEvent e) {
y +=10;
if (y >= my && y <= my+9 && x >= mx && x <= mx+9)
{
addx = (rand.nextInt(10))*10;
addy = (rand.nextInt(10))*10;
mx = ((rand.nextInt(5)+1)*100) + addx;
my = ((rand.nextInt(6)+1)*100) + addy;
point += 100;
length++;
bodyY.add(0, y);
}
if (y > 799)
{
new GameOver();
dispose();
}
drawPanel.repaint();
}
};
/* move snake left */
Action leftAction = new AbstractAction(){
public void actionPerformed(ActionEvent e) {
x -=10;
if (x >= mx && x <= mx+9 && y >= my && y <= my+9)
{
addx = (rand.nextInt(10))*10;
addy = (rand.nextInt(10))*10;
mx = ((rand.nextInt(5)+1)*100) + addx;
my = ((rand.nextInt(6)+1)*100) + addy;
point += 100;
length++;
bodyX.add(0, x);
}
if (x <99)
{
new GameOver();
dispose();
}
drawPanel.repaint();
}
};
/* move snake right */
Action rightAction = new AbstractAction(){
public void actionPerformed(ActionEvent e) {
x +=10;
if (x >= mx && x <= mx+9 && y >= my && y <= my+9)
{
addx = (rand.nextInt(10))*10;
addy = (rand.nextInt(10))*10;
mx = ((rand.nextInt(5)+1)*100) + addx;
my = ((rand.nextInt(6)+1)*100) + addy;
point += 100;
length++;
bodyX.add(0, x);
}
if (x > 699)
{
new GameOver();
dispose();
}
drawPanel.repaint();
}
};
InputMap inputMap = drawPanel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = drawPanel.getActionMap();
inputMap.put(KeyStroke.getKeyStroke("RIGHT"), "rightAction");
actionMap.put("rightAction", rightAction);
inputMap.put(KeyStroke.getKeyStroke("LEFT"), "leftAction");
actionMap.put("leftAction", leftAction);
inputMap.put(KeyStroke.getKeyStroke("DOWN"), "downAction");
actionMap.put("downAction", downAction);
inputMap.put(KeyStroke.getKeyStroke("UP"), "upAction");
actionMap.put("upAction", upAction);
add(drawPanel);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}//Snake2()
private class GameOver extends JFrame implements ActionListener{
JLabel answer = new JLabel("");
JPanel pane = new JPanel(); // create pane object
JButton pressme = new JButton("Quit");
JButton replay = new JButton("Replay?");
GameOver() // the constructor
{
super("Game Over"); setBounds(100,100,300,200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container con = this.getContentPane(); // inherit main frame
con.add(pane); pressme.setMnemonic('Q'); // associate hotkey
pressme.addActionListener(this); // register button listener
replay.addActionListener(this);
pane.add(answer); pane.add(pressme); pane.add(replay); pressme.requestFocus();
answer.setText("You Lose");
setVisible(true); // make frame visible
}//GameOver()
// here is the basic event handler
public void actionPerformed(ActionEvent event)
{
Object source = event.getSource();
if (source == pressme)
System.exit(0);
if (source == replay)
{
dispose();
EventQueue.invokeLater(new Runnable(){
public void run(){
new Snake2();
}
});
}
}//actionPreformed
}//GameOver
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Font ith = new Font("Ithornît", Font.BOLD, 78);
/* Background: Snake */
g.setColor(Color.darkGray);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.gray);
g.fillRect(100,100,600,700);
g.setColor(Color.white);
g.drawRect(99,99,601,701);
g.drawString("Quit",102,86);
g.drawRect(100,70,30,20);
g.drawString("Score: ", 602, 86);
g.setFont(ith);
g.drawString("SNAKE",350,60);
/* Sprite: Mouse */
g.setColor(Color.black);
g.fillRect(mx, my, width, height);
//System.out.print(mx + " " + my);
/* Sprite: Snake Body */
if (length != 0){
for(int i = 0; i >= length; i++)
{
g.setColor(Color.darkGray);
g.fillRect(bodyX.get(i), bodyY.get(i), width, height);
}
}
/* Sprite: Snake head */
g.setColor(Color.white);
g.fillRect(x, y, width, height);
}//Paint Component
public Dimension getPreferredSize() {
return new Dimension(800, 850);
}//Dimension
}//DrawPanel
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable(){
public void run(){
new Snake2();
}
});
}// main
}//Snake Class
/* Tracks where mouse is clicked */
class MouseListener extends MouseAdapter {
public void mouseReleased(MouseEvent me) {
if (me.getX() >= 101 && me.getX() <= 131 && me.getY() >= 94 && me.getY() <= 115){
System.exit(0);
}
String str="Mouse Released at "+me.getX()+","+me.getY();
System.out.println(str);
}
}//MouseAdapter
"but since it's a snake game I need the sprite to keep moving without stopping"
You need to use a javax.swing.Timer. The basic constructor is like this
Timer(int delay, ActionListener listener) // delay is in milliseconds
A basic implementation would be something like this
Timer timer = new Timer(100, new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do something
}
});
timer.start();
Basically what the timer does, is fire an ActionEvent every so many milliseconds. This is how you work animation with swing. So without you pressing any keys, the sprite will move by itself. It works pretty much like a button does. When you press a button an event is fired. But in the case of the timer, the timer is the one that fires the event. You can specify any delay you want. You many even want to change the duration dynamically when a certain level is reached. timer.setDelay(someInt)
UPDATE
I made very few changes to your current code. I did pretty much what I stated in the comment below
Ok so I thought about it, and I would do something like this: Have a direction variable. For the actions, all you should do is change the direction. In the timer actionPerformed thats where all you logic should go. Check for the direction. If if equals "left" then continue going left like you would on a key press. And so on
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
import java.util.ArrayList;
public class Snake2 extends JFrame {
String direction = "right";
//String duration =
/* Sprite: snake head co-ordinates */
int x = 400;
int y = 450;
int width = 10;
int height = 10;
/* Sprite: snake body */
int length = 0;
ArrayList<Integer> bodyX = new ArrayList<Integer>();
ArrayList<Integer> bodyY = new ArrayList<Integer>();
/* Score */
int point = 0;
/* Sprite: mouse co-ordinates */
Random rand = new Random();
int addx = (rand.nextInt(10)) * 10;
int addy = (rand.nextInt(10)) * 10;
int mx = ((rand.nextInt(5) + 1) * 100) + addx;
int my = ((rand.nextInt(6) + 2) * 100) + addy;
DrawPanel drawPanel = new DrawPanel();
Timer timer;
public Snake2() {
addMouseListener(new MouseListener());
timer = new Timer(50, new TimerListener()); ////////////// <<<<<<<<<<<<<<<<<< TIMER
timer.start();
System.out.print(mx + " " + my);
/* move snake up */
Action upAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
direction = "up"; ////////////// <<<<<<<<<<<<<<<<<< direction only change
}
};
/* move snake down */
Action downAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
direction = "down";
}
};
/* move snake left */
Action leftAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
direction = "left";
}
};
/* move snake right */
Action rightAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
direction = "right";
}
};
InputMap inputMap = drawPanel
.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = drawPanel.getActionMap();
inputMap.put(KeyStroke.getKeyStroke("RIGHT"), "rightAction");
actionMap.put("rightAction", rightAction);
inputMap.put(KeyStroke.getKeyStroke("LEFT"), "leftAction");
actionMap.put("leftAction", leftAction);
inputMap.put(KeyStroke.getKeyStroke("DOWN"), "downAction");
actionMap.put("downAction", downAction);
inputMap.put(KeyStroke.getKeyStroke("UP"), "upAction");
actionMap.put("upAction", upAction);
add(drawPanel);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}// Snake2()
private class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e) { /////////////////// <<<<<<<<<<<<<< All logic here
if ("right".equals(direction)) {
x += 10;
if (x >= mx && x <= mx + 9 && y >= my && y <= my + 9) {
addx = (rand.nextInt(10)) * 10;
addy = (rand.nextInt(10)) * 10;
mx = ((rand.nextInt(5) + 1) * 100) + addx;
my = ((rand.nextInt(6) + 1) * 100) + addy;
point += 100;
length++;
bodyX.add(0, x);
}
if (x > 699) {
new GameOver();
dispose();
}
} else if ("left".equals(direction)) {
x -= 10;
if (x >= mx && x <= mx + 9 && y >= my && y <= my + 9) {
addx = (rand.nextInt(10)) * 10;
addy = (rand.nextInt(10)) * 10;
mx = ((rand.nextInt(5) + 1) * 100) + addx;
my = ((rand.nextInt(6) + 1) * 100) + addy;
point += 100;
length++;
bodyX.add(0, x);
}
if (x < 99) {
new GameOver();
dispose();
}
} else if ("up".equals(direction)) {
y -= 10;
if (y >= my && y <= my + 9 && x >= mx && x <= mx + 9) {
addx = (rand.nextInt(10)) * 10;
addy = (rand.nextInt(10)) * 10;
mx = ((rand.nextInt(5) + 1) * 100) + addx;
my = ((rand.nextInt(6) + 1) * 100) + addy;
point += 100;
length++;
bodyY.add(0, y);
}
if (y < 99) {
new GameOver();
dispose();
}
} else if ("down".equals(direction)) {
y += 10;
if (y >= my && y <= my + 9 && x >= mx && x <= mx + 9) {
addx = (rand.nextInt(10)) * 10;
addy = (rand.nextInt(10)) * 10;
mx = ((rand.nextInt(5) + 1) * 100) + addx;
my = ((rand.nextInt(6) + 1) * 100) + addy;
point += 100;
length++;
bodyY.add(0, y);
}
if (y > 799) {
new GameOver();
dispose();
}
}
drawPanel.repaint();
}
}
private class GameOver extends JFrame implements ActionListener {
JLabel answer = new JLabel("");
JPanel pane = new JPanel(); // create pane object
JButton pressme = new JButton("Quit");
JButton replay = new JButton("Replay?");
GameOver() // the constructor
{
super("Game Over");
timer.stop(); ////////////////////// <<<<<<<<<< Stop TIMER
setBounds(100, 100, 300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container con = this.getContentPane(); // inherit main frame
con.add(pane);
pressme.setMnemonic('Q'); // associate hotkey
pressme.addActionListener(this); // register button listener
replay.addActionListener(this);
pane.add(answer);
pane.add(pressme);
pane.add(replay);
pressme.requestFocus();
answer.setText("You Lose");
setVisible(true); // make frame visible
}// GameOver()
// here is the basic event handler
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source == pressme)
System.exit(0);
if (source == replay) {
dispose();
EventQueue.invokeLater(new Runnable() {
public void run() {
new Snake2();
}
});
}
}// actionPreformed
}// GameOver
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Font ith = new Font("Ithornît", Font.BOLD, 78);
/* Background: Snake */
g.setColor(Color.darkGray);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.gray);
g.fillRect(100, 100, 600, 700);
g.setColor(Color.white);
g.drawRect(99, 99, 601, 701);
g.drawString("Quit", 102, 86);
g.drawRect(100, 70, 30, 20);
g.drawString("Score: ", 602, 86);
g.setFont(ith);
g.drawString("SNAKE", 350, 60);
/* Sprite: Mouse */
g.setColor(Color.black);
g.fillRect(mx, my, width, height);
// System.out.print(mx + " " + my);
/* Sprite: Snake Body */
if (length != 0) {
for (int i = 0; i >= length; i++) {
g.setColor(Color.darkGray);
g.fillRect(bodyX.get(i), bodyY.get(i), width, height);
}
}
/* Sprite: Snake head */
g.setColor(Color.white);
g.fillRect(x, y, width, height);
}// Paint Component
public Dimension getPreferredSize() {
return new Dimension(800, 850);
}// Dimension
}// DrawPanel
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Snake2();
}
});
}// main
}// Snake Class
/* Tracks where mouse is clicked */
class MouseListener extends MouseAdapter {
public void mouseReleased(MouseEvent me) {
if (me.getX() >= 101 && me.getX() <= 131 && me.getY() >= 94
&& me.getY() <= 115) {
System.exit(0);
}
String str = "Mouse Released at " + me.getX() + "," + me.getY();
System.out.println(str);
}
}// MouseAdapter
I am making a tile based platformer game in java. I render a map which is stored in a 2 dimensional array but when this array is very big my game starts to become slow. I realised that I had to only render the part of the map that is viewable, I tried to do that but i wrote very hacky code that only worked partly so I removed it. How can I do this properly? Here is my code (without the hacky stuff). Also how could I use System.nanoTime() rather than System.currentTimeMillis()?
package sexy_robot_from_another_dimension;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Game extends JPanel
{
int playerX = 50;
int playerY = 50;
static boolean up = false;
static boolean down = false;
static boolean right = false;
static boolean left = false;
int playerSpeed = 1;
String[][] map;
int blockSize = 20;
int jumpLoop = 0;
int maxJumpLoop = 280;
static BufferedImage block, player;
int playerWidth = 20;
int playerHeight = 35;
int cameraX = 0;
int cameraY = 0;
long nextSecond = System.currentTimeMillis() + 1000;
int frameInLastSecond = 0;
int framesInCurrentSecond = 0;
public Game()
{
super();
try
{
map = load("/maps/map1.txt");
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
if(up)
{
if((!playerIsOnBlock(playerX, playerY).equals("0")) || (!playerIsOnBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
timeToJump();
}
}
if(down)
{
}
if(right)
{
if((playerIsLeftBlock(playerX, playerY).equals("0")) && (playerIsLeftBlock(playerX, playerY + (playerHeight/2 - 1)).equals("0")) && (playerIsLeftBlock(playerX, playerY + (playerHeight - 1)).equals("0")))
{
playerX += playerSpeed;
}
}
if(left)
{
if((playerIsRightBlock(playerX, playerY).equals("0")) && (playerIsRightBlock(playerX, playerY + (playerHeight/2 - 1)).equals("0")) && (playerIsRightBlock(playerX, playerY + (playerHeight - 1)).equals("0")))
{
playerX -= playerSpeed;
}
}
repaint();
}
};
timer.scheduleAtFixedRate(task, 0, 10);
Timer timerGrav = new Timer();
TimerTask taskGrav = new TimerTask()
{
#Override
public void run()
{
if((playerIsOnBlock(playerX, playerY).equals("0")) && (playerIsOnBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
playerY += playerSpeed;
repaint();
}
}
};
timerGrav.scheduleAtFixedRate(taskGrav, 0, 6);
}
void timeToJump()
{
if(jumpLoop == 0)
{
jumpLoop = 1;
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
if((playerIsBelowBlock(playerX, playerY).equals("0")) && (playerIsBelowBlock(playerX + (playerWidth - 1), playerY).equals("0")))
{
playerY -= playerSpeed;
jumpLoop++;
repaint();
}
else
{
jumpLoop = maxJumpLoop;
}
if(jumpLoop == maxJumpLoop)
{
jumpLoop = 0;
cancel();
}
}
};
timer.scheduleAtFixedRate(task, 0, 3);
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
long currentTime = System.currentTimeMillis();
if (currentTime > nextSecond)
{
nextSecond += 1000;
frameInLastSecond = framesInCurrentSecond;
framesInCurrentSecond = 0;
}
framesInCurrentSecond++;
g.drawString(frameInLastSecond + " fps", 10, 20);
cameraX = -playerX + getWidth()/2;
cameraY = -playerY + getHeight()/2;
g.translate(cameraX, cameraY);
for (int x = 0; x < map.length; x++)
{
for (int y = 0; y < map[0].length; y++)
{
switch(map[x][y])
{
case "0":
break;
case "1":
if(block != null)
{
TexturePaint tp0 = new TexturePaint(block, new Rectangle(0, 0, blockSize, blockSize));
g2.setPaint(tp0);
}
g.fillRect(y*blockSize, x*blockSize, 20, 20);
break;
}
}
}
g.setColor(Color.BLACK);
if(player != null)
{
TexturePaint tp0 = new TexturePaint(player, new Rectangle(playerX, playerY, playerWidth, playerHeight));
g2.setPaint(tp0);
}
g.fillRect(playerX, playerY, playerWidth, playerHeight);
g.setColor(Color.black);
g.setFont(new Font("Droid Sans Mono", Font.PLAIN, 12));
g.drawString("Sexy!", playerX - 5, playerY - 10);
}
boolean outOfMap(int x, int y)
{
y -= blockSize - 1;
x -= blockSize - 1;
if((y/blockSize <= map.length - 2) && (y/blockSize >= 0) && (x/blockSize <= map[0].length-2) && (x/blockSize >= 0))
{
return false;
}
return true;
}
String playerIsOnBlock(int x, int y)
{
y += playerHeight;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsBelowBlock(int x, int y)
{
y -= playerSpeed;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsLeftBlock(int x, int y)
{
x += playerWidth;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String playerIsRightBlock(int x, int y)
{
x -= playerSpeed;
if(!outOfMap(x, y))
{
if(map[y/blockSize][x/blockSize] != "0")
{
return map[y/blockSize][x/blockSize];
}
}
return "0";
}
String[][] load(String file) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
int lines = 1;
int length = br.readLine().split(" ").length;
while (br.readLine() != null) lines++;
br.close();
br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
String[][] map = new String[lines][length];
for (int i = 0; i < lines; i++)
{
String line = br.readLine();
String[] parts = line.split(" ");
for (int y = 0; y < length; y++)
{
map[i][y] = parts[y];
}
}
br.close();
return map;
}
}
Thank you!
It seems your camera is centered on the player, then there are two ways of doing this, I like the first way, it is a bit cleaner:
1th: Create a rectangle that bounds your cameras view, and check if the map x,y is within this view, render only if true.
Rectangle cameraView = new Rectangle(playerX - getWidth() / 2, playerY - getHeight() / 2, getWidth(), getHeight());
for (int x = 0; x < map.length; x++) {
for (int y = 0; y < map[0].length; y++) {
if (!cameraView.contains(x*blockSize, y*blockSize))
continue;
switch (map[x][y]) {
case "0":
break;
case "1":
if (block != null) {
TexturePaint tp0 = new TexturePaint(block, new Rectangle(0, 0, blockSize, blockSize));
g2.setPaint(tp0);
}
g.fillRect(y * blockSize, x * blockSize, 20, 20);
break;
}
}
}
The second option is to simply calculate the distance to the center of the screen (playerX,playerY) from each map[x][y] and skip all map[x][y] that falls outside your viewing bounds, this is a bit uglier to code and I really don't recommend this, the rectangle option above should be fast enough.
Edit:
#JasonC That is true, I didn't consider for instance when an x value is definitely outside the view, it will still go into the y loop through all the y values. One can simply create a dummy variable in the x-loop and do the following check
for (int x = 0; x < map.length; x++) {
int dummyY = playerY
if(!cameraView.contains(x,dummyY))
continue;
....
//rest of code ommitted
Another optimization you can do is considering not setting a TexturePaint (expensive operation) but instead simply drawing the image of the block:
g.fillRect(y * blockSize, x * blockSize, 20, 20);
Replaced with
g.drawImage(block, y*blockSize, x*blockSize, null);
The same with the playerimage.
Set the clipping region to the visible area with Graphics.setClip(), that will prevent most rendering operations from taking effect outside that region.
For drawing operations where this isn't sufficient (perhaps you also want to avoid doing calculations or something for objects outside the clipping area), test your objects bounds against the clipping rectangle and skip the object if it doesn't intersect.
See Graphics.setClip().
A further optimization can be done by, for example, calculating the range of blocks on your map that is definitely outside of the visible area, and excluding that from your for loop (no sense testing blocks against the clipping region if you know they are outside already). Take the clipping region, transform it to map index coordinates, then you will know where in your map the visible area is and you can just iterate over that subsection of the map.