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

2024-03-23

我的游戏中有一个非常烦人的错误,帧的底部似乎比帧的顶部渲染得更早,我不确定为什么会发生这种情况。

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

如果需要,我可以发布任何代码,以帮助诊断问题,但是代码太多,我不知道哪一部分有问题;所以我主要是想问是否有人知道我的解释可能出了什么问题。

我无法发布该问题的视频,因为它没有出现在视频中,但是您可以在游戏中自己看到它,游戏链接 https://dl.dropboxusercontent.com/u/39172300/Game.jar,以及病毒扫描here https://www.virustotal.com/uk/file/2e205938abec7fb201e232727f3616ac696239aeaf16c3d4af572404b807b858/analysis/1408598332/

如果您下载游戏,那么当您打开它时,输入名称(或保留默认值),然后在询问服务器时单击“否”。当您使用 WASD 四处移动时,您应该会在屏幕上的某处看到水平线闪烁的效果。如果游戏打不开,请重试,有很小的几率打不开(这是一个已知的bug,我计划尽快修复)

抱歉,解释不好,我发现很难描述我的问题。我已经被这个问题困扰了几个小时,即使在互联网上搜索后也找不到解决方案。

编辑:整个源代码:Here https://dl.dropboxusercontent.com/u/39172300/RPGRTS.zip

EDIT2:它需要 kryonet 库,位于here https://dl.dropboxusercontent.com/u/39172300/kryonet-2.21-all.jar

EDIT3: Github https://github.com/Draesia/RPGRTS/


这是两个基本原理的演示,但基本上是一系列缓冲区,旨在减少paintComponent does...

一般来说,速度比较快BLIT http://en.wikipedia.org/wiki/Bit_blit将图像复制到显卡上,然后“绘制”像素,考虑到这一点,这个例子做了两件事......

首先,它预渲染背景图。此示例在运行时只是随机生成地图,但创建的地图大约是全高清地图的 4 倍。

其次,它使用自己的双缓冲。 “视图”有两个缓冲区,一个active and an update. The activebuffer 是绘制到屏幕上的内容,update缓冲区是由Engine呈现输出的当前状态...

这很重要,因为视图的缓冲区始终与视图的大小相同,因此您永远不会渲染未出现在屏幕外的任何内容。

此示例将附加内容(如动画、特效)的渲染推送到Engine...

我在我的 30" 显示器上以 2560x1600 运行此示例,几乎没有出现任何问题,运动增量非常小,因此我可以更快地平移,使其变大可以消除这些问题...

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);
        }

    }

}

在我的实验过程中,我确实注意到,如果您尝试渲染内容“超出”缓冲区的范围(即允许地图的顶部在缓冲区内滑落),您会得到令人讨厌的绘画效果,所以要小心您始终在缓冲区的可视区域内渲染...

可能还有其他区域需要整理,但这展示了基础知识......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

框架在不同时间绘画? [关闭] 的相关文章

  • “_加载小部件时出现问题”消息

    加载小部件时 如果找不到资源或其他内容 则会显示 加载小部件时出现问题 就这样 惊人的 此消息保留在主屏幕上 甚至没有说明加载时遇到问题的小部件 我通过反复试验弄清楚了这一点 但我想知道发生这种情况时是否有任何地方可以找到错误消息 Andr
  • 添加动态数量的监听器(Spring JMS)

    我需要添加多个侦听器 如中所述application properties文件 就像下面这样 InTopics Sample QUT4 Sample T05 Sample T01 Sample JT7 注意 这个数字可以多一些 也可以少一些
  • 对话框上的 EditText 不返回任何文本

    我太累了 找不到错误 我没有发现任何错误 但我没有从 editText 收到任何文本 请看下面的代码 活动密码 xml
  • 在 HTTP 标头中发送 UTF-8 值会导致 Mojibake

    我想使用 servlet 发送阿拉伯语数据HTTPServletResponse给客户 我正在尝试这个 response setCharacterEncoding UTF 8 response setHeader Info arabicWo
  • 记录骆驼路线

    我的项目中有几个 Camel 上下文 如果可能的话 我想以逆向工程方式记录路线 因为我们希望保持与上下文相关的文档最新 最好的方法是什么 我们倾向于预先实际设计路线 并使用来自EIP book http www eaipatterns co
  • 如果使用的 JVM 是 x86 或 x64,则以不同的方式解决 Maven 依赖关系?

    我设置了一个 Maven 存储库来托管一些 dll 但我需要我的 Maven 项目根据使用的 JVM 是 x86 还是 x64 下载不同的 dll 例如 在运行 x86 版本 JVM 的计算机上 我需要从存储库下载 ABC dll 作为依赖
  • 如何将 android.net.Uri 转换为 java.net.URL? [复制]

    这个问题在这里已经有答案了 有没有办法从Uri to URL 我正在使用的库需要这个 它only接受一个URL但我需要在我的设备上使用图像 如果该方案的Uri is http or https new URL uri toString 应该
  • Java:正则表达式排除空值

    在问题中here https stackoverflow com questions 51359056 java regexp for a separated group of digits 我得到了正则表达式来匹配 1 到 99 之间的一
  • Java 8 中函数式接口的使用

    这是来自的后续问题Java 8 中的 双冒号 运算符 https stackoverflow com questions 20001427 double colon operator in java 8其中 Java 允许您使用以下方式引用
  • Java 数组的最大维数

    出于好奇 在 Java 中数组可以有多少维 爪哇language不限制维数 但是JavaVM规范将维度数限制为 255 例如 以下代码将无法编译 class Main public static void main String args
  • 无法加载或查找主类,可以在命令行中使用,但不能在 IDE 中使用[重复]

    这个问题在这里已经有答案了 在将其标记为重复之前 请先听我说完 我正在尝试使用 gradle 导入一个 java 项目 功能齐全 适用于所有其他笔记本电脑 没有问题 我的项目 100 正常运行 适用于所有其他笔记本电脑 当我的笔记本电脑被重
  • 获取给定类文件的目录路径

    我遇到的代码尝试从类本身的 class 文件所在的同一目录中读取一些配置文件 File configFiles new File this getClass getResource getPath listFiles new Filenam
  • Cloudfoundry:如何组合两个运行时

    cloundfoundry 有没有办法结合两个运行时环境 我正在将 NodeJS 应用程序部署到 IBM Bluemix 现在 我还希望能够执行独立的 jar 文件 但应用程序失败 APP 0 bin sh 1 java not found
  • Spring Security OAuth2简单配置

    我有一个简单的项目 需要以下简单的配置 我有一个 密码 grant type 这意味着我可以提交用户名 密码 用户在登录表单中输入 并在成功时获得 access token 有了该 access token 我就可以请求 API 并获取用户
  • Dispatcher-servlet 无法映射到 websocket 请求

    我正在开发一个以Spring为主要框架的Java web应用程序 特别使用Spring core Spring mvc Spring security Spring data Spring websocket 像这样在 Spring 上下文
  • 尝试使用等于“是”或“否”的字符串变量重新启动 do-while 循环

    计算行程距离的非常简单的程序 一周前刚刚开始 我有这个循环用于解决真或假问题 但我希望它适用于简单的 是 或 否 我为此分配的字符串是答案 public class Main public static void main String a
  • 解决错误javax.mail.AuthenticationFailedException

    我不熟悉java中发送邮件的这个功能 我在发送电子邮件重置密码时遇到错误 希望你能给我一个解决方案 下面是我的代码 public synchronized static boolean sendMailAdvance String emai
  • Java:拆箱整数时出现空指针异常?

    此代码导致空指针异常 我不知道为什么 private void setSiblings PhylogenyTree node Color color throws InvalidCellNumberException PhylogenyTr
  • Java &= 运算符应用 & 或 && 吗?

    Assuming boolean a false 我想知道是否这样做 a b 相当于 a a b logical AND a is false hence b is not evaluated 或者另一方面 这意味着 a a b Bitwi
  • Android 和 Java 中绘制椭圆的区别

    在Java中由于某种原因Ellipse2D Double使用参数 height width x y 当我创建一个RectF在Android中参数是 left top right bottom 所以我对适应差异有点困惑 如果在 Java 中创

随机推荐

  • 如何安全地镜像 git 存储库?

    我想通过后台作业镜像一些 git 存储库 git clone mirror and git remote update不会保留通过强制推送未引用的对象 但我也想保留这些对象以防黑客攻击 有没有什么工具可以执行安全的 git 镜像 虽然缺少
  • Tone.PitchShift 和 Howler.js 问题

    我喜欢在我的 Meteor 应用程序中使用 Howler js 然而 播放速率功能导致了我不想要的音调变化 我只想延长时间 并保持音调 因此 我的解决方案是对其进行音调变换以 纠正 音调 看起来很简单 这就是我选择使用的原因https to
  • 在 Qt 中显示 QImage 的灰度并调整其大小

    我已经能够使用如下内容在 Qt 中的标签中显示图像 transformPixels 0 0 1 imheight imwidth 1 sets unsigned char imageData unsigned char fullCharAr
  • 与Netty相比,vert.x如何实现卓越的性能?

    最近的TechEmpower 性能基准 http www techempower com benchmarks 一直在 Netty 之上展示 vert x 有时数量很大 根据其网站 vert x 使用 Netty 来实现 大部分网络 IO
  • jquery:无法获取div的“value”属性

    这是我的 chrome javascript 控制台的屏幕截图 展示了我的困境 我真的无法理解为什么我无法获取 值 属性 class 属性工作得很好 所以我认为同样应该适用于 value 我在我的应用程序中测试的代码 coffeescrip
  • 没有WebRTC的nodeJS中的简单SIP电话

    您好 我需要实现类似 SIP 电话的功能 但使用不带 WebRTC 的 经典 SIP 大多数 JS 库都专注于基于 websockets 和 WebRTC 的 SIP 但在我的基础设施中 我没有 WebSocket 有像 JsSIP 这样的
  • PHP preg_match_all:提取逗号分隔列表

    例如 我有以下字符串 WIDGET TEST abc 456 我希望能够使用 preg match all 返回逗号分隔参数的数组 有人可以帮我解决我需要的正则表达式吗 我已经尝试过 并且返回以下查询 a b preg match all
  • 方案中的延续传递风格?

    我遇到了这段代码在维基百科上 http en wikipedia org wiki Continuation passing style define pyth x y k x x lambda x2 y y lambda y2 x2 y2
  • 图像 PropertyItems 和已处置的 MemoryStream

    我正在加载一个Image from a byte using MemoryStream并通过检查图像来获取有关图像的信息ProperyItems 但在这样做的过程中 我注意到一些奇怪的行为 其中一些图像的PropertyItems正在消失
  • sqlite:如何获取组计数

    我在网站上有一个用户操作的 SQLite 表 每一行都是网站上的相同操作 只是时间 日期不同 并用用户 ID 标记 该表有超过 2000 万条条目 我了解如何使用按用户 ID 进行分组的功能来获取用户计数 即 A 执行了 3 次操作 B 4
  • 键入字符时搜索字符串

    我的手机中存储了联系人 假设我的联系人是 Ram Hello Hi Feat Eat At 当我打字时 A 我应该得到所有匹配的联系人说 Ram Feat Eat At 现在我再输入一个字母T 现在我的总字符串是 AT 现在我的程序应该重用
  • 序列不包含匹配元素 - 使用 LINQ 返回与自定义属性匹配的 SiteMapNode

    我有一个 Web sitemap 文件 使用siteMapNodeXML 中的元素 我已为每个标签添加了自定义属性 我正在尝试提取自定义属性的值id 我想找一个单身siteMapNode in the SiteMapNodeCollecti
  • 使用 PHP 和 MySQL 创建多维数组

    我是 PHP 新手 正在寻找从数据库返回数据的有效方法 假设我有一个 UserProfile 表 它与 UserInterest 和 UserContact 具有一对多关系 Select p Id p FirstName p LastNam
  • ItemsControl 和 Canvas 中的多个数据模板

    我试图在画布上显示一些框 我自己的 userControl 在其自己的名为 singleNodeControl 的 xaml 文件中定义 并用线连接它们 普通 xaml Line 元素绑定到 LineToParent 类 这两项都存储在 v
  • 使用 PHPExcel 读取电子表格

    我正在尝试上传电子表格并使用 PHPExcel 将其读入 MySQL 数据库 对于 xlsx 文件 它工作正常 但每当我尝试上传 ods 文件时 它都会抛出错误 PHP Fatal error Call to a member functi
  • 如何使用 C# 更新数据透视表数据源?

    我想知道如何更新现有的数据透视表数据源 我在用Microsoft Office Interop Excel并针对使用 Excel 2010 的用户 我目前能够刷新工作正常的数据透视表 但是当添加更多行时 我希望将这些行包含在数据透视表数据源
  • WPF:我可以强制窗口重新评估其所有绑定和验证吗?

    我可以强制窗口重新评估其所有绑定和验证吗 由于某种原因 它似乎在一种奇怪的情况下忽略了 INotifyPropertyChanged PropertyChanged 我正在寻找一种解决方法 直到找到真正的原因 不幸的是 我知道没有办法强制窗
  • 如何在Linux中安装chrome(无头)

    我有一个运行 linux redhad 的 AWS EC2 有没有办法在上面安装最新的 Chrome v59 以便我可以像 PhantomJS 一样以无头模式运行它 我在 google 上能找到的所有资源都是关于如何在有 UI 的 ubun
  • 无法转换“UICollectionViewCell”类型的值

    我在 Storyboard 中使用自定义 CollectionViewCell 当我启动应用程序时 我收到以下消息 无法将 UICollectionViewCell 类型的值转换为 TestProject CollectionViewCel
  • 框架在不同时间绘画? [关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我的游戏中有一个非常烦人的错误 帧的底部似乎比帧的顶部渲染得更早 我不确定为什么会发生这种情况 我正在使用 JPanel