初始化设置

首先,创建Main类,作为JME3程序的启动入口。

package mygame;

import com.jme3.app.SimpleApplication;

public class Main extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        // TODO Auto-generated method stub
    }

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

}

运行程序,选择分辨率后就会看到一个黑漆漆的窗口。没什么好说的。

重点

本文的重点是实体系统(Entity System),所以在编写具体的游戏内容前,让我们先把Zay-ES框架初始化。

package mygame;

import com.jme3.app.state.AbstractAppState;
import com.simsilica.es.EntityData;
import com.simsilica.es.base.DefaultEntityData;

public class EntityDataState extends AbstractAppState {
    private EntityData entityData;

    public EntityDataState() {
        this(new DefaultEntityData());
    }

    public EntityDataState(EntityData ed) {
        this.entityData = ed;
    }

    public EntityData getEntityData() {
        return entityData;
    }

    @Override
    public void cleanup() {
        entityData.close();
        entityData = null; // 不允许复用
    }
}

关于AppState的用法也没什么需要特别解释的,这段代码相当简单。

还是黑屏?

我的意思是...好吧,一个没有视觉表现的游戏确实称不上多有趣。能在屏幕上看到一些东西,并根据玩家的操作给予反馈,总能帮助我们开心起来。

一艘太空船

我们需要一些能在Blender中处理的模型,然后导入到JME3中。

首先我们需要一个太空船,所以请启动Blender自己建模吧。好吧,我并不打算让你浪费时间,所以我已经在google drive上分享了一个模型。这并不是说你就不能用自己的模型了,随你喜欢。

如果你已经有模型了,就把这个 blend 文件放在"Models"资源目录下,然后在JME3中右键单击这个blender模型,选择"Convert to j3o Binary"。

译注:原作者使用Blender建模了一个太空船模型,并使用JME3 SDK的模型转换功能生成了j3o文件。我把源文件和转换好的模型都上传到了百度网盘,分享给大家。

译注2:如果你不使用JME3 SDK开发游戏,可以根据下图的结构来存放SpaceShip.j3o文件。

模型工厂

在你解决上面的问题后,就要准备加载模型了。在JME3中使用专门的模型工厂(ModelFactory)类来获取模型,是个非常不错的想法,这会让很多事情变得更简单。

package mygame;

import com.jme3.asset.AssetManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

public class ModelFactory {
    private final AssetManager assetManager;

    public ModelFactory(AssetManager assetManager) {
        this.assetManager = assetManager;
    }

    public Spatial create(String name) {
        Node visual = new Node("Visual");
        Node model = (Node) assetManager.loadModel("Models/" + name + ".j3o");
        visual.attachChild(model);
        return visual;
    }
}

这个实现的思路是把j3o文件都放在Models目录下,然后根据模型的名称去加载模型文件。我觉得没有必要解释太多,看代码就清楚了。当然你可以把这个工厂类设计得更完善,更加面向对象或者别的什么东西,我不在乎。

快速原型

现在我们想在屏幕上看到这艘太空船。为此,我们开始编写游戏的显示系统(表示层),我将其称为 VisualAppState,代码如下。

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.light.DirectionalLight;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;

public class VisualAppState extends AbstractAppState {
    private SimpleApplication app;
    private ModelFactory modelFactory;

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        this.app = (SimpleApplication) app;
        
        // 初始化摄像机,从Z轴正上方往下看。
        app.getCamera().lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y);
        app.getCamera().setLocation(new Vector3f(0, 0, 60));
        
        // 添加定向光源
        DirectionalLight light = new DirectionalLight();
        light.setDirection(new Vector3f(1, 1, -1));
        this.app.getRootNode().addLight(light);
        
        // 加载太空船模型
        modelFactory = new ModelFactory(this.app.getAssetManager());
        Spatial myVisual = modelFactory.create("SpaceShip");
        this.app.getRootNode().attachChild(myVisual);
    }

    @Override
    public void cleanup() {
    }

    @Override
    public void update(float tpf) {
    }
}

我假设你的太空船模型保存路径为 Models/SpaceShip.j3o。

如果你的模型使用的是感光材质(Lighting.j3md),那么就会需要在场景中增加光源,我添加了一个定向光源。然后,在Main类中注册我们写好的两个AppState类,代码如下:

public Main() {
    super(new VisualAppState(),
            new EntityDataState());
}

好了,运行程序然后看看结果。

VisualSystem

到目前为止,程序中还没有太多与ES有关的东西,不过我们已经为开发第一个真正的ES游戏做好了准备。实际上,我们还需要让VisualAppState变得更丰满一些,再增加一些各种功能的GameAppState。