02 Jmonkey3.2.0+Nifty1.4.2实现GUI

GUI选择

实现Jmonkey的GUI有三四种方式,我尝试了三种,有

  • Lemur
  • tonegod
  • nifty
    最终选择了Nifty, 因为Lemur用java语法完成,不方便编写,用起来又复杂,读了半天文档也没明白;tonegod的参考资料又少,没看懂;而Nifty可以用xml和java做界面,对于开发过Android的我来说最方便,用起来也灵活,重点是它还开源,github的wiki里有详细的使用说明,用它来做GUI最方便。

Nifty与Jmonkey的集成

添加依赖

在jme中用nifty需要把jme3-niftygui的jar包导入项目,这个maven仓库好像没有,没关系,到jmonkey的sdk里把它复制到项目就行,然后在build.gradle的dependencies中添加:

compile files('libs/jme3-niftygui-3.2.0-v3.2-prealpha-sdk1-SNAPSHOT.jar') // 写jar包目录和jar包名称

// nifty
compile "com.github.nifty-gui:nifty:1.4.2"
compile "com.github.nifty-gui:nifty-style-black:1.4.2"
compile "com.github.nifty-gui:nifty-default-controls:1.4.2"
新建screens.xml

Nifty的好处在于可以用xml做显示界面。在resource文件里创建一个文件夹scenes,新建一个xml文件,叫screens.xml。
之所以叫screens,是因为所有的界面都可以在一个xml文件里,以<screen></screen>标签区分,给screen一个id就能用代码找到它。每一屏是一个screen。切换时通过nifty.gotoScreen("screen id")语句来实现切换。
xml有很多标签,跟Android一样,通过标签嵌套实现界面,nifty的xml里标签层级如下:

Nifty标签

看懂这个图写起来就很容易了,screens.xml示例代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<nifty xmlns="http://nifty-gui.lessvoid.com/nifty-gui">
    <useStyles filename="nifty-default-styles.xml"/>
    <useControls filename="nifty-default-controls.xml"/>
    <screen id="start_screen" controller="com.happykai.demo.appstates.StartScreenState">
        <layer id="layer" childLayout="center" backgroundImage="Textures/background.png">
            <panel id="dialog" style="nifty-panel" childLayout="vertical" padding="18px,28px,28px,16px" width="80%"
                   height="70%" align="center" valign="center">
                <effect>
                    <onStartScreen name="move" timeType="exp" factor="3.5" direction="top" mode="in" length="500"
                                   inherit="true"/>
                    <onEndScreen name="move" timeType="exp" factor="3.5" direction="bottom" mode="out" length="500"
                                 inherit="true"/>
                    <panel id="namePanel" childLayout="horizontal">
                          <text id="name_text" text="你的名字: " width="150px" align="left" textVAlign="center" textHAlign="left" style="nifty-label"/>
                          <control id="name_text_ctl" name="textfield" width="15%" text="300"/>
                    </panel>
                    <panel id="img_btn_panel" childLayout="horizontal">
                           <control id="name_text_ctl" name="button" width="15%" text="300"/>
                          <image id="img" align="right" filename="Textures/add.png" height="20px" width="20px"
                               visibleToMouse="true">
                            <interact onClick="addDropClick(2)"/>
                          </image>
                    </panel>
                </effect>
            </panel>
        </layer>
    </screen>
</nifty>

在nifty中,所有能与用户交互的控件都用<control></control>标签,通过name来区别控件,name是固定字段,有button,textfield,dropdown等,具体可以参考nifty的wiki,里面有每个控件详细的使用说明。

如果想给一个非control的控件加交互,比如一个image(官方的button很难看,想换成自己想要的button,不妨让美工做一个button,然后直接把图片放过来),在加一个<interact>子标签,如上面例子中的<interact onClick="addDropClick(2)"/>,然后再java中实现该代码即可,括号中的2是传入的参数。

新建AppState

从GUI到项目运行场景肯定需要一个场景切换,也是找了好久才找到Jmonkey里每一个场景是一个AppState,配套上nifty,就可以实现场景切换。GUI的AppState给它起名为StartScreenState。

  • 新建StartScreenState.java,继承自AbstractAppState,实现ScreenController接口(实现该接口是为了与xml建立联系,与上面的xml中<screen>标签里的controller对应)。上面例子中的image点击函数addDropClick(2)就写在该java文件中。
public class StartScreenState extends AbstractAppState implements ScreenController {
    private SimpleApplication app;
    private AssetManager assetManager;
    private InputManager inputManager;
    private ViewPort guiViewPort;
    private AudioRenderer audioRenderer;
    private NiftyJmeDisplay niftyDisplay;
    private Nifty nifty;
    private Screen screen;

    @Override
    public void initialize(AppStateManager stateManager,
                           Application app) {
        super.initialize(stateManager, app);
        this.app = (SimpleApplication) app;
        this.assetManager = this.app.getAssetManager();
        this.inputManager = this.app.getInputManager();
        this.guiViewPort = this.app.getGuiViewPort();
        this.audioRenderer = this.app.getAudioRenderer();

        this.niftyDisplay = new NiftyJmeDisplay(assetManager,
                inputManager, audioRenderer, guiViewPort);
        this.nifty = niftyDisplay.getNifty();
        this.nifty.fromXml("Scenes/start_screen.xml",
                "start", this);
        this.screen = nifty.getScreen("start");
        inputManager.setCursorVisible(true);
        guiViewPort.addProcessor(niftyDisplay);
    }

    // 在intellij中该方法会显示没有用过(灰色),没关系,正常
    public void addDropClick(String num) {
          // do something
    }

   @Override
    public void bind(@Nonnull Nifty nifty, @Nonnull Screen screen) {

    }

    @Override
    public void onStartScreen() {
        
    }

    @Override
    public void onEndScreen() {

    }

}

在该文件里实现对控件的动态控制,比如获得textfield中输入的文字:

TextField textField = screen.findElementById("ctr_id").getNiftyControl(TextField.class);
String str = textField .getDisplayedText();
if (str.isEmpty()){
    JOptionPane.showMessageDialog(
                            null,
                    "输入不能为空",
                            "错误!",
                    JOptionPane.ERROR_MESSAGE);
}

举一反三,其他诸如button、dropdown的control控制也是如此,先从screen上根据元素id找到元素,然后开始做自己需要的操作。

用java开发很好的一点就是可以用一切熟悉的java的东西,比如swing里的一些控件,像上例的输入错误框,如果用nifty弹出一个窗口,恐怕又好麻烦,但是用这个就相当简单了。当然jmonkey也可以用swing做图形界面,但是美观什么的就不一定了。

将AppState添加到Main中

创建完AppState后就把它加到SimpleApplication中,这是最后一步。

/**
 * Created by Roman on 2017/10/12.
 */
public class Main extends SimpleApplication {


    public static void main(String[] args) throws BackingStoreException {
//        PrintStream ps= null;
//        try {
//            ps = new PrintStream(new FileOutputStream("log.txt"));
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
//        System.setOut(ps);

        AppSettings settings = new AppSettings(true);

        settings.setTitle("你的名字");
        settings.setVSync(true);
        settings.setWidth(1280);
        settings.setHeight(800);
        Main app = new Main();
        app.setSettings(settings);
        app.setDisplayStatView(false); // 设置状态小窗是否可见
        app.setShowSettings(true); // 设置界面是否显示
        app.setPauseOnLostFocus(false); // 设置程序后台运行,默认为true,即焦点不在程序上则停止显示渲染
        app.start();
    }
    @Override
    public void simpleInitApp() {
        StartScreenState startScreenState = new StartScreenState();
        stateManager.attach(startScreenState);
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容