在Swing中添加JMonkey画布

在Swing中添加JMonkey画布

3D 游戏通常全屏播放,或者在接管鼠标和所有输入的窗口中播放。但是,也可以在标准 Swing 应用程序中嵌入 jME 3 画布。
当您使用比HUD更复杂的用户界面创建某种交互式3D查看器时,这可能很有用:例如交互式科学演示,关卡编辑器或游戏角色设计器。

优势:

您可以在jME3游戏旁边使用Swing组件(框架,面板,菜单,控件)。

NetBeans GUI 构建器与 jMonkeyEngine 兼容;你可以用它来布置 Swing GUI 框架,然后将 jME 画布添加到其中。通过工具→插件→可用插件安装 GUI 构建器。

弊:

您不能使用SimpleApplication的默认鼠标捕获进行相机导航,而必须提出自定义解决方案。

Extending SimpleApplication

您的开始与任何jME3游戏相同:基本应用程序,此处为SwingCanvasTest,扩展。像往常一样,您用于初始化场景,并作为事件循环。
相机在 SimpleApplication 中的默认行为是捕获鼠标,这在 Swing 窗口中没有意义。您必须在初始化应用程序时停用并替换此行为:

public void simpleInitApp() {
  // activate windowed input behaviour
  flyCam.setDragToRotate(true);
  // Set up inputs and load your scene as usual
  ...
}

简而言之:首先不同的是方法。我们不会像往常一样在 SwingCanvasTest 对象上调用 start()。相反,我们创建一个 Runnable() 来创建并打开一个标准的 Swing jFrame。在可运行中,我们还使用特殊设置创建我们的 SwingCanvasTest 游戏,为其创建一个 Canvas,并将其添加到 jFrame 中。然后我们调用 startCanvas()main()

main() 和 runnable()

Swing 不是线程安全的,不允许我们保持 jME3 画布最新。这就是为什么我们为 jME canvas 创建一个可运行的 canvas 并将其排队在 AWT 事件线程中,这样当 Swing 准备好更新它自己的东西时,它可以在循环中“稍后”调用。
在 SwingCanvasTest 的 main() 方法中,创建一个排队的 runnable()。它将包含 jME 画布和 Swing 框架。

  public static void main(String[] args) {
    java.awt.EventQueue.invokeLater(new Runnable() {
      public void run() {
         // ... see below ...
      }
    });
  }

请注意,从 AWT 事件队列修改场景中的对象时,您必须使用 app.enqueue(),就像在更改swing元素时必须使用来自其他线程(例如更新循环)的 java.awt.EventQueue.invokeLater() 一样。如果您没有计划适当的线程模型,这可能会很快变得毛茸茸的,因此您可能希望使用 NiftyGUI,因为它嵌入在更新循环线程中并且也是跨平台兼容的(例如 android 等)。

创建画布

在该方法中,我们启动 jME 应用程序,创建其画布,创建一个 Swing 框架,然后将所有内容加在一起。
指定 com.jme3.system.AppSettings,以便 jME 知道我们放入的 Swing 面板的大小。应用程序不会要求用户提供显示设置,您必须提前指定它们。run()

AppSettings settings = new AppSettings(true);
settings.setWidth(640);
settings.setHeight(480);

我们创建画布应用程序 SwingCanvasTest,并为其提供设置。我们手动为这个游戏创建一个画布,并配置com.jme3.system.JmeCanvasContext。方法setSystemListener() 确保侦听器接收与context创建、更新和销毁相关的事件。

SwingCanvasTest canvasApplication = new SwingCanvasTest();
canvasApplication.setSettings(settings);
canvasApplication.createCanvas(); // create canvas!
JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication.getContext();
ctx.setSystemListener(canvasApplication);//监听器
Dimension dim = new Dimension(640, 480);
ctx.getCanvas().setPreferredSize(dim);

请注意,我们没有像通常在main()方法中那样在应用程序上调用start()。我们稍后会调用 startCanvas()。

Creating the Swing Frame

在 run() 方法中,您可以像往常一样创建 Swing 窗口。为其创建一个空的 jFrame 和 add() 组件,或者在另一个类文件中创建自定义 jFrame 对象(例如,通过使用 NetBeans GUI 构建器),并在此处创建该对象的实例。无论你做什么,我们称之为jFrame。window

JFrame window = new JFrame("Swing Application");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

我们在 JFrame 中创建了一个标准的 JPanel。给它任何你想要的布局 - 这里我们使用一个简单的流布局。代码示例显示“一些 Swing 组件”,这是您添加按钮和控件的位置。
重要的步骤是将 canvas 组件添加到面板中,就像所有其他 Swing 组件一样。

JPanel panel = new JPanel(new FlowLayout()); // a panel
// add all your Swing components ...
panel.add(new JButton("Some Swing Component"));
...
// add the JME canvas
panel.add(ctx.getCanvas());

好的,jFrame和面板已经准备好了。我们将面板添加到jFrame中,并将所有内容打包在一起。将窗口的可见性设置为 true 使其显示。

window.add(panel);
window.pack();
window.setVisible(true);

还记得我们还没有在 jME 应用程序上调用 start() 吗?对于画布,您现在必须调用一个特殊方法:startCanvas()

canvasApplication.startCanvas();

官方案例:

package SwingGUI;

/**
 * @author xiaosongChen
 * @create 2022-11-12 11:38
 * @description :主线程中加入了swing的runnable多线程
 * 创建了两个窗口
 */

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
import com.jme3.system.awt.AwtPanel;
import com.jme3.system.awt.AwtPanelsContext;
import com.jme3.system.awt.PaintMode;

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TestAwtPanels extends SimpleApplication {

    final private static CountDownLatch panelsAreReady = new CountDownLatch(1);
    private static TestAwtPanels app;
    private static AwtPanel panel, panel2;
    private static int panelsClosed = 0;

    //创建jframe窗口
    private static void createWindowForPanel(AwtPanel panel, int location){
        JFrame frame = new JFrame("Render Display " + location);
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(panel, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                if (++panelsClosed == 2){
                    app.stop();
                }
            }
        });
        frame.pack();
        frame.setLocation(location, Toolkit.getDefaultToolkit().getScreenSize().height - 400);
        frame.setVisible(true);
    }

    public static void main(String[] args){
        Logger.getLogger("com.jme3").setLevel(Level.WARNING);

        app = new TestAwtPanels();
        app.setShowSettings(false);
        AppSettings settings = new AppSettings(true);
        settings.setCustomRenderer(AwtPanelsContext.class);
        settings.setFrameRate(60);
        app.setSettings(settings);
        app.start();

        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run(){
                /*
                 * Sleep 2 seconds to ensure there's no race condition.
                 * The sleep is not required for correctness.
                 */
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException exception) {
                    return;
                }

                final AwtPanelsContext ctx = (AwtPanelsContext) app.getContext();
                panel = ctx.createPanel(PaintMode.Accelerated);
                panel.setPreferredSize(new Dimension(400, 300));
                ctx.setInputSource(panel);

                panel2 = ctx.createPanel(PaintMode.Accelerated);
                panel2.setPreferredSize(new Dimension(400, 300));

                createWindowForPanel(panel, 300);
                createWindowForPanel(panel2, 700);
                /*
                 * Both panels are ready.
                 */
                panelsAreReady.countDown();
            }
        });
    }

    @Override
    public void simpleInitApp() {
        flyCam.setDragToRotate(true);

        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
//        mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
        geom.setMaterial(mat);
        rootNode.attachChild(geom);
        /*
         * Wait until both AWT panels are ready.
         */
        try {
            panelsAreReady.await();
        } catch (InterruptedException exception) {
            throw new RuntimeException("Interrupted while waiting for panels", exception);
        }

        panel.attachTo(true, viewPort);
        guiViewPort.setClearFlags(true, true, true);
        panel2.attachTo(false, guiViewPort);
    }
}
package SwingGUI;

/**
 * @author xiaosongChen
 * @create 2022-11-15 22:34
 * @description :将jme的画布添加进jframe画布中,添加后又删除画布,随后又添加画布
 */

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class TestSafeCanvas extends SimpleApplication {

    public static void main(String[] args) throws InterruptedException{

        AppSettings settings = new AppSettings(true);
        settings.setWidth(640);
        settings.setHeight(480);


        final TestSafeCanvas app = new TestSafeCanvas();
        app.setPauseOnLostFocus(false);
        app.setSettings(settings);
        //初始化应用程序的画布以便使用。
        //调用此方法后,将getContext()上下文转换为JmeCanvasContext,
        // 然后使用JmeCanvasContext. getcanvas()获取画布并将其附加到AWT/Swing Frame。
        // 渲染线程将在画布在屏幕上显示时启动,但是如果你希望立即启动上下文,可以调用startCanvas()来强制启动渲染线程。
        app.createCanvas();

        app.startCanvas(true);

        JmeCanvasContext context = (JmeCanvasContext) app.getContext();
        Canvas canvas = context.getCanvas();
        canvas.setSize(settings.getWidth(), settings.getHeight());

        Thread.sleep(3000);

        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                app.stop();
            }
        });
        frame.getContentPane().add(canvas);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        Thread.sleep(3000);

        frame.getContentPane().remove(canvas);

        Thread.sleep(3000);

        frame.getContentPane().add(canvas);
    }

    @Override
    public void simpleInitApp() {
        flyCam.setDragToRotate(true);

        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
//        mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
        geom.setMaterial(mat);
        rootNode.attachChild(geom);
    }
}

官方网址:

https://wiki.jmonkeyengine.org/docs/3.4/tutorials/how-to/java/swing_canvas.html

我的学习仓库地址:https://github.com/chenxiaosong0/JME-NEW.git

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值