我正在尝试用Java编写一个非常简单的平台游戏。我有一部分使用被动渲染(使用调用repaint()
和revalidate()
等的Timer
对象),但我一直在尝试实现主动渲染。它有点工作 - 因为它渲染,动画工作,但它似乎阻止了关键监听器(以前工作正常),出于某种我不太明白的原因。
我已经在下面尽可能少地重现了这个问题。当您按下某个键时,应该有终端输出,但没有。如果有人能告诉我为什么keyPressed
等方法没有发射,将不胜感激。
编辑 - 根据请求将演示代码修改为一个复制/粘贴
Edit2 - 正如Andrew Thompson所建议的那样,我已经删除了所有全屏代码,但keylistener仍然不起作用
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
class FullScreenRenderWithListener implements KeyListener {
private JFrame frame;
private World world;
public static void main(String[] args)
{
FullScreenRenderWithListener main = new FullScreenRenderWithListener();
SwingUtilities.invokeLater(main::run);
}
private void run()
{
initWindow();
setupWorld();
frame.setIgnoreRepaint(true);
frame.pack();
frame.createBufferStrategy(2);
frame.setVisible(true);
world.startActive(frame.getBufferStrategy());
}
private void initWindow()
{
frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
}
private void setupWorld()
{
world = new World();
frame.addKeyListener(this);
frame.add(world);
world.addKeyListener(this);
}
@Override
public void keyPressed(KeyEvent event)
{
System.out.println("Pressed");
}
@Override
public void keyReleased(KeyEvent event)
{
System.out.println("Released");
}
@Override
public void keyTyped(KeyEvent event)
{
System.out.println("Typed");
}
}
class World extends JPanel {
private static final int FRAMES_PER_SEC = 60;
private static final int MILL_IN_SEC = 1000;
private static final int TICK_LENGTH =
MILL_IN_SEC / FRAMES_PER_SEC;
private BufferStrategy strategy;
private void sleepUntilEndOfFrame()
{
try {
long used = System.currentTimeMillis() % TICK_LENGTH;
long left = TICK_LENGTH - used;
Thread.sleep(left);
} catch(InterruptedException e) {
// ... Handle this error
}
}
public void startActive(BufferStrategy strategy)
{
this.strategy = strategy;
setIgnoreRepaint(true);
while(true) {
doFrame();
}
}
private void doFrame()
{
updateGameState();
activeRenderFrame();
}
private void updateGameState()
{
// ..
}
private void activeRenderFrame()
{
Graphics2D graphicsContext = (Graphics2D)strategy
.getDrawGraphics();
paintComponent(graphicsContext);
strategy.show();
Toolkit.getDefaultToolkit().sync();
graphicsContext.dispose();
sleepUntilEndOfFrame();
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(500, 500);
}
// Have overridden this method because the class
// also implements passive rendering if active is
// not supported
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// .. drawing code
}
}
第一个问题是不要考虑使用KeyBinding
策略,而不是在处理JPanel
时特别考虑使用KeyListener
。您可以通过注释无限while
循环(禁用活动渲染)来检查这一点。
当你使用 KeyListener
时,无论是否使用活动渲染过程,都不会触发KeyEvent
(或者至少我们可以说我们的侦听器没有调用)。
使用keyBinding
将解决此问题。
但是,当您取消注释无限while
循环时,问题会再次出现。那么什么可以解决这个问题呢?更新框架的新Thread
是关键!
检查您的程序,该程序由KeyBinding
和用于使用活动渲染策略更新帧的新Thread
:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class FullScreenRenderWithListener implements Runnable
{
private JFrame frame;
private World world;
public static void main ( String[] args )
{
FullScreenRenderWithListener main = new FullScreenRenderWithListener ();
SwingUtilities.invokeLater ( main );
}
public void run ()
{
initWindow ();
setupWorld ();
frame.setIgnoreRepaint ( true );
frame.pack ();
frame.createBufferStrategy ( 2 );
frame.setVisible ( true );
world.startActive ( frame.getBufferStrategy () );
}
private void initWindow ()
{
frame = new JFrame ();
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setLocationByPlatform ( true );
}
private void setupWorld ()
{
world = new World ();
frame.add ( world );
frame.setFocusable ( true );
world.setFocusable ( true );
}
}
class World extends JPanel
{
private static final int FRAMES_PER_SEC = 10;
private static final int MILL_IN_SEC = 1000;
private static final int TICK_LENGTH = MILL_IN_SEC / FRAMES_PER_SEC;
private BufferStrategy strategy;
//
private static final String PRESSED = "Pressed";
private static final String RELEASED = "Released";
private Map < Direction , Boolean > directionMap = new HashMap < Direction , Boolean > ();
private void sleepUntilEndOfFrame ()
{
try
{
long used = System.currentTimeMillis () % TICK_LENGTH;
long left = TICK_LENGTH - used;
Thread.sleep ( left );
}
catch ( InterruptedException e )
{
// ... Handle this error
e.printStackTrace ();
}
}
private void setBindings() {
int context = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(context);
ActionMap actionMap = getActionMap();
for (Direction direction : Direction.values()) {
inputMap.put(KeyStroke.getKeyStroke(direction.getKeyCode(), 0, false), direction.getName() + PRESSED);
inputMap.put(KeyStroke.getKeyStroke(direction.getKeyCode(), 0, true), direction.getName() + RELEASED);
// set corresponding actions for the key presses and releases above
actionMap.put(direction.getName() + PRESSED, new ArrowKeyAction(true, direction));
actionMap.put(direction.getName() + RELEASED, new ArrowKeyAction(false, direction));
}
}
public void startActive ( BufferStrategy strategy )
{
for ( Direction direction : Direction.values () )
{
directionMap.put ( direction , Boolean.FALSE );
}
setBindings ();
//
this.strategy = strategy;
setIgnoreRepaint ( true );
Thread t = new Thread (){
@Override
public void run ()
{
while ( true )
{
doFrame ();
}
}
};
t.start ();
}
private void doFrame ()
{
updateGameState ();
activeRenderFrame ();
}
private void updateGameState ()
{
// ..
}
private void activeRenderFrame ()
{
Graphics2D graphicsContext = (Graphics2D) strategy.getDrawGraphics ();
paintComponent ( graphicsContext );
strategy.show ();
Toolkit.getDefaultToolkit ().sync ();
graphicsContext.dispose ();
sleepUntilEndOfFrame ();
}
@Override
public Dimension getPreferredSize ()
{
return new Dimension ( 500 , 500 );
}
// Have overridden this method because the class
// also implements passive rendering if active is
// not supported
@Override
public void paintComponent ( Graphics g )
{
super.paintComponent ( g );
// .. drawing code
}
private class ArrowKeyAction extends AbstractAction
{
private Boolean pressed;
private Direction direction;
public ArrowKeyAction ( boolean pressed , Direction direction )
{
this.pressed = Boolean.valueOf ( pressed );
this.direction = direction;
}
@Override
public void actionPerformed ( ActionEvent arg0 )
{
directionMap.put ( direction , pressed );
System.out.println ("Direction: "+ direction + ", State: " + pressed);
}
}
}
enum Direction {
UP("Up", KeyEvent.VK_UP, new Point(0, -1)),
DOWN("Down", KeyEvent.VK_DOWN, new Point(0, 1)),
LEFT("Left", KeyEvent.VK_LEFT, new Point(-1, 0)),
Right("Right", KeyEvent.VK_RIGHT, new Point(1, 0));
private String name;
private int keyCode;
private Point vector;
private Direction(String name, int keyCode, Point vector) {
this.name = name;
this.keyCode = keyCode;
this.vector = vector;
}
public String getName() {
return name;
}
public int getKeyCode() {
return keyCode;
}
public Point getVector() {
return vector;
}
@Override
public String toString() {
return name;
}
}
此示例仅绑定箭头键的某些KeyBinding
。您也可以通过更改setBindings
方法签出其他键。此外,您可能希望为其他键定义更多Event
和另一个enum
。
希望这有帮助。