JMonkeyEngine3——NiftyGui ScreenController
前言
在前面的文章中,你学会了创建基本的NiftyGui用户界面。但它仍然是静态的,比如,当你单击按钮时,什么也不会发生。GUI 的目的是与你的 Java 类进行通信:你的游戏需要知道用户单击了什么、他们选择了哪些设置、他们在字段中输入了哪些值等。同样,用户需要知道当前的游戏状态(得分、健康状况等)。
要让 Nifty 屏幕与 JME3 应用程序通信,你需要向每个 NiftyGUI 屏幕注册一个ScreenController。通过创建实现ScreenController接口及其抽象方法的Java 类来创建 ScreenController 。
专业提示:由于您正在编写 jME3 应用程序,因此您还可以使 ScreenController 类扩展BaseAppState或SimpleApplication类!这使 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行:
- 为<control name="button">添加
visibleToMouse="true"属性 - 为<control name="button">添加一个子元素<interact/>
- 指定当用户执行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

浙公网安备 33010602011771号