3

this is my first question here so please be patient if I'm doing anything wrong.

I'm trying to create some kind of map editor. Basically I have a 2D array of tiles, tiles have a terrain type and a JPanel draws images for every tile. Now when I click a tile the terrain type is changed and the JPanel repaints every tile that has changed.

The problem is that the image on the JPanel somehow moves a bit when I click a tile. When I resize the window so that every tile is repainted everything looks ok again. But I can't repaint everything when I just changed one tile, that would work very slowly.

I don't know which code samples are relevant for this problem but here is my overridden paintComponent method:

@Override
protected void paintComponent(Graphics g)
{
    super.paintComponents(g);
    Tile[][] tiles = field.getTiles();

    for(int i = 0; i < field.getRows(); i++)
        for(int j = 0; j < field.getColumns(); j++)
        {
            if(field.tileHasChanges(i, j))
            {
                GroundType gt = tiles[i][j].getGround();
                g.drawImage(getGroundImage(gt), j*20, i*20, null);
                field.handledTileChange(i, j);
            }
        }
}
slowdive
  • 31
  • 2
  • So you are calling the `repaint()` that has parameters? – Justin Dec 21 '13 at 23:29
  • Are you sure that these are correct: `j*20, i*20` in your `g.drawImage(...)` method call? A pixel off one way or another will make a huge difference. Also as an aside, `getGroundImage(gt)` doesn't read in a file or resource does it? This only gets an image that you've saved in memory in your program, right? – Hovercraft Full Of Eels Dec 21 '13 at 23:32
  • No, should I? I'm using the parameters of the drawImage method. Repainting the JPanel is just repaint() right now. – slowdive Dec 21 '13 at 23:33
  • the j*20 and i*20 is because the images are 20*20 pixels big. getGroundImage returns a BufferedImage dependent on the GroundType. I'm using the ImageIO.readFile() in it. – slowdive Dec 21 '13 at 23:35
  • 2
    OK, this won't help your spacing problem, but don't read in the images in from the paintComponent method. This method will largely determine how responsive your GUI appears to be to the user, and thus it must be lean and blindingly fast. Instead, read your images in to variables held by your class, and then use those variables. The only exception is when the images are very large, so that holding many of them makes you run out of memory. Then you will have to cache to disk. Regarding your problem, while the images are 20 by 20, are they supposed to be displayed 20 by 20? – Hovercraft Full Of Eels Dec 21 '13 at 23:39
  • You are right that makes sense. Yes they are. Also like I said, everything works if I repaint all images but that is terribly slow. – slowdive Dec 21 '13 at 23:41
  • 1
    Consider creating an [sscce](http://sscce.org) that reproduces your problem. Please read the link. – Hovercraft Full Of Eels Dec 21 '13 at 23:41
  • Ok loading the images just one time helped because now I can redraw the whole thing and it's still fast enough. If I still need a solution for the spacing problem I will do an sscce. Thanks for helping! – slowdive Dec 21 '13 at 23:53
  • One way to get image(s) for an example (an SSCCE) is to hot-link to the images seen in [this answer](http://stackoverflow.com/a/19209651/418556). – Andrew Thompson Dec 22 '13 at 00:02

2 Answers2

3

Rather than drawing images, consider using a grid of JLabels held in a GridLayout for your map's grid, and then simply swapping ImageIcons if you want to change the grid cell's image.

For example to combine code from a previous answer of mine and of TrashGod's, check out this implementation:

import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;

@SuppressWarnings("serial")
public class GridExample extends JPanel {
   private Ground[][] groundMap = {
         { Ground.GRASS, Ground.DIRT, Ground.DIRT, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.DIRT, Ground.WATER,
               Ground.WATER, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.WATER,
               Ground.WATER, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.DIRT,
               Ground.WATER, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.DIRT,
               Ground.WATER, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.DIRT,
               Ground.DIRT, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.DIRT, Ground.DIRT,
               Ground.WATER, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.DIRT,
               Ground.DIRT, Ground.WATER, Ground.WATER, Ground.WATER,
               Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.GRASS,
               Ground.GRASS, Ground.DIRT, Ground.DIRT, Ground.DIRT,
               Ground.DIRT, Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.GRASS,
               Ground.GRASS, Ground.DIRT, Ground.DIRT, Ground.DIRT,
               Ground.WATER, Ground.WATER, Ground.WATER },
         { Ground.GRASS, Ground.GRASS, Ground.GRASS, Ground.GRASS,
               Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.DIRT,
               Ground.DIRT, Ground.WATER, Ground.WATER }, };

   private JLabel[][] labelGrid = new JLabel[groundMap.length][groundMap[0].length];

   public GridExample() {
      setLayout(new GridLayout(groundMap.length, groundMap[0].length));
      for (int r = 0; r < labelGrid.length; r++) {
         for (int c = 0; c < labelGrid[r].length; c++) {
            labelGrid[r][c] = new JLabel();
            labelGrid[r][c].setIcon(groundMap[r][c].getIcon());
            add(labelGrid[r][c]);
         }
      }

      addMouseListener(new MyMouseListener());
   }

   private class MyMouseListener extends MouseAdapter {
      @Override
      public void mousePressed(MouseEvent mEvt) {
         Component comp = getComponentAt(mEvt.getPoint());
         for (int row = 0; row < labelGrid.length; row++) {
            for (int col = 0; col < labelGrid[row].length; col++) {
               if (labelGrid[row][col] == comp) {
                  Ground ground = groundMap[row][col];
                  int mapCode = ground.getValue();
                  mapCode++;
                  mapCode %= Ground.values().length;
                  groundMap[row][col] = Ground.values()[mapCode];
                  labelGrid[row][col].setIcon(groundMap[row][col].getIcon());
               }
            }
         }
      }
   }

   private static void createAndShowGui() {
      GridExample mainPanel = new GridExample();

      JFrame frame = new JFrame("GridExample");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

enum Ground {
   DIRT(0, new Color(205, 133, 63)), GRASS(1, new Color(0, 107, 60)), WATER(2,
         new Color(29, 172, 214));
   private int value;
   private Color color;
   private Icon icon;
   private Random random = new Random();

   private Ground(int value, Color color) {
      this.value = value;
      this.color = color;

      icon = createIcon();
   }

   private Icon createIcon() {
      int width = 24;
      BufferedImage img = new BufferedImage(width, width,
            BufferedImage.TYPE_INT_ARGB);
      for (int row = 0; row < width; row++) {
         for (int col = 0; col < width; col++) {
            if (random.nextBoolean()) {
               img.setRGB(col, row, color.getRGB());
            } else {
               if (random.nextBoolean()) {
                  img.setRGB(col, row, color.darker().getRGB());
               } else {
                  img.setRGB(col, row, color.brighter().getRGB());
               }
            }
         }
      }
      return new ImageIcon(img);
   }

   public int getValue() {
      return value;
   }

   public Color getColor() {
      return color;
   }

   public Icon getIcon() {
      return icon;
   }

   public static Ground getGround(int value) {
      for (Ground ground : Ground.values()) {
         if (ground.getValue() == value) {
            return ground;
         }
      }
      return null;
   }

}
Community
  • 1
  • 1
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
2
 super.paintComponent's'(g);

Maybe the typo is causing the problem. You should be invoking super.paintComponent(g), without the "s".

camickr
  • 321,443
  • 19
  • 166
  • 288