Перемещение спрайтов в Java-играх на фоне перевода происходит неправильно
Я делаю игру с птичками для настольных компьютеров в качестве учебного опыта, но, к сожалению, что-то пошло не так, что я не знаю, как это исправить. Это моя первая фоновая игра с прокруткой и подсчетом кадров в секунду.
Позвольте мне объяснить проблему:
Когда моя игра работает на скорости 60 кадров в секунду, птица будет выглядеть так, как будто она движется назад и вперед очень быстро с фоном, например так:
И когда я изменю свою игру на 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();
}
}
Окно, вам на самом деле не нужно, так как оно содержит только мою 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);
}
}
}
}
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);
}
}
Что вызывает этот странный рендеринг?