Why JLabel is not repainted? - java

I have a project in which I have several components, . The problem is that when the repaint () command is executed, the JLabels are not painted. I want to make it clear that it is NOT a problem that there are graphics that are being painted on or anything. Apparently what happens is that within repaint (), when I force the JLabel to repaint, they do not paint unless you change the JLabel chain.
Initialization of the JLabel and define its parameters:
puntuacionL.setFont(new Font("Marker Felt", Font.PLAIN, 20));
puntuacionL.setBounds(710, 212, 150, 30);
puntuacionL.setOpaque(true);
puntuacionL.setBackground(Color.CYAN);
puntuacionL.setForeground(Color.white);
puntuacionL.setVisible(true);
comoJugar.setFont(new Font("Marker Felt", Font.PLAIN, 20));
comoJugar.setBounds(710, 245, 150, 30);
comoJugar.setOpaque(true);
comoJugar.setBackground(Color.CYAN);
comoJugar.setForeground(Color.white);
comoJugar.setVisible(true);
I have tried two options to force the redefinition of JLabel:
1. mietiqueta.setText("whatever").
In this option what I do is to redefine in the repaint () label string.
2.
Repaint the component: mietiqueta.paintComponent(g)
I would have uploaded a capture of the result, which would have been very useful, but you need at least 10 reputation points
Repaint() method:
#Override
public void update(Graphics g){
paint(g);
}
#Override
public void paint(Graphics g) {
if (offGraphics == null) {
offImage = createImage(900,900);
offGraphics = (Graphics2D) offImage.getGraphics();
}
puntuacionL.setText(" Puntuación: "+Integer.toString(puntuacion));
comoJugar.setText(" Pulsa H para ayuda");
Graphics2D g2d = (Graphics2D) g;
puntoIncialBala();
Image fondo = new ImageIcon(getClass().getResource("/imagenes/fondo.jpeg")).getImage();
Image img2 = new ImageIcon(getClass().getResource("/imagenes/fondoMapa.png")).getImage();
offGraphics.drawImage(img2, 30, 30, this);
int posVidaX = 710;
offGraphics.setColor(Color.CYAN);
offGraphics.fillRoundRect(710, 30, 150, 100, 10, 10);
for (int k = 0; k < vidas; k++) {
Image vida = new ImageIcon(getClass().getResource("/imagenes/vida.png")).getImage();
offGraphics.drawImage(vida, posVidaX, 50, this);
posVidaX = posVidaX + vida.getWidth(this);
}
timeLabel.paintComponents(g);
for (int k = 0; k < arrayCasilla.size(); k++) {
offGraphics.drawImage(arrayCasilla.get(k).getImg(), (int) arrayCasilla.get(k).getY(), (int) arrayCasilla.get(k).getX(), this);
}
Image img = new ImageIcon(getClass().getResource("/imagenes/BalaCanon.png")).getImage();
if (flagBala == true) {
//puntuacionL.setText(" Puntuación: "+Integer.toString(puntuacion));
if (pintarCasilla == true) {
puntuacionL.setText(" Puntuación: "+Integer.toString(puntuacion));
comoJugar.setText(" Pulsa H para ayuda");
queHayCasilla();
if (casilla[x][y].getTipo().compareTo("cangrejo") == 0) {
puntuacion = puntuacion + 1;
} else if (casilla[x][y].getTipo().compareTo("ron") == 0) {
posVidaX = 710;
//puntuacion = puntuacion + 1;
vidas--;
if (vidas == 0) {
timer.stop();
etiquetaFin.setVisible(true);
finalizar.setVisible(true);
contenedorFinal.setVisible(true);
}
offGraphics.setColor(Color.CYAN);
offGraphics.fillRoundRect(710, 30, 150, 100, 10, 10);
for (int k = 0; k < vidas; k++) {
Image vida = new ImageIcon(getClass().getResource("/imagenes/vida.png")).getImage();
offGraphics.drawImage(vida, posVidaX, 50, this);
posVidaX = posVidaX + vida.getWidth(this);
}
} else if (casilla[x][y].getTipo().compareTo("cofre") == 0) {
puntuacion = puntuacion + 2;
}
if (posY[y] < 441) {
arrayCasilla.add(casilla[x][y]);
}
for (int k = 0; k < arrayCasilla.size(); k++) {
if (posY[y] < 441) {
offGraphics.drawImage(arrayCasilla.get(k).getImg(), (int) arrayCasilla.get(k).getY(), (int) arrayCasilla.get(k).getX(), this);
}
}
animacion.stop();
if (!animacion.isRunning()) {
i = 0;
}
pintarCasilla = false;
flagBala = false;
} else {
offGraphics.drawImage(img, (int) x1, (int) x2, this);
}
}
try {
imgB = ImageIO.read(getClass().getResource("/imagenes/canon.png"));
} catch (IOException ex) {
Logger.getLogger(PanelCanon.class.getName()).log(Level.SEVERE, null, ex);
}
offGraphics.setColor(Color.gray);
offGraphics.fillRect(30, 720, 646, 122);
AffineTransform tx = AffineTransform.getRotateInstance(Math.PI / 2 - anguloRotacion, imgB.getWidth(this) / 2, imgB.getHeight(this) / 2);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
tx.rotate(Math.PI / 2 - anguloRotacion);
System.out.println(tx.toString());
offGraphics.drawImage(op.filter(imgB, null), 360, 740, null);
g2d.drawImage(offImage, 0, 0, this);
puntuacionL.setText(" Puntuación: "+Integer.toString(puntuacion));
comoJugar.setText(" Pulsa H para ayuda");
System.out.println("Posicion bala x" + x1);
System.out.println("Posicion bala y: " + x2);
}
I have a timer that may be interfering in some way, this timer is responsible for controlling the countdown.
Timer code:
timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (seconds == 0 && minutes == 0) {
timer.stop();
etiquetaFin.setVisible(true);
finalizar.setVisible(true);
contenedorFinal.setVisible(true);
} else if (seconds > 0) {
seconds--;
} else if (minutes > 0) {
minutes--;
seconds = 59;
}
revalidate();
//puntuacionL.paintImmediately(710, 212, 150, 30);
//puntuacionL.setText(" Puntuación: "+Integer.toString(puntuacion));
//comoJugar.setText(" Pulsa H para ayuda");
timeLabel.setText(" "+timeFormatter.format(minutes) + ":" + timeFormatter.format(seconds));
}
});
timer.start();
And this is where the magic happens and I do not understand anything anymore. As you can see inside the event I have commented the lines:
puntuacionL.setText(" Puntuación: "+Integer.toString(puntuacion));
comoJugar.setText(" Pulsa H para ayuda");
If within the event of the Timer I redefine the Labels, with a different string from the paint () they are painted, but of course it does not work for me, since I want the string not to be modified.
I have not put the whole class because it is quite large, but if someone still needs the complete context, happy to edit the question and put all the code.

I am really not sure what you are trying to do, but you appear not to understand the basic concepts of using components and of custom painting.
Here are the problems I see:
Don't override update(...). There is no reason to do this.
Don't override paint(). Custom painting is done by overriding the paintComponent(...) method of the component.
Don't read images in the painting method. A component is repainted when you specifically invoke repaint() on the component or when Swing determines the components needs to be painted. Images should be read in the constructor of your class to make the painting code as efficient as possible.
Don't change the properties of your class in the painting method. This would especially apply to the changing the text of an external component like a JLabel.
Don't invoke paintComponents() on any component from within the painting method.
Don't access the Timer in the painting method. Again, the whole purpose of the painting code is to paint the current state of the component, not alter the state. The purpose of the Timer is the change the state of the component every time it fires. So it is the Timer logic that should be changing the text on the JLabel. The JLabel will then automatically repaint itself. See: Program freezes during Thread.sleep() and with Timer for a basic example of undateing a JLabel from withing a Timer.
Read the section from the Swing tutorial on Custom Painting for more information on the basic of painting.

Related

Fill an oval every second

I've got 8 ovals all set to the same colour. After a second I want the first oval to change colours and then after another second i want the first oval to go back to its original colour and then change the second ovals colour. I've drawn the circles and I've tried implementing a thread but i think it's not executing...
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Circle extends JPanel implements Runnable
{
Graphics g;
Thread t = new Thread();
int[][] fillCircles = new int[8][4];
#Override
public void paintComponent(Graphics g)
{
this.g = g;
super.paintComponent(this.g);
this.g.setColor(new java.awt.Color(237, 54, 26));
this.g.drawOval(300, 50, 100, 100);
this.g.drawOval(450, 125, 100, 100);
this.g.drawOval(500, 250, 100, 100);
this.g.drawOval(425, 375, 100, 100);
this.g.drawOval(300, 400, 100, 100);
this.g.drawOval(175, 350, 100, 100);
this.g.drawOval(125, 225, 100, 100);
this.g.drawOval(175, 100, 100, 100);
this.g.fillOval(300, 50, 100, 100);
this.g.fillOval(450, 125, 100, 100);
this.g.fillOval(500, 250, 100, 100);
this.g.fillOval(425, 375, 100, 100);
this.g.fillOval(300, 400, 100, 100);
this.g.fillOval(175, 350, 100, 100);
this.g.fillOval(125, 225, 100, 100);
this.g.fillOval(175, 100, 100, 100);
fillCircles[0][0] = 300;
fillCircles[0][1] = 50;
fillCircles[0][2] = 100;
fillCircles[0][3] = 100;
fillCircles[1][0] = 450;
fillCircles[1][1] = 125;
fillCircles[1][2] = 100;
fillCircles[1][3] = 100;
fillCircles[2][0] = 500;
fillCircles[2][1] = 250;
fillCircles[2][2] = 100;
fillCircles[2][3] = 100;
fillCircles[3][0] = 425;
fillCircles[3][1] = 375;
fillCircles[3][2] = 100;
fillCircles[3][3] = 100;
fillCircles[4][0] = 300;
fillCircles[4][1] = 400;
fillCircles[4][2] = 100;
fillCircles[4][3] = 100;
fillCircles[5][0] = 175;
fillCircles[5][1] = 350;
fillCircles[5][2] = 100;
fillCircles[5][3] = 100;
fillCircles[6][0] = 125;
fillCircles[6][1] = 225;
fillCircles[6][2] = 100;
fillCircles[6][3] = 100;
fillCircles[7][0] = 175;
fillCircles[7][1] = 100;
fillCircles[7][2] = 100;
fillCircles[7][3] = 100;
}
Circle () {
t.start();
}
public void run () {
int circle = 0;
try {
for (;;) {
Thread.sleep(1000);
if (circle > 0) {
this.g.setColor(new java.awt.Color(237, 54, 26));
circle--;
this.g.fillOval(fillCircles[circle][0], fillCircles[circle][1], fillCircles[circle][2], fillCircles[circle][2]);
circle++;
}
this.g.setColor(Color.red);
this.g.fillOval(fillCircles[circle][0], fillCircles[circle][1], fillCircles[circle][2], fillCircles[circle][2]);
circle++;
if (circle == 8) {
circle = 0;
}
}
} catch (InterruptedException e) {
System.out.println ("Thread Interrupted");
}
}
public static void main(String[] args) {
Circle c;
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(c=new Circle());
application.setSize(1200, 900);
application.setVisible(true);
}
}
You're just instantiating a Thread without giving it a Runnable, so it's not running what you're intending.
Thread t = new Thread();
should be
Thread t = new Thread(this);
this because the Circle is Runnable and the run() method is in it.
Regarding color changing, I would give you an idea.
When drawing the circles, check the current circle number/index against another variable which changes every second colorChangeIndex
if(circleIndex == colorChangeIndex){
// another color
else
//the default color
colorChangeIndex++; (modulo number of cricles to reset it)
The basic structure of your code is wrong. Painting should only be done in the paintComponent() method. You should never reference the Graphics object in your thread.
So, instead what you might want to do is keep an ArrayList of "circle" objects. This custom object will contain the circle to be painted and its color. Then in the paintComponent() method you just iterate through the ArrayList and paint all the circles in the ArrayList. Check out the DrawOnComponent example found in Custom Painting Approches.
After a second I want the first oval to change colours and then after another second i want the first oval to go back to its original colour and then change the second ovals colour.
So now you need to use a Swing Timer to animate the painting. When the Timer fires you reset the Color of the appropriate circle and then just repaint the panel.

Does the repaint() method in Java require a timer or action?

I've been working on a small "game," which I think is called Pachinko. I have uploaded an image of what the game screen looks like. I will be dropping balls, and having them look like they are rolling off pegs, ending up being caught in the bottom "gates."
My problem is that I cannot get the repaint() method to work. Does the repaint() method require a timer, or action to work? Please look at at these two classes. I have created a Ball class object inside the GameWindow class (near the bottom), and would like to update the ball's x/y values using the Ball's setPos() method, then repaint, so the ball appears to move.
What am I doing wrong? Do I need an update() method to use the repaint() method?
Game Window Image:
public class GameWindow extends JPanel{
private int numBalls = 0;
// GameWindow Constructor (Sets Ball amount from user)
public GameWindow(int balls){
JFrame myFrame = new JFrame("Game Window");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Globally set ball amount
setBallAmount(balls);
myFrame.add(this);
myFrame.setSize(325, 790);
myFrame.setLocationRelativeTo(this);
myFrame.setResizable(false);
myFrame.setVisible(true);
} // End GameWindow Constructor
// Function setPegAmount;
// Passes the amount of balls the user to class variable.
public void setBallAmount(int balls)
{
numBalls = balls * 2;
}
public void paintComponent(Graphics g){
super.paintComponent(g); // housekeeping, etc.
this.setBackground(Color.WHITE); // Background
int counter = 0; // count what number peg we are painting
int row = 1; // calculate what row we are creating
int rowSpacer = 55;
boolean evenRow = false;
int columnSpacer = 60;
// DRAW PEGS TO SCREEN (4 rows of 8, 4 rows of 7)
for (int x = 0; x < 60; x++)
{
// For odd rows
if (row % 2 == 1)
{
g.setColor(Color.BLACK);
g.fillOval(rowSpacer - 40, columnSpacer, 10, 10);
rowSpacer += 40;
counter++;
}
// For Even rows
else
{
g.setColor(Color.BLACK);
g.fillOval(rowSpacer - 20, columnSpacer, 10, 10);
rowSpacer += 40;
counter++;
}
// Check to see if we are finished with odd row
if (counter % 8 == 0 && evenRow == false)
{
row++;
rowSpacer = 55;
columnSpacer += 60;
evenRow = true;
counter = 0;
}
else if(counter % 7 == 0 && evenRow == true)
{
row++;
rowSpacer = 55;
columnSpacer += 60;
evenRow = false;
counter = 0;
}
} // END DRAWING PEGS TO SCREEN
// DRAW RECTANGULAR WALLS TO SCREEN
g.setColor(Color.BLACK); // Wall Color
g.fillRect(0, 0, 5, 760); // LEFT Wall
g.fillRect(315, 0, 5, 760); // RIGHT Wall
g.fillRect(0, 0, 315, 5); // TOP Wall
g.fillRect(0, 755, 320, 5); // BOTTOM Wall
// DRAW BOTTOM GATES
int gateSeperator = 35;
for (int x = 0; x < 7; x++)
{
g.setColor(Color.BLACK);
g.fillRect(gateSeperator, 500, 10, 255);
gateSeperator += 40;
}
// Create instance of ball object
Ball myBall = new Ball();
// Test draw ball
myBall.drawBall(g); // The ball is drawn to screen
myBall.setPos(50, 50); // Change the x and y coordinates of the Ball
repaint(); // Also tried "this.repaint();" but neither does anything
} // Ends paintComponent
} // End GameWindow Class
Ball.java:
public class Ball{
private int x = 5;
private int y = 30;
public void setPos(int xPos, int yPos)
{
x = xPos;
y = yPos;
}
public void drawBall(Graphics g)
{
g.setColor(Color.GREEN);
g.fillOval(x, y, 30, 30);
}
}
I don't think that's the way to do it. Swing's not my specialty but calling repaint in paintComponent, according to my experience, is incorrect.
For example, tell the component to repaint itself.
/**
* Tells the view to repaint itself.
*/
public void update() {
repaint();
}
As soon as possible, repaint ends up calling paintComponent via paint.
/**
* Paints the component.
* #param g The graphics object for the view.
*/
#Override
protected void paintComponent(Graphics g) {
// Draw some stuff...
}
So calling repaint inside of paintComponentis likely not what you're wanting to do. What you should be doing is using repaint to invode paintComponent.
I don't think you can rely on putting the repaint or update at the end of paintComponent because, I believe, multiple calls to repaint get lumped into a single update. So, yes, to properly animate object you should look into using a Swing Timer. For example,
Timer timer = Timer(delay, action);
timer.start();
The above timer will invoke the given action on the delay given in milliseconds. Please see this for more details.

Flicker on movement (Not using JPanel or Swing)

first post here so forgive me if this is the absolute wrong thing but I'm in a tiny bit of a pickle. - My computer science class has us making our own game/program from scratch, but our teacher specifically ignored the existence of swing and said that it was out of his realm to teach it so from my readings it seems as if a game is almost out of the question, with what I've written so far my main sprite and debugging information flickers violently on key input. Is there a way around this?
I know JPanel/JFrame/Swing would fix this, but we weren't taught it and all I've read so far is confusing me to no end, if someone would be able to help me convert my code to use it that would be amazing as I learn easier from dissecting it.. Which may seem a bit dodgy but it really is the easiest way to learn from. My code so far is;
public class GameThing extends Applet implements KeyListener {
Image rightsprite, picture;
Image leftsprite, picture2;
Image spritedead, picture3;
Image background, picture4;
boolean left;
boolean dead;
boolean wall;
boolean menu;
int widthX, heightY;
int bgx, bgy;
String sr = " ";
char ch = '*';
Graphics bufferGraphics;
Image offscreen;
Dimension dim;
#Override
public void init() {
rightsprite = getImage(getDocumentBase(), "SpriteRight.png");
leftsprite = getImage(getDocumentBase(), "SpriteLeft.png");
spritedead = getImage(getDocumentBase(), "Sprite.png");
background = getImage(getDocumentBase(), "Background.png");
requestFocus();
addKeyListener(this);
left = false;
menu = true;
setSize(600, 380);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
bgx = 0;
bgy = 0;
}
public void pause(final int delay) { // PAUSE COMMAND LIFTED FROM RAY
// waits a while
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
return;
}
}
public GameThing() { // SPRITE INITIAL SPAWN LOCATION
widthX = 100;
heightY = 300;
}
#Override
public void paint(final Graphics g) { // DRAW MAIN CHARACTER SPRITE AND BACKGROUND
bufferGraphics.setColor(Color.black); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(0, 334, 500, 50);
bufferGraphics.setColor(Color.green);
bufferGraphics.fillRect(0, 335, 500, 50);
g.drawImage(offscreen, 0, 0, this);
if (menu == true) {
pause(500);
g.drawString("Side Scroller Test.", 180, 250);
pause(1500);
menu = false;
}
if (left == false) { // LEFT KEY MOVEMENT COMMANDS
g.drawImage(rightsprite, widthX, heightY, this);
g.drawString("Sprites Current X Location is: " + widthX, 20, 30);
g.drawString("Sprites Current Y Location is: " + heightY, 20, 50);
g.drawString("Background X location is: " + bgx, 20, 90);
g.drawString("Currently: " + sr, 20, 70);
}
if (left == true) { // RIGHT KEY MOVEMENT COMMANDS
g.drawImage(leftsprite, widthX, heightY, this);
g.drawString("Sprites Current X Location is: " + widthX, 20, 30);
g.drawString("Sprites Current Y Location is: " + heightY, 20, 50);
g.drawString("Background X location is: " + bgx, 20, 90);
g.drawString("Currently: " + sr, 20, 70);
}
if (dead == true) { // COMMANDS TO EXECUTE WHEN MAIN CHARACTER DIES
widthX = 506;
heightY = 314;
g.drawImage(spritedead, widthX, heightY, this);
pause(500);
g.setColor(Color.white);
g.drawRect(widthX, heightY, widthX, heightY);
g.fillRect(widthX, heightY, widthX, heightY);
pause(500);
widthX = 100;
heightY = 300;
bgx = 0;
left = true;
dead = false;
}
}
#Override
public void update(final Graphics g) { // KEEPS BACKGROUND STATIC
paint(g);
}
public void keyPressed(final KeyEvent e) {
sr = "blank!";
ch = '1';
if (e.getKeyCode() == 39) {
sr = "Moving Right!";
widthX = widthX + 7;
if (widthX <= 121) {
bgx = bgx;
} else {
bgx = bgx - 7;
}
left = false;
if ((widthX > 490) && (heightY > 300)) { // FALL DEATH
sr = "You Died!";
widthX = 900;
heightY = 900;
dead = true;
}
if (widthX == 499) {
heightY = heightY + 7;
}
}
if (widthX == 2) {
wall = true;
}
if (e.getKeyCode() == 37) {
if (wall == true) {
sr = "Wall!";
if (widthX > 2) {
wall = false;
}
}
if (wall == false) {
sr = "Moving Left!";
widthX = widthX - 7;
if (bgx >= 0) {
bgx = bgx;
} else {
bgx = bgx + 7;
}
left = true;
}
}
if (e.getKeyCode() == 38) {
sr = "Jumping!";
}
repaint();
}
public void keyTyped(final KeyEvent e) {
ch = e.getKeyChar();
repaint();
}
public void keyReleased(final KeyEvent e) {
} // key released
}
ps: Jumping with the up key has been a problem too, I can't simply add to the Y variable and then subtract as it just goes so fast it's as if it never moved.. The basics of a sidescroller are a lot more than I ever expected..
I edited your code:
public class GameThing extends Applet {
// External resources
BufferedImage rightSprite, leftSprite, spriteDead, backgroundImg;
// Game data
int state = 1; //0 = left, 1 = right, 2 = dead
// Geometry
int locX = 100, locY = 300;
int bgX = 0, bgY = 0;
int groundX = 0, groundY = 334, groundW = 500, groundH = 50;
int appletW = 600, appletH = 480;
int wallW = 20, wallH = 40, wallX = 20, wallY = groundY - wallH;
final int STEP_SIZE = 7;
// Information
final String X_LOC_STR = "Sprites Current X Location is: ";
final String Y_LOC_STR = "Sprites Current Y Location is: ";
final String STATE_STR = "Currently: ";
final String X_BG_STR = "Background X location is: ";
String stateString = "";
// Double buffering
Image offscreen;
Graphics bufferGraphics;
Dimension dim;
// GUI components
Panel gamePanel;
Panel statusPanel = new Panel();
Label xLocLabel = new Label();
Label yLocLabel = new Label();
Label stateLabel = new Label();
Label xBGLocLabel = new Label();
#Override
public void init() {
// Load images
try {
rightSprite = ImageIO.read(new File(getClass().getResource("SpriteRIght.png").getPath()));
leftSprite = ImageIO.read(new File(getClass().getResource("SpriteLeft.png").getPath()));
spriteDead = ImageIO.read(new File(getClass().getResource("SpriteDead.png").getPath()));
backgroundImg = ImageIO.read(new File(getClass().getResource("Background.png").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
// Set the panel that displays data
statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.Y_AXIS));
statusPanel.add(xLocLabel);
statusPanel.add(yLocLabel);
statusPanel.add(stateLabel);
statusPanel.add(xBGLocLabel);
// Create the panel that draws the game and specify its behavior
gamePanel = new Panel() {
// Reduces flickering
#Override
public void update(Graphics g) {
paint(g);
}
// DRAW MAIN CHARACTER SPRITE AND BACKGROUND
#Override
public void paint(Graphics g) {
bufferGraphics.clearRect(0, 0, dim.width, dim.width);
bufferGraphics.drawImage(backgroundImg, bgX, bgY, this);
bufferGraphics.setColor(Color.BLACK); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(groundX, groundY, groundW, groundH);
bufferGraphics.setColor(Color.GREEN);
bufferGraphics.fillRect(groundX, groundY + 1, groundW, groundH);
bufferGraphics.setColor(Color.RED);
bufferGraphics.fillRect(wallX, wallY, wallW, wallH);
switch (state) {
case 0: bufferGraphics.drawImage(leftSprite, locX, locY, this);
break;
case 1: bufferGraphics.drawImage(rightSprite, locX, locY, this);
break;
case 2: bufferGraphics.drawImage(spriteDead, locX, locY, this);
// After death wait a bit and reset the game
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
pause(2000);
reset();
}
});
break;
}
g.drawImage(offscreen, 0, 0, this);
}
};
// Set the applet window
setLayout(new BorderLayout());
add(statusPanel, BorderLayout.PAGE_START);
add(gamePanel);
// Set double buffering
setSize(appletW, appletH);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
// Set the panel that draws the game
gamePanel.addKeyListener(new Controls());
gamePanel.requestFocusInWindow();
updateLabels();
}
// PAUSE COMMAND LIFTED FROM RAY (who is Ray?)
// waits a while
public void pause(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Set to start parameters
public void reset() {
locX = 100; locY = 300;
bgX = 0;
state = 1;
gamePanel.repaint(); // These 2 lines automatically restart the game
updateLabels(); // Remove if you want to stay on the death screen until next press
}
public void updateLabels() {
xLocLabel.setText(X_LOC_STR + locX);
yLocLabel.setText(Y_LOC_STR + locY);
stateLabel.setText(STATE_STR + stateString);
xBGLocLabel.setText(X_BG_STR + bgX);
}
private class Controls extends KeyAdapter {
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
stateString = "Moving Right!";
locX += STEP_SIZE;
state = 1;
if (locX > 121)
bgX -= STEP_SIZE;
// FALL DEATH
if ((locX > 490) && (locY > 300)) {
stateString = "You Died!";
state = 2;
}
// Start fall
if (locX == 499)
locY += STEP_SIZE;
// Check wall collision
if (locX >= wallX && locX <= wallX + wallW) {
locX -= STEP_SIZE;
stateString = "Wall!";
}
break;
case KeyEvent.VK_LEFT:
stateString = "Moving Left!";
locX -= STEP_SIZE;
state = 0;
if (bgX < 0)
bgX += STEP_SIZE;
// Check wall collision
if (locX >= wallX && locX <= wallX + wallW) {
locX += STEP_SIZE;
stateString = "Wall!";
}
break;
case KeyEvent.VK_UP:
// Implement jumping
stateString = "Jumping!";
break;
}
// Repaint the game panel and update the data panel
gamePanel.repaint();
updateLabels();
}
}
}
Start by testing and understanding the changes:
I created a separate panel for the current data display and one for the game (which gets repainted).
I added a few help functions.
I made a lot of syntax changes (e.g. multiple if else replaced by switch, VK_UP instead of 37 and such).
Better to have all values in one place and read them from there instead of spreading them in various places (still needs a bit of work but it will change when advancing with the code).
Make constant values (values that don't change) final.
In Java conventions, constants are named with uppercase and underscore (_) (so Color.RED, not Color.red).
Better to use #Override when overriding methods even when the compiler understands it.
Do not block the event dispatch thread (EDT) with sleep or expensive computations. Methods like paint key events are meant to execute quickly. I used invokeLater to pause only after the EDT finished processing all queued events. (I understand that this threading subject comes as a bomb from nowhere, just bear with me).
Looking forward for the current state of the code (no new features):
state should probably be an enum or something close to it instead of random numbers.
Using Shape for drawn objects instead of specifying their parameters during painting.
Double buffering implementation notes:
You create an off-screen Image object offscreen and a Graphics object to draw on it - bufferGraphics. Insidepaint, you use only bufferGraphics for the drawings. You in fact draw on offscreen even though the paint belongs to gamePanel (or any other object and its paint), slightly confusing. When you finish drawing everything off-screen, you throw it all in one go onto the screen with g.drawImage(offscreen...) which uses gamePanel's Graphics object g.
The idea is like drawing everything on a paper and then putting it in front of the camera instead of drawing bit by bit on the camera, or something like that...
Currently, the off-screen image is the size of the whole applet and not just the game panel, but it makes little difference.
Relevant differences from a Swing approach:
Replace Label with JLabel, Panel with JPanel etc.
Swing components automatically implement double buffering (see JComponent's setDoubleBuffered and isDoubleBuffered). Remove all of those off-screen shenanigans and draw directly with Graphics g. No need to override update also.
Painting in Swing is done in paintComponent and not in paint.
Swing runs from its own thread and not from the EDT (not automatically, but easily done). There are some benefits to this I won't get into right now.
Key events are better handled by key bindings (Swing only) rather than the key listener. This manifests in responsiveness, native environment independence and more (e.g. concurrent key events).
#user1803551 Okay, so it's all sorted back to how it was again. - The scrolling background is in (Just a couple things around the place needed some tweaking from your post), and I understand that the final line "gamePanel.repaint(); is what's refreshing everything on the screen, since the background needs to be updated with every key input, it shouldn't be limited to the tiny square from your code, right? This now refreshes the whole screen, but I assume this isn't what is wanted because it just results in the flickering, the buffergraphics of the green rectangle at the bottom however don't flicker. So my assumption is to convert
g.drawImage(backgroundImg, bgx, bgy,this);
into buffergraphics?
-EDIT-
Moved the main Sprite into bufferGraphics aswell as the background image. Flickering is now almost gone, but after holding down an arrow key for a little bit the screen will flicker just for a moment and then return to normal.
public class GameTest extends Applet {
/**
*
*/
private static final long serialVersionUID = -1533028791433336859L;
Image rightsprite, leftsprite, spritedead, backgroundImg, offscreen;
boolean left = false, dead = false, wall = false, menu = true;
int locX = 100, locY = 300, width, height, bgx = 0, bgy = 0;
final int STEP_SIZE = 7;
final String X_LOC_STR= "Sprites Current X Location is: ";
final String Y_LOC_STR= "Sprites Current Y Location is: ";
final String STATE_STR= "Currently: ";
final String X_BG_STR= "Background X location is: ";
String sr = " ";
Graphics bufferGraphics;
Dimension dim;
Panel gamePanel;
Panel statusPanel = new Panel();
Label xLoc = new Label(X_LOC_STR);
Label yLoc = new Label(Y_LOC_STR);
Label state = new Label(STATE_STR);
Label xBGLoc = new Label(X_BG_STR);
public void init() {
try {
rightsprite = ImageIO.read(new File(getClass().getResource("SpriteRight.png").getPath()));
leftsprite = ImageIO.read(new File(getClass().getResource("SpriteLeft.png").getPath()));
spritedead = ImageIO.read(new File(getClass().getResource("SpriteDead.png").getPath()));
backgroundImg = ImageIO.read(new File(getClass().getResource("Background.png").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
height = rightsprite.getHeight(this);
width = rightsprite.getWidth(this);
statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.Y_AXIS));
statusPanel.add(xLoc);
statusPanel.add(yLoc);
statusPanel.add(state);
statusPanel.add(xBGLoc);
gamePanel = new Panel() {
/**
*
*/
private static final long serialVersionUID = 1L;
// DRAW MAIN CHARACTER SPRITE AND BACKGROUND
#Override
public void paint(Graphics g) {
bufferGraphics.drawImage(backgroundImg,bgx, bgy, this);
bufferGraphics.setColor(Color.black); // DRAW GROUND (REPLACE WITH BLOCKS SOON)
bufferGraphics.drawRect(0, 334, 500, 50);
bufferGraphics.setColor(Color.green);
bufferGraphics.fillRect(0, 335, 500, 50);
if (menu == true) {
pause(500);
g.drawString("Side Scroller Test.", 180, 250);
pause(1500);
menu = false;
}
// LEFT KEY MOVEMENT COMMANDS
if (left == false)
bufferGraphics.drawImage(rightsprite, locX, locY, this);
// RIGHT KEY MOVEMENT COMMANDS
else
bufferGraphics.drawImage(leftsprite, locX, locY, this);
// COMMANDS TO EXECUTE WHEN MAIN CHARACTER DIES
if (dead == true) {
locX = 506;
locY = 314;
bufferGraphics.drawImage(spritedead, locX, locY, this);
pause(500);
bufferGraphics.setColor(Color.white);
bufferGraphics.drawRect(locX, locY, width, height);
bufferGraphics.fillRect(locX, locY, width, height);
pause(500);
locX = 100;
locY = 300;
bgx = 0;
bgy = 0;
bufferGraphics.clearRect(locX, locY, width, height);
left = true;
dead = false;
}
g.drawImage(offscreen, 0, 0, this);
super.paint(g);
}
};
setLayout(new BorderLayout());
add(statusPanel, BorderLayout.PAGE_START);
add(gamePanel);
setSize(600, 480);
dim = getSize();
offscreen = createImage(dim.width, dim.height);
bufferGraphics = offscreen.getGraphics();
gamePanel.addKeyListener(new Controls());
gamePanel.requestFocusInWindow();
}
// waits a while
public void pause(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private class Controls extends KeyAdapter {
public void keyPressed(KeyEvent e) {
sr = "blank!";
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
sr = "Moving Right!";
locX += STEP_SIZE;
if (locX > 121)
bgx = bgx - STEP_SIZE;
left = false;
// FALL DEATH
if ((locX > 490) && (locY > 300)) {
sr = "You Died!";
locX = 900;
locY = 900;
dead = true;
gamePanel.repaint();
}
if (locX == 499) {
locY += STEP_SIZE;
}
}
if (locX == 2) {
wall = true;
}
else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (wall == true) {
sr = "Wall!";
if (locX > 2) {
wall = false;
}
}
else {
sr = "Moving Left!";
locX -= STEP_SIZE;
if (bgx < 0)
bgx += STEP_SIZE;
left = true;
}
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
sr = "Jumping!";
}
xLoc.setText(X_LOC_STR + locX);
yLoc.setText(Y_LOC_STR + locY);
state.setText(STATE_STR + sr);
xBGLoc.setText(X_BG_STR + bgx);
gamePanel.repaint();
}
}
}

How can it save the previous graphics using repaint() in java?

I need to update my graphic, In which there are rectangulars. And at each step I need to highlight and modify the status of some of them.
But now in my code, for each repaint(), it draw a totally new picture.
I have read the followint similar quesion:
Keeping draw graphics - removing super.paintComponent ,which cannot solve my problem.
The next time it calls repaint(), those rectangulars disappear. It means it erases the prevous graphic.
Please help!
Here is the code: ("firstPaint = false" is just before the next repaint())
#Override
protected void paintComponent(Graphics g2)
{
super.paintComponent(g2);
final Graphics2D g = (Graphics2D) g2.create();
try{
if(firstPaint){
int status;
g.setColor(Color.BLACK);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
status = current[i][j];
if(status == 1)
{
g.fillRect(j * 20, i * 20, 20, 20);
}
}
}
}
if(executeResult == 2){
g.setColor(Color.BLUE);
for(Cell c:highlightedCells){
g.drawRect(c.j * 20, c.i * 20, 20, 20);
}
}
if(executeResult == 1){
g.setColor(Color.RED);
for(Cell c:highlightedCells){
g.fillRect(c.j * 20, c.i * 20, 5, 5);
}
}
}
finally{
g.dispose();
}
}

Graphics painting strategy(performance)

In my game I have a gamepanel which draws my map layers: 'ground/buildings/objects layer', then player sprite, then enemies/npcs/mobs, then 'above layer(tiles to draw above player)'. This was working great and running smoothly.
I then started to work on a minimap JInternalFrame. It actually looks great for what I need but I am concerned with performance. After adding the minimap I noted some slowdown of painting. My biggest resolution supports a map of:
else if (scrnsize.width >= 1440 && scrnsize.height >= 1024){ //large&wide
//45x29(32x32px tiles)
//1440, 1024
Basically my question is, is there a better way I can do this(than below code) or methods I can call for offscreen buffer or something?
This is the Minimap code. As you can see I have logic in the paintComponent to not redraw unless the refreshMinimap == true(the player moves or dies). This helped get rid of most noticable lag, but I still am noticing some. Any help would be super appreciated.
public MinimapGamePanel() {
super();
logger.addAppender(GUILog4JFileHelper.fileAppender);
gamePanelImage = new BufferedImage(32 * MyClient.xTiles, 32 * MyClient.yTiles, BufferedImage.TYPE_INT_ARGB);
setLayout(new BorderLayout());
setBorder(BorderFactory.createLineBorder(Color.black));
}
public void paintComponent(Graphics g) {
logger.trace("begin: REPAINTNG...");
g2 = (Graphics2D)g;
//map
if (refreshMinimap){
RefreshMinimap();
refreshMinimap = false;
}
g2.drawImage(gamePanelImage, 0, 0, null);
g2.dispose();
logger.trace("end: REPAINTNG...");
}
private void RefreshMinimap() {
logger.trace("Map drawing started.");
int count = (int) ((MyClient.characterX - (MyClient.xTiles*1.5)) + ((MyClient.characterY - (MyClient.yTiles*2)) * MyClient.mapWidth));
for (int x = 0; x < MyClient.xTiles*3; x++){
for (int y = 0; y < MyClient.yTiles*4; y++){
if (count > -1 && count < (MyClient.mapWidth * MyClient.mapHeight)){
if (!MyClient.groundLayer[count].equals("0")){ //don't draw full transparent tiles
//SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[count] + ".png").draw(gamePanelImage, x, y);
SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
if (!MyClient.buildingLayer[count].equals("0")){ //don't draw full transparent tiles
SpriteStore.get().getSprite("images/tiles/" + MyClient.buildingLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
if (!MyClient.objectLayer[count].equals("0")){ //don't draw full transparent tiles
SpriteStore.get().getSprite("images/tiles/" + MyClient.objectLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
} else {
SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[0] + ".png").drawFirstPixel(gamePanelImage, x, y);
}
count += MyClient.mapWidth;
}
count -= MyClient.yTiles * 4 * MyClient.mapWidth;
count++;
}
logger.trace("Map drawing done.");
}
This is the drawing code which just draws pixel 0,0
public void drawFirstPixel(BufferedImage gamePanelImage, int xDraw, int yDraw) {
BufferedImage bufferedVersion = (BufferedImage) image;
gamePanelImage.getGraphics().drawImage(bufferedVersion.getSubimage(0, 0, 1, 1), xDraw, yDraw, null);
}
I am actually pretty happy with the look of just taking pixel 0,0. Apologies for the bad quality, shaky cell phone pic.
After adding the minimap I noted some slowdown of painting
SpriteStore.get().getSprite("images/tiles/" + MyClient.buildingLayer
Don't do I/O in a painting method. All images should be read into memory when you create your class.
Is this in tune to what you guys were suggesting I do?
public class MinimapGamePanel extends JPanel {
...
private static Thread t;
public MinimapGamePanel() {
super();
logger.addAppender(GUILog4JFileHelper.fileAppender);
gamePanelImage = new BufferedImage(320, 320, BufferedImage.TYPE_INT_ARGB);
//TODO:: change values above upon picking minimap size
setLayout(new BorderLayout());
setBorder(BorderFactory.createLineBorder(Color.black));
t = new Thread(new RefreshMinimapThread());
}
public void paintComponent(Graphics g) {
logger.trace("begin: REPAINTNG...");
g2 = (Graphics2D)g;
if (refreshMinimap){
t.start();
//RefreshMinimap();
refreshMinimap = false;
}
g2.drawImage(gamePanelImage, 0, 0, null);
g2.dispose();
logger.trace("end: REPAINTNG...");
}
private class RefreshMinimapThread implements Runnable {
#Override
public void run() {
RefreshMinimap();
}
}

Categories