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();
}