nedb
nedb

Reputation: 666

How do I make the frame rate consistent?

I'm making simple 2D games. I have a game loop, and in the game loop i have an update method. I make things move by adding 1 to xPos whenever it loops. This means that if you have a slow fps then everything goes in slow motion and if you have a high fps everything moves really quick.

This is my code:

    long fpsTimer;
    int frames;
    public void run(){
        running = true;
        fpsTimer = System.nanoTime();

        while(running){
            render();
            update();

            try{
                Thread.sleep(6);
            }catch(InterruptedException e){}
            frames++;

            if(System.nanoTime() >= fpsTimer+1000000000){
                System.out.println(frames+" fps");
                frames = 0;
                fpsTimer = System.nanoTime();
            }
        }
    }

All Code

    import java.awt.*;
    import java.awt.image.*;
    import javax.swing.JFrame;
    import java.awt.event.*;

    public class Game extends Canvas implements Runnable{
        public static final int WIDTH = 800;
        public static final int HEIGHT = 300;
        public JFrame f;
        private String title = "Untitled Test";
        private Image image;
        private Sprite player;

        public Game(){
            player = new Sprite(100, 100);
            setPreferredSize(new Dimension(WIDTH, HEIGHT));
            setMaximumSize(new Dimension(WIDTH, HEIGHT));
            setMinimumSize(new Dimension(WIDTH, HEIGHT));

            addKeyListener(new KeyAdapter(){
                public void keyPressed(KeyEvent e){
                    player.keyPressed(e.getKeyCode());
                }

                public void keyReleased(KeyEvent e){
                    player.keyReleased(e.getKeyCode());
                }
            });
        }

        public static void main(String[] args){
            Game g = new Game();
            g.f = new JFrame(g.title);
            g.f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            g.f.add(g);
            g.f.setResizable(false);
            g.f.pack();
            g.f.setLocationRelativeTo(null);
            Thread gameLoop = new Thread(g);
            gameLoop.start();
            g.f.setVisible(true);
        }

        private void render(){
            BufferStrategy bs = getBufferStrategy();
            if(bs==null){
                createBufferStrategy(3);
                return;
            }

            image = createImage(WIDTH, HEIGHT);
            Graphics g = image.getGraphics();

            player.draw(g);

            g.dispose();
            Graphics bsg = bs.getDrawGraphics();
            bsg.drawImage(image, 0, 0, WIDTH, HEIGHT, null);
            bsg.dispose();
            bs.show();
        }

        private void update(){
            player.move();
        }

        long fpsTime;
        int frames;

        public void run(){
            fpsTime = System.nanoTime();

            while(true){

                render();
                update();

                try{
                    Thread.sleep(6);
                }catch(InterruptedException e){}

                frames++;
                if(System.nanoTime() >= fpsTime+1000000000){
                    System.out.println(frames+" fps");
                    frames = 0;
                    fpsTime = System.nanoTime();
                }
            }
        }
    }

Upvotes: 1

Views: 3530

Answers (1)

isnot2bad
isnot2bad

Reputation: 24444

First of all you should not have a constant sleep time. Instead, the value should be dynamically computed. Maybe its easier to use Timer#scheduleAtFixedRate(...) cause this already takes care of that for you.

Then, 6 ms per iteration seems much too less. 60 frames per second is ideal (if you just have 30, its ok too i think), so 16 ms is enough (or about 32 for 30 fps). (Note that the refresh rate of your screen is the upper limit - it should be about 60 Hz - more doesn't make sense).

Thirdly think about dynamically computing the moves of your objects. Instead of having a constant delta that you add to the coordinates you should better have some kind of 'move function' that calculates the coordinates based on the current time.

Let's say you want to move an object along the x-axis with a constant speed of pps pixels per second. The function for the x coordinate of that object would then be:

x(t) := x0 + (t - t0) * pps / 1000

(t is the time that has passed since start in ms, x0 is the initial x coordinate of the object when it appeared first, t0 is the time at which the object appeared, pps is the number of pixels the object should move per second).

This makes your objects move with the same speed - no matter what framerate you've got.

Note that this approach gets more complex if your objects should react to user input or other events (like collisions etc.).

Upvotes: 2

Related Questions