I have a University Project to create a simple game in Java. To challenge myself am trying to utilise sprite sheets. My issue is that the whole Sprite sheet is being shown rather than the section that I would like, the whole sprite sheet is shown. Below I will show all my classes. Any help would be much appreciated.
The Game class:
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static final int WIDTH = 320; //Declares width of window
public static final int HEIGHT = WIDTH / 12 * 9;//Declares height
public static final int SCALE = 2;// Declares scale of window
public final String TITLE = "2D Space Game";
private boolean running =false;
private Thread thread;
private BufferedImage image = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
//Buffers the display of images, "loads" it.
private BufferedImage spriteSheet = null;
private Player p;
public void init(){ //initialise
BufferedImageLoader loader = new BufferedImageLoader();
try {
spriteSheet = loader.loadImage("/Sprite_Sheet.png");
}catch(IOException e){
e.printStackTrace();
//if image path cannot be found, error produced
}
p = new Player(200, 200, this);
}
private synchronized void start(){
if (running)
return;//if the method is running, leave the method
running = true;
thread = new Thread(this);
thread.start();
}
private synchronized void stop() {
if(!running)
return;
running = false;// stops game loop
try {
thread.join();//joins all threads
} catch (InterruptedException e) { //thread joining can sometimes fail. catch identifies this
e.printStackTrace();
}
System.exit(1);
}
public void run() {
init();
long lastTime = System.nanoTime(); //long = int, but can store higher positive/lower negative numbers
final double amountOfTicks = 60.0;//Will lock game at 60fps
double ns = 1000000000 / amountOfTicks;
double delta = 0; //calculate time passed, so if fps is behind game can catch up
int updates = 0;// to check fps
int frames = 0;// to check fps
long timer = System.currentTimeMillis();
while(running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now; //This negates the time taken to get from above code to here
if(delta >= 1) {
tick();
updates++;
delta--;// returns delta to not being >=1
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer +=1000;
System.out.println(updates + "Ticks, Fps " + frames); //This code was used to check the ticks and fps of the game
updates = 0;
frames = 0;//resets, so we see ticks and fps per second rather than total
}
}
stop();//calling stop method
}
//This game loop allows for slow computers to play the game as there is a maximum tick speed. Standard for all games.
private void tick() {
p.tick();
}
private void render() {
//Buffer strategy handles all the buffering behind the scenes.
BufferStrategy bs = this.getBufferStrategy(); //Returns a buffer strategy to be used by BufferStrategy, or null if one has not been created
// Above initilises the buffer strategy
if(bs == null) {
createBufferStrategy(3);
return; //If there is no buffer strategy, create 3
//Three buffer strategy loads 3 images, which will be projected one after another. Increases speed.
}
Graphics g = bs.getDrawGraphics(); //creates graphics for buffers
///////////////////////////////////
//Can draw in this area
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
p.render(g);
///////////////////////////////////
g.dispose(); //gets rid of buffer once done with it
bs.show();
}
public static void main(String args[]) {
Game game = new Game();
game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE)); //Creates good aspect ratio
game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame(game.TITLE);
frame.add(game);
frame.pack();//takes all of the components and sizes the frame to the sizes of its components
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//Allows exit button to work
frame.setResizable(false); //Stops player resizing window
frame.setLocationRelativeTo(null);//Not run
frame.setVisible(true);//Window is visible
game.start();
}
public BufferedImage getSpriteSheet() {
return spriteSheet;
//setter, so spritesheet doesnt have to be initialised in musltple clasess
}
}
The Player Class:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class Player {
private double x;//x coor'd of player
private double y;//y coor'd of player
private BufferedImage player;
public Player(double x, double y, Game game) { //constructor method, will be called when player is initialised
this.x = x;
this.y = y;
//sets the coordinates of the player when initialised
/* eg p = new Player(100,100); */
SpriteSheet ss = new SpriteSheet(game.getSpriteSheet());
player = ss.grabImage(1, 1, 32, 32);
//Getter is also in this method, calling the spriteSheet method from the G ame class.
}
public void tick() {
}
public void render(Graphics g) {
g.drawImage(player, (int)x, (int)y, null);
//.drawImage only works with integers, but we declared as double, so must cast as int
}
}
The BufferedImageLoader Class:
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class BufferedImageLoader {
private BufferedImage image;
public BufferedImage loadImage(String path) throws IOException {
image =ImageIO.read(getClass().getResource(path));
return image;
}
}
//This class is made to load in the buffered images.
The Sprite Sheet Class:
import java.awt.image.BufferedImage;
public class SpriteSheet {
private BufferedImage image; // creating another Buffered Image
public SpriteSheet(BufferedImage image) {
this.image = image;
}
public BufferedImage grabImage(int col, int row, int width, int height) {
BufferedImage img = image.getSubimage((col * 32) - 32, (row * 32) -32, width, height);
return image;
//Selecting the rows and columns of the spritesheet in folder res.
//This determines what image from the sprite sheet to display, in this case the player.
}
}
I appreciate that here I have dumped alot of code, but it would be challenging for me to pick it apart to just the sprite sheet code lines as the span across multiple classes. I believe the issue lies within the spritesheet class or the player class, but again I cannot be to sure. Thanks for any help!
EDIT
The sprite I would like to capture is 32 by 32, and the sprite sheet as a whole is 256 by 256.