Java, Imported Images flickering even after double buffer (JFrame) - java

After using a double buffer which I will show below after using imported images they still occasionally flicker. The flickering occurs only in the animated GIF sprites and not in solid tiles such as the ground of the program. I thought it may be the fact that when changing image from a standing position to the moving position of the player there is a moment with no image so it disappears but that cannot be right since the player flickers even when not changing image and just moving.
Base code of the program (compressed a lot of it and removed a bunch for simplicity of reading) :
import javax.swing.*;
import java.awt.*;
public class FrontHand extends JFrame implements Runnable{
public static void main(String []args){
// Start Main Thread
FrontHand Thread1 = new FrontHand();
Thread1.run();
}
// Define Frame Properties
private FrontHand(){
ImageImport.main();
setSize(1600, 900);
setResizable(false);
setFocusable(true);
setVisible(true);
requestFocus();
addKeyListener(new AL());
addMouseListener(new ML());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
// Frame Double Buffer
public void paint(Graphics g){
Image dbImage = createImage(getWidth(), getHeight());
Graphics dbg = dbImage.getGraphics();
paintComponents(dbg);
g.drawImage(dbImage, 0, 0, this);
}
// Call paint functions
public void paintComponents(Graphics g){
// draw GUI
InVar.GUI.paintIcon(this, g, 0, 0);
// draw Ground Tiles
for (int i = 0; i != 10; i++) {
for (int n = 0; n != 22; n++) {
InVar.grass_tile.paintIcon(this, g, n * 62 + 118, 106 + i * 62);
// g.drawRect(n * 62 + 118, 106 + i * 62, 62, 62);
}
}
// Draw Player
InVar.player.paintIcon(this, g, InVar.player_x, InVar.player_y);
repaint();
}
// player movement
private void move(){PRETEBD THERE IS ADDITION AND SUBTRACTION TO THE PLAYER X AND Y HERE}
// check if there is any tiles left to move too and if null then redefine as non moving
if (InVar.p_m_x_counter == 0 && InVar.p_m_y_counter == 0){
InVar.player_moving = false;
// change the sprite of the player to the corresponding stationary one to the current player's sprite
if (InVar.player == InVar.player_m_up){
InVar.player = InVar.player_s_up;
}
else if (InVar.player == InVar.player_m_down){
InVar.player = InVar.player_s_down;
}
else if (InVar.player == InVar.player_m_left){
InVar.player = InVar.player_s_left;
}
else if (InVar.player == InVar.player_m_right){
InVar.player = InVar.player_s_right;
}
}
else{
InVar.player_moving = true;
}
}
#Override
public void run(){
while (InVar.game_running){
try{
Thread.sleep(5);
move();
}catch (InterruptedException e){System.out.println("Interrupted Exception Caught, ignore");}
}
}
}
The code that is used to import images :
ImageIcon a_1 = new ImageIcon(FrontHand.class.getResource("/Sprites/Tiles/GrassTile1.jpg"));
a_1.setImage(a_1.getImage());
InVar.grass_tile = a_1;
Before I was using .getScaledInstance for the animated GIF's which is what I also thought was causing the flickering but not using it and just externally changing the size of them did not fix the issue of the flickering.

Related

Only able to add JLabel background or JPanel background

I am making my first game in Java. This is the first time using swing too. I am having difficulties adding objects correctly to the frame.
Some irrelevant code has been removed from other classes. There may be several components missing that are referenced.
Main:
public class Main {
Ball ball = new Ball();
int yBall = 0;
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
main.run();
while (true) {
main.ball(100);
}
}
public void run() throws InterruptedException {
Data data = new Data();
Frame frame = new Frame(700, 500);
test.setAngleX();
frame.add(data, BorderLayout.CENTER);
}
}
When I add ball after data(background) my ball is visible on the frame, overlapping my data. If I would switch position here my data would be visible but not my ball, the ball still exists underneath.
frame.add(ball);
frame.setVisible(true);
public void ball(int yBall) throws InterruptedException {
ball.Ball();
yBall = ball.SendY();
}
This is my frame, no problems in here what I know:
public class Frame extends JFrame {
private int frameWidth;
private int frameHeight;
int yBall = 0;
public Frame(int frameWidth, int frameHeight) {
this.frameWidth=frameWidth;
this.frameHeight = frameHeight;
setSize(new Dimension(frameWidth, frameHeight));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Here's my Ball. paintComponent is at the end, before that it is just working logics that I'm guessing will be irrelevant.
Ball:
public class Ball extends JLabel{
Graphics g1;
int x = 325, y = 225;
int speed = 1;
int angleX = 0;
int angleY = 0;
public Ball() {
}
public void setAngleX() {
angleX = ThreadLocalRandom.current().nextInt(-5, 5 + 1);
angleY = ThreadLocalRandom.current().nextInt(-1, 1 + 1);
if (angleX == 0) {
setAngleX();
}
if (angleY == 0) {
setAngleX();
}
}
public void move() {
if (x + angleX < 0) {
angleX = speed;
} else if (x + angleX > getWidth() - 25) {
angleX = -speed;
} else if (y + angleY < 0) {
angleY = speed;
} else if (y + angleY > getHeight() - 25) {
angleY = -speed;
}
x = x + angleX;
y = y + angleY;
}
public void Ball() throws InterruptedException {
move();
repaint();
int sleepSpeed = angleX * speed;
if (sleepSpeed < 0) {
sleepSpeed = -sleepSpeed;
Thread.sleep(10*sleepSpeed);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillOval(x, y, 25, 25);
}
public int SendY() {
return y;
}
And finally my Data;
public class Data extends JPanel{
private static final long serialVersionUID = 1L;
private Graphics2D g2;
public Data() {
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, getSize().width, getSize().height);
g2.setColor(Color.lightGray);
g2.fillRect(350, 0, 10, 400);
}
So what happens is that I either get my data(background) on my frame or I get my Ball bouncing around as it should but on an empty background, depending on which one I add last in my main. I'm guessing it's bcz of overlapping but I can't find a solution working for my code, no matter how much I search..
that I either get my data(background) on my frame or I get my Ball bouncing around as it should but on an empty background, depending on which one I add last in my main.
You can't add both components to the frame. you need to have a parent/child relationship.
So the logic should be something like:
background.add( ball );
frame.add( background );
Then the frame will be painted, then the background and finally the ball.
Also, the Ball class will need to implement the getPreferredSize() method so component has a reasonable size. You should be painting the Ball at offset (0, 0) in the paint component method. When you want to move the ball on the background panel, then you just use setLocation(...) on the ball to reposition it on the background. When you create the Ball you would also need to set the size of the Ball to the preferred size so the Ball has a size/location to be painted on the background panel. The layout of the background panel would need to be set to null, so you can freely move the ball around the panel.
You should NOT be extending JLabel, you are not using any features of the JLabel. You are doing custom painting so just extend JPanel or JComponent. Read the section from the Swing tutorial on Custom Painting for more information and working examples.
Also, method names should NOT start with an upper case character. Ball() is not a proper method name. It looks like the class name. The method should be more descriptive.
Or the other approach, instead of having two components, is to have a single component. Then the paintComopnent(...) method of that component would paint both:
the background
the ball
Then this way you would paint the Ball relative to the background panel which is how your logic currently works.

Java - Pac-Man - GUI - Drawing Graphics issue, and general tips for an aspiring programmer

I am making Pac-Man and I'm having trouble with drawing graphics on a frame, when i draw my point image it looks like a game of snake, i tried putting my drawing methods for background and char both in the render method, but than my point image flickers
What it currently looks like, feel free to ignore the random face it was an inside joke.
Also this is my very first game so any tips on structure, pointers on what I am doing right (if anything) and what I'm doing wrong, and general tips would be extremely helpful!
Also I am aware that i have a couple unused methods
Code:
package game;
import graphics.map;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
public class main extends Canvas implements Runnable{
private static final long serialVersionUID = 1L; //not sure why it wanted me to do this, maybe ask bender, or just google it later
public static boolean running = false;
public static int HEIGHT = 800;
public static int WIDTH = 600;
public static int posX = 50;
public static int posY = 50;
public static final String name = "Pac Man Alpha 1.4";
private static final double speed = 1.2;
public input input;
static BufferedImage background = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
static BufferedImage pacman = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
static BufferedImage settingsBackground = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
static BufferedImage level1 = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
static BufferedImage level2 = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
static BufferedImage points = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
static BufferedImage point = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);;
static JFrame frame;
private input keypress = new input();
private map map;
private static boolean charLoaded = false;
public static boolean MAIN_MENU = true;
public static boolean GAME = false;
public static boolean level1test = true;
public static boolean level2test = false;
public static boolean level3test = false;
public static boolean level4test = false;
static boolean drawn = false;
public static boolean key_down;
public static boolean key_up;
public static boolean key_right;
public static boolean key_left;
//private Screen screen;
JButton startButton = new JButton("Start"); //Start
JButton settingsButton = new JButton("Settings"); //Settings
JButton exitButton = new JButton("Exit"); //Exit
public main()
{
setMinimumSize(new Dimension(WIDTH , HEIGHT ));
setMaximumSize(new Dimension(WIDTH , HEIGHT )); // keeps the canvas same size
setPreferredSize(new Dimension(WIDTH, HEIGHT));
frame = new JFrame(name);
if(MAIN_MENU == true && GAME == false){
buttons(frame.getContentPane());
}
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // ends program on
// close
frame.addKeyListener(new input() );
frame.add(this, BorderLayout.CENTER);
frame.pack(); // keeps size correct
frame.setResizable(false);
frame.setVisible(true);
this.addKeyListener(keypress);
}
public static void main(String[] args)
{
try {
background = ImageIO.read(new File("res\\Background.png"));
pacman = ImageIO.read(new File("res\\pacmansprites.png"));
settingsBackground = ImageIO.read(new File("res\\Background.png"));
level1 = ImageIO.read(new File("res\\level1.png"));
//level2 = ImageIO.read(new File("res\\level2.png"));
point = ImageIO.read(new File("res\\Points for pacman.png"));
} catch (IOException e) {
}
running = true;
new main().start();
}
public void run()
{
long lastTime = System.nanoTime();
double nsPerTick = 1000000000 / 60D;
long lastTimer = System.currentTimeMillis();
double delta = 0;
int frames = 0;
int ticks = 0;
while (running == true) {
long now = System.nanoTime();
delta += (now - lastTime) / nsPerTick;
lastTime = now;
boolean render = false;
while (delta >= 1) {
ticks++;
tick();
delta -= 1;
render = true;
}
try {
Thread.sleep(3); //keep the Frames from going to high
} catch (InterruptedException e) {
e.printStackTrace();
}
if(render == true){
frames++;
render();
}
if (System.currentTimeMillis() - lastTimer >= 1000) {
lastTimer +=1000;
//System.out.println("Frames: " + frames + " Ticks: " + ticks);
frames = 0;
ticks = 0;
}
}
}
public synchronized void start()
{
new Thread(this).start();
run();
}
public synchronized void stop()
{
running = false;
}
public void tick()
{
if (key_up) posY -= speed / 2;
if (key_down) posY += speed;
if (key_left) posX -= speed / 2;
if (key_right) posX += speed;
}
public void render()
{
drawn = false;
if(MAIN_MENU == false && GAME == true)
{
drawMap();
drawChar();
}
else if(MAIN_MENU == false && GAME == false) {
Graphics g = getGraphics();
{
g.drawImage(settingsBackground,0,0,getWidth(),getHeight(),null);
g.dispose();
}
} else {
Graphics g = getGraphics();{
g.drawImage(background,0,0,getWidth(), getHeight(),null);
g.dispose(); //kill it
}
}
}
public void drawMap(){
if(level1test == true){
Graphics g = getGraphics();
{
g.drawImage(level1,0,0,getWidth(),getHeight(),null);
g.dispose();
}
}
if(level2test == true && drawn == false){
Graphics g = getGraphics();
{
g.drawImage(level2,0,0,getWidth(),getHeight(),null);
}
g.dispose();
}
drawn = true;
}
public void drawChar(){
//drawMap();
Graphics g = getGraphics();{
g.drawImage(point,posX,posY,20, 20,null);
g.dispose();
revalidate();
}
}
public void begin() {
if (key_up) System.out.println("up");
if (key_down) System.out.println("down");
if (key_left) System.out.println("left");
if (key_right) System.out.println("right");
}
public void loadMap(){
if(!drawn && level1test){
}else if(!drawn && level2test){
//draw 2nd map here
}else if(!drawn && level3test){
//draw 3rd map here
}
}
public void buttons(Container pane)
{
pane.setLayout(null);
startButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
MAIN_MENU = false;
GAME = true;
frame.remove(startButton);
frame.remove(settingsButton);
frame.remove(exitButton);
frame.revalidate();
drawMap();
System.out.println("Start Button Clicked");
}
} );
settingsButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
MAIN_MENU = false;
GAME = false;
frame.remove(startButton);
frame.remove(settingsButton);
frame.remove(exitButton);
frame.revalidate();
frame.repaint();
System.out.println("Settings Button Clicked");
}
} );
exitButton.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.out.println("Exit Button Clicked");
System.exit(0);
}
} );
pane.add(startButton);
pane.add(settingsButton);
pane.add(exitButton);
Insets insets = pane.getInsets();
Dimension size = startButton.getPreferredSize();
startButton.setBackground(new Color(0, 0, 0));
startButton.setForeground(Color.CYAN);
startButton.setFocusPainted(false);
startButton.setFont(new Font("Calabri", Font.BOLD, 16));
settingsButton.setBackground(new Color(0, 0, 0));
settingsButton.setForeground(Color.RED);
settingsButton.setFocusPainted(false);
settingsButton.setFont(new Font("Calabri", Font.BOLD, 16));
exitButton.setBackground(new Color(0, 0, 0));
exitButton.setForeground(Color.YELLOW);
exitButton.setFocusPainted(false);
exitButton.setFont(new Font("Calabri", Font.BOLD, 16));
startButton.setBounds((WIDTH - 125) + insets.left, 10 + insets.top,
size.width + 50, size.height + 10);
settingsButton.setBounds((WIDTH - 125) + insets.left, 55 + insets.top,
size.width + 50, size.height + 10);
exitButton.setBounds((WIDTH - 125) + insets.left, 100 + insets.top,
size.width + 50, size.height + 10);
}
}
getGraphics is not how custom painting is done. You should, in your case, override the paint method, and make sure you call super.paint before doing any custom painting.
getGraphics returns the Graphics context last used to paint the component, which could be discarded on the next paint cycle, be null or no longer used by the component
Remember, painting uses the "painters canvas" approach, that is, just like painting in a physical canvas, when you paint into it, you paint over what was previously there, but not erasing it.
Now, if you override paint, you will find that you will have a flickering problem. This is because Canvas
is not double buffered
To solve this, you should consider user a BufferStrategy, which allows you to not only generate multiple buffers to paint to, but also to take control of the paint process itself
Just don't forget to clear each buffer before you paint to it...
Double buffering is the trick that allows you to have flicker-free animation. Basically you have two representations of your canvas, one that's currently being displayed and one that you can draw on. If you're finished with drawing, you copy the draw-canvas over the display-canvas. Depending on system and hardware there are more elegant ways where you can just tell the hardware to switch canvases (page flipping).
Without double buffering or a similar techniques, it is almost impossible to have flicker-free animation.
With double buffering you can afford to draw the background and then the foreground sprites. It is possibly more efficient to draw only those parts of the background that have been destroyed by the foreground sprites (there are various techniques for that as well, including of taking a snapshot image of the affected areas before you paint the sprites).
You can find a simple example for Java double buffering here. Java's BufferStrategy is a more complex solution that can use hardware features for page flipping.
I think the problem is that you only draw onto the image background, never erasing the old drawing from your image. You will need to clear the area and then start drawing in order to get your desired results.
I have never attempted to make a game but when I do simple animations I usually will do them on a JFrame or JPanel. With a JFrame you can Override the paint() method and with a JPanel, the paintComponent() method. It helps to keep everything that I'm drawing centralized, which makes it much easier for me to modify my code. When you call the respective super method in your overridden method, it will start you off with a clean slate, meaning you will have to paint the (image) background and your characters all over again. Calling the super method is also necessary to paint that component's children if you decide to add anything onto the JFrame/JPanel.
If you chose to use one of the above then I would recommend a JPanel due to it offering double buffering, which will help make your animations look smooth/fluid. Also, do not forget to call repaint();.
Here is a quick example, which can be used to duplicate your issue if you comment out super.paintComponent(g);.
*Note that I am extending and using a JPanel for this example.
Code:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Trial extends JPanel{
public static void main(String[] args)
{
new Trial();
}
int x = 5; // will represent the x axis position for our crude animation.
javax.swing.Timer timer = new javax.swing.Timer( 500, new ActionListener(){
// Timer used to control the animation and
// the listener is used to update x position and tell it to paint every .5 seconds.
#Override
public void actionPerformed(ActionEvent e) {
x += 5;
if ( x > 250)
timer.stop();
repaint(); // will call the paintComponent method.
}
});
Trial()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(this);
frame.setSize(300, 200);
frame.setVisible(true);
timer.start();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g); // calling the super paintComponent method to paint children. can comment it
// out to duplicate your error, which happens when the frame isn't "refreshed".
Graphics2D g2d = (Graphics2D) g.create(); // creating a copy of the graphics object. // I do this to not alter the original
// Good practice and also Graphics2D
// offers a bit more to work with.
g2d.drawString("Graphics", x, 60); // painting a string.
g2d.drawRect(x, 80, 10, 10); // painting a rectangle.
}
}
Edit:
If you have a bunch of stuff to do and don't want to add it all into your paintComponent(); method you could create a method for various things and call them from inside your Overriden paint method, you could also pass them your graphics object. It will help you keep things relatively simple.

Why doesn't my code go in the keyPressed() method? (Even if i press keys)

I'm trying to create a brickbreakers game. Obviously i'm still struggling with the beginning of it. I need my "block" to move but if i press some key my code won't even go in the keyPressed()-method. Does anybody know why?
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class VGKernel extends JPanel implements KeyListener {
public Rectangle screen, ball, block; // The screen area and ball location/size.
public Rectangle bounds; // The boundaries of the drawing area.
public JFrame frame; // A JFrame to put the graphics into.
public VGTimerTask vgTask; // The TimerTask that runs the game.
public boolean down, right; // Direction of ball's travel.
public VGKernel(){
super();
screen = new Rectangle(0, 0, 600, 400);
ball = new Rectangle(0, 0, 20, 20);
block = new Rectangle(0, 350, 40, 10);
bounds = new Rectangle(0, 0, 600, 400); // Give some starter values.
frame = new JFrame("VGKernel");
vgTask = new VGTimerTask();
addKeyListener(this);
}
class VGTimerTask extends TimerTask{
public void run(){
moveBall();
frame.repaint();
}
}
// Now the instance methods:
public void paintComponent(Graphics g){
// Get the drawing area bounds for game logic.
bounds = g.getClipBounds();
// Clear the drawing area, then draw the ball.
g.clearRect(screen.x, screen.y, screen.width, screen.height);
g.fillRect(ball.x, ball.y, ball.width, ball.height);
g.fillRect(block.x, block.y, block.width, block.height);
}
public void moveBall(){
// Ball should really be its own class with this as a method.
if (right) ball.x+=1; // If right is true, move ball right,
else ball.x-=1; // otherwise move left.
if (down) ball.y+=1; // Same for up/down.
else ball.y-=1;
if (ball.x > (bounds.width - ball.width)) // Detect edges and bounce.
{ right = false; ball.x = bounds.width - ball.width; }
if (ball.y > (bounds.height - ball.height))
{ down = false; ball.y = bounds.height - ball.height;}
if (ball.x <= 0) { right = true; ball.x = 0; }
if (ball.y <= 0) { down = true; ball.y = 0; }
}
public void keyPressed(KeyEvent evt) {
if(evt.getKeyCode() == KeyEvent.VK_G && block.x > 0) {
block.x -= 10;
}
if(evt.getKeyCode() == KeyEvent.VK_H && block.x < 560) {
block.x += 10;
}
}
public void keyTyped(KeyEvent evt){ }
public void keyReleased(KeyEvent evt){ }
public static void main(String arg[]){
java.util.Timer vgTimer = new java.util.Timer(); // Create a Timer.
VGKernel panel = new VGKernel(); // Create and instance of our kernel.
// Set the intial ball movement direction.
panel.down = true;
panel.right = true;
// Set up our JFRame
panel.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.frame.setSize(panel.screen.width, panel.screen.height);
panel.frame.setContentPane(panel);
panel.frame.setVisible(true);
// Set up a timer to do the vgTask regularly.
vgTimer.schedule(panel.vgTask, 0, 10);
}
}
Put
setFocusable(true);
inside your constructor.

Repaint leaves trail

I know that this isn't the first time that this question has been asked, but the responses haven't helped me much, so I am helping I am finally going to get my answer
I have made this little game where I have made a car drive around a track (using rectangles was mandatory). When I use the repaint() metod the rectangle representing the car repaints in the new location, but leaves a trail behind.
I have this code:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class MAP extends JFrame
{
//constant for the screen size and used for the drawing the terrain
final int WIDTH = 900, HEIGHT = 650;
//the URL and Img designed for the images
URL cLeft,cRight,cUp;
Image img1,img2,img3;
//these will keep track of each player’s speed:
double p1Speed =.5;
//these are ints that represent directions:
final int UP = 0, RIGHT = 1, DOWN = 2, LEFT = 3;
//these will keep track of the player’s directions (default = up)
int p1Direction = 0;
JPanel panel;
//draw the terrain
Rectangle left = new Rectangle(0,0,WIDTH/9,HEIGHT);
Rectangle right = new Rectangle((WIDTH/9)*9,0,WIDTH/9,HEIGHT);
Rectangle top = new Rectangle(0,0,WIDTH, HEIGHT/9);
Rectangle bottom = new Rectangle(0,(HEIGHT/9)*9,WIDTH,HEIGHT/9);
Rectangle center = new Rectangle((int)((WIDTH/9)*2.5),(int)((HEIGHT/9)*2.5),(int)((WIDTH/9)*5),(HEIGHT/9)*4);
//these obstacles will obstruct the path and make navigating harder
Rectangle obstacle = new
Rectangle(WIDTH/2,(int)((HEIGHT/9)*7),WIDTH/10,HEIGHT/9);
Rectangle obstacle2 = new
Rectangle(WIDTH/3,(int)((HEIGHT/9)*5),WIDTH/10,HEIGHT/4);
Rectangle obstacle3 = new
Rectangle(2*(WIDTH/3),(int)((HEIGHT/9)*5),WIDTH/10,HEIGHT/4);
Rectangle obstacle4 = new Rectangle(WIDTH/3,HEIGHT/9,WIDTH/30,HEIGHT/9);
Rectangle obstacle5 = new Rectangle(WIDTH/2,(int)((HEIGHT/9)*1.5),WIDTH/30,HEIGHT/4);
Rectangle finish = new Rectangle(WIDTH/9,(HEIGHT/2)-HEIGHT/9,(int)((WIDTH/9)*1.5),HEIGHT/70);
Rectangle lineO=new Rectangle(WIDTH/9,HEIGHT/2,(int)((WIDTH/9)*1.5)/2,HEIGHT/140);
Rectangle lineI = new Rectangle(((WIDTH/9)+((int)((WIDTH/9)*1.5)/2)),(HEIGHT/2)+(HEIGHT/10),(int)((WIDTH/9)*1.5)/2, HEIGHT/140);
//this is the rectangle for player 1’s (outer) car:
Rectangle p1 = new Rectangle(WIDTH/9,HEIGHT/2, WIDTH/30,WIDTH/30);
public MAP(){
super("Radical Racing");
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setResizable(false);
setVisible(true);
setLocationRelativeTo(null);
//the following code creates the JFrame
try
{
cUp = this.getClass().getResource("carUp.jpg");
cLeft = this.getClass().getResource("carLeft.jpg");
cRight = this.getClass().getResource("carRight.jpg");
}catch(Exception e)
{}
//attach the URLs to the images
img1 = Toolkit.getDefaultToolkit().getImage(cUp);
img2 = Toolkit.getDefaultToolkit().getImage(cLeft);
img3 = Toolkit.getDefaultToolkit().getImage(cRight);
panel=new JPanel(){
public void paint(Graphics g)
{
//super.paint(g);
//draw the background for the racetrack
g.setColor(Color.DARK_GRAY);
g.fillRect(0,0,WIDTH,HEIGHT);
//when we draw, the border will be green
g.setColor(Color.GREEN);
//fill rectangle
g.fillRect(left.x,left.y,left.width,left.height);
g.fillRect(right.x,right.y,right.width,right.height);
g.fillRect(top.x,top.y,top.width,top.height);
g.fillRect(bottom.x,bottom.y,bottom.width,bottom.height);
g.fillRect(center.x,center.y,center.width,center.height);
g.fillRect(obstacle.x,obstacle.y,obstacle.width,obstacle.height);
g.fillRect(obstacle2.x,obstacle2.y,obstacle2.width,obstacle2.height);
g.fillRect(obstacle3.x,obstacle3.y,obstacle3.width,obstacle3.height);
g.fillRect(obstacle4.x,obstacle4.y,obstacle3.width,obstacle4.height);
g.fillRect(obstacle5.x,obstacle5.y,obstacle5.width,obstacle5.height);
//set the starting line color to white
g.setColor(Color.WHITE);
//now draw the starting line
g.fillRect(lineO.x,lineO.y,lineO.width,lineO.height);
g.fillRect(lineI.x,lineI.y,lineI.width,lineI.height);
//set the color of the finish line to yellow
g.setColor(Color.YELLOW);
//now draw the finish line
g.fillRect(finish.x,finish.y,finish.width,finish.height);
//set the color to blue for p1
g.setColor(Color.WHITE);
//now draw the actual player
g.fill3DRect(p1.x,p1.y,p1.width,p1.height,true);
//draw the images for the player
if(p1Direction==UP)
g.drawImage(img1,p1.x,p1.y,this);
if(p1Direction==LEFT)
g.drawImage(img2,p1.x,p1.y,this);
if(p1Direction==RIGHT)
g.drawImage(img3,p1.x,p1.y,this);
}
};
panel.setPreferredSize(new Dimension(950,600));
this.add(panel);
pack();
Move1 move=new Move1();
move.start();
}
private class Move1 extends Thread implements KeyListener
{
public void run()
{
//now, this should all be in an infinite loop, so the process
//repeats
addKeyListener(this);
while(true)
{
//now, put the code in a try block. This will let the
//program exit
//if there is an error.
try
{
//increase speed a bit
// if(p1Speed<=5)
// p1Speed+=.2;
//p1.y-=(int) p1Speed;
if(p1.intersects(left) || p1.intersects(right) || p1.intersects(top) ||
p1.intersects(bottom) || p1.intersects(obstacle) || p1.intersects(obstacle2)||
p1.intersects(obstacle3) || p1.intersects(obstacle4) || p1.intersects(obstacle5))
{
p1Speed = -5;
}
//if the car hits the center, do the same as above
//but make the speed -2.5.
if(p1.intersects(center))
{
p1Speed = -2.5;
}
//increase speed a bit
if(p1Speed<=5)
p1Speed+=.2;
//these will move the player based on direction
if(p1Direction==UP)
{
p1.y-=(int)p1Speed;
}
if(p1Direction==DOWN)
{
p1.y+=(int)p1Speed;
}
if(p1Direction==LEFT)
{
p1.x-=(int)p1Speed;
}
if(p1Direction==RIGHT)
{
p1.x+=(int)p1Speed;
}
panel.repaint();
//this delays the refresh rate:
Thread.sleep(200);
}
catch(Exception e)
{
//if there is an exception (an error), exit the loop.
break;
}
}
}
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent event) {
if(event.getKeyChar()=='a')
{
p1Direction = LEFT;
}
if(event.getKeyChar()=='s')
{
p1Direction = DOWN;
}
if(event.getKeyChar()=='d')
{
p1Direction = RIGHT;
}
if(event.getKeyChar()=='w')
{
p1Direction = UP;
}
}
}
//this starts the program by calling the constructor:
public static void main (String[ ] args)
{
new MAP();
}
}
Uncomment super.paint(g) [line 87] in your paint method.
It is responsible for clearing the canvas of any stale objects.
The reason is that in the line g.fillRect(0,0,WIDTH,HEIGHT); it actually is referring to java.awt.image.ImageObserver#WIDTH and java.awt.image.ImageObserver#HEIGHT instead of your instance variables. You can see this with any IDE.
Because of that the expected black background is painted only in a 1x2 pixel area in the top-left corner. And since the call to super.paint(g); is commented out, not even the gray background is repainted. As a result, old drawings of the car are not overdrawn.
The code needs to be changed to use MAP.this.WIDTH, or the WIDTH field needs to be renamed to something which does not conflict with the fields inherited from JPanel, or the field needs to be moved into the same JPanel to have higher precedence, or you could also use the getWidth() method from JPanel. And likewise for height.

My JFrame Flickers

I'm trying to make a simple java game -- driving a car through a track. I am conditioned by my teacher to use rectangles and stuff like that . My problem is when I use the method repaint() that my frame flickers. I have tried to put the repaint method just when the car changes direction, but it doesn't do any good. Any suggestions?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class MAP extends JFrame
{
//the URL and Img designed for the images
URL cLeft,cRight,cUp;
Image img1,img2,img3;
//these will keep track of each player’s speed:
double p1Speed =.5, p2Speed =.5;
//constant for the screen size and used for the drawing the terrain
final int WIDTH = 900, HEIGHT = 650;
//these are ints that represent directions:
final int UP = 0, RIGHT = 1, DOWN = 2, LEFT = 3;
//these will keep track of the player’s directions (default = up)
int p1Direction = 0;
//this is the rectangle for player 1’s (outer) car:
Rectangle p1 = new Rectangle(WIDTH/9,HEIGHT/2, WIDTH/30,WIDTH/30);
//draw the terrain
Rectangle left = new Rectangle(0,0,WIDTH/9,HEIGHT);
Rectangle right = new Rectangle((WIDTH/9)*9,0,WIDTH/9,HEIGHT);
Rectangle top = new Rectangle(0,0,WIDTH, HEIGHT/9);
Rectangle bottom = new Rectangle(0,(HEIGHT/9)*9,WIDTH,HEIGHT/9);
Rectangle center = new Rectangle((int)((WIDTH/9)*2.5),(int)((HEIGHT/9)*2.5), (int)((WIDTH/9)*5),(HEIGHT/9)*4);
//these obstacles will obstruct the path and make navigating harder
Rectangle obstacle = new
Rectangle(WIDTH/2,(int)((HEIGHT/9)*7),WIDTH/10,HEIGHT/9);
Rectangle obstacle2 = new
Rectangle(WIDTH/3,(int)((HEIGHT/9)*5),WIDTH/10,HEIGHT/4);
Rectangle obstacle3 = new
Rectangle(2*(WIDTH/3),(int)((HEIGHT/9)*5),WIDTH/10,HEIGHT/4);
Rectangle obstacle4 = new Rectangle(WIDTH/3,HEIGHT/9,WIDTH/30,HEIGHT/9);
Rectangle obstacle5 = new Rectangle(WIDTH/2,(int)((HEIGHT/9)*1.5),WIDTH/30,HEIGHT/4);
Rectangle finish = new Rectangle(WIDTH/9,(HEIGHT/2)-HEIGHT/9,(int)((WIDTH/9)*1.5),HEIGHT/70);
Rectangle lineO=new Rectangle(WIDTH/9,HEIGHT/2,(int)((WIDTH/9)*1.5)/2,HEIGHT/140);
Rectangle lineI = new Rectangle(((WIDTH/9)+((int)((WIDTH/9)*1.5)/2)),(HEIGHT/2)+(HEIGHT/10),(int)((WIDTH/9)*1.5)/2, HEIGHT/140);
//the constructor:
public MAP() {
//the following code creates the JFrame
super("Radical Racing");
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setResizable(false);
try {
cUp = this.getClass().getResource("carUp.jpg");
cLeft = this.getClass().getResource("carLeft.jpg");
cRight = this.getClass().getResource("carRight.jpg");
} catch(Exception e) {
}
//attach the URLs to the images
img1 = Toolkit.getDefaultToolkit().getImage(cUp);
img2 = Toolkit.getDefaultToolkit().getImage(cLeft);
img3 = Toolkit.getDefaultToolkit().getImage(cRight);
repaint();
Move1 m1 = new Move1();
m1.start();
}
//this will draw the cars and the race track
public void paint(Graphics g) {
super.paint(g);
//draw the background for the racetrack
g.setColor(Color.DARK_GRAY);
g.fillRect(0,0,WIDTH,HEIGHT);
//when we draw, the border will be green
g.setColor(Color.GREEN);
//fill rectangle
g.fillRect(left.x,left.y,left.width,left.height);
g.fillRect(right.x,right.y,right.width,right.height);
g.fillRect(top.x,top.y,top.width,top.height);
g.fillRect(bottom.x,bottom.y,bottom.width,bottom.height);
g.fillRect(center.x,center.y,center.width,center.height);
g.fillRect(obstacle.x,obstacle.y,obstacle.width,obstacle.height);
g.fillRect(obstacle2.x,obstacle2.y,obstacle2.width,obstacle2.height);
g.fillRect(obstacle3.x,obstacle3.y,obstacle3.width,obstacle3.height);
g.fillRect(obstacle4.x,obstacle4.y,obstacle3.width,obstacle4.height);
g.fillRect(obstacle5.x,obstacle5.y,obstacle5.width,obstacle5.height);
//set the starting line color to white
g.setColor(Color.WHITE);
//now draw the starting line
g.fillRect(lineO.x,lineO.y,lineO.width,lineO.height);
g.fillRect(lineI.x,lineI.y,lineI.width,lineI.height);
//set the color of the finish line to yellow
g.setColor(Color.YELLOW);
//now draw the finish line
g.fillRect(finish.x,finish.y,finish.width,finish.height);
//set the color to blue for p1
g.setColor(Color.WHITE);
//now draw the actual player
g.fill3DRect(p1.x,p1.y,p1.width,p1.height,true);
//draw the images for the player
if(p1Direction==UP)
g.drawImage(img1,p1.x,p1.y,this);
if(p1Direction==LEFT)
g.drawImage(img2,p1.x,p1.y,this);
if(p1Direction==RIGHT)
g.drawImage(img3,p1.x,p1.y,this);
}
private class Move1 extends Thread implements KeyListener {
public void run() {
//now, this should all be in an infinite loop, so the process
//repeats
addKeyListener(this);
while(true) {
//now, put the code in a try block. This will let the
//program exit
//if there is an error.
try {
//first, refresh the screen:
repaint();
//increase speed a bit
// if(p1Speed<=5)
// p1Speed+=.2;
//p1.y-=(int) p1Speed;
if(p1.intersects(left) || p1.intersects(right) || p1.intersects(top) || p1.intersects(bottom) || p1.intersects(obstacle) || p1.intersects(obstacle2)|| p1.intersects(obstacle3) || p1.intersects(obstacle4) || p1.intersects(obstacle5)) {
p1Speed = -5;
}
//if the car hits the center, do the same as above
//but make the speed -2.5.
if(p1.intersects(center)) {
p1Speed = -5;
}
//increase speed a bit
if(p1Speed<=5)
p1Speed+=.2;
//these will move the player based on direction
if(p1Direction==UP) {
p1.y-=(int)p1Speed;
//repaint();
}
if(p1Direction==DOWN) {
p1.y+=(int)p1Speed;
//repaint();
}
if(p1Direction==LEFT) {
p1.x-=(int)p1Speed;
//repaint();
}
if(p1Direction==RIGHT) {
p1.x+=(int)p1Speed;
//repaint();
}
//this delays the refresh rate:
Thread.sleep(200);
} catch(Exception e) {
//if there is an exception (an error), exit the loop.
break;
}
}
}
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent event) {
if(event.getKeyChar()=='a') {
p1Direction = LEFT;
repaint();
}
if(event.getKeyChar()=='s') {
p1Direction = DOWN;
repaint();
}
if(event.getKeyChar()=='d') {
p1Direction = RIGHT;
repaint();
}
if(event.getKeyChar()=='w') {
p1Direction = UP;
repaint();
}
}
}
//this starts the program by calling the constructor:
public static void main (String[ ] args) {
new MAP();
}
}
Don't draw directly in the JFrame in its paint method. Instead draw in a JPanel or JComponent and override its paintComponent method since Swing does double buffering by default, and this will allow you to take advantage of this.
You need to buffer your drawing instead of drawing right on the frame. See this related question and also the API for BufferStrategy.
You can avoid the flickering on your graphical interface by using the Double Buffering Technique. Here is an article that gives one example of double buffering.
This part of the Java Tutorial also gives further advice and examples.

Categories