1

Is there an easier way to code my program such that I can draw my tile-based map onto a Panel (of some sort), such that the map wont redraw each time I resize the window (with resizable off)? I realize that that is great for debugging and testing my mapDrawing function, but, I also don't think I'm doing it ideally, or even in a smart way at all.

My code is as follows.. if you need my subclasses for some reason, I can edit those in too.

import java.awt.*;
import javax.swing.*;


public class AhnkorMyst extends JPanel {  // main game class

static final int screenWidth = 760;
static final int screenHeight = 760;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);    // paint background
        setBackground(Color.BLACK);

        Graphics2D g2d = (Graphics2D) g;
        Map newMap = new Map(g2d, screenWidth, screenHeight);
        newMap.generateBaseMap();
        newMap.populateSurroundings();
        newMap.quadSmoothingIteration ();

        int i, j;
        for (j = 0; j < (newMap.mapHeight / 20); j++) {
            for (i = 0; i < (newMap.mapWidth / 20); i++) {
                newMap.mainMap[i][j].paint();
            }
        }

    }

    public static void main (String[] args) {
        AhnkorMyst game = new AhnkorMyst();
        JFrame frame = new JFrame("Ahnkor Myst");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(game);
        frame.setSize(screenWidth + 10, screenHeight + 30);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setResizable(false);

    }
}

edit** my Map is randomly generated with the generateBaseMap () function.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 1
    Draw the map to a `BufferedImage` first, then draw the image. Only update the map as required... – MadProgrammer Jul 30 '14 at 01:47
  • The way to achieve what you asked for is to render everything to a `BuffereImage` displayed in a `JLabel`. OTOH, this sounds like 'premature optimization'. Java 2-D can paint hundreds of objects each paint before there is any noticeable lag. – Andrew Thompson Jul 30 '14 at 01:47
  • *"I also have a question regarding the JPanel concept itself..."* Not only was that off topic (requesting links to off site resources) but SO Q&As should be one good question with (hopefully) one accepted answer. – Andrew Thompson Jul 30 '14 at 01:50
  • Oh, I should clarify something in my main post. doing so now... but, my 'map' is a randomly generated thing each time is my issue... but I thank you a tonne for the bufferedImage referral! – user2146047 Jul 30 '14 at 01:50
  • Tip: Be sure to add @MadProgrammer (or whoever, the `@` is important) to *notify* a person of a new comment. Exactly one person can be notified per comment. – Andrew Thompson Jul 30 '14 at 01:52
  • Also, don't make edits to a question using the back button. Always use the link below the post. – Andrew Thompson Jul 30 '14 at 01:53
  • @AndrewThompson I apologize for my poor formatting, I will keep this in mind for the future. I also thought I had hit the edit button, but I did not fill in the edit summary. Is that where I should have placed it? – user2146047 Jul 30 '14 at 01:58
  • No, not in the edit summary (to be honest, I often skip that entirely). It must have just been a glitch in the software. No biggie.. – Andrew Thompson Jul 30 '14 at 02:00

2 Answers2

4

This is "very" basic example of the concept. Basically, this re-builds the BufferedImage which represents the basic "view" of the map every time the JPanel is invalidated.

You should note, that I simple randomise the map each time it is built, presumably, you will be using some kind of virtual structure which defines the map itself and would use this to build the map instead...

enter image description here

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestTiles {

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

    public TestTiles() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TileMap());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TileMap extends JPanel {

        private int tileColumns = 8;
        private int tileRows = 8;

        private BufferedImage tileSheet;
        private BufferedImage tileMap;

        public TileMap() {

            try {
                tileSheet = ImageIO.read(getClass().getResource("/TileSet.png"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        }

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

        @Override
        public void invalidate() {
            tileMap = null;
            super.invalidate(); 
        }



        protected void buildMap() {

            tileMap = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = tileMap.createGraphics();
            int tileWidth = tileSheet.getWidth() / tileColumns;
            int tileHeight = tileSheet.getHeight() / tileRows;
            Random random = new Random();
            for (int x = 0; x < getWidth(); x += tileWidth) {
                for (int y = 0; y < getHeight(); y += tileHeight) {
                    int xCell = random.nextInt(tileColumns - 1) * tileWidth;
                    int yCell = random.nextInt(tileRows - 1) * tileHeight;
                    BufferedImage tile = tileSheet.getSubimage(xCell, yCell, tileWidth, tileHeight);
                    g2d.drawImage(tile, x, y, this);
                }
            }
            g2d.dispose();

        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (tileSheet != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                if (tileMap == null) {
                    buildMap();
                }
                g2d.drawImage(tileMap, 0, 0, this);
                g2d.dispose();
            }
        }
    }

}

You could take this concept further and pre-generate the entire world into a single BufferedImage and use getSubImage to grab a smaller portion which what you want to display. This starts to form the basic concept of scrolling, as you could maintain a virtual position in the world and calculate what portion of the map would need to be shown to represent it...

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

Avoid lengthy calculations and instantiations in your implementation of paintComponent(). You can get an idea of the available rendering budget on your target platform using the approach shown in this AnimationTest. Instead, pre-compute as much as possible. In this tile example, the ground map is entirely static, and the rendering is handled by paintIcon(). A related example is examined here.

image

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045