在屏幕上移动对象没有延迟/滞后

时间:2015-07-27 17:35:23

标签: java multithreading

我希望在画布或其他组件上移动对象,但这样做没有明显的延迟/延迟。现在,为了解决这个问题,我正在使用具有多个图层的双缓冲(大多数图层只能绘制一次)。在我的情况下,我有一个背景,在地图生成时绘制一次,一个遮罩层(对象后面的东西)和一个边缘层(对象前面的东西)。实际上重新绘制的唯一内容是在屏幕上移动的实际对象。

话虽如此,我还设置了KeyListeners,并将我的对象移动移动到绘图中并处理成单独的线程(例如专用于绘制画布的线程,以及专用于处理移动的线程)。

我注意到当我按下一个键来移动物体(W,A,S,D)时,它会移动一小部分(一个移动动作),然后稍微延迟,然后不断移动直到我停止按键。

是否有更好的方法从键盘实现移动以减少/消除延迟?

CNC中 我意识到我可以在一个声明中总结一下:如何为感觉敏感的游戏制作控件,而不会出现笨拙的延迟/延迟。

2 个答案:

答案 0 :(得分:1)

您使用的是哪种操作系统?在linux /可能的mac上,按住一个键会执行几个周期,包括不断调用按下和释放方法(你知道如果你在一个文本框中按住一个键,一个字符出现,然后有一个延迟,然后还有几个看起来很快?在linux中,操作系统执行按下,然后释放,真的非常快。)然而,在Windows中,直到密钥实际发布才会调用发布方法。 (基本上,它只是继续快速调用印刷方法。)

一个非常丑陋和讨厌的答案,但我希望它有所帮助:

知道这些周期的时间是有帮助的。通常情况下,它是这样的:

  • 用户按下并按住按钮(例如,“a”键)。立即调用'a'键的按键功能。
  • 延迟时间约为四分之一秒(通常在系统设置中可调)。
  • 'a'键的释放功能称为
  • 毫秒毫秒延迟(我的测试在2到10毫秒之间)
  • 再次调用按下功能
  • 稍微延迟(我的测试在20到30毫秒之间)。
  • 从第三个项目符号点开始重复。

考虑到这个循环,理论上可以(实际上,完全可能。我已经测试过它。)使用时间来检测用户是否实际释放了密钥。基本上,如果密钥被释放的时间超过10毫秒(我会去15,为了安全起见),那么你知道用户已经释放了密钥。

此解决方案涉及为每个相关键创建一个布尔值。反过来,你的KeyListeners只会将这些布尔值设置为true / false(可能在按下它们时,它们被设置为true,当它们被释放时,它们被设置为false。)

然后,为每个键创建一个布尔值,表示键是否真正被按下。创建一个监视第一个布尔值(由KeyListeners控制的布尔值)的线程。每次将第一个布尔值设置为true时,将相应的第二个布尔值设置为true。

但是,每当第一个布尔值设置为false时,请等待15毫秒,然后再次检查。如果它们仍设置为false,则将相应的第二个布尔值设置为false。为什么?如果它是15毫秒并且它们仍然是假的,那么您知道用户实际已释放该密钥。

KeyListener:

@Override
public void keyPressed(KeyEvent e){
    if(e.getKeyCode == 65){
        aPressed = true;
    }
}

@Override
public void keyReleased(KeyEvent e){
    if(e.getKeyCode == 65){
        aPressed = false;
    }
}

单独的线程(称之为trueKeyListener

boolean waiting = false;
long tWaitStart;
Runnable trueKeyListener = new Runnable(){
    @Override
    public void run(){
        while(true){//Instead of true, use some variable, like running
            if(aPressed){
                aTruePressed = true;
            }else if(!waiting){//not yet waiting for the 15 ms
                waiting = true;
                tWaitStart = System.nanoTime();
            }else if(System.nanoTime() - tWaitStart >= 15000000){//waiting is over
                waiting = false;
                if(!aPressed){//a still isn't pressed
                    aTruePressed = false;
                }
            }
        }
    }
};

据我所知,对布尔值进行计时是使其适用于任何操作系统的唯一方法。正如我所说,在Windows操作系统上,这不应该是(或者至少不是我上次编码的时候)。

编辑:最后,正如我忘记提到的那样,您可以再创建一个线程来监视truePressed布尔值并相应地处理移动。

其次,只是一些建议,如果你的应用程序中有几个相关的键,我建议使用按下的布尔数组,truePressed布尔值,等待布尔值和tWaitStart long来处理一个for循环中的多个键。

第二次修改:我发现this问题与您的问题非常相似。我希望它有所帮助。

答案 1 :(得分:0)

尝试在重绘循环中的repaint()之前调用。

Toolkit.getDefaultToolkit().sync()