4

I have made a game using CardLayout and have one JPanel for the leaderboard. In leaderboard, I read two files (one with names, one with times), sort them and draw the top 5 fastest times like this:

private void setBoard(){
        reset();
        try{
            br = new BufferedReader (new FileReader("username.txt"));
            accounts = ""+br.readLine().trim();
            arrNames = accounts.split(" ");
            br.close();
            br = new BufferedReader (new FileReader("time.txt"));
            allTimes = ""+br.readLine().trim();
            arrTimesTemp = allTimes.split(" ");
            arrTime = new double [arrTimesTemp.length];
            br.close();
        } catch (Exception er){
            System.out.print("Error" + er);
        }   
        for (int i=0;i<arrTime.length;i++)
            arrTime[i]=Double.parseDouble(arrTimesTemp[i]);;
        scores = new Score[arrTime.length];
        for (int i=0;i<arrTime.length;i++)
            scores[i] = new Score (arrNames[i],arrTime[i]);
        Arrays.sort(scores, new MyComparator());

        System.out.println("\n\n\n");
        for (int i=0;i<arrTime.length;i++)
            System.out.println(arrTime[i]+" "+arrNames[i]);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(title, getWidth()/2-title.getWidth()/2+10, 125, this);
        g.drawImage(txt, getWidth()/2-txt.getWidth()/2+10, 250, this);
        setBoard();
        g.setFont(new Font("Serif", Font.PLAIN, 25));
        g.setColor(Color.white);

        for (int i=0;i<5;i++){
            g.drawString((i+1)+"-", numX, lineY);
            g.drawString(scores[i].getName(), nameX, lineY);
            g.drawString(""+scores[i].getTime(), timeX, lineY);
            g.drawString("s", 515, lineY);
            lineY+=40;
        }
    }

There is no problem the first time I open the leaderboard. However, if I return to the menu and then come back to the leaderboard, the strings to not draw, but the images do. I also so not get any errors. I can't figure out why this is happening.

How I return to menu:

public void actionPerformed(ActionEvent e) {
        if (e.getSource()==back)
            RunGame.card.first(RunGame.c);
    }

How I go to leaderboard from menu:

    else if (e.getSource()==d) //d is a JButton
            RunGame.card.show(RunGame.c,"Leaderboard");

leaderboard first time leaderboard when return to it

Faiq
  • 41
  • 7
  • I'd avoid calling `setBoard` in your `paintComponent` method, this will cause a slow down in the rendering, as each time the component needs to be paint, it needs to reload all the data - better to have a seperate work flow that does this and then the paint component method can just focus on painting the data – MadProgrammer Jun 20 '20 at 00:21
  • How can I do that? The `setBoard` method only works if I call it somewhere and the only place I could think of was in `paintComponent` since the constructor only runs once at the beginning. – Faiq Jun 20 '20 at 00:23
  • Have you thought about calling it BEFORE you present it? How about using a `LeaderBoarder` model instead, which can be shared between the various aspects of your code – MadProgrammer Jun 20 '20 at 00:26
  • I changed my code, so now I have `RunGame.leaderboard.setBoard();` in the menu before I change to leaderboard. But this did not solve the problem. – Faiq Jun 20 '20 at 00:30
  • I solved my problem. I just had to reset `numX nameX timeX lineY` each time I switched to leaderboard. – Faiq Jun 20 '20 at 00:39

1 Answers1

3

I solved my problem. I just had to reset numX nameX timeX lineY each time I switched to leaderboard

Ok, so we've learnt a lesson, global state is a bad thing ...

Ok, since I spent some time putting the example together, I'd post it to demonstrate a basic concept of shared state.

This example creates a LeaderBoardModel which is shared between the main views. This allows the state to be shared between different aspects of your code, without the need to explicitly understand how it actually works - the model becomes a self contained unit of work and can manage it's own state.

Another aspect here, is trying to keep your paint path as fast as possible - so, even in this example, sorting and splitting the list could be considered for optimisation ... but we're not trying to achieve 200fps ... so, balancing...

import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                LeaderBoardModel model = new LeaderBoardModel();
                JFrame frame = new JFrame();
                frame.setLayout(new GridBagLayout());

                GridBagConstraints gbc = new GridBagConstraints();
                gbc.weightx = 1;
                gbc.weighty = 1;
                gbc.gridx = 0;
                gbc.gridy = 0;

                CardLayout cardLayout = new CardLayout();

                JPanel mainPane = new JPanel(cardLayout);
                mainPane.add(new GamePanel(model), "main");
                mainPane.add(new LeaderBoardPanel(model), "leaderBoard");

                cardLayout.show(mainPane, "main");

                frame.add(mainPane, gbc);

                JPanel buttons = new JPanel(new GridBagLayout());
                JButton main = new JButton("Main");
                JButton board = new JButton("Board");

                buttons.add(main);
                buttons.add(board);

                main.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        cardLayout.show(mainPane, "main");
                    }
                });
                board.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        cardLayout.show(mainPane, "leaderBoard");
                    }
                });

                gbc.weightx = 1;
                gbc.weighty = 0;
                gbc.gridy++;
                frame.add(buttons, gbc);

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    // I'd prefer to use interfaces as a base line concept, but, this is
    // is a demonstration of a concept, not a tutorial
    public class Score implements Comparable<Score> {

        private String name;
        private int score;

        public Score(String name, int score) {
            this.name = name;
            this.score = score;
        }

        public String getName() {
            return name;
        }

        public int getScore() {
            return score;
        }

        @Override
        public int compareTo(Score o) {
            return o.score - score;
        }

        @Override
        public String toString() {
            return name + " ~ " + score;
        }

    }

    // I'd prefer to use interfaces as a base line concept, but, this is
    // is a demonstration of a concept, not a tutorial
    public class LeaderBoardModel {

        private List<Score> scores;

        public LeaderBoardModel() {
            // This is where you'd consider loading the scores from
            // the original file...
            scores = new ArrayList<>(25);

            Random rnd = new Random();

            add(new Score("Shekhar", rnd.nextInt(99) + 1));
            add(new Score("Priyanka", rnd.nextInt(99) + 1));
            add(new Score("Vivi", rnd.nextInt(99) + 1));
            add(new Score("Darwin", rnd.nextInt(99) + 1));
            add(new Score("Hálfdan", rnd.nextInt(99) + 1));
            add(new Score("Emmerson", rnd.nextInt(99) + 1));
            add(new Score("Marie", rnd.nextInt(99) + 1));
            add(new Score("Mikha'il", rnd.nextInt(99) + 1));
            add(new Score("Jayanta", rnd.nextInt(99) + 1));
            add(new Score("Theodosia", rnd.nextInt(99) + 1));
            add(new Score("Sharleen", rnd.nextInt(99) + 1));
            add(new Score("Kristian", rnd.nextInt(99) + 1));
            add(new Score("Alberte", rnd.nextInt(99) + 1));
            add(new Score("Maylis", rnd.nextInt(99) + 1));
            add(new Score("Katayun", rnd.nextInt(99) + 1));
        }

        public void save() {
            // Well, because it's nice to remember this stuff
        }

        public void add(Score score) {
            // We could sort the list here, lots of ways to do it ...
            // but waste the time if we don't actually need to
            scores.add(score);
        }

        public List<Score> getScores() {
            // Could use a flag to determine if this actually needs to be
            // sorted or not ... but this is just a demonstration :/
            Collections.sort(scores);
            return Collections.unmodifiableList(scores);
        }

    }

    public class GamePanel extends JPanel {

        private LeaderBoardModel model;
        private Random rnd = new Random();

        public GamePanel(LeaderBoardModel model) {
            this.model = model;

            setLayout(new GridBagLayout());

            JButton btn = new JButton("Win");
            add(btn);
            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    model.add(new Score("l334", rnd.nextInt(99) + 100));
                }
            });
        }

    }

    public class LeaderBoardPanel extends JPanel {

        private LeaderBoardModel model;

        public LeaderBoardPanel(LeaderBoardModel model) {
            this.model = model;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(300, 300);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            String highScore = "High Score";

            FontMetrics fm = g2d.getFontMetrics();
            int xPos = (getWidth() - fm.stringWidth(highScore)) / 2;
            int yPos = 10;
            g2d.drawString(highScore, xPos, yPos + fm.getAscent());

            yPos += fm.getHeight() * 2;

            List<Score> scores = model.getScores();
            scores = scores.subList(0, Math.min(10, scores.size() - 1));

            for (Score score : scores) {
                String name = score.getName();
                String value = Integer.toString(score.getScore());

                xPos = (getWidth() / 2) - fm.stringWidth(name) - 5;
                g2d.drawString(name, xPos, yPos + fm.getAscent());
                xPos = (getWidth() / 2) + 5;
                g2d.drawString(value, xPos, yPos + fm.getAscent());

                yPos += fm.getHeight();
            }

            g2d.dispose();
        }

    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366