当前位置: 首页 > 知识库问答 >
问题:

画框画在不同的时间?[关闭]

章睿
2023-03-14

我在我的游戏中有一个非常烦人的错误,帧的底部似乎比帧的顶部渲染得早,我不知道为什么会这样。

我使用的JPanel会重新绘制每个游戏循环,我的游戏循环设置为60帧/秒。在绘制函数开始时,它将播放器X和Y设置为一个变量,然后用于绘制每个元素(因为它们是相对于播放器绘制的,因为相机跟随播放器)

如果需要,我可以发布任何代码来帮助诊断问题,但是代码太多了,我不知道问题出在哪里;所以我主要是问,是否有人知道我的解释可能会出错。

我不能发布关于这个问题的视频,因为它不会在视频中出现,但是请在游戏中查看它,链接到游戏,并在这里进行病毒扫描

如果你下载了游戏,那么当你打开它时,输入一个名字(或者保留默认值),当它询问服务器时点击否。当您使用WASD移动时,您应该会在屏幕上的某个地方看到水平线闪烁效果。如果游戏没有打开,再试一次,有一个小的可能性,它无法打开(这是一个已知的错误,我计划很快修复它)

抱歉解释得不好,我发现很难描述我的问题。我已经坚持了几个小时,甚至在网上搜索后也找不到解决方案。

编辑:全部源代码:此处

EDIT2:它需要位于此处的kryonet库

编辑3:Github

共有3个答案

雍嘉勋
2023-03-14

在进行更改时,JPanel不会等待您调用repaint()。

为了防止出现这种情况,我认为可以按如下方式使用RepaitManager:-

RepaintManager.currentManager(yourJPanel).markCompletelyClean(yourJPanel)

还有一种技术使用两个不同的JPanel实例。基本思想是这样的:

  • 设置一个可见,另一个不可见。
  • 将更改反映到不可见(当不可见时,面板不会更新自己)
  • 当你完成游戏循环迭代时,切换它们的可见性并重新开始

不过,我不知道第二个会如何影响性能。

尹庆
2023-03-14

对不起,我之前的回答是: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方法了解更多详细信息

麻阳
2023-03-14

这是两个基本原则的演示,但基本上是一系列缓冲区,旨在减少的工作量...

一般来说,将图像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呢?