JMonkeyEngine3——NiftyGui ScreenController

前言

前面的文章中,你学会了创建基本的NiftyGui用户界面。但它仍然是静态的,比如,当你单击按钮时,什么也不会发生。GUI 的目的与你的 Java 类进行通信:你的游戏需要知道用户单击了什么、他们选择了哪些设置、他们在字段中输入了哪些值等。同样,用户需要知道当前的游戏状态(得分、健康状况等)。

要让 Nifty 屏幕与 JME3 应用程序通信,你需要向每个 NiftyGUI 屏幕注册一个ScreenController。通过创建实现ScreenController接口及其抽象方法的Java 类来创建 ScreenController 。

专业提示:由于您正在编写 jME3 应用程序,因此您还可以使 ScreenController 类扩展BaseAppStateSimpleApplication类!这使 ScreenController 可以访问Application对象和更新循环!

将NiftyGui连接到Java控制器

创建一个NifityGui03 xml,修改如下:

 1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 2 <nifty xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://nifty-gui.lessvoid.com/nifty-gui" xsi:schemaLocation="https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd">
 3     <useControls filename="nifty-default-controls.xml"/>
 4     <useStyles filename="nifty-default-styles.xml"/>
 5     <screen id="screen0">
 6         <layer id="layer0" childLayout="center">
 7             <panel id="panel0" height="95px" style="nifty-panel-simple" width="400px" childLayout="vertical" y="376px" x="259px">
 8                 <control name="label" id="title" text="TEST!" height="46px" font="aurulent-sans-16.fnt" width="100px" y="35px" x="142px"/>
 9                 <control name="button" id="button0" align="center" backgroundImage="button/button.png" label="YES" childLayout="center" valign="center" y="107" x="146"/>
10             </panel>
11         </layer>
12     </screen>
13 </nifty>

注意:这里出现了未学习到的标签(如<control name="label">和<control name="button">标签),不必担心,后续文章会介绍,暂时只需要将其复制到你的文件中,用于学习本章的主题。

然后创建一个NiftyGui03.java,修改代码如下:

 1 import com.jme3.app.SimpleApplication;
 2 import com.jme3.niftygui.NiftyJmeDisplay;
 3 import de.lessvoid.nifty.Nifty;
 4 import de.lessvoid.nifty.screen.Screen;
 5 import de.lessvoid.nifty.screen.ScreenController;
 6 
 7 /**
 8  * @date 2024年7月11日19点45分
 9  * @author JohnKkk
10  */
11 public class NiftyGui03 extends SimpleApplication implements ScreenController{
12 
13     private Nifty m_Nifty;
14     public static void main(String[] args) {
15         NiftyGui03 niftyGui03 = new NiftyGui03();
16         niftyGui03.start();
17     }
18 
19     @Override
20     public void simpleInitApp() {
21         // 初始化Nifty
22         NiftyJmeDisplay niftyDisplay = NiftyJmeDisplay.newNiftyJmeDisplay(
23                 assetManager,
24                 inputManager,
25                 audioRenderer,
26                 guiViewPort);
27         m_Nifty = niftyDisplay.getNifty();
28         // 将NiftyGUI显示对象添加到JME3中
29         guiViewPort.addProcessor(niftyDisplay);
30         
31         m_Nifty.fromXml("Interface/NiftyGui03.xml", "screen0");
32         
33         // 禁用flyCam并显示鼠标
34         flyCam.setEnabled(false);
35         inputManager.setCursorVisible(true);
36     }
37 
38     // --------------------------------ScreenController接口-----------------------------------↓
39     @Override
40     public void bind(Nifty nifty, Screen screen) {
41         System.out.println("bind screen : " + screen.getScreenId());
42     }
43 
44     @Override
45     public void onStartScreen() {
46         System.out.println("onStartScreen------------------!");
47     }
48 
49     @Override
50     public void onEndScreen() {
51         System.out.println("onEndScreen------------------!");
52     }
53     // ---------------------------------ScreenController接口----------------------------------↑
54     
55 }

注意第11行,我们继承SimpleApplication并实现ScreenController接口,其中第38行~第53行是实现来自ScreenController接口的方法。

此时启动JME3程序,发现点击按钮并没有发生什么事:

 接下来我们将NiftyGui与Java控制器(实现ScreenController的类,本文中就是NiftyGui03.java类)连接绑定,绑定必须指定要将ScreenController与哪个screen进行关联(毕竟接口名叫ScreenController,自然就是与某个具体的screen进行关联绑定)。

首先通过fromXml()函数进行注册ScreenController,将如下代码:

m_Nifty.fromXml("Interface/NiftyGui03.xml", "screen0");

改为

m_Nifty.fromXml("Interface/NiftyGui03.xml", "screen0", this);

别忘了this类就是NiftyGui03.java(也就是一个实现了ScreenController接口的类),这里通过fromXml()函数将NiftyGui03.java这个ScreenController注册到NiftyEngine中(似乎可以不必进行注册?)。

然后是通过标签属性单独指定每个screen要绑定的ScreenController类,如下:

1 <screen id="screen0" controller="mygame.NifityGui03">
2         ...
3 </screen>

注意第1行的controller属性,指定了screen0这个screen与mygame.NifityGui03这个类(ScreenController)绑定,这里需要输入类全路径名。

重新启动JME3程序,执行如下:

 说明成功将NiftyGui screen0与NiftyGui03.java这个ScreenController进行了绑定。

ScreenController接口的几个生命周期函数:

  • bind():当ScreenController与一个screen绑定时回调,在onStartScreen()之前执行,注意:当你将多个screen与同一个ScreenController关联时,通过gotoScreen()切换到其他screen时,会依次执行onEndScreen() 上一个screen的结束 ->bind() 新screen的绑定回调(参数screen是当前切换的screen) ->onStartScreen() 新screen的开始
  • onStartScreen():当ScreenController与一个screen绑定并执行bind()函数之后,并在显示之后回调,标志该screen已经启动
  • onEndScreen():当通过gotoScreen()切换不同screen时,由于NiftyGui一次只能显示一个screen,旧的screen关闭时将回调该函数

NiftyGui和Java交互

在大多数情况下,您需要将游戏数据传入和传出 ScreenController。请注意,您可以将 Java 类中的任何自定义参数传递到 ScreenController 构造函数 ( public MyStartScreen(GameData data) {})。

使用以下三种方法的任意组合来使 Java 类与 GUI交互

 

从NiftyGui调用void Java方法

简单的说就是你对GUI交互(比如点击按钮)的响应方式(回调监听器),修改NiftyGui03.java,添加一个修改testClickEvent方法,如下:

 1 import com.jme3.app.SimpleApplication;
 2 import com.jme3.niftygui.NiftyJmeDisplay;
 3 import de.lessvoid.nifty.Nifty;
 4 import de.lessvoid.nifty.screen.Screen;
 5 import de.lessvoid.nifty.screen.ScreenController;
 6 
 7 /**
 8  * @date 2024年7月11日19点45分
 9  * @author JohnKkk
10  */
11 public class NiftyGui03 extends SimpleApplication implements ScreenController{
12 
13     ...
14     
15     /**
16      * 添加一个public void函数,用于NiftyGui xml绑定.<br/>
17      * @param text 传递的参数
18      */
19     public void testClickEvent(String text){
20         System.out.println("点击按钮,传递的参数:" + text);
21     }
22 
23     ...
24     
25 }

然后修改xml,为<control name="button">添加如下代码:

 1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 2 <nifty xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://nifty-gui.lessvoid.com/nifty-gui" xsi:schemaLocation="https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd">
 3     <useControls filename="nifty-default-controls.xml"/>
 4     <useStyles filename="nifty-default-styles.xml"/>
 5     <screen id="screen0" controller="mygame.NiftyGui03">
 6         <layer id="layer0" childLayout="center">
 7             <panel id="panel0" height="95px" style="nifty-panel-simple" width="400px" childLayout="vertical" y="376px" x="259px">
 8                 <control name="label" id="title" text="TEST!" height="46px" font="aurulent-sans-16.fnt" width="100px" y="35px" x="142px"/>
 9                 <control name="button" id="button0" visibleToMouse="true" align="center" backgroundImage="button/button.png" label="YES" childLayout="center" valign="center" y="107" x="146">
10                     <interact onClick="testClickEvent(测试参数)" />
11                 </control>
12             </panel>
13         </layer>
14     </screen>
15 </nifty>

注意第9行到第11行:

  1. 为<control name="button">添加visibleToMouse="true"属性
  2. 为<control name="button">添加一个子元素<interact/>
  3. 指定当用户执行onClick(<control name="button">组件的一个事件名称)时要调用的Java方法并传递对应的参数(注意:由于<interact/>的属性名为onClick,所以你的Java函数不要命名为onClick,同名会导致监听器绑定失败,可以命名为onClick2,onClick3或其他名称,同理<interact/>的其他属性事件名称

启动JME3程序,点击按钮,执行结果如下:

 十分方便简洁。

使NiftyGui从Java方法获取返回值

当 Nifty GUI初始化时,您可以从 Java 获取数据。在此示例中,<control name="label">默认text属性显示TEST!文字,我们希望从指定Java方法中获取要显示的文本,修改NiftyGui03.java,添加如下方法:

 1 public class NiftyGui03 extends SimpleApplication implements ScreenController{
 2 
 3     private Nifty m_Nifty;
 4     private float m_TPF;
 5     ...
 6     
 7     /**
 8      * 返回当前标题内容.<br/>
 9      * @return 用于要在xml中id=title的label显示
10      */
11     public String getTitle(){
12         return "tpf:" + m_TPF;
13     }
14 
15     @Override
16     public void simpleUpdate(float tpf) {
17         super.simpleUpdate(tpf); //To change body of generated methods, choose Tools | Templates.
18         m_TPF = tpf;
19     }
20     
21     ...
22     
23 }

注意第11行添加的getTitle()方法,然后修改xml,添加如下代码:

 1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 2 <nifty xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://nifty-gui.lessvoid.com/nifty-gui" xsi:schemaLocation="https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd https://raw.githubusercontent.com/void256/nifty-gui/1.4/nifty-core/src/main/resources/nifty.xsd">
 3     <useControls filename="nifty-default-controls.xml"/>
 4     <useStyles filename="nifty-default-styles.xml"/>
 5     <screen id="screen0" controller="mygame.NiftyGui03">
 6         <layer id="layer0" childLayout="center">
 7             <panel id="panel0" height="95px" style="nifty-panel-simple" width="400px" childLayout="vertical" y="376px" x="259px">
 8                 <control name="label" id="title" text="${CALL.getTitle()}" height="46px" font="aurulent-sans-16.fnt" width="100px" y="35px" x="142px"/>
 9                 <control name="button" id="button0" visibleToMouse="true" align="center" backgroundImage="button/button.png" label="YES" childLayout="center" valign="center" y="107" x="146">
10                     <interact onClick="testClickEvent(测试参数)" />
11                 </control>
12             </panel>
13         </layer>
14     </screen>
15 </nifty>

注意第8行,这里NiftyGui 用${CALL.getTitle()}从 ScreenController Java 类中获取 getTitle() 方法的返回值,你可以将其用于字符串和数值(例如,当你从文件读取设置时,你可以在GUI中显示结果),也可以将其用于具有副作用的方法。

启动JME3程序,显示如下:

 值得注意的是,虽然simpleUpdate()中每帧更新了m_TPF变量,但是NiftyGui上只显示了第一次的默认值,说明NiftyGui无法显示动态变化(或者说只会调用getTitle()方法一次)

从Java中修改NiftyGui元素和事件

你还可以在运行时通过Java代码更改NiftyGui元素的外观和功能,请确保为NiftyGui元素添加了id,以便可以识别和处理对应元素。

修改Java代码,添加如下:

 1 public class NiftyGui03 extends SimpleApplication implements ScreenController{
 2 
 3     ...
 4     
 5     /**
 6      * 添加一个public void函数,用于NiftyGui xml绑定.<br/>
 7      * @param text 传递的参数
 8      */
 9     public void testClickEvent(String text){
10         System.out.println("点击按钮,传递的参数:" + text);
11     }
12     /**
13      * 测试函数.<br/>
14      * @param text 传递的参数
15      */
16     public void testClickEvent2(String text){
17         System.out.println("执行测试函数:" + text);
18     }
19 
20     // --------------------------------ScreenController接口-----------------------------------↓
21     @Override
22     public void bind(Nifty nifty, Screen screen) {
23         System.out.println("bind screen : " + screen.getScreenId());
24         Element button0 = screen.findElementById("button0");
25         for(ElementRenderer elementRenderer : button0.getElementRenderer()){
26             System.out.println("elementRender:" + elementRenderer.toString());
27         }
28         // 通过Java代码重新绑定button0的onClick事件回调
29         button0.getElementInteraction().getPrimary().setOnClickMethod(new NiftyMethodInvoker(nifty, "testClickEvent2(111)", this));
30     }
31 
32     ...
33     // ---------------------------------ScreenController接口----------------------------------↑
34     
35 }

我们在第16~18行添加了一个testClickEvent2()函数,然后在bind()函数回调时,我们通过screen.findElementById()(传入button0这个id)获取button0,然后打印一些输出,接着在第29行为button0的onClick事件回调重新绑定监听器为testClickEvent2()函数并在回调时传递111参数。

从这里可以看出,很像Android的Java与xml视图文件元素的交互,为了使其<interact />工作,你的 xml 元素中需要预先存在一个(可能处于非活动状态的)标签,而不能在xml文件中不存在<interact />标签下调用getElementInteraction()方法。

参考

https://wiki.jmonkeyengine.org/docs/3.4/core/gui/nifty_gui_java_interaction.html

posted @ 2024-07-11 21:26  JhonKkk  阅读(50)  评论(0)    收藏  举报