gpt4 book ai didi

Java游戏循环: Frame Jumps

转载 作者:太空宇宙 更新时间:2023-11-04 14:48:05 28 4
gpt4 key购买 nike

我在 YouTube 上发布了一个视频,您可以在其中看到该问题。我放慢了视频速度,这样你就能真正看到跳跃。

链接:https://www.youtube.com/watch?v=17Wftj2-MRM&feature=youtu.be

问题在视频的最后部分最为明显。你可以清楚地看到跳帧,就像跳过了一两帧一样..

我没有任何延迟或性能问题,因为我运行游戏的计算机拥有最新的技术..

我认为这可能与游戏循环有关,例如舍入错误或其他问题,但我似乎在代码中找不到任何错误。我在网上找过类似的问题,但找不到..

这是我的代码图形用户界面

import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

public class Gui extends JFrame implements Runnable {

private static final long serialVersionUID = 1L;
public static final int FRAMES_PR_SEC = 60;
public static final int TIME_PR_FRAME = 1000 / FRAMES_PR_SEC;
public static final int MARGIN_X = 3;
public static final int MARGIN_Y = 32;
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
public static int TILE_SIZE = 32;
public static int SPEED = 5;

public static void main(final String[] args) {
new Gui();
}

private Game _game;

private Container _cp;
private BufferedImage _img;

private Graphics2D _g;

public Gui() {
super("Tile Based Game");
setSize(WIDTH + MARGIN_X + 3, HEIGHT + MARGIN_Y - 4);
setResizable(false);
setVisible(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
_cp = getContentPane();
_game = new Game();
newGraphics();
addMouseListener(new MouseAdapter() {

@Override
public void mousePressed(final MouseEvent arg0) {
_game.mP(arg0.getX() - MARGIN_X, arg0.getY() - MARGIN_Y);
}

@Override
public void mouseReleased(final MouseEvent arg0) {
_game.mR(arg0.getX() - MARGIN_X, arg0.getY() - MARGIN_Y);
}
});
addKeyListener(new KeyAdapter() {

@Override
public void keyPressed(final KeyEvent arg0) {
_game.keyP(arg0.getKeyCode());
}

@Override
public void keyReleased(final KeyEvent arg0) {
_game.keyR(arg0.getKeyCode());
}
});
new Thread(this).start();
}

private void newGraphics() {
_img = getGraphicsConfiguration().createCompatibleImage(WIDTH, HEIGHT);
_g = _img.createGraphics();
}

@Override
public void run() {
while (true) {
long before = System.currentTimeMillis();
_game.update();
newGraphics();
_game.draw(_g);
_cp.getGraphics().drawImage(_img, 0, 0, null);
long after = System.currentTimeMillis();
long sleep = TIME_PR_FRAME - (after - before);
System.out.println(sleep);
if (sleep < 5) {
sleep = 5;
}
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
}
}
}

我想您需要的一切都在 GUI 类中,但我已经包含了其他类,以防您需要它们中的任何内容。

游戏类

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;

public class Game {
private Map[] _maps;
private String[] _mapNames;
private int _currentMap;

private boolean _r;
private boolean _l;
private boolean _u;
private boolean _d;

public Game() {
_mapNames = new String[0];
_maps = new Map[0];
addMap("test.map");

}

public void addMap(final String s) {
String[] stringTemp = _mapNames;
_mapNames = new String[stringTemp.length + 1];
for (int i = 0; i < stringTemp.length; i++) {
_mapNames[i] = stringTemp[i];
}
_mapNames[_mapNames.length - 1] = s.replace(".map", "");
Map[] mapTemp = _maps;
_maps = new Map[mapTemp.length + 1];
for (int i = 0; i < mapTemp.length; i++) {
_maps[i] = mapTemp[i];
}
_maps[_maps.length - 1] = new Map(s);
}

public void draw(final Graphics2D _g) {
_maps[_currentMap].draw(_g);
_g.setColor(Color.WHITE);
_g.drawString(_mapNames[_currentMap], 5, 595);
}

public void keyP(final int keyCode) {
if (keyCode == KeyEvent.VK_RIGHT) {
_r = true;
}
if (keyCode == KeyEvent.VK_LEFT) {
_l = true;
}

if (keyCode == KeyEvent.VK_UP) {
_u = true;
}
if (keyCode == KeyEvent.VK_DOWN) {
_d = true;
}
}

public void keyR(final int keyCode) {
if (keyCode == KeyEvent.VK_SPACE) {
if (_currentMap < _maps.length - 1) {
_currentMap++;
} else {
_currentMap = 0;
}
}
if (keyCode == KeyEvent.VK_RIGHT) {
_r = false;
}
if (keyCode == KeyEvent.VK_LEFT) {
_l = false;
}

if (keyCode == KeyEvent.VK_UP) {
_u = false;
}
if (keyCode == KeyEvent.VK_DOWN) {
_d = false;
}
}

public void mP(final int i, final int j) {

}

public void mR(final int i, final int j) {

}

public void update() {

if (_r) {
_maps[_currentMap].incOffsetX(Gui.SPEED);
}
if (_l) {
_maps[_currentMap].decOffsetX(Gui.SPEED);
}
if (_u) {
_maps[_currentMap].decOffsetY(Gui.SPEED);
}
if (_d) {
_maps[_currentMap].incOffsetY(Gui.SPEED);
}
_maps[_currentMap].update();
}
}

最后是 Map 类(处理 .map 文件等的加载)

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import javax.imageio.ImageIO;

public class Map {

private class Tile {
private Image _img;
private int _x;
private int _y;

public Tile(final Image img) {
_img = img;
}

public Image getImg() {
return _img;
}

public int getX() {
return _x;
}

public int getY() {
return _y;
}

public void setLoc(final int x, final int y) {
_x = x * Gui.TILE_SIZE;
_y = y * Gui.TILE_SIZE;
}
}

private int _offsetX;
private int _offsetY;

private String[][] _map;

private Tile[][] _tileMap;

private BufferedImage _terrain;

private Color _waterColor;

public Map(final String s) {
_terrain = loadImg("terrain.png");

_waterColor = new Color(21, 108, 153);

_map = loadMap(s);

_offsetX = _map[0].length * Gui.TILE_SIZE / 2 - Gui.WIDTH / 2;
_offsetY = _map.length * Gui.TILE_SIZE / 2 - Gui.HEIGHT / 2;

_tileMap = new Tile[_map.length][_map[0].length];

for (int i = 0; _map.length > i; i++) {
for (int j = 0; _map[0].length > j; j++) {
Image img;
switch (_map[i][j]) {
case "o":
img = getSubimage(_terrain, 0, 0, 3, 6);
break;
case "0":
img = getSubimage(_terrain, 0, 1, 3, 6);
break;
case "a":
img = getSubimage(_terrain, 1, 0, 3, 6);
break;
case "b":
img = getSubimage(_terrain, 2, 0, 3, 6);
break;
case "c":
img = getSubimage(_terrain, 1, 1, 3, 6);
break;
case "d":
img = getSubimage(_terrain, 2, 1, 3, 6);
break;
case "1":
img = getSubimage(_terrain, 0, 2, 3, 6);
break;
case "2":
img = getSubimage(_terrain, 1, 2, 3, 6);
break;
case "3":
img = getSubimage(_terrain, 2, 2, 3, 6);
break;
case "4":
img = getSubimage(_terrain, 0, 3, 3, 6);
break;
case "5":
img = getSubimage(_terrain, 1, 3, 3, 6);
break;
case "6":
img = getSubimage(_terrain, 2, 3, 3, 6);
break;
case "7":
img = getSubimage(_terrain, 0, 4, 3, 6);
break;
case "8":
img = getSubimage(_terrain, 1, 4, 3, 6);
break;
case "9":
img = getSubimage(_terrain, 2, 4, 3, 6);
break;
case "#":
img = getSubimage(_terrain, 0, 5, 3, 6);
break;
case "&":
img = getSubimage(_terrain, 1, 5, 3, 6);
break;
case "%":
img = getSubimage(_terrain, 2, 5, 3, 6);
break;
default:
img = null;
break;
}
Tile tile = new Tile(img);
int x = j;
int y = i;
tile.setLoc(x, y);
_tileMap[i][j] = tile;
}
}
}

public void decOffsetX(final int i) {
_offsetX -= i;
}

public void decOffsetY(final int i) {
_offsetY -= i;
}

public void draw(final Graphics2D _g) {
for (int i = 0; _tileMap.length > i; i++) {
for (int j = 0; _tileMap[0].length > j; j++) {
if (_tileMap[i][j].getImg() != null
&& isVisible(_tileMap[i][j])) {
_g.drawImage(_tileMap[i][j].getImg(), _tileMap[i][j].getX()
- _offsetX, _tileMap[i][j].getY() - _offsetY, null);
} else if (_tileMap[i][j].getImg() == null
&& isVisible(_tileMap[i][j])) {
_g.setColor(_waterColor);
_g.fillRect(_tileMap[i][j].getX() - _offsetX,
_tileMap[i][j].getY() - _offsetY, Gui.TILE_SIZE,
Gui.TILE_SIZE);
}
}
}
}

private Image getSubimage(final BufferedImage img, final int x,
final int y, final int w, final int h) {
return img.getSubimage(x * Gui.TILE_SIZE, y * Gui.TILE_SIZE,
Gui.TILE_SIZE, Gui.TILE_SIZE);

}

public void incOffsetX(final int i) {
_offsetX += i;
}

public void incOffsetY(final int i) {
_offsetY += i;
}

public boolean isVisible(final Tile tile) {
if (tile.getX() - _offsetX < 0 - Gui.TILE_SIZE) {
return false;
} else if (tile.getX() - _offsetX > Gui.WIDTH) {
return false;
}
if (tile.getY() - _offsetY < 0 - Gui.TILE_SIZE) {
return false;
} else if (tile.getY() - _offsetY > Gui.HEIGHT) {
return false;
}
return true;
}

private BufferedImage loadImg(final String s) {
BufferedImage img = null;
try {
img = ImageIO.read(new File("res/" + s));
} catch (IOException e) {
}
return img;
}

private String[][] loadMap(final String s) {
String _s = null;
String string = null;
int appear = 0;
try {
BufferedReader br;
br = new BufferedReader(new FileReader("res/maps/" + s));
while ((_s = br.readLine()) != null) {
string = string + _s + ",";
appear++;
}
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String[] ss = string.replaceAll("null", "").split(",");
String[][] items = new String[appear][];
for (int i = 0; i < items.length; i++) {
items[i] = ss[i].split(" ");
}

return items;
}

public void update() {
if (_offsetX < 0) {
_offsetX += Gui.SPEED;
} else if (_offsetX > _map[0].length * Gui.TILE_SIZE - Gui.WIDTH
- Gui.SPEED) {
_offsetX -= Gui.SPEED;
}
if (_offsetY < 0) {
_offsetY += Gui.SPEED;
} else if (_offsetY > _map.length * Gui.TILE_SIZE - Gui.HEIGHT
- Gui.SPEED) {
_offsetY -= Gui.SPEED;
}
}
}

我知道代码中可能存在一些错误,并且一切可能都不是最佳的,但这个项目是非常早期的开发,我是一个非常缺乏经验的游戏开发人员..如果您愿意,我将不胜感激专注于发布的问题,而不是我的其他垃圾代码。 :-D

如果您有任何建议,我真的很希望您发表评论:-D谢谢

编辑:我更新了帖子,因为问题不清楚。所以我上传了一个视频,您可以在其中查看该错误。我还清除了其他一些东西:-)

编辑2:在 gamedev.stackexchange 上创建了一个新线程。或许可以帮助您解决问题:https://gamedev.stackexchange.com/questions/77998/java-game-loop-frame-jumps

解决方案:经过大量研究后,我得出的结论是,这与屏幕刷新率和我的循环刷新率有关。我的屏幕只是跳帧,这是非常明显的!

如果您想要深入的解决方案,谷歌制作了一个视频,他们在其中描述了问题:https://www.youtube.com/watch?v=hAzhayTnhEI

有两种方法(根据我的研究)可以解决这个问题:

您必须通过 OpenGL 启用垂直同步,因为 java 没有这样做的选项..

或者你必须在java中混合使用swing和awt(我选择的选项)..这是一个描述如何做的线程:Smooth Drawing using Java2d without the Opengl or Direct3d Pipelines?

感谢您的所有回复!

最佳答案

首先,一个想法是研究 BufferStrategy作为一种创建更强大、更高性能的游戏循环的机制。其次,您的游戏循环会根据更新/渲染所需的时间而 hibernate 不同的值。这很好,但是您的 update 方法也应该反射(reflect)这一点,而不是以恒定的速度移动事物。您希望实体相对于自上次循环周期以来经过的增量时间(lastTime - currentTime)移动。

一些常量不应该是整数。例如,TIME_PR_FRAME 实际上不应该是 16,而应该是 16.67,因此请将其设置为 double 型,或者至少为浮点型。您的if(sleep < 5) sleep = 5;代码没有任何意义。如果渲染和更新需要很长时间,你不应该浪费时间 sleep ,基本上在这些情况下你是有意或无意引入抖动。

您是否调试过代码以查看 sleep 的平均值是多少?变量是,以及它是否尖峰?如果没有的话我也会这样做。

关于Java游戏循环: Frame Jumps,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24149104/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com