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 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());
renderManager = new RenderManager(gameManager);
running = true;
}
public void run() {
// game loop
while (running) {
gameManager.update();
KeyInput.getKeyInputInstance().update();
MouseInput.getMouseInputInstance().update();
repaint();
try {
Thread.sleep(15);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void update(Graphics graphic) {
super.update(graphic);
}
@Override
protected void paintComponent(Graphics graphic) {
super.paintComponent(graphic);
graphic2D = image.createGraphics();
image.setAccelerationPriority(1);
// to best visualization of the shapes and all graphics components
graphic2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphic2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
graphic2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
graphic2D.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
graphic2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
graphic2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphic2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphic2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
renderManager.setRenderState(graphic2D);
// draw the graphic of the setRender into the panel image
graphic.drawImage(image, 0, 0, this);
// clear graphics resources after use them
graphic2D.dispose();
image.flush();
}
GameManager
is a class that controls logic update.
RenderManager
is a class that controls the type of objects to be displayed in correlation with the GameManager
, for example only the main function.
After creating renderManager
on GamePanel
class, I call this function inside paintComponent()
:
public void setRenderState(Graphics2D graphic) {
for (int i = 0; i < states.size(); i++) {
if (gameManager.getCurrentState() == States.LOADING.getValue()) {
if (states.get(i) instanceof Loading) {
// draw until the state is the current,if i' m going to
// change the state i stop drawing the current state
if (states.get(i).getTimeUntilChangeLevel() == -1) {
renderLoading = new RenderLoading(states.get(i));
renderLoading.showRender(graphic);
break;
} else {
break;
}
}
}
Inside this function, if the current state is loading i'll create a new instance of RenderLoading
, this is the class that displays the graphics of the currentState
:
public class RenderLoading implements Render {
private GameState gameState;
public RenderLoading(GameState gameState) {
this.gameState = gameState;
}
public void showRender(Graphics2D graphic) {
graphic.drawImage(ImageProvider.LOADING, 0, 0, null);
// draw loading string
graphic.setColor(Color.BLACK);
graphic.setFont(ImageProvider.loadingFont);
graphic.drawString("Loading...", 25, 800);
graphic.setStroke(ImageProvider.STROKE_2);
// draw loading bar
graphic.setColor(Color.BLACK);
graphic.fillRect(20, 820, gameState.getLoadStatus(), 4);
}
}
ImageProvider
is a class that I use to load texture to my game:
public final class ImageProvider {
public static BufferedImage LOADING;
// best visualization of shapes
public static BasicStroke STROKE_2 = new BasicStroke(2);
public static BufferedImage loadImages(String string) {
BufferedImage image = null;
try {
image = ImageIO.read(ImageProvider.class.getResourceAsStream(string));
} catch (Exception e) {
e.printStackTrace();
}
return image;
}
//this function is called before the renderManager
//this load the images
public static void loadImages() {
ImageProvider.LOADING = ImageProvider.loadImages("/images/loading.jpg");
}
public static BasicStroke getSTROKE_2() {
return STROKE_2;
}
}
GameManager Class it manage every aspect of game,select the current state and other stuff.
public class GameManager {
// game's states
private Game game;
private Menu menu;
private SelectPath path;
private Loading loading;
private Editor editor;
private WorldManager worldManager;
private int currentState = 0;
public GameManager(int state) {
this(state, null);
}
public GameManager(int state, WorldManager worldManager) {
this.currentState = state;
this.worldManager = worldManager;
}
public void loadCurrentState(int state) {
if (state == States.LOADING.getValue()) {
loading = new Loading(this);
loading.init();
} else if (state == States.EDITOR.getValue()) {
editor = new Editor(this, worldManager);
editor.init();
} else if (state == States.MENU.getValue()) {
menu = new Menu(this, worldManager);
menu.init();
} else if (state == States.PATHSELECT.getValue()) {
path = new SelectPath(this, worldManager);
path.init();
} else if (state == States.GAME.getValue()) {
game = new Game(this, worldManager);
game.init();
}
}
public void setState(int state) {
currentState = state;
loadCurrentState(currentState);
}
public void update() {
if (currentState == States.LOADING.getValue())
loading.update();
else if (currentState == States.EDITOR.getValue())
editor.update();
else if (currentState == States.MENU.getValue())
menu.update();
else if (currentState == States.GAME.getValue())
game.update();
else if (currentState == States.PATHSELECT.getValue())
path.update();
}
public int getCurrentState() {
return currentState;
}
public WorldManager getWorldManager() {
return worldManager;
}
public void setWorldManager(WorldManager worldManager) {
this.worldManager = worldManager;
}
}
GameState
is an abstract class that is extended in each gamestate
,it is to imagine how each gamestate is implemented
public abstract class GameState {
// gameManager
protected GameManager gameManager;
// worldManager
protected WorldManager worldManager;
public GameState(GameManager gameManager) {
this(gameManager, null);
}
public GameState(GameManager gameManager, WorldManager worldManager) {
this.gameManager = gameManager;
this.worldManager = worldManager;
}
public abstract void init();
public abstract void update();
public abstract void controllerUserInput();
public abstract void changingState();
}
My game works fine, but with this code, my objects on the screen visibly lag.
Can I get a performance review of my code?
-
\$\begingroup\$ Do you think it's possible to show us a video on youtube or another platform on what exactly is happening \$\endgroup\$Nzall– Nzall2014年06月03日 09:50:29 +00:00Commented Jun 3, 2014 at 9:50
-
\$\begingroup\$ @BenVlodgi i posted the gameState class that is extended on each game state,see update. \$\endgroup\$OiRc– OiRc2014年06月03日 17:16:29 +00:00Commented Jun 3, 2014 at 17:16
1 Answer 1
Java
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.
-
\$\begingroup\$ can you provide me a little example with your advices on my code above? \$\endgroup\$OiRc– OiRc2014年06月03日 15:25:52 +00:00Commented Jun 3, 2014 at 15:25
Explore related questions
See similar questions with these tags.