什么是 AppState
一般来说 JME 中的 AppState 是为了解决简单游戏场景中各种游戏元素的治理问题,当游戏越来越大,越来越简单的时候,游戏中的各种元素(例如:模型、环境、事件、特效等), 将会变得越来越难以治理和管制。
AppState 的存在就能够用来将游戏场景中的各种元素拆散、解耦到不同的 AppState 下进行运行, 再通过将不同的 AppState 组合到一起放到游戏中,就能够组成不同状态的游戏场景。
先看一下 AppState 在 Application 中的存在及生命周期是怎么的?
一个 JME 游戏利用中能够存在多个 AppState, 通过应用以下两个办法来将 AppState 增加到游戏或从游戏中移除。
stateManager.attach(AppState appState);
stateManager.detach(AppState appState);
每个 AppState 的一个残缺生命周期会经验:initialize -> update -> cleanup 这几个办法。
当 AppState 被增加 (attach) 到游戏中时, initialize 办法会被调用一次, 以初始化 AppState
而后 AppState 会在游戏中不停的循环执行 update 办法。
直到有人调用了 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() {
stateManawww.cungun.comger.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();
}
}