[Solved]Having trouble with drawing the right way (Top-Down Tile Game)

I’m having trouble drawing the right way it seems, it takes about 500-800 milliseconds to complete a single loop in my render method. Any suggestions or links that might explain what to do. Can’t find anything on the forum, please help! :slight_smile:

This is my render code:



public class GamePanel extends Canvas{

	private BufferedImage backBuffer;
	private int windowHeight;
	private int windowWidth;
	private ZompocalypseGame game;
	private GUI gui;
	private Position position;

	public GamePanel(int windowWidth, int windowHeight, ZompocalypseGame sGame, GUI gui){
		setIgnoreRepaint(true);
		this.windowWidth = windowWidth;
		this.windowHeight = windowHeight;
		setVisible(true);
		this.game = sGame;
		this.gui = gui;
		this.position = new Position(0,0);
		
		this.backBuffer = new BufferedImage(this.windowWidth, this.windowHeight, BufferedImage.TYPE_INT_RGB);
	}

	public void draw(){
		Graphics2D g = (Graphics2D) getGraphics();
		
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, windowWidth, windowHeight);
		
		g.drawImage(this.backBuffer, 0, 0, this);
		g.dispose();
	}

	public void render() {
		Graphics2D bbg = (Graphics2D) this.backBuffer.getGraphics();
		BufferedImage image = null;
		
		Player player = this.game.getTileMap().getPlayer();

		long foo = System.currentTimeMillis();
		
		for(int y = 0; y < 24; y++){
			for(int x = 0; x < 42; x++){
				position.setX(x + player.getPosition().getX()-21);
				position.setY(y + player.getPosition().getY()-12);
				int yOffset = 0;
				//Draw tiles
				Tile tile = this.game.getTileMap().getTileAt(position);
				if(tile != null){
					image = tile.getImage();
				}else{
					tile = new Tile(position, "WATER");
					image = tile.getImage();
				}

				//Draw items
				Item item = this.game.getTileMap().getItemAt(position);
				if(item != null){
					image = item.getImage();
				}

				//Draw buildings
				Building building = this.game.getTileMap().getBuildingAt(position);
				if(building != null){
					image = building.getImage();
				}

				//Draw units
				Unit unit = this.game.getTileMap().getUnitAt(position);
				if(unit != null){
					image = unit.getImage();
					yOffset = -40; //Unit height
				}
				
				bbg.drawImage(image, x*20, y*20+yOffset, this);
				
				bbg.setColor(Color.WHITE);
				bbg.fillRect(0, 580, 800, 20);
				
				bbg.setColor(Color.BLACK);
				bbg.drawString(String.valueOf(gui.getFps()), 10, 595);
			}
		}
		long bar = System.currentTimeMillis();
		System.out.println(bar - foo);
	}
}


Haven’t really looked at the code but at a glance would suggest to create your background image outside your main loop,and in your main loop simply render your ‘already created image’ background…

Can you specify, I don’t quite understand what you mean :slight_smile:

Also found out that I should use a canvas and not a JPanel to draw on, but it didn’t help though… :frowning:

Don’t fill 80020 * 2442 = 16 128 000 pixels per update?

well the only thing i can see that would make it slow is the image format. Create your bufferedImage with GraphicsDevice.createCompatableImage();. Any colour space/format conversions fall back to a really slow software renderer.

In fact you should copy all your images onto a compatible image. For example using ImageIO to load a jpg will return a BufferedImage in a YCrCb or whatever colour space in some cases.

I already use ImageIO, but it’s in the tile class, and I use .png

This is the code I use in Tile.class:



	public BufferedImage getImage() {
		BufferedImage image = null;
		try{
			image = ImageIO.read(new File("bin/image/tile/" + this.type + ".png"));
		}catch (Exception e) {
			System.out.println("Couldn't find " + this.type + "'s image file!");
		}
		if(this.isTargeted){
			Graphics2D g2d = image.createGraphics();

			// Draw on the image
			g2d.setColor(Color.red);
			g2d.fill(new Ellipse2D.Float(0, 0, 20, 20));
			g2d.dispose();
		}
		return image;
	}


There you find the problem: You are loading your image everytime.

Where do you get that calculation from? :stuck_out_tongue:

My point wasn’t to use ImageIO. But that ImageIO will often use a incompatible image format from the device. So that draw image will result in a colour translation per pixel rather than just a mem copy of the data to the video memory.

Now I load the image in the constructor, and it got better, down at 200+ milliseconds, but not quite the time i hoped for… Was aiming for 16 milliseconds (to reach 60 fps)… would it help to only load into memory the images that I draw?

If so, how would I do such a thing?

Are you even reading the posts?

I can’t find the method, that you are referring to :slight_smile:

And I meant loading ONLY the images that I draw into memory :slight_smile:

Your loops:

for(int y = 0; y < 24; y++){
     for(int x = 0; x < 42; x++){

24*42 times runs.

Fills a rectangle 800x20 pixel rectangle at (0, 580). This is done ONCE FOR EACH LOOP = 2442 times * 80020 pixels. Why are you filling a that big rectangle for each tile AT THE SAME PLACE every loop? My post was supposed to make you notice that. :point:

Ohh I feel stupid, will look into that :stuck_out_tongue: Thanks :slight_smile:

Just to make things anymore cryptic (for me especially), when I move around, the time it takes to do the loop is going towards 16 (as I want)… Still having 200+ in the start though o.O

I really can’t figure this out myself, my knowledge on this area isn’t enough :slight_smile:

This is how you create a compatible image (lines 101-113).

As others have pointed out:

  • Try to do as little drawing calls as possible, Java2D is quite slow.
  • Don’t load images in your loop
  • Try to create as little number of objects in your loop
  • Find better ways than using lots of for loops

EDIT: I took a good look at your render() method:

  • I believe theagentd pointed out, don’t fillRect 24*24 times! This should only be done once and on the whole image.
  • Why are you drawing the FPS 24*24 times?! This should also be done once and at the end of your render() method.

Thanks again a lot :slight_smile: I just found out, that if I don’t have any null tiles on my screen it only takes 1-7 milliseconds to draw, so it must be something with the null tiles I draw… Just have to figure out how to solve that problem :slight_smile:

Would need to make my player go off center when he reaches the edge of the world…

But I will look into the link you posted :slight_smile:

And thanks to delt0r and Lordpomeroy too :slight_smile:

If the Tile is null, you create a new Tile object, which means…what is in your Tile constructor? Looks like you are loading the “WATER” image every time :wink:

You’re having a point there… Even though I fixed that… But it works after I just created a null tile, and just using that tiles image :slight_smile:

It down to 1-6 time in milliseconds now! :smiley:

I’m so happy, have been working on those two problems I’ve posted to day for like a month!!!

Gave an appreciate to everyone who helped :slight_smile:

Of cause, now I see the problem… The problem was that I created a new tile if the tile was null, and in the constructor I loaded the image :stuck_out_tongue: