patternjavaMinor
Avoid flickering on game screen
Viewed 0 times
flickeringgamescreenavoid
Problem
This is the smallest SSCCE of my project. I get flickering on the screen with this code.
Main class (on which I start the application)
```
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable {
// game manager
private GameManager gameManager;
// renderManager
private RenderManager renderManager;
// image
private BufferedImage image;
Graphics2D graphic2D;
// game thread
private Thread thread;
private boolean running;
public GamePanel() {
setPreferredSize(new Dimension(GuiDimension.WIDTH.getValue(), GuiDimension.HEIGHT.getValue()));
setIgnoreRepaint(true);
setFocusable(true);
requestFocusInWindow();
}
public void startGame() {
init();
if (thread == null) {
this.addKeyListener(KeyInput.getKeyInputInstance());
this.addMouseListener(MouseInput.getMouseInputInstance());
this.addMouseMotionListener(MouseInput.getMouseInputInstance());
this.addMouseWheelListener(MouseInput.getMouseInputInstance());
thread = new Thread(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(GuiDimension.WIDTH.getValue(), GuiDimension.HEIGHT.getValue(), BufferedImage.TYPE_INT_RGB);
gameManager = new GameManager(States.LOADING.getValue());
gameManager.loadCurrentState(States.LOADING.getValue());
Main class (on which I start the application)
import javax.swing.JFrame;
public class MainFrame {
public static void main(String[] args) {
final JFrame frame = new JFrame();
final GamePanel gamePanel = new GamePanel();
frame.add(gamePanel);
gamePanel.startGame();
frame.setUndecorated(true);
frame.pack();
frame.setVisible(true);
}
}GamePanel class (start/running game loop)```
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable {
// game manager
private GameManager gameManager;
// renderManager
private RenderManager renderManager;
// image
private BufferedImage image;
Graphics2D graphic2D;
// game thread
private Thread thread;
private boolean running;
public GamePanel() {
setPreferredSize(new Dimension(GuiDimension.WIDTH.getValue(), GuiDimension.HEIGHT.getValue()));
setIgnoreRepaint(true);
setFocusable(true);
requestFocusInWindow();
}
public void startGame() {
init();
if (thread == null) {
this.addKeyListener(KeyInput.getKeyInputInstance());
this.addMouseListener(MouseInput.getMouseInputInstance());
this.addMouseMotionListener(MouseInput.getMouseInputInstance());
this.addMouseWheelListener(MouseInput.getMouseInputInstance());
thread = new Thread(this);
thread.start();
}
}
private void init() {
image = new BufferedImage(GuiDimension.WIDTH.getValue(), GuiDimension.HEIGHT.getValue(), BufferedImage.TYPE_INT_RGB);
gameManager = new GameManager(States.LOADING.getValue());
gameManager.loadCurrentState(States.LOADING.getValue());
Solution
Java
You tabbing, makes your code less readable, after every
looks like you're going to break either way, how bout doing this
Structure
Instead of making
You could even have a class called
To prevent flickering, Double Buffer your rendering process.
Ensure that in your
If you want to see an example of this, check out the Source Engine's implementation.
https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/server/player.cpp
Here you will see after events happen in game, the programmer manually sets the next time the think() method will be ran, which is called update() for you
You also see in here, that the programmer will specify when an item should start or stop thinking.
Separate your main-loop logic from your main menu rendering logic.
You should have a main-loop, which calls recalculate, and draw, then in your draw you should draw the appropriate menu, based on the games current state, the
You tabbing, makes your code less readable, after every
bracket {
you should be indenting the following lines
}break;
} else {
break;looks like you're going to break either way, how bout doing this
}
break;Structure
Instead of making
ImageProvider a final class, like you have there, make it a class responsible for loading one image, then maintain a dictionary of all the resources you've loaded, this way your game will be much more scalable, and values wont be hardcoded.You could even have a class called
DefaultImages to directly point to some of these commonly needed images, who's names wont change. You would still load the images with ImageProvder, but would simply maintain a direct pointer to your desired images.To prevent flickering, Double Buffer your rendering process.
Ensure that in your
gameManager.update(); you aren't actually doing the logical calculations every time. I assume you are doing your calculations based on a delta based system, where your calculations used the elapsed time since your last .update() to determine how much (lets say) something has moved. So be sure you've implemented a system, where the gameManager calls update on all the items in the list of items, and each one of them determine if the time since they last updated their stuff, was long enough ago to warrant recalculating their position. (Each item class maintaining how often they need calculations)If you want to see an example of this, check out the Source Engine's implementation.
https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/server/player.cpp
Here you will see after events happen in game, the programmer manually sets the next time the think() method will be ran, which is called update() for you
SetNextThink( gpGlobals->curtime + 0.1f );You also see in here, that the programmer will specify when an item should start or stop thinking.
bool CBasePlayer::RemovePlayerItem( CBaseCombatWeapon *pItem )
{
if (GetActiveWeapon() == pItem)
{
ResetAutoaim( );
pItem->Holster( );
pItem->SetNextThink( TICK_NEVER_THINK );; // crowbar may be trying to swing again, etc
pItem->SetThink( NULL );
}
if ( m_hLastWeapon.Get() == pItem )
{
Weapon_SetLast( NULL );
}
return Weapon_Detach( pItem );
}Separate your main-loop logic from your main menu rendering logic.
You should have a main-loop, which calls recalculate, and draw, then in your draw you should draw the appropriate menu, based on the games current state, the
GamePanel logic should stay in its own class away from the main-loop.Code Snippets
bracket {
you should be indenting the following lines
}break;
} else {
break;SetNextThink( gpGlobals->curtime + 0.1f );bool CBasePlayer::RemovePlayerItem( CBaseCombatWeapon *pItem )
{
if (GetActiveWeapon() == pItem)
{
ResetAutoaim( );
pItem->Holster( );
pItem->SetNextThink( TICK_NEVER_THINK );; // crowbar may be trying to swing again, etc
pItem->SetThink( NULL );
}
if ( m_hLastWeapon.Get() == pItem )
{
Weapon_SetLast( NULL );
}
return Weapon_Detach( pItem );
}Context
StackExchange Code Review Q#52341, answer score: 4
Revisions (0)
No revisions yet.