0

I am making a flappy bird game for desktop as a learning experience, but unfortunately, something has went wrong that I have no idea how to fix. this is my first scrolling background game, with fps calculations.

Let me explain the problem:

When my game runs at 60 fps, the bird will basically look like it's going back & forward really fast with the background, like this:

(Watch in MP4 format)

And when I change my game to 20 fps, it will fly fine, but sometimes make a small jump forward.

I am not sure whether if its my pc, or my rendering, or my game loop, or my bird movement system.

What could cause this issue? this is my source:

FlappyBird.java

public class FlappyBird extends Window {

    private static final long serialVersionUID = 1L;

    private Screen gameScreen;
    private boolean game = false;
    private Level level = new Level(this);
    private long lastLoop = System.nanoTime();
    private long lastFps = 0;
    private int fps = 0;

    private Bird myBird = new Bird(Constants.START_X, Constants.START_Y, this);

    public FlappyBird() {
        this.gameScreen = new Screen(this);
        this.level.generateLevel();
    }

    public void initialize() {
        super.setGameScreen(this.gameScreen);
        this.game = true;
        this.gameLoop();
    }

    public Bird getBird() {
        return this.myBird;
    }

    public Level getLevel() {
        return this.level;
    }

      private void gameLoop() {
            new Thread() {

                private long last;
                private long start;
                private int wait;

                public void run() {
                    long millisPerSecond = TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS);
                    long optimalDelay =  Math.round(millisPerSecond / Constants.OPTIMAL);
                    optimalDelay = TimeUnit.MILLISECONDS.toNanos(optimalDelay);              
                    long loop = System.nanoTime();
                    while (game) {
                        long now = System.nanoTime();
                        update(0);
                        render();
                        long timeTaken = System.nanoTime();
                        long delta = timeTaken - now;
                        long delay = optimalDelay - delta;
                        if (delay > 0) {
                            try {
                                delay = TimeUnit.NANOSECONDS.toMillis(delay);
                                Thread.sleep(delay);
                            } catch (InterruptedException ex) {
                                ex.printStackTrace();
                            }
                        }
                        long loopDelay = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - loop);
                        if (loopDelay >= 1) {

                            loop = System.nanoTime();
                            System.out.println("FPS = " + fps);
                            fps = 0;
                        } else {
                            fps++;
                        }
                    }
                }
            }.start();
        }

    private void update(double delta) {
        if (System.currentTimeMillis() - this.level.getTime() >= Constants.TRANSLATE_SPEED) {
            this.level.updateTranslate();
            this.getBird().move();
            this.level.setTime();
        }


    }

    private void render() {
        super.repaint();
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                FlappyBird bird = new FlappyBird();
                bird.initialize();
            }
        }).start();
    }
}

Window, you don't really need as it only holds my JPanel, screen.java:

@SuppressWarnings("serial")
public class Screen extends JPanel {

    private FlappyBird game;
    private Background background;

    public Screen(FlappyBird bird) {
        this.game = bird;
        this.background = new Background(this.game);
        this.background.generateBackgrounds();
    }

    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;

        int translateAmount = this.game.getLevel().getTranslate();
        g.translate(-translateAmount, 0);
        this.background.render(g2d);    
        this.game.getLevel().renderLevel(g2d);
        this.game.getBird().render(g2d);

    }
}

Background.java (for bg repeating):

public class Background {

    private class RepeatedBackground {

        private int x;

        public RepeatedBackground(int x) {
            this.x = x;
        }

        public int getX() {
            return this.x;
        }

        public void render(Graphics2D g) {
            g.drawImage(new Sprite(Constants.BACKGROUND).getSprite(), x, 0, null);
        }
    }

    private FlappyBird game;
    private List<RepeatedBackground> repeats = new ArrayList<RepeatedBackground>();

    public Background(FlappyBird game) {
        this.game = game;
    }

    public void generateBackgrounds() {
        int x = 0;
        for (int i = 0; i < Constants.BACKGROUND_WORLD_SIZE; i++) {
            this.repeats.add(new RepeatedBackground(x));
            x += Constants.BACKGORUND_SPRITE_WIDTH;
        }
    }

    public void render(Graphics2D g) {
        Iterator<RepeatedBackground> itr = this.repeats.iterator();
        while (itr.hasNext()) {
            RepeatedBackground b = itr.next();
            if (this.game.getLevel().getTranslate() - b.getX() >= 300) {
                itr.remove();
                continue;
            }
            if (b.getX() - this.game.getLevel().getTranslate() < Constants.WIDTH) {
                b.render(g);
            }
        }
    }
}

Constants.java:

public final class Constants {

    public static final String SPRITE_BASE = "Data/Sprites/";
    public static final String PILLAR = "pillar.png";
    public static final String END = "end.png";
    public static final String END_BOTTOM = "endBottom.png";    
    public static final String BACKGROUND = "background.png";

    public static final int WIDTH =700;
    public static final int HEIGHT = 512;

    public static final Random rand = new Random();

    public static final int LEVEL_AMOUNT = 25;
    public static final int BACKGROUND_WORLD_SIZE = 35;
    public static final int BACKGORUND_SPRITE_WIDTH = 288;
    public static final int BACKGROUNDS_PER_TIME = 4;

    public static final int TRANSLATE_SPEED = 35;
    public static final int TRANSLATE_SPEED_MOVE = 6;

    // GAME LOOP
    public static final long OPTIMAL = 60;

    // BIRD

    public static final int START_X = 450;
    public static final int START_Y = 250;
    public static final Sprite BIRD = new Sprite("bird.png");

    // Bird Physics constants

    public static final int MOVEMENT_SPEED = 6;
    public static final double MOVEMENT_TIME = 1;


}

Level.java:

public class Level {

    private FlappyBird instance;
    private List<Pillars> pillars = new ArrayList<Pillars>();
    private int translate = 0;
    private long lastMovementTime;

    public Level(FlappyBird bird) {
        this.instance = bird;
    }

    public void generateLevel() {
        this.pillars.clear();
        int x = 900;
        for (int i = 0; i < 25; i++) {
            int random = Constants.rand.nextInt(2);
            this.pillars.add(PillarsFactory.createPillars(x, this.intToEnum(random)));
            x += 275;
        }
    }

    public void renderLevel(Graphics2D g) {
        for (Pillars p : this.pillars) {
            p.renderPillars(g);
        }
    }

    public List<Pillars> getPillars() {
        return this.pillars;
    }

    public long getTime() {
        return this.lastMovementTime;
    }

    public int getTranslate() {
        return this.translate;
    }

    public void updateTranslate() {
        this.translate += Constants.TRANSLATE_SPEED_MOVE;
    }

    public void resetTranslate() {
        this.translate = 0;
    }

    public PillarType intToEnum(int i) {
        switch (i) {
            case 0:
                return PillarType.TOP;
            case 1:
                return PillarType.MIDDLE;
            case 2:
                return PillarType.BOTTOM;
        }
        return null;
    }

    public void setTime() {
        this.lastMovementTime = System.currentTimeMillis();
    }
}

Bird.java:

public class Bird {

    private int x;
    private int y;
    private Sprite bird = Constants.BIRD;
    private FlappyBird instance;
    private long lastMove;

    public Bird(int x, int y, FlappyBird instance) {
        this.x = x;
        this.y = y;
        this.instance = instance;
    }

    public Sprite getSprite() {
        return this.bird;
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;
    }

    public void move() {
        this.x += Constants.MOVEMENT_SPEED;
        for (Pillars p : this.instance.getLevel().getPillars()) {
            Pillar a = p.getBottom();
            Pillar b = p.getBottom();
            if (this.x >= a.getX() && this.x <= a.getX() + a.getWidth() &&
                    this.y >= a.getY() && this.y <= a.getY() + a.getHeight()) {
                System.out.print("yes!");
            }
        }
    }

    public void render(Graphics2D g) {
        g.drawImage(this.bird.getSprite(), this.x, this.y, null);
    }
}

Pillars package.

What is causing this weird rendering?

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
Artemkller545
  • 979
  • 3
  • 21
  • 55
  • 2
    1) For better help sooner, post a [MCTaRE](http://stackoverflow.com/help/mcve) (Minimal Complete Tested and Readable Example). 2) One way to get image(s) for an example is to hot-link to the images seen in [this answer](http://stackoverflow.com/a/19209651/418556). – Andrew Thompson Feb 21 '14 at 09:46
  • @AndrewThompson At (2) you mean posting my sprites links here? – Artemkller545 Feb 21 '14 at 09:48
  • No, point 2) means hot-linking to those images (already in the linked answer) *instead of* using the game default images. – Andrew Thompson Feb 21 '14 at 09:57
  • read [my comment to answer by @Kevin Workman](http://stackoverflow.com/a/21915233/714968) in your previous question – mKorbel Feb 21 '14 at 09:59
  • remove all `Graphics whatever` from all `classes`, all painting should be done in `public void paintComponent(Graphics g) {`, must be reseted by `super.paintComponent()` as 1st, code line in `public void paintComponent(Graphics g) {` – mKorbel Feb 21 '14 at 10:02
  • seems like as duplicate question – mKorbel Feb 21 '14 at 10:03
  • The problem is with the background repeating system, if I remove it, the bird will fly just fine. – Artemkller545 Feb 21 '14 at 10:28

0 Answers0