【问题标题】:ball moves however only once球只移动一次
【发布时间】:2016-02-01 02:32:40
【问题描述】:

我正在创建一个程序,该程序创建一个将使用箭头键移动的球,但是当我运行代码时,球开始在角落,我只能在每个方向移动一次。 代码如下:

package squareMovingUsingArrowKeys;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

@SuppressWarnings("serial")
public class squareMovingUsingArrowKeys extends JPanel implements ActionListener,KeyListener {
static int x;
static int y;
Timer timer;
squareMovingUsingArrowKeys() {
    x = 0;
    y = 0;
    timer = new Timer(20, this);
}
@SuppressWarnings("deprecation")
public void keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_LEFT) {
        x = -1;
    }
    else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
        x = 1;
    }
    else if (e.getKeyCode() == KeyEvent.VK_UP) {
        y = -1;
    }
    else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
        y = 1;
    }

    //first call move to update x and y and later repaint that JPanel
    move(x, y);
    repaint();
}
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.fillOval(x, y, 10, 10);
}
public void start() {
    keyPressed(null);
    paintComponent(null);
}
public static void main(String[] args) {
    JFrame f = new JFrame("Moving");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    squareMovingUsingArrowKeys m = new squareMovingUsingArrowKeys();
    f.add(m);
    f.setSize(500, 500);
    f.setVisible(true);
    m.timer.start();
    f.addKeyListener(m);
}
@Override
public void actionPerformed(ActionEvent e) {
    // TODO Auto-generated method stub

}
@Override
public void keyReleased(KeyEvent arg0) {
    // TODO Auto-generated method stub

}
@Override
public void keyTyped(KeyEvent arg0) {
    // TODO Auto-generated method stub

}
}

这不是作业或类似的东西,我只是在学习 java 并想尝试一下。 还请记住,我只有 9 岁,是个菜鸟。

【问题讨论】:

  • 快速解决方案:使具有 KeyListener 的 JPanel 具有焦点,然后将其赋予焦点。更好更健壮的解决方案:使用 Key Bindings 而不是 KeyListener。 Search for similar questions.

标签: java swing animation graphics keylistener


【解决方案1】:

它只移动一次的原因是,在您的关键监听器中,您直接将xy 分配给±1,而不是调整之前的值。要修改之前的值,您需要执行 x = x + 1;x += 1;x++; 或等效操作。例如:

if (e.getKeyCode() == KeyEvent.VK_LEFT) {
    x -= 1;
} 
else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
    x += 1;
}
else if (e.getKeyCode() == KeyEvent.VK_UP) {
    y -= 1;
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
    y += 1;
}

我在您的代码中看到的其他一些问题:

  • 约定类(如squareMovingUsingArrowKeys)应以大写字母开头。
  • xy 在这里不应该是 static,因为这样会在类的多个实例之间共享变量,而不是让每个实例都有自己的位置。
  • 你不应该打电话给move。这会移动整个JPanel,但JPanel 已经在绘制相对于自身移动的球,所以这就是你所需要的。你也不需要@SuppressWarnings("deprecation")。如果您确实想在没有弃用警告的情况下移动 JPanel,请改为调用 setLocation(Java 1.1 中重命名了 move 方法,这就是警告的含义)。
  • 系统发送关键事件的方式导致了一些问题。当您按住一个键时,球会立即移动一次,但在开始重复移动之前会有一点延迟。这是由于按住任何键时会出现键重复延迟。打字是正确的,但对于游戏对象的移动看起来是错误的。更糟糕的是,它的速度可能取决于系统,因此球在不同系统上不会以一致的速度移动。此外,对角线移动也不起作用;当同时按下第二个键时,似乎不会发送一个键的按下事件。解决所有这些问题的一种方法是使用键事件不直接调整球的位置,而是更新状态变量,说明当前按下了哪些键。然后,在您的TimerActionListener 中实现xy 的实际更新:

    private boolean movingLeft, movingRight, movingUp, movingDown;
    
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            movingLeft = true;
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            movingRight = true;
        } else if (e.getKeyCode() == KeyEvent.VK_UP) {
            movingUp = true;
        } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            movingDown = true;
        }
    }
    
    @Override
    public void keyReleased(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            movingLeft = false;
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            movingRight = false;
        } else if (e.getKeyCode() == KeyEvent.VK_UP) {
            movingUp = false;
        } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            movingDown = false;
        }
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if (movingLeft) x -= 1;
        if (movingRight) x += 1;
        if (movingUp) y -= 1;
        if (movingDown) y += 1;
        repaint();
    }
    
    /* ... rest of the code the same ... */
    
  • 所有与 Swing 的交互都应该发生在一个名为 Event Dispatch Thread 的专用线程上。 In main you should call SwingUtilities.invokeLater 移动到正确的线程:

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame("Moving");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            /* ... rest of code for setting up frame here ... */
        });
    }
    

    使用错误的线程与 Swing 交互目前不会导致您的程序出现问题,但它在技术上仍然不正确,而且这种事情可能会导致潜在的微妙问题。程序的其余部分是事件驱动的,因此它已经在正确的线程上运行。

祝你好运!

【讨论】:

  • 我更喜欢第一部分的 ifs,因为那样它可以沿对角线移动。
  • @bleh 它可以并且确实沿对角线移动。按下两个键会发送两个带有单独键码的 keyPress 事件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-22
  • 2023-03-08
  • 2020-05-05
  • 1970-01-01
  • 1970-01-01
  • 2010-11-22
相关资源
最近更新 更多