Java键盘输入无法使用KeyBindings - 自制渲染引擎出错?

时间:2014-11-10 16:41:17

标签: java keyboard key-bindings 2d-games

所以我从头开始创建自己的2D游戏(" StarGame")近一年了。
起初,图形只不过是一些不同颜色的AWT矩形/多边形,但最近,我决定转向正确的图形,我的一个朋友愿意为我创建一些复古风格的图像。 到目前为止,非常好。

事情是:因为我切换到正确的图形,我的游戏不再识别键盘输入。 我当时正在使用KeyListener作为输入,而在搜索解决方案时 ,我认为唯一有意义的是从KeyListener移动到KeyBindings
所以我这样做了,无济于事

我的调试只得到了这个:

键输入在主菜单中有效 关键输入在学分屏幕上有效 当在游戏屏幕上将任何内容绘制到屏幕上时,键输入可以正常工作。

我的游戏如下:

public static void main(String args[]) {
    Game game = new Game();
    game.mainMenu();
}

Game的构造函数初始化了许多变量,重要的部分是:

public Game() {
    // ...
    window = new JFrame("StarGame Beta "+version);
    menuPanel = new JPanel();
    gamePanel = new JPanel();
    mainMenu = new JLabel(new ImageIcon("gifs/mainMenuBG.gif"));
    credits = new JLabel(new ImageIcon("gifs/credits.gif"));

    window.addWindowListener(windowAdapter);
    window.setBounds(new Rectangle(WIDTH, HEIGHT));
    window.setResizable(false);
    window.setFocusable(true);
    window.setLocationRelativeTo(null);

    menuPanel.setFocusable(true);  // NEW, didn't fix it!
    gamePanel.setFocusable(true);  // NEW, didn't fix it!

    setTheKeyBindings();

    setNewGameState(MAIN_MENU);

    gameRenderer = new GameRenderer(gamePanel);
}

setTheKeyBindings()顾名思义,摘录:

    InputMap menuInputMap = menuPanel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
    menuInputMap.put(KeyStroke.getKeyStroke("S"), "runGame");
    ActionMap menuActionMap = menuPanel.getActionMap();
    menuActionMap.put("runGame", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            runGame();
        }
    });

方法runGame()停止菜单音乐播放并触发startGame()方法:

private void runGame()
    {
        menuOggClip.stop();
        System.out.println("Running game");

        startGame();
        stopGame();

        menuOggClip.loop();
        showMainMenu();
    }

startGame()将JPanel gamePanel添加到JFrame窗口并添加游戏中的项目和音乐:

public void startGame()
{
    window.remove(menuPanel);
    if(gameRenderer == null) {
        gameRenderer = new GameRenderer(gamePanel);
    }
    window.add(gamePanel);
    // See showMainMenu() for explanation
    SwingUtilities.updateComponentTreeUI(window);

    gameRenderer.add(ship);
    for (int i = 0; i < 21; i++) {
        rocks[i] = new Rock();
        gameRenderer.add(rocks[i]);
    }
    for (int i = 0; i < 4; i++) {
        aliens[i] = new Alien();
        gameRenderer.add(aliens[i]);
    }
    //gameRenderer.setFirstRun(false);

    shotSound = new ShotSound();
    shotSoundThread = new Thread(shotSound);

    bgMusic = new BackgroundMusic();
    bgMusicThread = new Thread(bgMusic);

    setNewGameState(INTRO);
    System.out.println("Game initialized!");

    gamePanel.requestFocus();

    game();
}    

有一个gameState变量可以跟踪&#34;状态&#34;目前的比赛是,例如G。 MAIN_MENU或GAME_RUNNING。主游戏逻辑是一个while(true)循环,带有一个开关,根据游戏状态确定要做什么:

       case GAME_RUNNING:
            gamePanel.requestFocus(); // this is just a failsafe to make sure the game stays in focus

            gameRenderer.remove(laser);

            if (!didICrash(ship.getShipRect())) { // if the player didn't crash, move the rocks/asteroids a bit to the left -> it seems like the ship is moving right.
                for (int i = 0; i < 21; i++) {
                    rocks[i].tick();
                }
                for (int i = 0; i < 4; i++) {
                    if (aliens[i].tick()) { // if the alien got to the left of the screen and respawned, add it again
                        gameRenderer.add(aliens[i]);
                    }
                    if (!aliens[i].isVisible()) { // if the alien is invisible, remove it from drawing queue
                        gameRenderer.remove(aliens[i]);
                    }
                }
                addToDistance(1);
            } else {
                setNewGameState(CRASHED);
            }

            // Animation
            repaint();

            timeDiff = System.currentTimeMillis() - beforeTime;
            sleep = sleepMax - timeDiff;
            if (sleep < 0) {
                sleep = 0;
            }

            try {
                Thread.sleep(sleep);
            }
            catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
            break;

每次/帧,该循环调用 repaint()方法,该方法又调用三个方法 clearScreen() draw() drawToScreen()在这个类中:

package Game;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;


public class GameRenderer {

    public JPanel gamePanel;
    public List<IDrawObject> listDOs;

    private BufferedImage completeImage;
    private Graphics2D g;

    private boolean firstRun = true;


    public GameRenderer(JPanel gamePanel) {
        this.gamePanel = gamePanel;
        listDOs=new ArrayList<>();
        completeImage = new BufferedImage(Game.WIDTH,Game.HEIGHT,BufferedImage.TYPE_INT_RGB);
        g = completeImage.createGraphics();
    }


    public void clearScreen() {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, Game.WIDTH, Game.HEIGHT);
    }


    public void draw() {
        for(int i = 0; i < listDOs.size(); i ++) {
            listDOs.get(i).draw(g);
        }
    }

    /*
     * Essential method, called last in repaint(): Draws the entire image to the screen.
     */
    public void drawToScreen() {
        Graphics g2 = gamePanel.getGraphics();
        g2.drawImage(completeImage, 0, 0, Game.WIDTH, Game.HEIGHT, null);
    }


    public void add(IDrawObject ido) {
        listDOs.add(ido);
    }
    public void remove(IDrawObject ido) {
        listDOs.remove(ido);
    }
    public JPanel getGamePanel() {
        return gamePanel;
    }
    public void setFirstRun(boolean firstRun) {
        this.firstRun = firstRun;
    }
}

经过一些调试后,我发现即使没有在循环中调用三种绘图方法,键盘输入也不起作用。

所以这就是问题所在:

如何让我的键盘输入再次工作?为什么它不起作用?

我会非常感谢你的帮助。提前谢谢!

1 个答案:

答案 0 :(得分:1)

我尝试在简化的测试工具中应用KeyBindings。在面板级别和切换面板上附加绑定的一般模式与您在代码中演示的相同。我遇到了几种绑定不起作用的情况,都是基于使用哪种工厂方法来创建 KeyStroke

令人惊讶的是无法正常工作getKeyStroke("s"),它必须是getKeyStroke("S")

您可能希望调整switchPanel()方法,可能在某些时候您有多个面板连接到您的窗口(您可能不会看到,因为您主动呈现)具有冲突的键绑定。只需删除附在框架上的任何东西,就可以确保始终只有一个活动面板。

除此之外,从你的所有代码中,我都没有看到为什么它不起作用的原因。

测试样本:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class KeyBindingTest {

    JFrame window = new JFrame("Demo");

    static class DemoPanel extends JPanel {
        private String text;

        public DemoPanel(String text) {
            this.text = text;
            setBounds(0, 0, 200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.BLACK);
            g.drawString(text, 50, 50);
        }
    }

    JPanel menuPanel = new DemoPanel("S to Start");
    JPanel gamePanel = new DemoPanel("S to Stop");

    public static void main(String[] argv) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                KeyBindingTest game = new KeyBindingTest();
                game.start();
            }
        });
    }

    public void start() {
        setup();
        window.setVisible(true);
    }

    private void setup() {
        window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        window.setBounds(50, 50, 200, 200);
        window.setLayout(null);
        window.add(menuPanel);
        bind(menuPanel, KeyStroke.getKeyStroke("S"), "start",
                new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                switchPanel(window, gamePanel);
                System.out.println("Start!");
            }
        });
        bind(gamePanel, KeyStroke.getKeyStroke("S"), "stop",
                new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                switchPanel(window, menuPanel);
                System.out.println("Stop!");
            }
        });
    }

    private void switchPanel(JFrame window, JComponent panel) {
        window.getContentPane().removeAll();
        window.add(panel);
        panel.revalidate();
        panel.repaint();
    }

    private static void bind(JComponent component, KeyStroke key, String string, Action action) {
        component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(key, string);
        component.getActionMap().put(string, action);
    }

}