'Creating Game using multithreading, but have problems with smoothness of background movement in Java Swing
I'm working on a project which is about creating a game. I was implementing threads to move my background but had some problems with the smoothness of background movement. I also have a bird component, but it is supposed to be used with keybindings. Both of these components are drawn by using the paintComponent()
method.
The problem is when I start the game, the background starts moving, but after 1-2 seconds, it starts lagging, but when I use keys to move my bird component right and left, the background movement becomes smooth again, and when I don't press any key, it starts lagging again. I also observed that when I hover the mouse over the window, it also starts moving smoothly. I couldn't realize the problem. Tried to use alternatives instead of original ones, like using labels instead of paintComponent()
and using timer
. instead of thread
, but nothing changes.
I also searched about this problem a lot, and found some options like PC performance and so on. But I don't think that I have a problem with that. Is there a way to solve this problem? Thanks beforehand! Here is the code:
FlyingChicken.java
This is the main class to call other classes
import java.io.*;
import java.time.*;
import java.lang.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
public class FlyingChicken extends JFrame implements ActionListener{
JButton startButton, firstLoginButton, firstRegisterButton, firstQuitButton;
boolean gameStarted = false, gameOver = false, gamePaused = false;
RegisterPage registerPage;
LoginPage loginPage;
GamePage gamePage;
public FlyingChicken(){
//FirstRegisterButton
firstRegisterButton = new JButton("Register Account");
firstRegisterButton.setBounds(360,445,200,50);
firstRegisterButton.setFocusable(false);
firstRegisterButton.addActionListener(this);
firstRegisterButton.setBackground(Color.green);
firstRegisterButton.setForeground(Color.white);
//FirstLoginButton
firstLoginButton = new JButton("Login Account");
firstLoginButton.setBounds(360,520,200,50);
firstLoginButton.setFocusable(false);
firstLoginButton.addActionListener(this);
firstLoginButton.setBackground(Color.green);
firstLoginButton.setForeground(Color.white);
//FirstQuitButton
firstQuitButton = new JButton("Quit the Game");
firstQuitButton.setBounds(385,595,150,50);
firstQuitButton.setFocusable(false);
firstQuitButton.addActionListener(this);
firstQuitButton.setBackground(Color.green);
firstQuitButton.setForeground(Color.white);
//StartButton
startButton = new JButton("Start Game");
startButton.setBounds(360,495,200,50);
startButton.setFocusable(false);
startButton.addActionListener(this);
startButton.setVisible(false);
startButton.setFont(new Font("Arial",Font.BOLD, 20));
loginPage = new LoginPage();
registerPage = new RegisterPage();
gamePage = new GamePage();
loginPage.loginPageVisibility(false);
registerPage.registerPageVisibility(false);
gamePage.gamePageVisibility(false);
loginPage.backButton.addActionListener(this);
loginPage.loginButton.addActionListener(this);
registerPage.registerButton.addActionListener(this);
registerPage.backButton.addActionListener(this);
gamePage.quitButton.addActionListener(this);
//Frame Settings
Image img = new ImageIcon("Flying_Chicken_Icon.png").getImage();
this.setIconImage(img);
this.setSize(920,1080);
this.setTitle("Flying Chicken");
this.getContentPane().setBackground(new Color(14, 194, 176));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setFocusable(true);
this.setLayout(null);
this.setVisible(true);
this.add(loginPage);this.add(registerPage);
this.add(gamePage);this.add(startButton);
this.add(firstQuitButton);this.add(firstRegisterButton);
this.add(firstLoginButton);
}
public void mainPanel(){
this.getContentPane().setBackground(new Color(14, 194, 176));
firstRegisterButton.setVisible(true);
firstQuitButton.setVisible(true);
firstLoginButton.setVisible(true);
loginPage.loginPageVisibility(false);
registerPage.registerPageVisibility(false);
loginPage.loginLabel.setVisible(false);
registerPage.label1.setVisible(false);
registerPage.label2.setVisible(false);
registerPage.label3.setVisible(false);
registerPage.label4.setVisible(false);
registerPage.label5.setVisible(false);
}
public void loginPanel(){
loginPage.tpassword.setText("");
loginPage.tusername.setText("");
firstRegisterButton.setVisible(false);
firstQuitButton.setVisible(false);
firstLoginButton.setVisible(false);
loginPage.loginPageVisibility(true);
registerPage.registerPageVisibility(false);
}
public void registerPanel(){
loginPage.loginPageVisibility(false);
registerPage.registerPageVisibility(true);
registerPage.tpassword.setText("");
registerPage.tusername.setText("");
firstRegisterButton.setVisible(false);
firstQuitButton.setVisible(false);
firstLoginButton.setVisible(false);
}
public void startPage(){
this.getContentPane().setBackground(new Color(128,198,209));
startButton.setVisible(true);
loginPage.loginPageVisibility(false);
registerPage.registerPageVisibility(false);
}
public void startGame(){
startButton.setVisible(false);
gamePage.gamePageVisibility(true);
// gamePage.timer.start();
gameStarted = true;
}
public void quitFunction(){
String[] resp = {"Resume", "Quit"};
int k = JOptionPane.showOptionDialog(this, "Are you sure you want to quit?","Quit Panel",JOptionPane.YES_NO_OPTION,JOptionPane.INFORMATION_MESSAGE, null, resp, 0);
if (k==1){
this.setVisible(false);
this.dispose();
}
}
@Override
public void actionPerformed(ActionEvent e){
if(e.getSource() == startButton){
startGame();
}
if(e.getSource() == gamePage.quitButton){
// gamePage.timer.stop();
quitFunction();
}
if(e.getSource() == firstQuitButton){
quitFunction();
}
if(e.getSource() == firstRegisterButton){
registerPanel();
}
if(e.getSource() == firstLoginButton){
loginPanel();
}
if(e.getSource() == registerPage.registerButton){
if(registerPage.registerValidate() == false){
registerPage.registerData();
loginPanel();
}
}
if(e.getSource() == loginPage.loginButton){
if(loginPage.loginValidate() == false)
startPage();
}
if(e.getSource() == loginPage.backButton || e.getSource() == registerPage.backButton){
mainPanel();
}
}
public static void main(String[] args){
FlyingChicken game = new FlyingChicken();
}
}
GamePage.java
This is the class that I'm supposed to create animations and game
import java.io.*;
import java.time.*;
import java.lang.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
public class GamePage extends JPanel implements ActionListener, Runnable{
JLabel scoreBoard;
Image backgroundImage, birdImage, catImage;
JButton quitButton;
int birdX, birdY, xpos=0, ypos=0, score = 0;
Timer timer;
Thread animator;
Action leftAction, rightAction;
public GamePage(){
//GamePage Panel Settings
this.setSize(920,1080);
this.setLocation(xpos,ypos);
this.setLayout(null);
this.setFocusable(true);
this.setVisible(true);
backgroundImage = new ImageIcon("background.jpg").getImage();
birdImage = new ImageIcon("bird.png").getImage();
birdX = 264;birdY = 230;
//Scoreboard
scoreBoard = new JLabel("Score: "+ score);
scoreBoard.setOpaque(false);
scoreBoard.setForeground(new Color(33,30,31));
scoreBoard.setFont(new Font("Arial", Font.BOLD, 25));
scoreBoard.setBounds(0,0,200,30);
scoreBoard.setVisible(true);
//QuitButton
quitButton = new JButton("Quit");
quitButton.setBounds(820,0,100,50);
quitButton.setFocusable(false);
quitButton.setVisible(true);
// timer = new Timer(10,this);
createKeyBindings(this);
this.add(quitButton);
this.add(scoreBoard);
}
public void addNotify(){
super.addNotify();
animator = new Thread(this);
animator.start();
}
@Override
public void run(){
while(true){
try{
ypos = ypos-1;
repaint();
Thread.sleep(25);
} catch(InterruptedException e){System.out.println("hey");}
}
}
//The function to draw background
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(backgroundImage, xpos, ypos, null);
g.drawImage(birdImage,birdX,birdY,null);
}
@Override
public void actionPerformed(ActionEvent e){
// scoreBoard.setText("Score: "+score);
repaint();
}
//The function to create keybindings
public void createKeyBindings(JPanel p) {
InputMap im = p.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = p.getActionMap();
leftAction = new LeftAction();
rightAction = new RightAction();
im.put(KeyStroke.getKeyStroke("LEFT"), "left");
im.put(KeyStroke.getKeyStroke("RIGHT"), "right");
am.put("left", leftAction);
am.put("right", rightAction);
}
public class LeftAction extends AbstractAction{
@Override
public void actionPerformed(ActionEvent e){
if(birdX>0)
birdX = birdX-10;
repaint();
}
}
public class RightAction extends AbstractAction{
@Override
public void actionPerformed(ActionEvent e){
if(birdX+530<1080)
birdX = birdX+10;
repaint();
}
}
public void gamePageVisibility(boolean x){
this.setVisible(x);
}
}
EDIT: I found the actual reason my application was lagging. The problem was mainly related with Linux OS graphics and solved it by adding Toolkit.getDefaultToolkit().sync()
to my loop.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|