- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我在 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/
我是 PHP 新手。我一直在脚本中使用 for 循环、while 循环、foreach 循环。我想知道 哪个性能更好? 选择循环的标准是什么? 当我们在另一个循环中循环时应该使用哪个? 我一直想知道要
我在高中的编程课上,我的作业是制作一个基本的小计和顶级计算器,但我在一家餐馆工作,所以制作一个只能让你在一种食物中读到。因此,我尝试让它能够接收多种食品并将它们添加到一个价格变量中。抱歉,如果某些代码
这是我正在学习的一本教科书。 var ingredients = ["eggs", "milk", "flour", "sugar", "baking soda", "baking powder",
我正在从字符串中提取数字并将其传递给函数。我想给它加 1,然后返回字符串,同时保留前导零。我可以使用 while 循环来完成此操作,但不能使用 for 循环。 for 循环只是跳过零。 var add
编辑:我已经在程序的输出中进行了编辑。 该程序要求估计给定值 mu。用户给出一个值 mu,同时还提供了四个不等于 1 的不同数字(称为 w、x、y、z)。然后,程序尝试使用 de Jaeger 公式找
我正在编写一个算法,该算法对一个整数数组从末尾到开头执行一个大循环,其中包含一个 if 条件。第一次条件为假时,循环可以终止。 因此,对于 for 循环,如果条件为假,它会继续迭代并进行简单的变量更改
现在我已经习惯了在内存非常有限的情况下进行编程,但我没有答案的一个问题是:哪个内存效率更高;- for(;;) 或 while() ?还是它们可以平等互换?如果有的话,还要对效率问题发表评论! 最佳答
这个问题已经有答案了: How do I compare strings in Java? (23 个回答) 已关闭 8 年前。 我正在尝试创建一个小程序,我可以在其中读取该程序的单词。如果单词有 6
这个问题在这里已经有了答案: python : list index out of range error while iteratively popping elements (12 个答案) 关
我正在尝试向用户请求 4 到 10 之间的整数。如果他们回答超出该范围,它将进入循环。当用户第一次正确输入数字时,它不会中断并继续执行 else 语句。如果用户在 else 语句中正确输入数字,它将正
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我想要做的事情是使用循环创建一个数组,然后在另一个类中调用该数组,这不会做,也可能永远不会做。解决这个问题最好的方法是什么?我已经寻找了所有解决方案,但它们无法编译。感谢您的帮助。 import ja
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我正在模拟一家快餐店三个多小时。这三个小时分为 18 个间隔,每个间隔 600 秒。每个间隔都会输出有关这 600 秒内发生的情况的统计信息。 我原来的结构是这样的: int i; for (i=0;
这个问题已经有答案了: IE8 for...in enumerator (3 个回答) How do I check if an object has a specific property in J
哪个对性能更好?这可能与其他编程语言不一致,所以如果它们不同,或者如果你能用你对特定语言的知识回答我的问题,请解释。 我将使用 c++ 作为示例,但我想知道它在 java、c 或任何其他主流语言中的工
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
我是 C 编程和编写代码的新手,以确定 M 测试用例的质因数分解。如果我一次只扫描一次,该功能本身就可以工作,但是当我尝试执行 M 次时却惨遭失败。 我不知道为什么 scanf() 循环有问题。 in
这个问题已经有答案了: JavaScript by reference vs. by value [duplicate] (4 个回答) 已关闭 3 年前。 我在使用 TSlint 时遇到问题,并且理
我尝试在下面的代码中添加 foreach 或 for 循环,以便为 Charts.js 创建多个数据集。这将允许我在此折线图上创建多条线。 我有一个 PHP 对象,我可以对其进行编码以稍后填充变量,但
我是一名优秀的程序员,十分优秀!