1. 简介

jMonkeyEngine 是一个现代化的、对开发者友好的游戏引擎,我们可以用它来构建游戏或其他 Java 3D 应用程序。

本教程将深入探讨 jMonkeyEngine,学习它的核心功能、应用场景以及使用方法。

2. 依赖管理

使用 jMonkeyEngine 前,需要将 最新版本 (目前是 3.7.0-stable)添加到构建文件中。

有两种主流管理方式:手动配置依赖,或使用 jMonkeyEngine 提供的初始化器(类似 Spring Initializr)生成现成项目。此外,jMonkeyEngine 还提供基于 NetBeans 的 SDK,集成了额外工具简化开发,但你可以自由选择其他 IDE。

2.1. 初始化器

最简单的方式是访问官方初始化页面 https://jmonkeyengine.org/start/

jMonkeyEngine 初始化器界面

只需填写几个关键选项:

  • 应用名称
  • 目标平台(桌面/Android/VR)
  • 部署系统(桌面端可选择 Windows/macOS/Linux)

下载后即可获得开箱即用的项目结构:

├── README.txt
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── scripts
│   └── desktopDeployment
│       ├── BaeldungTesting.bat
│       ├── BaeldungTesting.command
│       └── BaeldungTesting.sh
├── settings.gradle
└── src
    └── main
        └── java
            └── com
                └── baeldung
                    └── jmonkeyengine
                        └── BaeldungTesting.java

通过 ./gradlew build 构建项目,使用 ./gradlew run 运行: 初始化器生成的应用窗口

2.2. 手动管理项目

也可自行创建项目结构并添加依赖,获得更高的灵活性。

桌面端至少需要这些依赖:

<dependency>
    <groupId>org.jmonkeyengine</groupId>
    <artifactId>jme3-core</artifactId>
    <version>3.7.0-stable</version>
</dependency>
<dependency>
    <groupId>org.jmonkeyengine</groupId>
    <artifactId>jme3-desktop</artifactId>
    <version>3.7.0-stable</version>
</dependency>
<dependency>
    <groupId>org.jmonkeyengine</groupId>
    <artifactId>jme3-lwjgl3</artifactId>
    <version>3.7.0-stable</version>
</dependency>

⚠️ **启动时必须添加 JVM 参数 -XstartOnFirstThread**,否则会收到错误提示。手动启动方式为执行主类。

3. 创建第一个应用

现在我们有了 jMonkeyEngine 基础框架,开始构建第一个应用。

应用核心是继承 SimpleApplication 的类:

public class FirstApplication extends SimpleApplication {
    @Override
    public void simpleInitApp() {
    }
}

必须重写 simpleInitApp() 方法(稍后说明其用途)。启动应用:

public static void main(String[] args) {
    FirstApplication app = new FirstApplication();
    app.start();
}

虽然预期看到空白窗口,但实际获得更多功能: 包含调试信息的空白窗口

SimpleApplication 自动提供:

  • 左下角的调试信息
  • 场景漫游的飞行相机
  • 默认窗口标题/尺寸/位置
  • Esc 退出快捷键

3.1. 默认行为

查看 SimpleApplication 默认构造函数,发现它依赖多个 AppState 实例

public SimpleApplication() {
    this(new StatsAppState(), new FlyCamAppState(), new AudioListenerState(), new DebugKeysAppState(),
            new ConstantVerifierState());
}

这些类在应用主循环中执行,包括:

  • StatsAppState:渲染左下角调试统计
  • FlyCamAppState:实现场景飞行控制
  • AudioListenerState:更新音频监听器位置
  • DebugKeysAppState:设置按键映射(如打印相机位置)
  • ConstantVerifierState:检查引擎常量是否被违反

Esc 键处理是单独实现的。可传入自定义 AppState 替代默认配置

public FirstApplication() {
    super(new StatsAppState());
}

3.2. 应用设置

创建应用时可配置窗口参数,通过 setSettings() 方法覆盖默认值:

AppSettings settings = new AppSettings(true);
setSettings(settings);

AppSettings 构造参数 true 表示先加载默认设置,后续可覆盖:

settings.setWidth(1024);
settings.setHeight(768);
settings.setCenterWindow(false);
settings.setWindowXPosition(0);
settings.setWindowYPosition(0);
settings.setTitle("Our First Application");

这会将窗口定位到屏幕左上角,尺寸设为 1024×768,并更新标题。

4. 渲染几何体

有了应用窗口,现在渲染内容。

simpleInitApp() 方法中创建几何体。该方法在引擎完全初始化后执行,是初始化场景的理想位置。

渲染步骤:

  1. 创建网格(jMonkeyEngine 提供 Box/Sphere/Torus 等内置形状):

    Box mesh = new Box(1, 2, 3);
    

    创建一个 x 轴长度 1、y 轴 2、z 轴 3 的长方体。网格也可以是从文件加载的复杂模型。

  2. 将网格包装为 Geometry

    Geometry geometry = new Geometry("Box", mesh);
    
  3. 必须为几何体指定材质,否则无法正确渲染:

    Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    material.setColor("Color", ColorRGBA.Red);
    geometry.setMaterial(material);
    

    创建无光照材质,设置颜色为红色。材质可按需调整复杂度。

  4. 将几何体添加到场景根节点:

    rootNode.attachChild(geometry);
    

此时运行应用,屏幕中央显示红色矩形: 屏幕中央的红色矩形

实际是长方体,但因正对视角所以显示为矩形。

4.1. 几何体变换

目前几何体直接添加到根节点,未应用任何变换。通常需要平移/旋转/缩放几何体

通过 Node 应用变换:

Node rotation = new Node("rotation");
rotation.rotate(0.2f, 0.4f, 0.6f);

rotation.attachChild(geometry);
rootNode.attachChild(rotation);

将几何体绕 x 轴旋转 0.2 弧度、y 轴 0.4 弧度、z 轴 0.6 弧度。渲染后可见长方体结构: 旋转后的红色长方体

构建场景时,可创建 Node 树结构,每个节点应用独立变换,变换会作用于整个子树。

5. 更新循环

simpleInitApp() 初始化外,可通过 simpleUpdate() 执行每帧更新:

@Override
public void simpleUpdate(float timePerFrame) {
}

每帧渲染时调用一次,timePerFrame 参数表示距上一帧的时间(秒)。在方法内访问并更新场景:

@Override
public void simpleUpdate(float timePerFrame) {
    Spatial rotation = rootNode.getChild("rotation");
    rotation.rotate(0, timePerFrame, 0);
}

通过名称获取 "rotation" 节点,根据帧时间更新旋转,使长方体持续自转: 持续旋转的红色长方体

可在此循环中处理几何体更新或其他每帧逻辑。

6. 处理用户输入

除固定运行的场景外,还能响应用户输入(键盘/鼠标/手柄等)。

jMonkeyEngine 提供 InputManager 管理输入。首先为输入操作注册名称:

inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_J));
inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_K));

允许修改输入绑定或使用多个按键触发同一操作。然后绑定监听器:

inputManager.addListener(actionListener, "Rotate");
inputManager.addListener(analogListener, "Left", "Right");

注册了两种监听器类型:

ActionListener 响应离散动作(如单次按键):

ActionListener actionListener = new ActionListener() {
    @Override
    public void onAction(String name, boolean isPressed, float tpf) {
        if (name.equals("Rotate") && !isPressed) {
            rotationEnabled = !rotationEnabled;
        }
    }
};

空格键("Rotate")在释放时切换旋转状态。

AnalogListener 响应持续动作(如长按按键):

AnalogListener analogListener = new AnalogListener() {
    @Override
    public void onAnalog(String name, float value, float tpf) {
        if (name.equals("Left")) {
            rotation.rotate(0, -tpf, 0);
        } else if (name.equals("Right")) {
            rotation.rotate(0, tpf, 0);
        }
    }
};

J 键("Left")和 K 键("Right")长按时控制场景旋转方向。

7. 总结

本文快速介绍了 jMonkeyEngine 的核心功能。这个库能做的事情远不止于此,下次想用 Java 开发游戏时,不妨试试它!


原始标题:Getting Started With jMonkeyEngine | Baeldung