The Coding Wombat
The Coding Wombat

Reputation: 815

repaint() doesn't repaint on call?

I'm trying to make a match 3 game. I am trying to create some visual aid to what is actually happening by first marking the gems that need to be deleted "black", and after that letting gravity do it's job. I'm struggling to do this, I called repaint(); after I marked them "black", but it doesn't seem to work. I also tried adding in revalidate(); as suggested in another question but that doesn't seem to fix the problem either. Here's the piece of code that's troubling me.

Trouble code:

public void deletePattern(Set<Gem> gemsToDelete){
    for(Gem gem : gemsToDelete)
        gem.setType(7);

    repaint(); //This doesn't seem to work

    doGravity();
    switchedBack = true;
    checkPattern();
}

I want to repaint the board before doGravity() and after the enhanced for loop. Could it be that I'm not using the thread correctly in the doGravity() method?

Here's the full code:

Board.java

package Game;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedHashSet;
import java.util.Set;

public class Board extends JPanel{

    final int BOARDWIDTH = 8;
    final int BOARDHEIGHT = 8;

    private static final Color COLORS[] = { new Color(255, 0, 0), new Color(255, 128, 0), new Color(255, 255, 0), new Color(0, 255, 0), new Color(0, 255, 255), new Color(0, 0, 255), new Color(127, 0, 255), new Color(0, 0, 0), new Color(0, 0, 0), new Color(255, 255, 255)};

    boolean isAlive, isPattern, switchedBack;
    boolean isFirstSelected = false;
    Gem[][] gems;
    int fromX, fromY, toX, toY;

    public Board() {
        gems = new Gem[BOARDWIDTH][BOARDHEIGHT];
        addMouseListener(new MouseInputAdapter());
    }

    int cellWidth() { return (int) getSize().getWidth() / BOARDWIDTH; }

    int cellHeight() { return (int) getSize().getHeight() / BOARDHEIGHT; }

    public void start(){
        isPattern = switchedBack = false;
        isAlive = true;
        fillBoard();
        checkPattern();
        switchedBack = false;
    }

    public void paint(Graphics g) {
        super.paint(g);

        for (int x = 0; x < BOARDWIDTH; x++) {
            for (int y = 0; y < BOARDHEIGHT; y++)
                drawCell(g, x, y, gems[x][y]);
        }
    }

    public void fillBoard(){
        for (int x = 0; x < BOARDWIDTH; x++) {
            for (int y = 0; y < BOARDHEIGHT; y++)
                gems[x][y] = new Gem();
        }
    }

    public void drawCell(Graphics g, int x, int y, Gem gem) {
        x = x * cellWidth();
        y = y * cellHeight();

        g.setColor(COLORS[gem.getType()]);
        g.fillRect(x, y, x + cellWidth(), y + cellHeight());
    }

    class MouseInputAdapter extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { selectGems(e); } }

    public void selectGems(MouseEvent e){
        int x = e.getX() / cellWidth();
        int y = e.getY() / cellHeight();
        if(!isFirstSelected) {
            fromX = x;
            fromY = y;
            isFirstSelected = true;
        }else{
            toX = x;
            toY = y;
            if((Math.abs(fromX - toX) == 1 ^ Math.abs(fromY - toY) == 1) & (gems[fromX][fromY].getType() != gems[toX][toY].getType())) {
                switchGems();
                isFirstSelected = false;
            }
        }

    }

    public void switchGems(){
        int tempType = gems[fromX][fromY].getType();
        gems[fromX][fromY].setType(gems[toX][toY].getType());
        gems[toX][toY].setType(tempType);
        checkPattern();

        switchedBack = false;
        repaint();
    }

    public void checkPattern() {
        Set<Gem> gemsToDelete = new LinkedHashSet<>();

        isPattern = false;
        for (int x = 0; x < BOARDWIDTH; x++) {
            for (int y = 0; y < BOARDHEIGHT; y++) {
                if (x + 2 < BOARDWIDTH && (gems[x][y].getType() == gems[x + 1][y].getType()) && (gems[x + 1][y].getType() == gems[x + 2][y].getType())) { //Checks for 3 horizontal gems in a row
                    isPattern = true;
                    gemsToDelete.add(gems[x][y]);
                    gemsToDelete.add(gems[x + 1][y]);
                    gemsToDelete.add(gems[x + 2][y]);
                }
                if (y + 2 < BOARDHEIGHT && (gems[x][y].getType() == gems[x][y + 1].getType()) && (gems[x][y + 1].getType() == gems[x][y + 2].getType())) { //Check for 3 vertical gems in a row
                    isPattern = true;
                    gemsToDelete.add(gems[x][y]);
                    gemsToDelete.add(gems[x][y + 1]);
                    gemsToDelete.add(gems[x][y + 2]);
                }
            }
        }
        if(!gemsToDelete.isEmpty())
            deletePattern(gemsToDelete);

        if(!isPattern && !switchedBack){
            switchedBack = true;
            switchGems();
        }
    }

    public void deletePattern(Set<Gem> gemsToDelete){
        for(Gem gem : gemsToDelete)
            gem.setType(7);

        repaint(); //This doesn't seem to work

        doGravity();
        switchedBack = true;
        checkPattern();
    }

    public void doGravity(){
        try{
            Thread.sleep(1000);
        }catch(InterruptedException e){e.printStackTrace();}
        for (int y = 0; y < BOARDHEIGHT; y++) {
            for (int x = 0; x < BOARDWIDTH; x++) {
                if(gems[x][y].getType() == 7){
                    for (int i = y; i >= 0; i--) {
                        if(i == 0)
                            gems[x][i].setType(gems[x][i].genType());
                        else
                            gems[x][i].setType(gems[x][i-1].getType());
                    }
                }
            }
        }
    }
}

Gem.java

package Game;

public class Gem {

    private int type;

    public Gem(){
        this.type = genType();
    }

    public int genType(){ 
        return (int) (Math.random() * 7);
    }

    public void setType(int type){
        this.type = type;
    }

    public int getType(){
        return type;
    }
}

Game.java

package Game;

import javax.swing.*;

public class Game extends JFrame{

    public Game(){
        Board board = new Board();
        getContentPane().add(board);
        board.start();

        setTitle("Game");
        setSize(600, 600);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args){
        Game game = new Game();
        game.setLocationRelativeTo(null);
        game.setVisible(true);
    }
}

Upvotes: 0

Views: 231

Answers (2)

camickr
camickr

Reputation: 324197

Your code is initiated via a mouse click. Code invoked from a Swing listener is executed on the Event Dispatch Thread (EDT), which is also responsible for painting the GUI

The Thread.sleep() in your doGravaity() method causes the EDT to sleep, therefore the GUI can't repaint() itself until the whole looping code is finished, at which point it will just paint the final state of your animation.

Instead of sleeping, you need to use a Swing Timer to schedule animation. So basically, in the deletePattern() method you would start the Timer to do the gravity animation. This will free up the EDT to repaint itself and when the Timer fires you would animate your components one move and then do repaint() again. When the components are finished moving you stop the timer.

Read the section from the Swing tutorial on Concurrency for more information about the EDT.

Upvotes: 3

rogi1609
rogi1609

Reputation: 438

Call this.invalidate() or this.postInvalidate() which then forces a repaint.

Upvotes: -1

Related Questions