Game rendering issue

Hello,

This is my first post here. I’m very new to game programming. I’m trying to learn it using Java. I have been following this tutorial series on youtube for a little while, just fyi:

My issue is with a section of code with the tutorials provided. I did attempt to get an answer, but could not get enough information to solve it.

Basically the game loads tiles from a spritesheet made by this person for the player and the background. It is 128x128 and when I use the code from the tutorial and his spritesheet, it works flawlessly. The FPS is set to 60 and consistently stays at 60 during runtime.

The issue happens when I chose to try a different spritesheet. I used a few from this particular site just to test with. http://www.realmofdarkness.net/dq/games/nes/dw4/sprites. Some of the sheets work. All of the monsters spritesheets I have tried cause a severe slowdown in the game and cut the FPS literally in half. I have reviewed the image properties of both the spritesheet used in the tutorial and the one from this site and they seem the same to me.

I don’t know what could be causing this issue. Below I will post what I believe to be the relevant sections of code.

Worlds class:


package dev.codenmore.tilegame.worlds;

import java.awt.Graphics;

import dev.codenmore.tilegame.Game;
import dev.codenmore.tilegame.tiles.Tile;
import dev.codenmore.tilegame.utils.Utils;

public class World {

	private Game game;
	private int width, height;
	private int spawnX, spawnY;
	private int[][] tiles;
	
	public World(Game game, String path){
		this.game = game;
		loadWorld(path);
	}
	
	public void tick(){
		
	}
	
	public void render(Graphics g){
		for(int y = 0;y < height;y++){
			for(int x = 0;x < width;x++){
				getTile(x, y).render(g, (int) (x * Tile.TILEWIDTH - game.getGameCamera().getxOffset()),
						(int) (y * Tile.TILEHEIGHT - game.getGameCamera().getyOffset()));
			}
		}
	}
	
	public Tile getTile(int x, int y){
		Tile t = Tile.tiles[tiles[x][y]];
		if(t == null)
			return Tile.dirtTile;
		return t;
	}
	
	private void loadWorld(String path){
		String file = Utils.loadFileAsString(path);
		String[] tokens = file.split("\\s+");
		width = Utils.parseInt(tokens[0]);
		height = Utils.parseInt(tokens[1]);
		spawnX = Utils.parseInt(tokens[2]);
		spawnY = Utils.parseInt(tokens[3]);
		
		tiles = new int[width][height];
		for(int y = 0;y < height;y++){
			for(int x = 0;x < width;x++){
				tiles[x][y] = Utils.parseInt(tokens[(x + y * width) + 4]);
			}
		}
	}
	
}


Tile class:


package dev.codenmore.tilegame.tiles;

import java.awt.Graphics;
import java.awt.image.BufferedImage;

public class Tile {
	
	//STATIC STUFF HERE
	
	public static Tile[] tiles = new Tile[256];
	public static Tile grassTile = new GrassTile(0);
	public static Tile dirtTile = new DirtTile(1);
	public static Tile rockTile = new RockTile(2);
	
	//CLASS
	
	public static final int TILEWIDTH = 64, TILEHEIGHT = 64;
	
	protected BufferedImage texture;
	protected final int id;
	
	public Tile(BufferedImage texture, int id){
		this.texture = texture;
		this.id = id;
		
		tiles[id] = this;
	}
	
	public void tick(){
		
	}
	
	public void render(Graphics g, int x, int y){
		g.drawImage(texture, x, y, TILEWIDTH, TILEHEIGHT, null);
	}
	
	public boolean isSolid(){
		return false;
	}
	
	public int getId(){
		return id;
	}
	
}


Assets class:


package dev.codenmore.tilegame.gfx;

import java.awt.image.BufferedImage;

public class Assets {
private static final int width = 32, height = 32;
    private static final int WIDTH = 48, HEIGHT = 48;	
	public static BufferedImage player, dirt, grass, stone, tree;

	public static void init(){
		//SpriteSheet sheet = new SpriteSheet(ImageLoader.loadImage("/textures/sheet2.png"));
                SpriteSheet sheet = new SpriteSheet(ImageLoader.loadImage("/textures/sheet.png"));
		
		player = sheet.crop(0, 0, width, height);
		dirt = sheet.crop(width, 0, width, height);
		grass = sheet.crop(width * 2, 0, width, height);
		stone = sheet.crop(width * 3, 0, width, height);
		tree = sheet.crop(0, height, width, height);
                
//                player = sheet.crop(200, 200, width, height);
//                dirt = sheet.crop(WIDTH * 3, 16, WIDTH, HEIGHT);
//                tree = sheet.crop(144, 128, WIDTH, HEIGHT);
//                grass = sheet.crop(0, 128, WIDTH, HEIGHT);
//                stone = sheet.crop(144, 80, WIDTH, HEIGHT);
	}	
            
}


Spritesheet class:


package dev.codenmore.tilegame.gfx;

import java.awt.image.BufferedImage;

public class SpriteSheet {

	private BufferedImage sheet;
	
	public SpriteSheet(BufferedImage sheet){
		this.sheet = sheet;
	}
	
	public BufferedImage crop(int x, int y, int width, int height){
		return sheet.getSubimage(x, y, width, height);
	}
	
}


Image Loader class:


package dev.codenmore.tilegame.gfx;

import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;

public class ImageLoader {

	public static BufferedImage loadImage(String path){
		try {
			return ImageIO.read(ImageLoader.class.getResource(path));
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
		return null;
	}
	
}


Game loop



@Override
	public void run(){
	
		init();
		
		int fps = 60;
                // 1 billion nanoseconds is 1 second
		double timePerTick = 1000000000 / fps;
		double delta = 0;
		long now;
		long lastTime = System.nanoTime();
		long timer = 0;
		int ticks = 0;
		
                /* 
                    This game loop runs 60 times per second to achieve 60 FPS
                    Each loop is a frame
                    VARIABLE FRAME RATE 
                
                */
		while(running){
			now = System.nanoTime();
                        // delta is how much time that is required to pass until the loop runs tick and render
                        // In mathematics, the term Delta is used to describe a change in something.
			delta += (now - lastTime) / timePerTick;
                        // timer is for the display of FPS
			timer += now - lastTime;
                        // since delta updated, set lastTime to now for next check
			lastTime = now;
			
			if(delta >= 1){
				tick();
				render();
//                                if(delta >= 2){
//                                    render();   
//                                }
				ticks++;
				delta--;
			}
                        
                        
			
			if(timer >= 1000000000){
				System.out.println("Ticks and Frames: " + ticks);
				ticks = 0; // resets tick
				timer = 0; // resets timer to dislay again
			}
		}
		
		stop();
		
	}

Probably not the problem but in your render method, where you iterate over all the tiles and draw then, should it not be :

x + tileWidth

As well as the height.

Multiplying is probably giving you some weird number and messing with the crop method. Of course it will appear fine on sheets with 1 sprite as anything multiplied by one is itself.

Also, grab yourself a book on java. It will do you a damn sight better than following an online video tutorial.

Thanks for the reply Gibbo3771! Yes I believe your right! I testing using a 400x400 sized image that I made which was the size of one of the monster images from that site and it slowed it down. Then I tested using a 402x402 sized image and it works fine. So something with the calculation of the tiles is wrong? Crazy .

What book do you recommend for java game programming?

I’ve found this series to be very helpful:

I also recommend this one:

That could be causing the problem. Say you have a tile at position (506,769) and you multiply that by the tile width, 128 right? That means the crop method is looking for a position within the image that does not exist. That would be position (64768,98432). Perhaps this crazy number is causing the lag.

Specific to games? No, however try Head First Java or if you are confident, Java in Easy Steps.