I am in the process of making a game, and during this endeavour, I have come across problems maintaining a good frames per second while drawing my sprites. When I draw my background image, my frames drop from ~65 to ~30. My background image is simply a green tile 2000x2000 wide, constructed from 900 50*50 tiles.
What can I do to increase my frames per second?
My Main class
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.Timer;
public class Main extends JPanel implements ActionListener, KeyListener{
static int WIDTH;
static int HEIGHT;
static int SCREENWIDTH;
static int SCREENHEIGHT;
static int GRAVITY = 2;
BufferedImage background;
Tower tower;
Entity debug = new Entity(0, 0, 0, 30, 30, 300);
ArrayList<Entity> entities = new ArrayList<Entity>();
Random random = new Random();
public Main(){
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
requestFocus();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
gameLoop();
}
});
thread.start();
tower = new Tower(WIDTH, HEIGHT, 10, 10, 7);
background = generateBackground();
entities.add(debug);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
AffineTransform plain = g2d.getTransform();
float xscale = (float)SCREENWIDTH/WIDTH;
float yscale = (float)SCREENHEIGHT/HEIGHT;
g2d.scale(xscale, yscale);
g2d.rotate(Math.toRadians(45), WIDTH/2, HEIGHT/2);
AffineTransform normal = g2d.getTransform();
g2d.drawImage(background, 0, 0, this);
for (int e=0;e<entities.size();e++){
Entity entity = entities.get(e);
if (entity.x + entity.w >= tower.x-tower.d && entity.x < tower.x + tower.w || entity.y + entity.h >= tower.y-tower.d && entity.y < tower.y + tower.h){
entity.draw(g2d, normal, this);
}
}
for (int i=0;i<tower.map.size();i++){
for (int j=0;j<tower.map.get(i).size();j++){
for (int k=0; k<tower.map.get(i).get(j).size();k++){
Block renderBlock = tower.map.get(i).get(j).get(k);
int x = renderBlock.x;int y = renderBlock.y;int w = renderBlock.w;int h = renderBlock.h;int z = renderBlock.z;int d = renderBlock.d;
//block
g2d.setTransform(normal);
g2d.drawImage(renderBlock.top1, x-d-z,y-d-z, this);
g2d.transform(AffineTransform.getShearInstance(1, 0));
g2d.drawImage(renderBlock.side1, x-y-w, y-z-(d-h), this);
g2d.setTransform(normal);
g2d.transform(AffineTransform.getShearInstance(0, 1));
g2d.drawImage(renderBlock.side2, x-z-(d-w), y-x-h, this);
}
}
}
for (int e=0; e<entities.size();e++){
Entity entity = entities.get(e);
if (!(entity.x + entity.w >= tower.x-tower.d && entity.x < tower.x + tower.w) || !(entity.y + entity.h >= tower.y-tower.d && entity.y < tower.y + tower.h) || entity.z >= tower.d){
entity.draw(g2d, normal, this);
}
}
FPSticks++;
g2d.setTransform(normal);
g2d.drawLine(WIDTH/2, 0, WIDTH/2, HEIGHT);
g2d.drawLine(0, HEIGHT/2, WIDTH, HEIGHT/2);
g2d.setTransform(plain);
g2d.drawString("FPS: " + currentFPS + " | Logic Ticks: " + currentTPS, 0, 10);
g2d.drawLine(SCREENWIDTH/2, 0, SCREENWIDTH/2, SCREENHEIGHT);
g2d.drawLine(0, SCREENHEIGHT/2, SCREENWIDTH, SCREENHEIGHT/2);
}
public void update(){
if (debug.velx != 0 || debug.vely != 0 || debug.velz != 0){
//say("x: " + debug.x);
//say("y: " + debug.y);
}
debug.z-=GRAVITY;
doCollision();
debug.update();
}
public void doCollision(){
for (int e=0; e<entities.size();e++){
Entity entity = entities.get(e);
if (entity.x+entity.w>=tower.x && entity.x<=tower.x+tower.w && entity.y+entity.h>=tower.y && entity.y<=tower.y+tower.h){
int leftDistance = (entity.x+entity.w)-tower.x;
int rightDistance = entity.x-(tower.x+tower.w);
int topDistance = (entity.y+entity.h)-tower.y;
int bottomDistance = entity.y-(tower.y+tower.h);
int[] distanceArr = {leftDistance, rightDistance, topDistance, bottomDistance};
int[] smallestArr = findSmallest(distanceArr);
int index = smallestArr[1];
if (entity.z < tower.d){
entity.z+=GRAVITY;
}
if (entity.z < tower.d){
if (index == 0){
entity.x-=entity.speed;
} else if (index == 1){
entity.x+=entity.speed;
} else if (index == 2){
entity.y-=entity.speed;
} else if (index == 3){
entity.y+=entity.speed;
}
}
}
}
}
public BufferedImage generateBackground(){
Image grass1 = null;
File grass1f = new File("grass1.png");
File backgroundf = new File("background.png");
try {
grass1 = ImageIO.read(grass1f);
}catch (Exception e){
}
background = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics g = background.getGraphics();
for (int h=0;h<HEIGHT/50;h++){
for (int w=0;w<WIDTH/50;w++){
g.drawImage(grass1, w*50, h*50, this);
}
}
try {
ImageIO.write(background, "PNG", backgroundf);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return background;
}
public int[] findSmallest(int[] arr){
int smallest = arr[0];
int index = 0;
for (int i=0; i<arr.length;i++){
if (arr[i]<0){
arr[i]*=-1;
}
if (arr[i]<smallest){
smallest=arr[i];
index = i;
}
}
int[] returnArr = {smallest, index};
return returnArr;
}
public static void main(String[] args){
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
SCREENWIDTH = (int) screenSize.getWidth();
SCREENHEIGHT = (int) screenSize.getHeight();
//SCREENWIDTH = 800;
//SCREENHEIGHT = 600;
WIDTH = 2000;
HEIGHT = 2000;
Main main = new Main();
JFrame frame = new JFrame();
frame.setTitle("360 ATTACK");
frame.setSize(SCREENWIDTH, SCREENHEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setLocationRelativeTo(null);
frame.add(main);
frame.setVisible(true);
}
long targetFPS = 60;
long currentFPS = targetFPS;
long currentTPS = targetFPS;
long FPSticks = 0;
long TPSticks = 0;
long oldFPSTime = time();
long newFPSTime = oldFPSTime;
public void gameLoop(){
long previous = time();
long lag = 0;
while (true){
long current = time();
long elapsed = current-previous;
previous = current;
lag+= elapsed;
while (lag >= 1000/targetFPS){
update();
lag-= 1000/targetFPS;
TPSticks++;
}
repaint();
newFPSTime = time();
if (newFPSTime > oldFPSTime + 1000){
oldFPSTime = newFPSTime;
currentFPS = FPSticks;
currentTPS = TPSticks;
FPSticks = 0;
TPSticks = 0;
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
}
public long time(){
return System.currentTimeMillis();
}
public void say(String str){
System.out.println(str);
}
@Override
public void keyPressed(KeyEvent e) {
int c = e.getKeyCode();
if (c == KeyEvent.VK_ESCAPE){
System.exit(0);
}
if (c == KeyEvent.VK_W){
debug.vely = -2;
}
if (c == KeyEvent.VK_S){
debug.vely = 2;
}
if (c == KeyEvent.VK_A){
debug.velx = -2;
}
if (c == KeyEvent.VK_D){
debug.velx = 2;
}
if (c == KeyEvent.VK_Q){
debug.velz = -2-GRAVITY;
}
if (c == KeyEvent.VK_E){
debug.velz = 2+GRAVITY;
}
}
@Override
public void keyReleased(KeyEvent e) {
int c = e.getKeyCode();
if (c == KeyEvent.VK_W){
debug.vely = 0;
}
if (c == KeyEvent.VK_S){
debug.vely = 0;
}
if (c == KeyEvent.VK_A){
debug.velx = 0;
}
if (c == KeyEvent.VK_D){
debug.velx = 0;
}
if (c == KeyEvent.VK_Q){
debug.velz = 0;
}
if (c == KeyEvent.VK_E){
debug.velz = 0;
}
}
@Override
public void keyTyped(KeyEvent e) {
}
}
Tower class
import java.util.ArrayList;
public class Tower {
ArrayList<ArrayList<ArrayList<Block>>> map = new ArrayList<ArrayList<ArrayList<Block>>>();
int blockw = 30, blockh = 30, blockd = 30;
int WIDTH;int HEIGHT;
int x;int y;int z;int d;int w;int h;
public Tower(int WIDTH, int HEIGHT, int towerw, int towerh, int towerd){
this.WIDTH = WIDTH;
this.HEIGHT = HEIGHT;
ArrayList<Block> blocks;
ArrayList<ArrayList<Block>> height;
ArrayList<ArrayList<ArrayList<Block>>> depth;
x = ((WIDTH/2)-(towerw*blockw)/2)+(0);
y = ((HEIGHT/2)-(towerh*blockh)/2)+(0);
z = 0;
d = towerd*blockd;
w = towerw*blockw;
h = towerh*blockh;
depth = new ArrayList<ArrayList<ArrayList<Block>>>();
for (int d=0; d<towerd;d++){
height = new ArrayList<ArrayList<Block>>();
for (int h=0; h<towerh;h++){
blocks = new ArrayList<Block>();
for (int w=0; w<towerw;w++){
blocks.add(new Block(x+(w*blockw), y+(h*blockh), (d*blockd), blockw, blockh, blockd));
}
height.add(blocks);
}
depth.add(height);
}
map = depth;
}
}
Entity class
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Entity {
int velx = 0;
int vely = 0;
int velz = 0;
int speed = 2;
int x; int y; int z; int w; int h; int d;
Image side1;
Image side2;
Image top1;
public Entity(int x, int y, int z, int w, int h, int d){
this.x = x;
this.y = y;
this.z = z;
this.w = w;
this.h = h;
this.d = d;
assignImages();
}
private void assignImages(){
File fside1 = new File("side1.png");
File fside2 = new File("side2.png");
File ftop1 = new File("top1.png");
try {
side1 = (ImageIO.read(fside1)).getScaledInstance(w, d, Image.SCALE_DEFAULT);
side2 = (ImageIO.read(fside2)).getScaledInstance(d, h, Image.SCALE_DEFAULT);
top1 = (ImageIO.read(ftop1)).getScaledInstance(w, h, Image.SCALE_DEFAULT);
} catch (IOException e) {
}
}
public void update(){
this.x+=this.velx;
this.y+=this.vely;
this.z+=this.velz;
if (this.z < 0){
this.z=0;
}
}
public void draw(Graphics2D g2d, AffineTransform normal, Main observer){
// entity
g2d.setTransform(normal);
g2d.drawImage(this.top1,this.x-this.d-this.z,this.y-this.d-this.z,observer);
g2d.transform(AffineTransform.getShearInstance(1, 0));
g2d.drawImage(this.side1, this.x-this.y-this.w, this.y-this.z-(this.d-this.h), observer);
g2d.setTransform(normal);
g2d.transform(AffineTransform.getShearInstance(0, 1));
g2d.drawImage(this.side2, this.x-this.z-(this.d-this.w),this.y-this.x-this.h, observer);
}
}
Block class
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Block {
Image side1;
Image side2;
Image top1;
int x,y,z,w,h,d;
public Block(int x, int y, int z, int w, int h, int d){
this.x = x;
this.y = y;
this.z = z;
this.w = w;
this.h = h;
this.d = d;
assignImages();
}
private void assignImages(){
File fside1 = new File("side1.png");
File fside2 = new File("side2.png");
File ftop1 = new File("top1.png");
try {
side1 = ImageIO.read(fside1);
side2 = ImageIO.read(fside2);
top1 = ImageIO.read(ftop1);
} catch (IOException e) {
}
}
}
Images
top1, side2, side1, are all this image: and grass1 is this image: If you need any of the related classes, Just comment and I can add them. I'm not sure if they are necessary though to post.
Block[][][] map
\$\endgroup\$