Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/367.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java:删除可运行画布组件_Java_Swing_Awt_Runnable_Java Canvas - Fatal编程技术网

Java:删除可运行画布组件

Java:删除可运行画布组件,java,swing,awt,runnable,java-canvas,Java,Swing,Awt,Runnable,Java Canvas,我已经为这个游戏工作了一段时间了,它实际上内置了不同的游戏模式。首先,在用户丢失或想要退出程序后,我一直通过退出程序来处理执行。因为不仅必须重新打开程序很烦人,而且当用户不明白为什么它会关闭时,我有理由在丢失或想要退出后,让他们返回主菜单 此时出现的问题是,我在游戏设计的主要部分使用了swing。这不仅包括主菜单,还包括其他菜单,甚至是游戏的一部分Swing用于按钮和其他主要功能的交互。因此,现在我切换到返回主菜单和所有内容,我必须重写渲染和在窗口之间切换的整个基础 因为我正在重写游戏的rend

我已经为这个游戏工作了一段时间了,它实际上内置了不同的游戏模式。首先,在用户丢失或想要退出程序后,我一直通过退出程序来处理执行。因为不仅必须重新打开程序很烦人,而且当用户不明白为什么它会关闭时,我有理由在丢失或想要退出后,让他们返回主菜单

此时出现的问题是,我在游戏设计的主要部分使用了
swing
。这不仅包括主菜单,还包括其他菜单,甚至是游戏的一部分<代码>Swing用于按钮和其他主要功能的交互。因此,现在我切换到返回主菜单和所有内容,我必须重写
渲染和在窗口之间切换的整个基础

因为我正在重写游戏的
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)
My
renderer
是我的
StateRenderer
,当您实际处于具有
Canvas
元素且由
GameState
跟踪的屏幕上时,它将用于处理游戏的所有渲染。现在我将向您展示
renderer.switchDisplay(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();
}
当然,这让我完全感觉到错误在我的
StateRenderer
类中的某个地方,更具体地说是与
run()
方法相关的任何东西。我已经处理过的关于渲染的循环部分

摘要
因此,当从带有
Canvas
组件的面板切换时,我遇到了一个问题,该组件实现了
Runnable
。在我的代码中,我处理了当它没有可见的
画布
,或者当
游戏状态
不适合游戏渲染时的渲染问题。但是,当从当前正在渲染和更新的画布切换到未执行此操作的菜单时,会导致NullPointerException

我想继续感谢所有人和所有人的帮助,因为这个问题真的让我难堪

编辑

当我请求帮助时,我总是决定进行进一步的测试,我发现问题出现在
celldender.switchDisplay()
方法的
frame.validate()
行中。我不明白为什么会出现问题。

根据评论中的讨论,问题很可能与违反Swing“单线程规则”有关:

一旦实现了Swing组件,所有可能影响或依赖于该组件状态的代码都应该在事件调度线程中执行

违反此规则的结果可能是任意奇怪的,但是Swing管理基础架构内部的
NullPointerException
s是更常见的结果之一

在许多情况下,这个问题可以通过一个实用的模式来解决:假设您有一个修改Swing组件的方法:

void modifySwingComponents()
{
    someComponent.add(someOtherComponent);
    someComponent.remove(somethingElse);
    someTextComponent.setText("Text");
    ...
}
然后,您可以通过插入以下内容轻松检查是否违反了单线程规则

System.out.println(Thread.currentThread());
用这种方法。它应该始终打印
线程[AWT-EventQueue-0,6,main]
(或类似内容,表示该方法在事件调度线程上执行)。或者,您可以直接查询

System.out.println(SwingUtilities.isEventDispatchThread());
如果从非事件分派线程(EDT)的线程调用该方法,则可以将该方法“包装”到放置在事件队列上的可运行线程中:

void modifySwingComponents()
{
    if (SwingUtilities.isEventDispatchThread())
    {
        // We're on the right thread - direcly call the method
        modifySwingComponentsOnEDT();
    }
    else
    {
        // Put the task to execute the method in the event queue,
        // so that it will be executed on the EDT as soon as possible
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                modifySwingComponentsOnEDT();
            }
        });
    }
}

// Always called on the EDT!
void modifySwingComponentsOnEDT()
{
    someComponent.add(someOtherComponent);
    someComponent.remove(somethingElse);
    someTextComponent.setText("Text");
    ...
}

但是请注意尽管这看起来很简单,似乎很容易解决某些问题,但它并不能免除您认真检查和记录在哪个线程上执行的方法的责任

我将赌注押在线程问题上,并且非常确定某个线程(不是事件分派线程)正在以某种方式操纵Swing组件。从目前发布的代码来看,很难判断这可能发生在哪里(但无论如何,这仍然只是一个猜测)。
switchDisplay
方法是从
actionPerformed
方法之外的任何其他地方调用的吗?不是,调用它的地方很少,而且只有从
actionPerformed
点调用,以及当你输了游戏时调用。我确实用底部的编辑更新了我的帖子,正如我所说,错误首先在我的
CellDefender.switchDisplay()
方法中的
frame.validate()
抛出。它被称为“当你输掉比赛时”-在哪个线程上发生?在
switchDisplay
方法的开头插入
System.out.println(Thread.currentThread())
,查看它是否打印出一个不是AWT事件调度线程的线程的名称它从
CellDefender
switchDisplay()方法打印出的线程不是AWT事件调度线程,相反,它是
StateRenderer
线程。对于
StateRenderer
的switchDisplay方法,反之亦然。
System.out.println(SwingUtilities.isEventDispatchThread());
void modifySwingComponents()
{
    if (SwingUtilities.isEventDispatchThread())
    {
        // We're on the right thread - direcly call the method
        modifySwingComponentsOnEDT();
    }
    else
    {
        // Put the task to execute the method in the event queue,
        // so that it will be executed on the EDT as soon as possible
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                modifySwingComponentsOnEDT();
            }
        });
    }
}

// Always called on the EDT!
void modifySwingComponentsOnEDT()
{
    someComponent.add(someOtherComponent);
    someComponent.remove(somethingElse);
    someTextComponent.setText("Text");
    ...
}