文档章节

JMonkeyEngine中是如何使用AppState来管理和实现游戏场景切换的

h
 huliqing
发布于 2017/04/20 18:04
字数 1870
阅读 402
收藏 1

本文要讲解的是关于JMonkeyEngine游戏引擎中如何使用AppState来实现游戏场景的管理和切换。    

什么是AppState

一般来说JME中的AppState是为了解决复杂游戏场景中各种游戏元素的管理问题,当游戏越来越大,越来越复杂的时候,游戏中的各种元素(例如:模型、环境、事件、特效等), 将会变得越来越难以管理和控制。

AppState的存在就可以用来将游戏场景中的各种元素分离、解耦到不同的AppState下进行运行, 再通过将不同的AppState组合到一起放到游戏中,就可以组成不同状态的游戏场景。

先看一下AppState在Application中的存在及生命周期是怎样的,如图:

 

一个JME游戏应用中可以存在多个AppState, 通过使用以下两个方法来将AppState添加到游戏或从游戏中移除。

stateManager.attach(AppState appState);
stateManager.detach(AppState appState);

每个AppState的一个完整生命周期会经历:initialize -> update -> cleanup这几个方法。

  1. 当AppState被添加(attach)到游戏中时, initialize方法会被调用一次,以初始化AppState
  2. 然后AppState会在游戏中不停的循环执行update方法。
  3. 直到有人调用了stateManager的detach方法将AppState从游戏中移除,此时AppState的cleanup方法会被调用一次,以清理及释放该AppState所产生的资源,然后该AppState就会被彻底移除

AppState的实现原则

AppState的实现原则一般应该是这样的:

  • 当你在AppState的initialize方法或update方法中向游戏中添加了任何资源后,在cleanup方法就要确保将这些资源从游戏中移除或清理,否则AppState在移除后可能会残留一些游戏元素在游戏中占用资源,这可能会导致内存问题及性能影响。
  • 每个AppState应该实现各自不同的功能,各个AppState之间应该尽量独立互不影响,从而将游戏中的复杂度拆分到不同的AppState中。比如当添加或从游戏中移除一个AppState时,不应该导致其它AppState出错或崩溃。

AppState的使用示例

下面拿一个示例来说明,例如,我们可以实现这样一个游戏,这个游戏中包含以下AppState:

  • SceneAppStateA 游戏场景A, 存放游戏元素,如人物、房子、地形、植物、动物等。
  • SceneAppStateB 游戏场景B, 存放游戏元素,如人物、房子、地形、植物、动物等。
  • RainEnvAppState 环境, 下雨环境,可以在AppState中添加雨水声效、用于渲染雨水的特效过滤器等等。
  • SnowEnvAppState 环境, 下雪环境,可以在AppState中添加雪花飘飘的特效、声效等。
  • UIAppState 游戏UI界面

那么现在如果你想要初始化一个游戏场景,要实现一个让场景A下雨的游戏的话,就通过这样来操作:

    @Override
    public void simpleInitApp() {
        stateManager.attach(sceneAppStateA);
        stateManager.attach(rainEnvAppState);
        stateManager.attach(uiAppState);
    }

那么得到的Application中AppState的状态就像是上面这样的: A场景正在下雨中。

如果要让场景A下雪,则可以调用像类似下面这样的方法来从落雨场景切换到下雪的场景。

    public void changeToSnowEnv() {
        stateManager.detach(rainEnvAppState)
        stateManager.attach(snowEnvAppState);
    }

那么得到的Application中AppState的状态就像是上面这样的: A场景现在正在下雪。

同样的,如果你要从场景A转换场景B,例如:你在游戏中有一个传送门,可以让你从A地图传送到B地图,那么你就可以在传送的时候调用像下面这样的方法来切换到场景B:

    public void changeToSceneB() {
        stateManager.detach(sceneAppStateA)
        stateManager.attach(sceneAppStateB);
    }

 那么得到的Application中AppState的状态就像是上面这样的:一个正在下雪的场景B。

代码示例

那么下面使用完整的代码来示例如何在游戏中进行场景切换,主要的重点是放在AppState的切换以及实现AppState时的原则上,所以代码写得很简单,在明白了这些简单原理之后,我想,谁都可以把它变得更加复杂。

这个示例中演示了如何在游戏中切换场景A和场景B(通过按键A\B切换)

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;

/**
 * SimpleGame用于演示如何使用JME的AppState来实现切换场景A和场景B的功能。
 * @author huliqing
 */
public class SimpleGame extends SimpleApplication {
    
    private final AppStateA sceneA = new AppStateA();
    private final AppStateB sceneB = new AppStateB();
    
    @Override
    public void simpleInitApp() {
        
        // 注册按键事件:A、B
        // 用这两个按键来实现切换到A和B场景。
        getInputManager().addMapping("typeA", new KeyTrigger(KeyInput.KEY_A));
        getInputManager().addMapping("typeB", new KeyTrigger(KeyInput.KEY_B));
        getInputManager().addListener(new ActionListener() {
            @Override
            public void onAction(String name, boolean isPressed, float tpf) {
                if (name.equals("typeA")) {
                    // 当按了键盘A键的时候把场景B移除,然后把场景A添加上去。
                    getStateManager().detach(sceneB);
                    getStateManager().attach(sceneA);
                } else if (name.equals("typeB")) {
                    // 当按了键盘B键的时候把场景A移除,然后把场景B添加上去。
                    getStateManager().detach(sceneA);
                    getStateManager().attach(sceneB);
                }
            }
        }, "typeA", "typeB");
        
    }
    
    public static void main(String[] args) {
        SimpleGame app = new SimpleGame();
        app.start();
    }
}
package mygame;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;

/**
 * AppStateA用来模拟场景A, 显示一个立方体.
 * @author huliqing
 */
public class AppStateA extends AbstractAppState{
    
    // 这个节点可以作为当前AppState的本地根节点。
    // 在当前AppState被添加到游戏的时候把这个节点添加到场景中。
    // 在当前AppState被从游戏中移除时要把这个根节点从游戏中移除。
    private Node localRoot;

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        //  初始化当前AppState的本地根节点,并把这个根节点添加到游戏主场景中。
        // 后面所有在当前AppState中创建的物体都要添加到这个本地根节点中。
        localRoot = new Node();
        ((SimpleApplication)app).getRootNode().attachChild(localRoot);
        
        // 创建当前AppState场景的物体,并把物体添加到当前的AppState场景本地根节点(localRoot)中.
        Box box = new Box(0.5f, 0.5f, 0.5f);
        Geometry geo = new Geometry("testBox", box);
        geo.setMaterial(new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"));
        localRoot.attachChild(geo);
        
        // ... 添加其它物体(略)
    }

    // 当AppState被从游戏中移除时,cleanup方法会被调用,这里要把AppState的根节点从游戏中移除.
    // 因为当前AppState场景内中创建的所有物体都是放在localRoot下的,所以直接把localRoot移除就可以了。
    @Override
    public void cleanup() {
        localRoot.removeFromParent();
        super.cleanup();
    }
    
}
package mygame;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Sphere;

/**
 * AppStateB用来模拟一个场景B, 显示一个球体(原理和AppStateA是完全一样的). 
 * @author huliqing
 */
public class AppStateB extends AbstractAppState{
    
    private Node localRoot;

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        localRoot = new Node();
        ((SimpleApplication)app).getRootNode().attachChild(localRoot);
        
        Sphere sphere = new Sphere(20, 20, 1);
        Geometry geo = new Geometry("testShpere", sphere);
        geo.setMaterial(new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"));
        localRoot.attachChild(geo);
        
        // ... 添加其它物体(略)
    }

    @Override
    public void cleanup() {
        localRoot.removeFromParent();
        super.cleanup();
    }
}

    

初始化起动后,场景是黑的,什么也没有,因为没有添加任何AppState, 可以通过按键A和B来切换场景,如上图,特别注意AppState在被移除时要清理干净该AppState向游戏中添加的所有游戏元素。

-作者: 31703299@qq.com

-JMonkeyEngine游戏开发群:423979787

-JMonkeyEngine中文社区: http://www.jmecn.net/

© 著作权归作者所有

共有 人打赏支持
h

huliqing

粉丝 12
博文 5
码字总数 13706
作品 1
广州
程序员
私信 提问
加载中

评论(3)

是我啊耳朵s2

引用来自“是我啊耳朵s2”的评论

😎猜猜我是谁

引用来自“huliqing”的评论

没提示的话,猜不到😄
群里的小伙伴,代号'程序-Mkey'😄
h
huliqing

引用来自“是我啊耳朵s2”的评论

😎猜猜我是谁
没提示的话,猜不到😄
是我啊耳朵s2
😎猜猜我是谁
Java 3D游戏引擎

jMonkeyEngine 是生成快速现代3D游戏的引擎, jMonkeyEngine (jME)封装了OpenGL,提供一个完整强大的高性能的工具包,使3D游戏的开发变得容易。去年10月份发布了1.0版,今年6月发布了2.0版,...

夏天的沙漠
2008/09/24
3.8K
1
android如何使用jMonkeyEngine3游戏引擎

jMonkeyEngine怎么部署到Android上

甄齐北上
2014/08/24
716
0
MonkeyWorld3D的网友评论

MonkeyWorld3D是一个用于Java 3D游戏引擎jMonkeyEngine(jME)的情景图编辑器。可以利用MonkeyWorld3D编辑情景元素,地形,动画等。它的目标是成为一个 WYSIWYP(What you see is what you ...

红薯
2009/12/11
681
1
JMonkey Engine项目组写给中国3D程序员的一封信

May 18 Erlend and me: Announcement - JMonkey Engine in China Hello Chinese game developers! After being contacted by pengkai from china, the JME team decided this is a good time......

彭凯
2010/06/08
3.4K
0
用java编好游戏转成3D,用什么软件好?

小弟是一名学生,需要将一个跳棋转成3D的,这个跳棋程序是已经用Java编写好的,希望大家能给推荐一下图像软件。因为是初学者,游戏对画面要求也不 高,要求就是能好上手一些。可以是单纯的建...

Andreas2015
2015/06/30
364
2

没有更多内容

加载失败,请刷新页面

加载更多

mysql grant 用户权限总结

用户权限管理主要有以下作用: 1. 可以限制用户访问哪些库、哪些表 2. 可以限制用户对哪些表执行SELECT、CREATE、DELETE、DELETE、ALTER等操作 3. 可以限制用户登录的IP或域名 4. 可以限制用...

Airship
13分钟前
1
0
RabbitMQ学习(3)

1. 消费端 1. 消费端通过推模式或者拉模式从RabbitMQ中获取并消费消息,当消费者确认处理消息后,可以手动确认消息已被接收,然后就会将该消息从RabbitMQ的队列中标记再清除,消费者端还可以...

江左煤郎
24分钟前
1
0
linux mysql(5.7)开启慢查询

一、有3个配置需要设置, 1:相关开关 2:日志目录文件 3:慢查询的时间限制 二、设置完之后重启mysql service mariadb restart 三、重启后做个测试 连接mysql 并查询: select sleep(6); 四...

chro008
32分钟前
1
0
选择IDC机房、选择硬件、上架服务器、装系统

选择IDC机房 当业务量比较大的时候,往往选用IDC而不是公有云来跑业务。 IDC机房的服务一般分为两种 需要我们自己购买服务器和网络设备,托管到IDC机房。IDC机房会提供布线、巡检、硬件操作等...

李超小牛子
33分钟前
2
0
《傲慢与偏见》的读书笔记与读后感作文2400字

《傲慢与偏见》的读书笔记与读后感作文2400字: 作者:孙苑馨;笔者按:读书这个习惯是我爸妈养出来的。小时候父母赚的钱除了吃饭穿衣剩下的就是买书了,他们除了买各种新鲜出版的文艺派图书...

原创小博客
42分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部