4
\$\begingroup\$

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?

\$\endgroup\$
2
  • \$\begingroup\$ Do you think it's possible to show us a video on youtube or another platform on what exactly is happening \$\endgroup\$
    – Nzall
    Jun 3 '14 at 9:50
  • \$\begingroup\$ @BenVlodgi i posted the gameState class that is extended on each game state,see update. \$\endgroup\$
    – OiRc
    Jun 3 '14 at 17:16
4
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ can you provide me a little example with your advices on my code above? \$\endgroup\$
    – OiRc
    Jun 3 '14 at 15:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.