Движение спрайта Java-игры по фону перевода идет не так

Я делаю игру Flappy Bird для рабочего стола в качестве учебного опыта, но, к сожалению, что-то пошло не так, и я понятия не имею, как это исправить. это моя первая фоновая игра с прокруткой, с подсчетом кадров в секунду.

Позвольте мне объяснить проблему:

Когда моя игра работает со скоростью 60 кадров в секунду, птица в основном будет выглядеть так, как будто она движется назад и вперед очень быстро с фоном, например так:

(смотреть в формате MP4)

И когда я меняю игру на 20 кадров в секунду, она будет летать нормально, но иногда делает небольшой скачок вперед.

Я не уверен, то ли это мой компьютер, то ли мой рендеринг, то ли мой игровой цикл, то ли моя система движения птиц.

Что может вызвать эту проблему? это мой источник:

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 вам на самом деле не нужно, так как он содержит только мою 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 (для повторения bg):

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);
            }
        }
    }
}

Константы.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;


}

Уровень.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.

Что вызывает этот странный рендеринг?


person Artemkller545    schedule 21.02.2014    source источник
comment
1) Чтобы быстрее получить помощь, опубликуйте MCTaRE (минимальный полный проверенный и читаемый пример). 2) Один из способов получить изображение (я) для примера - это горячая ссылка на изображения, показанные в этом ответе.   -  person Andrew Thompson    schedule 21.02.2014
comment
@AndrewThompson В (2) вы имеете в виду размещение здесь ссылок на мои спрайты?   -  person Artemkller545    schedule 21.02.2014
comment
Нет, пункт 2) означает горячую ссылку на эти изображения (уже в связанном ответе) вместо использования изображений игры по умолчанию.   -  person Andrew Thompson    schedule 21.02.2014
comment
прочитайте мой комментарий к ответу @Kevin Workman в предыдущем вопросе   -  person mKorbel    schedule 21.02.2014
comment
удалить все Graphics whatever из всех classes, вся покраска должна быть выполнена в public void paintComponent(Graphics g) {, должна быть сброшена super.paintComponent() как 1-я, строка кода в public void paintComponent(Graphics g) {   -  person mKorbel    schedule 21.02.2014
comment
похоже на дублирующий вопрос   -  person mKorbel    schedule 21.02.2014
comment
Проблема в системе повторения фона, если ее убрать, птичка будет летать нормально.   -  person Artemkller545    schedule 21.02.2014