So, I have been working on a 2d rpg for some time now and I can't seem to fix this one problem. The graphics seem to "jump" or stutter every few seconds for an unknown reason. This is getting quite annoying because I don't know what is causing it.
Here is a very basic program I wrote that just has a red square that moves from one side of the screen to the other side. Even in this very basic program the square still stutters every few updates and I really can't figure it out for the life of me.
public class Main extends JPanel {
int x=0, y=0;
public JFrame window = new JFrame("Window");
public Main(){
window.setSize(1000, 500);
window.setVisible(true);
window.add(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(x, y, 500, 500);
x+=3;
if(x>900){
x=0;
}
}
public void start(){
while(true){
repaint();
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
Main game = new Main();
game.start();
}
}
If you run the class you will see what the problem is graphically. Obviously my game is made up of many more classes and is far more complex than this, but the same principal applies. If any one has any insight to my problem I would love to hear it. Thanks in advance.
Updated
Here are my two main classes:
Main Class:
package com.ultimatum.Main;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.ultimatum.Mangers.ImageLoader;
import com.ultimatum.Mangers.KeyStates;
import com.ultimatum.Mangers.ScreenUpdater;
import com.ultimatum.Mangers.UserInput;
@SuppressWarnings("serial")
public class Ultimatum extends JPanel {
/**
* @param x This is the start width of the screen and can be adjusted manually, but will not go any lower than this integer
* @param y This is the start height of the screen and can be adjusted manually, but will not go any lower than this integer
* @param contentPlaneX This is how much the width of the content plane is (Frame-Borders)
* @param contentPlaneY This is how much the height of the content plane is (Frame-Borders)
*/
public int x=850, y=610, contentPlaneX, contentPlaneY, middleX, middleY, tileSize=90;
public Dimension minimumSize = new Dimension(x, y);
public JFrame window = new JFrame("Ultimatum");//This makes the JFrame for the game
public KeyStates keyStates = new KeyStates();
public UserInput input = new UserInput(keyStates);
public ImageLoader imageLoader = new ImageLoader();
public static Ultimatum ultimatum;//Makes the object of this class
public static ScreenUpdater su;//This is creating the object that is going to be making changes to the screen. For example, the animation.
private BufferedImage screenImage;
public boolean isWindowInFullscreenMode;
private boolean imagesLoaded;
public void initializeUltimatum(){
toWindowedMode();
addMouseListener(input);
addMouseMotionListener(input);
contentPlaneX=window.getContentPane().getWidth();
contentPlaneY=window.getContentPane().getHeight();
middleX=(int)contentPlaneX/2;
middleY=(int)contentPlaneY/2;
su = new ScreenUpdater(ultimatum, keyStates, imageLoader, "Test", tileSize);
imageLoader.loadImages();
}
public void toFullscreenMode(){
window.dispose();
window.setUndecorated(true);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(0,0,Toolkit.getDefaultToolkit().getScreenSize().width,Toolkit.getDefaultToolkit().getScreenSize().height);
addListenersAndClassToWindow();
isWindowInFullscreenMode=true;
}
public void toWindowedMode(){
window.dispose();
window.setUndecorated(false);
window.setSize(x,y);
window.setMinimumSize(minimumSize);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setLocationRelativeTo(null);
addListenersAndClassToWindow();
isWindowInFullscreenMode=false;
}
public void addListenersAndClassToWindow(){
window.add(ultimatum);//This connects paintComponent and the frame to this class
window.addKeyListener(input);
}
public void paintComponent(Graphics g){
if(imagesLoaded){
super.paintComponent(g);
//su.updateScreen(g);
g.drawImage(screenImage, 0, 0, contentPlaneX, contentPlaneY, null);
}else imagesLoaded = true;
}
public void update(){
screenImage = su.updateScreen();
}
/**
* This main class sets up the program. The while loop that keeps the game running is also contained inside this class. Most of this class is easily
* readable so i'm not going to comment that much.
*/
public static void main(String[] args){
ultimatum = new Ultimatum();
ultimatum.initializeUltimatum();
final int FPS=60, TARGET_TIME=1000/FPS;
long start, elapsed, wait;
while(true){//This loops purpose is to keep the game running smooth on all computers
start = System.nanoTime();
ultimatum.update();
ultimatum.repaint();//This calls the paintComponent method
elapsed = System.nanoTime() - start;
wait = TARGET_TIME-elapsed/1000000;
if(wait<0) wait = TARGET_TIME;
try{//Catches the error in case the tries to give an error (which it won't)
Thread.sleep(wait);//This is how long it waits it till the screen gets repainted
}catch(Exception e){
e.printStackTrace();
}
}
}
}
Screen Updater:
package com.ultimatum.Mangers;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import com.ultimatum.Engine.BuildingGenerator;
import com.ultimatum.Engine.TextBoxGenerator;
import com.ultimatum.Entities.Character.Player;
import com.ultimatum.Gamestates.Buildings.HealingCenter;
import com.ultimatum.Gamestates.Menus.EscapeScreen;
import com.ultimatum.Gamestates.Menus.StartScreen;
import com.ultimatum.Gamestates.Menus.TitleScreen;
import com.ultimatum.Gamestates.Routes.RouteSuperClass;
import com.ultimatum.Gamestates.Towns.TownSuperClass;
import com.ultimatum.Main.Ultimatum;
public class ScreenUpdater {
public Ultimatum ul;
public Resizer rs;//This is the object that captures the resize in two integers
public KeyStates ks;
public ImageLoader loader;
public Fader fader;
public TextBoxGenerator textBox;
public Initializer initer;
public TileMap tm;
public Player p;
public BuildingGenerator bg;
//Menus
public TitleScreen titleScreen;
public StartScreen startScreen;
public EscapeScreen escScreen;
//Towns
public TownSuperClass towns;
//Routes
public RouteSuperClass routes;
//Buildings
public HealingCenter healingCenter;
public final int TITLE_SCREEN=0, START_SCREEN=TITLE_SCREEN+1, LOAD=START_SCREEN+1, TOWN_ONE=LOAD+1,
ROUTE_ONE=TOWN_ONE+1, HEALING_CENTER=ROUTE_ONE+1, ESC_MENU=HEALING_CENTER+1;
public int screenNo = TITLE_SCREEN;
public int prevScreen=0;
public boolean prevMenuState, menuState;//These variables are for the checkEsc method
public boolean isMouseVisible=true, prevIsMouseVisible;//Simple boolean for setting the mouse from visible to invisible and vice versa
public ScreenUpdater(Ultimatum ultimatum, KeyStates keyStates, ImageLoader imageloader, String location,
int tileSize){
ul = ultimatum;
ks = keyStates;
loader = imageloader;
fader = new Fader(ul, this);
textBox = new TextBoxGenerator(loader, ks, ul);
initer = new Initializer(fader, textBox);
fader.sendIniterData(initer);
p = new Player(ul, fader, loader, ks, initer, this);
fader.sendPlayerData(p);
tm = new TileMap(tileSize, loader, p);
fader.sendTileMapData(tm);
rs = new Resizer(ul, p);
bg = new BuildingGenerator(ul, p, loader, tm);
//Below are the game states being loaded
//Menus
titleScreen = new TitleScreen(ul, this, loader, ks, fader);
startScreen = new StartScreen(ul, this, fader, loader, ks, textBox);
escScreen = new EscapeScreen(ul, fader, loader, ks);
rs.sendEscapeScreenData(escScreen);
//Towns
towns = new TownSuperClass(p, fader, bg, tm, this);
//Routes
routes = new RouteSuperClass(p, fader, bg, tm, this);
//Buildings
healingCenter = new HealingCenter(ul, fader, loader, ks, textBox);
}
public void clearScreen(Graphics g){
g.setColor(Color.black);
g.fillRect(0, 0, ul.contentPlaneX, ul.contentPlaneY);
}
public void checkEsc(Graphics g){
if(ks.escReleased&&screenNo>LOAD&&!fader.fadingOut&&fader.fadingIn){
if(screenNo<HEALING_CENTER&&!p.isMoving){
menuState=true;
prevScreen=screenNo;
}
else if(screenNo==ESC_MENU) menuState=false;
}
if(prevMenuState!=menuState){
int toScreen;
boolean mouseVisiblity;
if(menuState){
toScreen=ESC_MENU;
mouseVisiblity=true;
}
else{
toScreen=prevScreen;
mouseVisiblity=false;
}
fader.FadeOut(g, 255, toScreen, false, "", 0, 0, false, 0, mouseVisiblity);//The zeros don't matter because the boolean is set to false
if(!fader.fadingOut){
prevMenuState=menuState;
initer.initFader();
}
}
}
public void checkForF11(){
if(ks.isF11PressedThenReleased){
if(ul.isWindowInFullscreenMode) ul.toWindowedMode();
else ul.toFullscreenMode();
}
}
public void setMouseVisible(){
ul.window.setCursor(ul.window.getToolkit().createCustomCursor(loader.cursor, new Point(0, 0),"Visible"));
}
public void setMouseInvisble(){
ul.window.setCursor(ul.window.getToolkit().createCustomCursor(new BufferedImage(3, 3, BufferedImage.TYPE_INT_ARGB), new Point(0, 0),"Clear"));
}
public void checkMouseState(){
if(isMouseVisible!=prevIsMouseVisible){
if(isMouseVisible) setMouseVisible();
else setMouseInvisble();
prevIsMouseVisible=isMouseVisible;
}
}
public BufferedImage updateScreen(){
BufferedImage screenImage = new BufferedImage(ul.contentPlaneX, ul.contentPlaneY, BufferedImage.TYPE_INT_ARGB);
Graphics2D screenGraphics = screenImage.createGraphics();
Color oldColor = screenGraphics.getColor();
screenGraphics.setPaint(Color.white);
screenGraphics.fillRect(0, 0, ul.contentPlaneX, ul.contentPlaneY);
screenGraphics.setColor(oldColor);
checkForF11();
clearScreen(screenGraphics);
switch(screenNo){
case TITLE_SCREEN:
titleScreen.titleScreen(screenGraphics);
break;
case START_SCREEN:
startScreen.startScreen(screenGraphics);
break;
case TOWN_ONE:
towns.townOne(screenGraphics);
break;
case ROUTE_ONE:
routes.routeOne(screenGraphics);
break;
case HEALING_CENTER:
healingCenter.healingCenter(screenGraphics);
break;
case ESC_MENU:
escScreen.escapeScreen(screenGraphics);
break;
}
checkEsc(screenGraphics);
rs.checkForResize();
ks.update();
checkMouseState();
//g.drawImage(screenImage, 0, 0, ul.contentPlaneX, ul.contentPlaneY, null);
//screenGraphics.dispose();
return screenImage;
}
}