【发布时间】:2015-01-07 00:58:54
【问题描述】:
因此,近一年来,我一直致力于从头开始构建自己的 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 游戏面板添加到 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 变量可以跟踪游戏当前所处的“状态”,例如。 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;
}
}
经过更多调试后,我发现即使没有在循环中调用三种绘图方法中的任何一种,键盘输入也不起作用。
那么问题来了:
如何让我的键盘输入再次起作用?为什么它不起作用?
非常感谢您的帮助。提前致谢!
【问题讨论】:
-
您显示的代码片段没有显示您 GamePanel 上安装的任何 KeyBinding,我猜这将是游戏运行时窗口中唯一的面板?此外,默认情况下,JPanel 可能不会响应 KeyListener(可能还有 KeyBinding),因为它不可聚焦(请参阅此处stackoverflow.com/questions/11487369)。尝试使用 setFocusable(true) 使面板具有焦点,并确保将 KeyBinding 添加到最顶部的相关容器中。
-
@Durandal 感谢您的提示。我已将 gamePanel.setFocusable(true) 添加到 Game() 构造函数中,但这并没有帮助。键盘仍然只能在主菜单中使用。在gamePanel上安装KeyBinding是什么意思,最顶层的容器是什么?我的 JFrame(我叫我的窗口)?
-
希望每个新用户都能提出这样的问题。
-
我不确定您是如何使用面板的,我错过了您实际将面板添加到窗口的地方;而且我不确定您是否有一个或多个键绑定(每个面板一个?) - “最顶层”是指将绑定添加到最外面的封闭容器,这 可能 是窗口(或它的 ContentPane),具体取决于您是在所有“状态”中使用一个绑定还是每个状态一个绑定。您能否在实际将面板添加到窗口的位置添加代码?
-
@Durandal 我添加了方法 runGame() 和 startGame() 以进一步澄清。 runGame() 只是暂停菜单音乐并调用 startGame(); startGame() 很有趣,因为它将 JPanel gamePanel 添加到 JFrame 窗口中,请参阅上面我更新的帖子。我有多个 KeyBindings 用于不同的键,如 S、C、ESCAPE 和 UP/DOWN。我通过将它们添加到相关的 JPanel 来区分每个状态的绑定;例如在 GAME_RUNNING 状态下,menuPanel 会从 JFrame 窗口中移除。
标签: java keyboard key-bindings 2d-games