第六章:AssetManager概述

资产(assets)指的是各种多媒体文件,诸如3D模型、材质、纹理、场景、着色器、音乐、字体等。JME3集成了一个资产管理器(AssetManager),用来帮你组织、管理游戏资产。你可以把它理解为一个独立于实际发布平台的文件系统。

AssetManager 有如下优点:

  • 无论游戏运行于何种操作系统(Windows、Mac、Linux等),资产路径都保持不变。
  • AssetManager 会自动缓存和优化处理 OpenGL 对象。例如,当多个模型使用了同一个纹理时,该纹理并不会被多次提交给显卡。
  • SDK的默认编译脚本会自动把资产目录下的文件打包到可执行jar文件中。

AssetManager 可以从这些地方加载资产:

  • 当前程序的 classpath
  • 项目的 asset 目录
  • 打包成 zip 压缩文件的资源
  • 通过URL访问HTTP资源

AssetManager 提供了多种方法,用于加载不同类型的资产。

  • 3D模型,加载为 com.jme3.scene.Spatial 对象。
  • 材质,加载为 com.jme3.material.Material 对象。
  • 纹理,加载为 com.jme3.texture.Texture 对象。
  • BMFont字体,加载为 com.jme3.font.BitmapFont 对象。
  • 音频文件,加载为 com.jme3.audio.AudioNode 对象。

高级用户可以自己改写编译和打包脚本,可以在AssetManager中注册自定义资产路径,还可以自定义资产加载格式,这都取决于你。

资产路径

默认情况下,应该把游戏资产文件保存于项目的 MyGame/assets 目录中。AssetManager 加载资产时,将以 MyGame/assets当前工作目录, 取资产的相对路径,并且 大小写敏感

使用 SDK 创建的 JME3 工程结构如下:

MyGame/assets/    # 游戏资产保存在此目录中! <------
MyGame/build/     # SDK 自动生成的编译结果 (*)
MyGame/build.xml  # 自定义的Ant编译脚本
MyGame/nbproject/ # SDK 默认的build.xml脚本和元数据 (*)
MyGame/dist/      # SDK 自动生成的可执行文件 (*)
MyGame/src/       # 保存Java代码源文件
MyGame/test/      # 保存测试代码源文件 (可选)
(*) 由 jMonkeyEngine SDK 自动管理 -- 不要修改!

MyGame/assets的子文件夹,分别存放不同类型的游戏资产。

MyGame/assets/Interface  # .font, .jpg, .png, .xml
MyGame/assets/MatDefs    # .j3md
MyGame/assets/Materials  # .j3m
MyGame/assets/Models     # .blend, .j3o
MyGame/assets/Scenes     # .j3o
MyGame/assets/Shaders    # .j3f, .vert, .frag
MyGame/assets/Sounds     # .ogg, .wav
MyGame/assets/Textures   # .jpg, .png; 包括 .mesh.xml+.material, .mtl+.obj, .blend,

根据这个标准结构,把模型储存在 MyGame/assets/Models 目录中, 纹理储存于 MyGame/assets/Textures 中。

例如:

MyGame/assets/Models/Monkey/monkey.j3o
MyGame/assets/Textures/Monkey/DiffuseMap.png

在代码中加载模型时,使用的路径是 Models/Monkey/monkey.j3o,模型内部所引用的纹理路径为 Textures/Monkey/DiffuseMap.png

获得AssetManager

在 JME3 程序中可以使用 assetManager 对象来加载资产,它是 com.jme3.asset.AssetManager 接口的一个实例。 assetManager 内部维护了资产根目录,并且默认包含了工程的 classpath ,因此可以加载classpath的任意资产。

任意类继承 com.jme3.app.SimpleApplication 后,都可以直接继承 assetManager 对象,也可以通过 app.getAssetManager() 来访问它。

下面的代码演示了如何使用 AssetManager 来加载资产。这行语句从 JME3 内置资产目录 Common/ 中加载了一个材质对象。

Material mat = (Material) assetManager.loadAsset(
    new AssetKey("Common/Materials/RedColor.j3m"));

这个材质文件被存储于 jME3-core.jar 文件中的某个地方。AssetManager 通过默认配置正确找到了 Common/.. 路径下的资产,因此你不需要自己去制定该资产实际储存的完整文件路径。

另外,可以在 AssetManager 中配置其他的资产根目录,例如: F:\Assets/usr/yan/home/resources。这意味着你可以加载任意目录下的资产。

加载资产的方法

资产类型方法返回类型
3D模型 assetManager.loadModel("Models/Monkey/monkey.j3o") Spatial
材质 assetManager.loadMaterial("Common/Materials/RedColor.j3m")

new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
Material
纹理 assetManager.loadTexture("Textures/Monkey/DiffuseMap.png") Texture
BMFont assetManager.loadFont("Interface/Fonts/Default.fnt"); BitmapFont
音频文件 new AudioNode(assetManager, "Sounds/Ambient/Nature.wav", DataType.Buffer); AudioNode
其他 assetManager.loadAsset("Models/Monkey/monkey.j3o") Object

演示代码

下面的代码将演示如何加载项目 assets 目录中的资产。

这是前几章出现过的代码,通过 assetManager.loadModel("Models/Monkey/monkey.j3o"); 语句加载了 MyGame/assets 目录下的猴头模型。

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;

/**
 * 测试加载j3o模型
 * @author yanmaoyuan
 */
public class Main extends SimpleApplication {

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

    @Override
    public void simpleInitApp() {
        // 加载j3o模型
        Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
        rootNode.attachChild(model);

        // 添加灯光
        DirectionalLight sun = new DirectionalLight();
        sun.setColor(new ColorRGBA(0.7f, 0.7f, 0.7f, 1f));
        sun.setDirection(new Vector3f(-3, -4, -5).normalizeLocal());
        rootNode.addLight(sun);

        AmbientLight ambient = new AmbientLight();
        ambient.setColor(new ColorRGBA(0.2f, 0.2f, 0.2f, 1f));
        rootNode.addLight(ambient);
    }

}

若使用 AppState,可以通过 app.getAssetManager() 来获得 assetManager 对象,再用它来加载资产。

package mygame;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.asset.AssetManager;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

/**
 * 测试加载j3o模型
 * @author yanmaoyuan
 *
 */
public class MyAppState extends BaseAppState {

    private Node rootNode = new Node("scene");
    private SimpleApplication simpleApp;
    private AssetManager assetManager;
    
    @Override
    protected void initialize(Application app) {
        this.simpleApp = (SimpleApplication) app;
        this.assetManager = app.getAssetManager();// 获得 assetManager
        
        // 加载j3o模型
        Spatial model = assetManager.loadModel("Models/Monkey/monkey.j3o");
        rootNode.attachChild(model);

        // 添加灯光
        DirectionalLight sun = new DirectionalLight();
        sun.setColor(new ColorRGBA(0.7f, 0.7f, 0.7f, 1f));
        sun.setDirection(new Vector3f(-3, -4, -5).normalizeLocal());
        rootNode.addLight(sun);

        AmbientLight ambient = new AmbientLight();
        ambient.setColor(new ColorRGBA(0.2f, 0.2f, 0.2f, 1f));
        rootNode.addLight(ambient);
    }

    @Override
    protected void cleanup(Application app) {
        // 清空场景
        rootNode.detachAllChildren();
    }

    @Override
    protected void onEnable() {
        // 开启场景
        simpleApp.getRootNode().attachChild(rootNode);
    }

    @Override
    protected void onDisable() {
        // 移除场景
        rootNode.removeFromParent();
    }

}