我已经在这个游戏上工作了一段时间,它实际上内置了不同的游戏模式。起初,我一直exiting the program
在用户丢失或想要退出之后处理执行。因为不仅必须重新打开程序很烦人,而且当用户不明白它为什么关闭时,我有理由在丢失或想要退出后返回主菜单。
此时出现的问题是我swing
在游戏设计的主要部分中使用。这不仅包括主菜单,还包括其他菜单,甚至是游戏的一部分。Swing
用于按钮和其他主要功能的交互性。所以现在我要切换到返回主菜单和所有内容,我不得不基本上重写整个窗口的基础rendering
和在窗口之间切换。
由于我正在重写render
游戏的方法,所以我决定制作一个StateRenderer
类。由此,它将处理并决定它当前是否需要处理。因此,在该run()
方法中,我放置了一行代码来检查它是否甚至需要在菜单状态下呈现。
@Override
public void run() {
long lastTime = System.nanoTime();
long timer = System.currentTimeMillis();
final double ns = BILLION / UPDATE_RATE;
double delta = 0;
int updates = 0, frames = 0;
while (running) {
// right here I am checking the state for it
GameState state = CellDefender.getGameState();
if (state == GameState.MAIN_MENU || state == GameState.STORE_MENU || state == GameState.SETTINGS_MENU) continue;
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
update();
updates++;
delta--;
}
render();
frames++;
if (System.currentTimeMillis() - timer >= 1000) {
while (System.currentTimeMillis() - timer >= 1000) // while idling it builds up much, and makes it less annoying when debugging
timer += 1000;
System.out.println("UPS: " + updates + ", FPS: " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
现在,当我决定从主菜单切换到实际游戏模式时效果很好,但是如果我在模式上失败,或者想退出主菜单,我会收到这个令人讨厌的错误,我不知道我是怎么做到的会修复它:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: component argument pData
at sun.java2d.windows.GDIWindowSurfaceData.initOps(Native Method)
at sun.java2d.windows.GDIWindowSurfaceData.<init>(Unknown Source)
at sun.java2d.windows.GDIWindowSurfaceData.createData(Unknown Source)
at sun.java2d.d3d.D3DScreenUpdateManager.getGdiSurface(Unknown Source)
at sun.java2d.d3d.D3DScreenUpdateManager.createGraphics(Unknown Source)
at sun.awt.windows.WComponentPeer.getGraphics(Unknown Source)
at java.awt.Component.getGraphics(Unknown Source)
at sun.awt.RepaintArea.paint(Unknown Source)
at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
我理解的唯一部分是我在使用 AWT 做一些完全错误的事情,它处理诸如Graphics
和之类的事情Canvas
。仅仅用 try..catch 方法吞没错误可能是一个可怕的想法,但话又说回来,我真的不知道它是在哪里引起的。
要详细了解我如何切换,这里是从我的主菜单到实际游戏模式:
private void initListeners() {
btnRegular.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setGameState(GameState.REGULAR);
renderer.switchDisplay(RegularMode.class);
}
});
// more code is here, but useless for now
}
My renderer
is myStateRenderer
旨在处理游戏的所有渲染,当您实际上位于具有该Canvas
元素的屏幕上并由GameState
. 现在我将向您展示该renderer.switchDisplay(Class class)
方法中的内容。
public void switchDisplay(Class<? extends GameMode> mode) {
if (mode == RegularMode.class) {
currentMode = new RegularMode(size);
setPreferredSize(size);
screen = new Screen(size.width, size.height);
panel.add(currentMode.getFunctionBar(), BorderLayout.NORTH);
panel.add(currentMode.getScoreBar(), BorderLayout.SOUTH);
// -- extra stuff that is similar --
} else return;
JFrame frame = CellDefender.getFrame();
frame.remove(CellDefender.getMainPanel());
panel.add(this, BorderLayout.CENTER);
frame.add(panel);
frame.validate();
frame.repaint();
frame.pack();
currentMode.initialize();
requestFocus();
}
这可能有点低效,但所有这些似乎都工作得很好。现在深入了解当一切都切换回引发所有错误的主菜单时!
这是直接导致错误的运行代码:
public static void switchDisplay() {
setGameState(GameState.MAIN_MENU); // Switched the game state, should stop looping.
frame.setContentPane(panel);
frame.validate();
frame.repaint();
frame.pack();
}
当然,这给了我一个完整的感觉,即错误存在于我的StateRenderer
班级的某个地方,更具体地说,是任何与该run()
方法相关的东西。我已经处理了有关渲染的循环的部分。
总结
因此,当我从具有Canvas
组件的面板切换时遇到问题,该组件实现Runnable
. 在我的代码中,我已经处理了当它没有可见时渲染的问题Canvas
,或者当GameState
它不是供游戏渲染的时候。但是,当从当前正在渲染和更新的画布切换到没有这样做的菜单时,会导致 NullPointerException。
我想继续感谢任何人和每个人的帮助,因为这个问题真的让我很难过。
编辑
通过在寻求帮助时我总是决定进行的进一步测试,我发现问题出现在CellDefender.switchDisplay()
该行的方法中frame.validate()
。我不明白为什么这会导致问题。