Докуметация Cтарт Статьи Форум Лента Вход
Не официальное русскоязычное сообщество
Главная
    Документация jMonkeyEngine
        jMonkeyEngine Уроки и Документация
            Документация для продвинутых пользователей
                Сохранение и загрузка игр(.j3o)

Сохранение и загрузка игр(.j3o)

Опубликованно: 08.05.2017, 18:11
Последняя редакция, Andry: 15.03.2018 16:59

Spatial (то есть узлы и геометрия) могут содержать аудио и световые узлы, эмиттеры частиц, элементы control и пользовательские данные (счет игрока, здоровье, инвентарь и.т.д.). Для вашего дистрибутива игры, вы должны преобразовать все оригинальные модели в более быстрый двоичным формат. Вы сохраняете отдельные Spatial объекты, а также сцены, используя com.jme3.export.binary.BinaryExporter.

Формат двоичного файла jMonkeyEngine называется .j3o. Вы можете конвертировать, просматривать и редактировать .j3o файлы и их материалы в jMonkeyEngine SDK и составлять сцены (сюда не входят монтажные сетки). Для преобразования вы можете использовать BinaryExporters или контекстное меню в SDK.

Система сериализации jMonkeyEngine — это интерфейс com.jme3.export.Savable. BinaryExporter JME3 может записывать стандартные Java-объекты, объекты JME3 и примитивные типы данных, которые входят в пользовательские данные Spatial объекта. Если вы используете классы пользовательских игровых данных, см. ниже, как сделать их «Savable.

Существует также com.jme3.export.xml.XMLExporter и com.jme3.export.xml.XMLImporter, который аналогичным образом преобразует jme3-пространства в формат XML. Но вы не использовали бы это для загрузки моделей во время выполнения (довольно медленно).

Образец кода

Сохранение Узла(Node)

Следующий пример переопределяет stop() в SimpleApplication, чтобы сохранить корневой узел в файл, когда пользователь закрывает приложение. Сохраненный корневой узел является обычным двоичным файлом .j3o, который вы можете открыть в SDK.

Обратите внимание, что при сохранении модели с текстурами, ссылки на эти текстуры сохраняются как абсолютные пути, и при загрузке файла j3o текстуры должны быть доступны по этому пути(по отношению к корню assetmanager, по умолчанию в папке assets), с которого они были загружены. Вот почему SDK управляет преобразованием на уровне проекта.
  /* Это вызывается, когда пользователь закрывает приложение. */
  @Override
  public void stop() {
    String userHome = System.getProperty("user.home");
    BinaryExporter exporter = BinaryExporter.getInstance();
    File file = new File(userHome+"/Models/"+"MyModel.j3o");
    try {
      exporter.save(rootNode, file);
    } catch (IOException ex) {
      Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error: Failed to save game!!", ex);
    }
    super.stop(); // продолжить выход из игры
  }

Загрузка Узла(Node)

Следующий пример переопределяет simpleInitApp() в SimpleApplication для загрузки Models/MyModel.j3o при инициализации игры.

  @Override
  public void simpleInitApp() {
     String userHome = System.getProperty("user.home");
     assetManager.registerLocator(userHome, FileLocator.class);
     Node loadedNode = (Node)assetManager.loadModel("Models/MyModel.j3o");
     loadedNode.setName("loaded node");
     rootNode.attachChild(loadedNode);
  }
Здесь вы видите, почему мы сохраняем пользовательские данные внутри Spatial объектов — поэтому что его можно сохранить и загрузить вместе с файлом .j3o. Если у вас есть игровые данные вне Spatials, вы должны запомнить save() и load(), а также get() и set() сами.

Пользовательский класс Savable

JME BinaryExporter может писать стандартные объекты Java (String, ArrayList, buffers и.т.д.), Объекты JME (Savables, такие как Material) и примитивные типы данных (int, float и.т.д.). Если вы используете какой-либо пользовательский класс вместе с Spatial, тогда пользовательский класс должен реализовать интерфейс com.jme3.export.Savable. Есть два распространенных случая, когда это уместно:

  • Spatial несет любые Пользовательские Control.
    Пример: Вы использовали что-то вроде mySpatial.addControl(myControl);
  • Пользовательские данные Spatial могут содержать пользовательский Java-объект.
    Пример: вы использовали что-то вроде mySpatial.setUserData(«inventory», myInventory);

Если ваши пользовательские классы (пользовательские данные или Control) не реализуют Savable, то BinaryImporter/BinaryExporter не могут сохранить Spatial!

Поэтому каждый раз, когда вы создаете пользовательский класс Control или пользовательские данные, не забудьте внедрить Savable:

import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.material.Material;
import java.io.IOException;

public class MyCustomClass implements Savable {
    private int      someIntValue;   // некоторые пользовательские данные
    private float    someFloatValue; // некоторые пользовательские данные
    private Material someJmeObject;  // некоторые пользовательские данные

    ...
    // ваш другой код ...
    ...

    public void write(JmeExporter ex) throws IOException {
        OutputCapsule capsule = ex.getCapsule(this);
        capsule.write(someIntValue,   "someIntValue",   1);
        capsule.write(someFloatValue, "someFloatValue", 0f);
        capsule.write(someJmeObject,  "someJmeObject",  new Material());
    }

    public void read(JmeImporter im) throws IOException {
        InputCapsule capsule = im.getCapsule(this);
        someIntValue   = capsule.readInt(    "someIntValue",   1);
        someFloatValue = capsule.readFloat(  "someFloatValue", 0f);
        someJmeObject  = capsule.readSavable("someJmeObject",  new Material());
    }
}

Чтобы сделать пользовательский класс более безопасным:

  1. Внедрите Savable и добавьте методы write() и read(), как показано в примере выше.
  2. Выполните следующие действия для каждого поля без временного класса:
    • Добавьте одну строку, которая write()(запишет) данные в капсулу вывода JmeExport.
      • Укажите переменную, чтобы сохранить, дайте ей имя типа String (может быть таким же, как имя переменной), и укажите значение по умолчанию.
    • Добавьте одну строку, которая read…()(считает) s данные в капсулу ввода JmeImport.
      • В левой части задания укажите поле класса, которое вы восстанавливаете.
      • С правой стороны используйте соответствующий метод capsule.read…() для типа данных. Укажите String имя переменной (должно быть таким же, как вы использовали в методе write()), и снова укажите значение по умолчанию.
Как и при любой сериализации, помните, что если вы когда-либо меняете типы данных в пользовательских классах, обновленные методы read() больше не смогут читать ваши старые файлы. Также должен быть конструктор, который не принимает Параметров.

Значение по умолчанию(Default Value)

Значение по умолчанию играет важную роль в том, какие данные сохраняются в файле.

write() (запись)

public void write(int value, String name, int defVal) throws IOException {
    if (value == defVal)
        return;
    writeAlias(name, BinaryClassField.INT);
    write(value);
}

Методы записи класса BinaryOutputCapsule.java не записывают defVal в файл. Вместо этого они проверяют, value равно ли значение defVal, и если да, то вообще ничего не запишут.

Для этого есть очень веские причины.

  1. Так занимается меньше места, если все значение по умолчанию.
  2. Вы можете принять решение о новых значениях по умолчанию позже, и ваши объекты будут автоматически обновляться, если у них не было явно переопределенных значений.

read() (чтение)

public int readInt(String name, int defVal) throws IOException {
    BinaryClassField field = cObj.nameFields.get(name);
    if (field == null || !fieldData.containsKey(field.alias))
        return defVal;
    return ((Integer) fieldData.get(field.alias)).intValue();
}

При чтении сохраненного файла BinaryInputCapsule.java классом вы будете видеть, что поле name равно null, и это когда задано defVal.

Если вы полагаетесь на компилятор для инициализации переменных класса или экземпляра, это может привести к непредвиденным последствиям.

Например:

capsule.write(someIntValue,   "someIntValue",   1);

Если вы разрешите компилятору инициализировать someIntValue, он будет инициализирован как ноль и, если он не будет изменен после инициализации, ноль будет записан в файл.

someIntValue   = capsule.readInt(    "someIntValue",   1);

Теперь, когда вызывается read, он будет виден с названием «someIntValue» и задаст значение переменной someIntValue равным нулю. Не один, как вы ожидали. Помните об этом при использовании Savable.


Переведено для jmonkeyengine.ru, оригинал
Автор перевода: Andry

Добавить комментарий

jMonkeyEngine.ru © 2017. Все права сохранены.