0
package Game;

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.*;

import Maps.*;

public class Window extends JFrame implements KeyListener
{
    private Insets insets;
    private int currentMapX;
    private int currentMapY;

    public Window()
    {
        super();
        setSize(new Dimension(1920, 1080));
        setLayout(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setUndecorated(true);
        setFocusable(true);
        setContentPane(new Container());
        setBackground(Color.BLACK);
        addKeyListener(this);
    }

    public void startGame()
    {
        insets = getInsets();
        currentMapX = 960 - (Game.level_01.getWidth() / 2); 
        currentMapY = 540 - (Game.level_01.getHeight() / 2);
        Game.level_01.setBounds(currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
        add(Game.level_01);
    }

    private void moveMapRight()
    {
        Game.level_01.setBounds(++currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
    }

    private void moveMapLeft()
    {
        Game.level_01.setBounds(--currentMapX, currentMapY, Game.level_01.getWidth(), Game.level_01.getHeight());
    }

    public void paint(Graphics g)
    {
        super.paint(g);
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Game.player.paint(g2d);
    }

    public void keyPressed(KeyEvent k) 
    {
        if(k.getKeyCode() == KeyEvent.VK_RIGHT) moveMapRight();
        if(k.getKeyCode() == KeyEvent.VK_LEFT) moveMapLeft();
    }
    public void keyReleased(KeyEvent k){}
    public void keyTyped(KeyEvent k){}
}

I've got the first class that extends the JFrame and contains the following class.

package Maps;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.io.*;
import java.util.*;

import javax.swing.*;

import Game.*;
import Tiles.*;

public class Level extends JPanel
{
    protected final File txt_MAP;
    protected final ArrayList<Tile> jLabel_MAP;
    protected final ArrayList<Integer> linesLength;
    protected Insets insets = Game.window.getInsets();
    protected int arrayIndex;
    protected int leftIndex;
    protected int topIndex;
    protected int width;
    protected int height;

    public Level(File f)
    {
        super();
        setLayout(null);
        setBackground(Color.BLACK);
        leftIndex = 0;
        topIndex = 0;
        txt_MAP = f;
        jLabel_MAP = new ArrayList<>();
        linesLength = new ArrayList<>();
        arrayIndex = 0;
        readTxt();
        width = linesLength.get(0) * Game.tileSize;
        height = linesLength.size() * Game.tileSize;
        addTiles();
    }

    private void readTxt()
    {
        BufferedReader br = null;
        try
        {
            br = new BufferedReader(new FileReader(txt_MAP));
        }
        catch(FileNotFoundException e){}
        try
        {
            String line = br.readLine();
            while(line != null)
            {
                String[] words = line.split(" ");
                for(int a = 0; a < words.length; a++)
                {
                    for(int b = 0; b < Game.tilesIcons.length; b++)
                    {
                        if(Game.tilesList[b].getName().equals(words[a] + ".gif"))
                        {
                            if(Game.tilesList[b].getName().contains(Game.grass_TYPE)) jLabel_MAP.add(arrayIndex, new Grass(Game.tilesIcons[b]));
                            if(Game.tilesList[b].getName().contains(Game.soil_TYPE)) jLabel_MAP.add(arrayIndex, new Soil(Game.tilesIcons[b]));
                            if(Game.tilesList[b].getName().contains(Game.sky_TYPE)) jLabel_MAP.add(arrayIndex, new Sky(Game.tilesIcons[b]));
                            arrayIndex++;
                        }
                    }
                }
                linesLength.add(words.length);
                line = br.readLine();
            }
        }
        catch(IOException e){}
    }

    private void addTiles()
    {
        for(int i = 0; i < jLabel_MAP.size(); i++)
        {
            jLabel_MAP.get(i).setBorder(null);
            jLabel_MAP.get(i).setBounds(insets.left + leftIndex, insets.top + topIndex, 64, 64);
            add(jLabel_MAP.get(i));
            if(leftIndex == width - Game.tileSize) 
            {
                leftIndex = 0;
                topIndex += 64;
            }
            else leftIndex += 64;
        }
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }
}

This class extends JPanel and contains an arrayList of Jlabels.

package Player;

import java.awt.*;

import Game.*;

public class Player
{
    private int x;
    private int y;
    private int xa;
    private int ya;
    private Graphics2D g2d; 
    public Player(double x, double y)
    {
        super();
        this.x = (int)x;
        this.y = (int)y;
        xa = 0;
        ya = 1;
    }

    public void movePlayer()
    {
        x += xa;
        y += ya;
    }

    public void setDirection(int xa, int ya)
    {
        this.xa = xa;
        this.ya = ya;   
    }

    public void paint(Graphics g)
    {
        g2d = (Graphics2D)g;
        g2d.drawImage(Game.playerImages[6], x , y, Game.tileSize, Game.tileSize, null);
    }

    public int getX()
    {
        return x;
    }

    public int getY()
    {
        return y;
    }

    public int getXA()
    {
        return xa;
    }

    public int getYA()
    {
        return ya;
    }
}

Finally this class is the class for the player, that is a BufferedImage. My problem is that when I start the program, the player image starts flickering, because when I call the paint() method in the JFrame class, this paints the first the jpanel and then the player image. How can I solve the Image flickering? Thanks in advance.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • You can bet entire stackoverflow archive history that you could fix the flickering. There could be several things that are actually causing the issue.. ... i wonder how your game loop code is programmed and how do you translate the input on screen to game logic. – Victor Jul 06 '15 at 21:12
  • 1) For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete Verifiable Example) or [SSCCE](http://www.sscce.org/) (Short, Self Contained, Correct Example). 2) One way to get image(s) for an example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). – Andrew Thompson Jul 06 '15 at 21:31

2 Answers2

4

As others have said, you shouldn't override paint(Graphics g) in top-level components, so that's part of the problem, but you also need to be careful to make sure you only paint to the screen once per repaint:

My problem is that when I start the program, the player image starts flickering, because when I call the paint() method in the JFrame class, this paints the first the jpanel and then the player image.

What's happening now is, every time you call a method that modifies the Graphics object in your paint(Graphics screen) method, it's directly modifying the screen contents, forcing the screen to refresh before you've finished drawing what you really wanted to - the Player. By first painting to super, then again with your custom rendering, you're actually painting to screen at least twice, causing the flicker. You can fix this by double buffering.

Double Buffering involves first rendering to an image, then painting that image to screen. Using built-in methods provided by the Component class (remember that JPanel extends Component), you can get the size of the viewable area of your component. Create a BufferedImage of the same size, then call bufferedImage.createGraphics() - this will give you a Graphics2D object that you can use to draw onto your bufferedImage with. Once you're done rendering to bufferedImage, call screen.drawImage(bufferedImage,0,0,null). This allows you to modify the bufferedImage as many times as you want without actually doing a screen refresh, then draw the contents of the buffer to screen in a single call.

ADDITIONAL INFO

I should have pointed out earlier that I know nothing about how things are actually laid out on screen for you, so you may need to do some additional checks on the bounds of your Player's screen area and where the upper left should be placed in the call to drawImage(Image,x,y,ImageObserver). Also be aware of transparency or the lack thereof in your BufferedImage - you can easily get lost if you paint opaque pixels over other important stuff on screen.

TRY THIS (but don't keep it)

Again, I don't recommend doing all of your painting in top-level components, but you could try doing something like this in the interim:

//This is in the Window class:
public void paint(Graphics screen)
{
    //Render everything first to a BufferedImage:
    BufferedImage bufferedImage = ... //Initialize based on Window's
                                      //size and bounds.
    Graphics2D buf = bufferedImage.createGraphics();
    super.paint(buf);
    buf.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);
    Game.player.paint(buf);

    //Now paint the buffer to screen:
    int x = ... //set to top-left x-coordinate, probably 0
    int y = ... //set to top-left y-coordinate, probably 0
    screen.drawImage(buf,x,y,null);
}
CodeBlind
  • 4,519
  • 1
  • 24
  • 36
  • Thank you very much!! I understood what I have to do, but I didn't get where I have to write the missing code. Could you please edit my code or explain me where to put the new one? – Andrea Ferrando Jul 07 '15 at 18:57
  • Your Window's `paint` method first calls `super.paint(g)`, then delegates painting to the `Player` instance using the same `Graphics` object - this is the screen's `Graphics` object. At the top of `Window.paint(Graphics screen)`, you need to create a `BufferedImage` and use it to do all of your drawing there. Then, the last call of `Window.paint(Graphics screen)` will be to call `screen.drawImage(bufferedImage,x,y,null)` - give it a try and let us know what happens. – CodeBlind Jul 08 '15 at 17:07
  • I'm sorry but I didn't get it. I tried but I think I did something wrong. Could you please edit my original code and show me the new one? – Andrea Ferrando Jul 08 '15 at 17:45
  • I posted some incomplete example code to get you started. Should make things a bit clearer. – CodeBlind Jul 08 '15 at 18:00
  • I've one more question. I tried to put the paint method in the jPanel class but when I launch the program, I can't see the player. What should I change to make it work? – Andrea Ferrando Jul 09 '15 at 23:08
2
  1. Don't override paint(Graphics) in a top level container like JFrame (which is not double buffered). Instead do custom painting in a JPanel (which is double buffered).
  2. Also, when overriding any of the paint methods, immediately call the super method thereby painting the background and effectively erasing the earlier drawing(s).
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433