我在我的游戏中有一个非常烦人的错误,帧的底部似乎比帧的顶部渲染得早,我不知道为什么会这样。
我使用的JPanel会重新绘制每个游戏循环,我的游戏循环设置为60帧/秒。在绘制函数开始时,它将播放器X和Y设置为一个变量,然后用于绘制每个元素(因为它们是相对于播放器绘制的,因为相机跟随播放器)
如果需要,我可以发布任何代码来帮助诊断问题,但是代码太多了,我不知道问题出在哪里;所以我主要是问,是否有人知道我的解释可能会出错。
我不能发布关于这个问题的视频,因为它不会在视频中出现,但是请在游戏中查看它,链接到游戏,并在这里进行病毒扫描
如果你下载了游戏,那么当你打开它时,输入一个名字(或者保留默认值),当它询问服务器时点击否。当您使用WASD移动时,您应该会在屏幕上的某个地方看到水平线闪烁效果。如果游戏没有打开,再试一次,有一个小的可能性,它无法打开(这是一个已知的错误,我计划很快修复它)
抱歉解释得不好,我发现很难描述我的问题。我已经坚持了几个小时,甚至在网上搜索后也找不到解决方案。
编辑:全部源代码:此处
EDIT2:它需要位于此处的kryonet库
编辑3:Github
在进行更改时,JPanel不会等待您调用repaint()。
为了防止出现这种情况,我认为可以按如下方式使用RepaitManager:-
RepaintManager.currentManager(yourJPanel).markCompletelyClean(yourJPanel)
还有一种技术使用两个不同的JPanel实例。基本思想是这样的:
不过,我不知道第二个会如何影响性能。
对不起,我之前的回答是:P
用VolitileImage
示例更新
现在,在你兴奋之前,我对VolitileImage
的体验比我创建这个例子多了大约20分钟,所以这可能不是最好的例子...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.io.IOException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import static javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestVolitile {
public static void main(String[] args) {
new TestVolitile();
}
public TestVolitile() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ViewPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface View {
public VolatileImage getOffscreenBuffer();
public void show(VolatileImage img);
public boolean isIncompatiable(VolatileImage img);
public int getWidth();
public int getHeight();
}
public enum KeyState {
UP, DOWN, LEFT, RIGHT;
}
public class ViewPane extends JPanel implements View {
private VolatileImage offscreen;
private BufferedImage onscreen;
private ReentrantLock lckBuffers;
private Engine engine;
public ViewPane() {
lckBuffers = new ReentrantLock();
engine = new Engine(this);
engine.gameStart();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "left_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up_released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down_released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "left_released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right_released");
ActionMap am = getActionMap();
am.put("up_pressed", new AddState(engine, KeyState.UP));
am.put("up_released", new RemoveState(engine, KeyState.UP));
am.put("down_pressed", new AddState(engine, KeyState.DOWN));
am.put("down_released", new RemoveState(engine, KeyState.DOWN));
am.put("left_pressed", new AddState(engine, KeyState.LEFT));
am.put("left_released", new RemoveState(engine, KeyState.LEFT));
am.put("right_pressed", new AddState(engine, KeyState.RIGHT));
am.put("right_released", new RemoveState(engine, KeyState.RIGHT));
}
@Override
public void invalidate() {
super.invalidate();
onscreen = null;
// offscreen = null;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
try {
lckBuffers.lock();
// Make sure the buffer is okay for painting....
if (onscreen != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(onscreen, 0, 0, this);
g2d.dispose();
}
} finally {
lckBuffers.unlock();
}
}
protected VolatileImage createVolatileImage(int width, int height, int transparency) {
GraphicsConfiguration gc = getGraphicsConfiguration();
VolatileImage image = null;
if (gc != null && width > 0 && height > 0) {
image = gc.createCompatibleVolatileImage(width, height, transparency);
int valid = image.validate(gc);
if (valid == VolatileImage.IMAGE_INCOMPATIBLE) {
image = this.createVolatileImage(width, height, transparency);
}
}
return image;
}
@Override
public VolatileImage getOffscreenBuffer() {
if (isIncompatiable(offscreen)) {
offscreen = createVolatileImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
}
return offscreen;
}
@Override
public void show(VolatileImage img) {
try {
lckBuffers.lock();
GraphicsConfiguration gc = getGraphicsConfiguration();
if (gc != null) {
if (onscreen == null) {
onscreen = gc.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
}
if (isOkay(img)) {
Graphics2D g2d = onscreen.createGraphics();
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
repaint();
}
}
} finally {
lckBuffers.unlock();
}
}
@Override
public boolean isIncompatiable(VolatileImage offscreen) {
boolean isIncompatiable = true;
GraphicsConfiguration gc = getGraphicsConfiguration();
if (gc != null) {
if (offscreen != null) {
if (offscreen.getWidth() == getWidth() && offscreen.getHeight() == getHeight()) {
if (offscreen.validate(gc) != VolatileImage.IMAGE_INCOMPATIBLE) {
isIncompatiable = false;
}
}
}
}
return isIncompatiable;
}
public boolean isOkay(VolatileImage buffer) {
boolean isOkay = false;
GraphicsConfiguration gc = getGraphicsConfiguration();
if (gc != null) {
if (buffer != null) {
if (buffer.getWidth() == getWidth() && buffer.getHeight() == getHeight()) {
if (buffer.validate(gc) == VolatileImage.IMAGE_OK) {
isOkay = true;
}
}
}
}
return isOkay;
}
}
public static class Engine {
public static final int MAP_WIDTH = 15 * 4;
public static final int MAP_HEIGHT = 9 * 4;
public static final int X_DELTA = 4;
public static final int Y_DELTA = 4;
public boolean isGameFinished = false;
//This value would probably be stored elsewhere.
public static final long GAME_HERTZ = 25;
//Calculate how many ns each frame should take for our target game hertz.
public static final long TIME_BETWEEN_UPDATES = Math.round(1000000000 / (double) GAME_HERTZ);
//We will need the last update time.
static long lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
static long lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final static long TARGET_FPS = GAME_HERTZ;
final static long TARGET_TIME_BETWEEN_RENDERS = Math.round(1000000000 / (double) TARGET_FPS);
//Simple way of finding FPS.
static int lastSecondTime = (int) (lastUpdateTime / 1000000000);
public int fps = 60;
public int frameCount = 0;
private View view;
private int camX, camY;
private Set<KeyState> keyStates;
private BufferedImage map;
private BufferedImage tiles[];
public Engine(View view) {
this.view = view;
keyStates = new HashSet<>(4);
tiles = new BufferedImage[22];
Random rnd = new Random();
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
map = gc.createCompatibleImage(MAP_WIDTH * 128, MAP_HEIGHT * 128, Transparency.TRANSLUCENT)
Graphics2D g2d = map.createGraphics();
for (int row = 0; row < MAP_HEIGHT; row++) {
for (int col = 0; col < MAP_WIDTH; col++) {
int tile = rnd.nextInt(22);
int x = col * 128;
int y = row * 128;
g2d.drawImage(getTile(tile), x, y, null);
}
}
g2d.dispose();
}
protected BufferedImage getTile(int tile) {
BufferedImage img = tiles[tile];
if (img == null) {
try {
img = ImageIO.read(getClass().getResource("/" + tile + ".png"));
img = img.getSubimage(0, 64, 128, 128);
img = toCompatiableImage(img);
} catch (IOException ex) {
ex.printStackTrace();
}
tiles[tile] = img;
}
return img;
}
public void gameStart() {
Thread gameThread = new Thread() {
// Override run() to provide the running behavior of this thread.
@Override
public void run() {
gameLoop();
}
};
// Start the thread. start() calls run(), which in turn calls gameLoop().
gameThread.start();
}
public void gameLoop() {
while (!isGameFinished) {
long startTime = System.nanoTime();
lastUpdateTime += TIME_BETWEEN_UPDATES;
updateGame();
renerGame();
frameCount++;
lastRenderTime = startTime;
long duration = System.nanoTime() - startTime;
int thisSecond = (int) (lastUpdateTime / 1000000000);
if (thisSecond > lastSecondTime) {
fps = frameCount;
frameCount = 0;
lastSecondTime = thisSecond;
}
if (duration < TARGET_TIME_BETWEEN_RENDERS) {
duration = TARGET_TIME_BETWEEN_RENDERS - duration;
long milli = TimeUnit.NANOSECONDS.toMillis(duration);
try {
Thread.sleep(milli);
} catch (InterruptedException ex) {
}
}
}
}
protected void updateGame() {
if (keyStates.contains(KeyState.DOWN)) {
camY -= Y_DELTA;
} else if (keyStates.contains(KeyState.UP)) {
camY += Y_DELTA;
}
if (camY < -(map.getHeight() - view.getHeight())) {
camY = -(map.getHeight() - view.getHeight());
} else if (camY > 0) {
camY = 0;
}
if (keyStates.contains(KeyState.RIGHT)) {
camX -= Y_DELTA;
} else if (keyStates.contains(KeyState.LEFT)) {
camX += Y_DELTA;
}
if (camX < -(map.getWidth() - view.getWidth())) {
camX = -(map.getWidth() - view.getWidth());
} else if (camX > 0) {
camX = 0;
}
}
protected void renerGame() {
VolatileImage buffer = view.getOffscreenBuffer();
if (buffer != null) {
Graphics2D g2d = null;
do {
if (view.isIncompatiable(buffer)) {
buffer = view.getOffscreenBuffer();
}
try {
g2d = buffer.createGraphics();
} finally {
if (g2d != null) {
g2d.drawImage(map, camX, camY, null);
// Draw effects here...
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.RED);
g2d.drawString(Integer.toString(fps), 0, fm.getAscent());
g2d.dispose();
}
}
} while (buffer.contentsLost());
view.show(buffer);
}
}
public void addKeyState(KeyState state) {
keyStates.add(state);
}
public void removeKeyState(KeyState state) {
keyStates.remove(state);
}
protected BufferedImage toCompatiableImage(BufferedImage img) {
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
BufferedImage compImg = gc.createCompatibleImage(img.getWidth(), img.getHeight(), img.getTransparency());
Graphics2D g2d = compImg.createGraphics();
g2d.drawImage(img, 0, 0, null);
g2d.dispose();
return compImg;
}
}
public class AddState extends AbstractAction {
private Engine engine;
private KeyState state;
public AddState(Engine engine, KeyState state) {
this.engine = engine;
this.state = state;
}
@Override
public void actionPerformed(ActionEvent e) {
engine.addKeyState(state);
}
}
public class RemoveState extends AbstractAction {
private Engine engine;
private KeyState state;
public RemoveState(Engine engine, KeyState state) {
this.engine = engine;
this.state = state;
}
@Override
public void actionPerformed(ActionEvent e) {
engine.removeKeyState(state);
}
}
}
现在,如果这仍然给你带来麻烦,你可以尝试更换。。。
g2d.drawImage(map, camX, camY, null);
具有
BufferedImage clip = map.getSubimage(camX * -1, camY * -1, view.getWidth(), view.getHeight());
g2d.drawImage(clip, 0, 0, null);
这减少了图形缓冲区中可能存在的任何可能的“悬置”。不能100%确定这是否会有所不同,但不会有坏处。你可以在两个例子中这样做...
如果您仍然有撕裂/剪切发生,您可以尝试禁用Directx或opengl渲染管道(这些是命令行选项),看看它是否会有所不同...
查看游戏编程维基VolatileImage
,Java:Tutorials:VolatileImage和Java 2D:Hardware Accelerating-Part 1-Volatile Images了解更多想法。
我也改变了你的游戏循环时间循环一点,不知道它是否会有所不同,但Thread.sleep(1)
总是吓到我...
更新
我已经更新了代码,所以只有一个VolatileImage
。该画图组件
使用实际上的BufferedImage
,优化为GraphicsConfiguration
。这确保了内容总是被绘制成只需要更新(在show
方法中)。应该有助于防止闪烁...
我还优化了所有的瓷砖,因为它们在加载时被优化为图形配置
,这意味着它们的颜色模型在渲染到屏幕上时不需要转换,因为它们是相同的,应该有助于节省一些时间。。。
查看ToCompatibleImage
方法了解更多详细信息
这是两个基本原则的演示,但基本上是一系列缓冲区,旨在减少的
的工作量...
一般来说,将图像BLIT到显卡上比“绘制”像素更快,考虑到这一点,这个例子做了两件事...
首先,它预渲染背景地图。本例只是在运行时随机生成贴图,但创建的贴图大约是全高清的4倍。
其次,它有自己的双重缓冲。“视图”有两个缓冲区,活动
和更新
。活动的
缓冲区被绘制到屏幕上,更新
缓冲区被引擎
用来呈现输出的当前状态。。。
这很重要,因为视图的缓冲区始终与视图的大小相同,所以您永远不会渲染屏幕上没有显示的任何内容。
本例将其他内容(如动画、特效)的渲染推送到引擎
。。。
我在2560x1600的30英寸显示器上运行了这个例子,几乎没有什么问题,移动增量非常小,所以我可以更快地平移,使其变大会消除这些问题。。。
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestRender {
public static void main(String[] args) {
new TestRender();
}
public TestRender() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface View {
public BufferedImage switchBuffers();
public int getWidth();
public int getHeight();
}
public enum KeyState {
UP, DOWN, LEFT, RIGHT;
}
public class TestPane extends JPanel implements View {
private Engine engine;
private BufferedImage active;
private BufferedImage update;
private ReentrantLock lckBuffer;
public TestPane() {
lckBuffer = new ReentrantLock();
initBuffers();
engine = new Engine(this);
engine.gameStart();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "left_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right_pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up_released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down_released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "left_released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right_released");
ActionMap am = getActionMap();
am.put("up_pressed", new AddState(engine, KeyState.UP));
am.put("up_released", new RemoveState(engine, KeyState.UP));
am.put("down_pressed", new AddState(engine, KeyState.DOWN));
am.put("down_released", new RemoveState(engine, KeyState.DOWN));
am.put("left_pressed", new AddState(engine, KeyState.LEFT));
am.put("left_released", new RemoveState(engine, KeyState.LEFT));
am.put("right_pressed", new AddState(engine, KeyState.RIGHT));
am.put("right_released", new RemoveState(engine, KeyState.RIGHT));
}
protected void initBuffers() {
if (getWidth() > 0 && getHeight() > 0) {
try {
lckBuffer.lock();
active = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
update = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
} finally {
lckBuffer.unlock();
}
}
}
@Override
public void invalidate() {
super.invalidate();
initBuffers();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(1920, 1080);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
try {
lckBuffer.lock();
if (active != null) {
g2d.drawImage(active, 0, 0, this);
}
} finally {
lckBuffer.unlock();
}
g2d.dispose();
}
@Override
public BufferedImage switchBuffers() {
try {
lckBuffer.lock();
BufferedImage tmp = active;
active = update;
update = tmp;
repaint();
} finally {
lckBuffer.unlock();
}
return update;
}
}
public static class Engine {
public static final int MAP_WIDTH = 15 * 4;
public static final int MAP_HEIGHT = 9 * 4;
public static final int X_DELTA = 32;
public static final int Y_DELTA = 32;
//This value would probably be stored elsewhere.
public static final double GAME_HERTZ = 60.0;
//Calculate how many ns each frame should take for our target game hertz.
public static final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//We will need the last update time.
static double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
static double lastRenderTime = System.nanoTime();
//If we are able to get as high as this FPS, don't render again.
final static double TARGET_FPS = GAME_HERTZ;
final static double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
//Simple way of finding FPS.
static int lastSecondTime = (int) (lastUpdateTime / 1000000000);
public static int fps = 60;
public static int frameCount = 0;
private boolean isGameFinished;
private BufferedImage map;
private BufferedImage tiles[];
private View view;
private int camX, camY;
private Set<KeyState> keyStates;
public Engine(View bufferRenderer) {
keyStates = new HashSet<>(4);
this.view = bufferRenderer;
tiles = new BufferedImage[7];
Random rnd = new Random();
map = new BufferedImage(MAP_WIDTH * 128, MAP_HEIGHT * 128, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = map.createGraphics();
for (int row = 0; row < MAP_HEIGHT; row++) {
for (int col = 0; col < MAP_WIDTH; col++) {
int tile = rnd.nextInt(7);
int x = col * 128;
int y = row * 128;
g2d.drawImage(getTile(tile), x, y, null);
}
}
g2d.dispose();
}
protected BufferedImage getTile(int tile) {
BufferedImage img = tiles[tile];
if (img == null) {
try {
img = ImageIO.read(getClass().getResource("/" + tile + ".png"));
img = img.getSubimage(0, 64, 128, 128);
} catch (IOException ex) {
ex.printStackTrace();
}
tiles[tile] = img;
}
return img;
}
public void gameStart() {
Thread gameThread = new Thread() {
// Override run() to provide the running behavior of this thread.
@Override
public void run() {
gameLoop();
}
};
gameThread.setDaemon(false);
// Start the thread. start() calls run(), which in turn calls gameLoop().
gameThread.start();
}
public void gameLoop() {
BufferedImage buffer = view.switchBuffers(); // initial buffer...
while (!isGameFinished) {
double now = System.nanoTime();
lastUpdateTime += TIME_BETWEEN_UPDATES;
gameUpdate(buffer);
renderBuffer(buffer);
buffer = view.switchBuffers(); // Push the buffer back
frameCount++;
lastRenderTime = now;
int thisSecond = (int) (lastUpdateTime / 1000000000);
if (thisSecond > lastSecondTime) {
fps = frameCount;
frameCount = 0;
lastSecondTime = thisSecond;
}
//Yield until it has been at least the target time between renders. This saves the CPU from hogging.
while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
//Thread.yield();
//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
try {
Thread.sleep(1);
} catch (Exception e) {
}
now = System.nanoTime();
}
}
}
protected void renderBuffer(BufferedImage buffer) {
if (buffer != null) {
Graphics2D g2d = buffer.createGraphics();
g2d.drawImage(map, camX, camY, null);
g2d.dispose();
}
}
protected void gameUpdate(BufferedImage buffer) {
// render transient effects here
if (keyStates.contains(KeyState.DOWN)) {
camY -= Y_DELTA;
} else if (keyStates.contains(KeyState.UP)) {
camY += Y_DELTA;
}
if (camY < -(map.getHeight() - view.getHeight())) {
camY = -(map.getHeight() - view.getHeight());
} else if (camY > 0) {
camY = 0;
}
if (keyStates.contains(KeyState.RIGHT)) {
camX -= Y_DELTA;
} else if (keyStates.contains(KeyState.LEFT)) {
camX += Y_DELTA;
}
if (camX < -(map.getWidth() - view.getWidth())) {
camX = -(map.getWidth() - view.getWidth());
} else if (camX > 0) {
camX = 0;
}
}
public void addKeyState(KeyState state) {
keyStates.add(state);
}
public void removeKeyState(KeyState state) {
keyStates.remove(state);
}
}
public class AddState extends AbstractAction {
private Engine engine;
private KeyState state;
public AddState(Engine engine, KeyState state) {
this.engine = engine;
this.state = state;
}
@Override
public void actionPerformed(ActionEvent e) {
engine.addKeyState(state);
}
}
public class RemoveState extends AbstractAction {
private Engine engine;
private KeyState state;
public RemoveState(Engine engine, KeyState state) {
this.engine = engine;
this.state = state;
}
@Override
public void actionPerformed(ActionEvent e) {
engine.removeKeyState(state);
}
}
}
在我的实验中,我确实注意到,如果你试图渲染内容“超出”缓冲区的范围(即允许地图的顶部在缓冲区内滑动),你会得到令人讨厌的油漆效果,所以要小心你总是在缓冲区的可视区域内渲染...
可能还有其他方面需要清理,但这说明了基本情况。。。
我想知道为什么我的图表每次移动后屏幕都不清晰。这是我的代码: MainClass,我想删除其中的“时间”,并将其放在绘图方法updateScene中的lambda表达式中,但还不知道如何做到这一点:/ Axes类描述如何绘制轴: 缩放和绘制图表中我需要的一切。 绘图课负责制作动画,精确绘制我想要的图形。 屏幕显示我编译后得到的内容。就像我自上而下的问题一样,不知道是什么原因导致在每个时间线阶段后不
我一直试图在我的UI中添加一个动画到我的气泡图中,但遇到了一些问题。我试图在不同的阶段增加气泡的大小,以显示渐变,但它只是画出它的全部大小,而不是在每个阶段。 体育推特计数是泡沫的最终值,我将其除以不同的数量,以显示最终值的建立。 有人有什么想法为什么这不像我期望的那样工作吗?
我最近发现了一些UIView动画代码,并注意到它没有正确使用animateKeyframesWithDuration:delay:options:animations:completion:方法,并且一直在尝试修复它,但由于某些原因,动画不完全相同。 以下是我找到的代码: 这有两个问题: 应该真正使用关键帧动画,而不是在完成块中嵌套动画 使用animateKeyframesWithDuration
问题内容: 我想在我的WP博客中添加它。这样,每个新的div帖子的边框上都会有此动画。但是问题在于它在SVG中。无论如何,我可以在不使用SVG的情况下使此动画工作,并且我也不想使用javascript。 可以说代码是: 问题答案: CSS可以做到这一点,并且在使用多个背景并使用动画更改其位置时非常简单。 这是一个页面加载后边框连续不断移动的示例。 归功于web-tiki,它有助于修复最初在动画每个
我目前正在创建一个非常简单的JavaFX程序,模拟城市之间运送乘客的飞机和船只。到目前为止,我已经能够让飞机在几个城市进行短途飞行,但问题是,当我添加超过3或4架飞机时,动画速度非常慢。 我正在做的是使用Timeline类作为我的主游戏循环,清除并重新绘制画布上每帧60帧的平面图像。以下是时间表部分: 以下是我如何为平面创建新线程: 这是Plane类中定义的run()方法: 我知道代码非常混乱,但
我的印象是,在JFrame上放置图形需要画布或JPanel,但我之前看到一个视频,其中一个人在扩展JFrame时使用绘画(graphics g),而没有制作面板或画布。如果是这样的话,为什么人们要费心制作画布或JPanel呢?