Внешний вид сайта:

Как выполнять сохранение и загрузку игры в Unity

Описанный ниже подход работает на любой платформе, поддерживаемой Unity, кроме Web Player. Для получения информации о сохранении игровых данных в веб-плеере, взгляните на официальную документацию.

Подготовка к сериализации

Первое, что нам нужно сделать, это сериализовать наши данные, которые мы сохраним, а затем в нужное время восстановим. Для этого нам потребуется создать скрипт (в качестве языка программирования будем использовать C#). Давайте назовем его SaveLoad. Этот сценарий будет обрабатывать все, что связано с сохранением и восстановлением данных.

Мы будем ссылаться на этот сценарий из других скриптов, а потому сделаем класс статичным, добавив ключевое слово static между public и class. Также не забудем удалить два автоматически созданных метода, поскольку нам не потребуется крепить скрипт ни к какому игровому объекту.

Полученный сценарий должен выглядеть вот так:

using UnityEngine;
using System.Collections;
public static class SaveLoad {
}

Мы хотим добавить немного функциональных возможностей нашему скрипту, а потому давайте пропишем несколько директив:

using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

Первая строка позволит нам использовать динамические списки. Вторая — даст нужный функционал операционной системы по сериализации данных. А последняя директива позволяет нам работать с потоками ввода и вывода. Иными словами, она используется для создания и чтения файлов.

Отлично, теперь мы готовы к сериализации данных!

Создание класса, с возможностью сериализации

Игры, похожие на Final Fantasy (то есть многие RPG), предлагают игроку выбрать класс персонажа. Например, рыцарь, разбойник или маг. Создайте новый скрипт, назовите его Game и объявите в нем переменные:

using UnityEngine;
using System.Collections;
[System.Serializable]
public class Game { public static Game current; public Character knight; public Character rogue; public Character wizard; public Game () { knight = new Character(); rogue = new Character(); wizard = new Character(); }
}

Обратите внимание на строчку [System.Serializable], которая говорит движку, что этот скрипт может быть сериализован. Круто, не так ли? Как говорит официальная документация, Unity умеет сериализовать следующие типы данных:

  • Все базовые типы (int, string, float, bool и т.д.).
  • Некоторые встроенные типы (Vector2, Vector3, Vector4, Quaternion, Matrix4x4, Color, Rect и LayerMask).
  • Все классы, унаследованные от UnityEngine.Object (GameObject, Component, MonoBehavior, Texture2D и AnimationClip).
  • Перечисляемый тип (Enums).
  • Массивы и списки типов данных, перечисленных выше.

Первая переменная, объявленная в нашем классе, — current — является статической ссылкой на экземпляр игры. Когда мы будем сохранять или загружать какие-либо данные, нам потребуется обратиться к «текущей» игре. При использовании статических переменных сделать это особенно просто, без вызова лишних методов. Очень удобно!

Обратите внимание на класс Character. Его еще у нас нет — давайте создадим новый скрипт с таким названием:

using UnityEngine;
using System.Collections;
[System.Serializable]
    public class Character { public string name; public Character () { this.name = ""; }
}

Ничего странного не заметили? Да, мы действительно создали новый класс, внутри которого есть ровно одна строковая переменная. Мы, конечно же, могли использовать в скрипте Game переменные типа string вместо экземпляров класса Character. Но целью нашей статьи является не то, как сделать лучше, а рассказать, как можно решить нашу проблему.

Теперь, когда наши классы настроены, перейдем обратно к скрипту SaveLoad и добавим возможность сохранения игры.

Сохранение игры

Что происходит по нажатию на кнопку «Загрузить игру»? Правильно — показывается список уже сохраненных игры, которые мы можем восстановить. Так давайте создадим список игр и назовем его savedGames:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public static class SaveLoad { 
    public static List<Game> savedGames = new List<Game>();
}

А теперь напишем статическую функцию сохранения игры:

public static void Save() { 
    savedGames.Add(Game.current); 
    BinaryFormatter bf = new BinaryFormatter(); 
    FileStream file = File.Create (Application.persistentDataPath + "/savedGames.gd"); 
    bf.Serialize(file, SaveLoad.savedGames); 
    file.Close();
}

Разберемся с тем, что же выполняет эта функция. Сначала мы добавляем в ранее созданный список текующую игру. Так как в скрипте Game мы использовали статическую переменную current, то мы можем достаточно просто обратиться к ней: Game.current. После этого нам следует сериализовать список, для чего и создается экземпляр класса BinaryFormatter.

Далее при помощи класса FileStream мы создаем новый файл под названием savedGames.gd в специальной директории, в которой и должны храниться все игровые данные. Для названия файла сохранений будем использовать savedGames, а расширением будет gd (от словосочетания game data).

Примечание автора В качестве расширения файла вы можете использовать все, что угодно. Так, например, в играх The Elder Scrolls используется .esm.

Далее вызывается метод Serialize экземпляра класса BinaryFormatter, который и сохраняет в файл сериализованные данные. И на этом все — файл закрывается. Наша игра сохранена!

Загрузка игры

А здесь все довольно просто. Так как при сохранении игры мы создали файл и записали в него сериализованный список, то сейчас нам придется открыть его и десериализовать имеющиеся в нем данные:

public static void Load() { if(File.Exists(Application.persistentDataPath + "/savedGames.gd")) { BinaryFormatter bf = new BinaryFormatter(); FileStream file = File.Open(Application.persistentDataPath + "/savedGames.gd", FileMode.Open); SaveLoad.savedGames = (List<Game>)bf.Deserialize(file); file.Close(); }
}

Алгоритм предельно прост. Для начала мы проверяем, существует ли файл сохранений. Если существует, то мы создаем новый экземпляр класса BinaryFormatter, открываем файл savedGames.gd и записываем в список savedGames десериализованные данные, считанные из файла.

Обратите внимание на 5 строчку. Метод Deserialize вернет нам битовую последовательность и, присвоив списку savedGames то, что возвращает функция Deserialize, мы ни к чему хорошему не придем. И поэтому нам следует полученную битовую последовательность преобразовать (привести) к типу List <Game>.

Примечание автора Тип данных, к которому вы приводите десериализованные данные, может быть совершенно разным. Например: Player.lives = (int)bf.Deserialize(file);.

Вывод

Итак, теперь вы знаете, как реализовать систему сохранения и загрузки игровых данных. Наш скрипт готов, и в окончательном виде он выглядит вот так:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public static class SaveLoad { 
public static List<Game> savedGames = new List<Game>(); 

//методы загрузки и сохранения статические, поэтому их можно вызвать откуда угодно 
public static void Save() { 
    SaveLoad.savedGames.Add(Game.current); 
    BinaryFormatter bf = new BinaryFormatter(); 

    //Application.persistentDataPath это строка; выведите ее в логах и вы увидите расположение файла сохранений 
    FileStream file = File.Create (Application.persistentDataPath + "/savedGames.gd"); 
    bf.Serialize(file, SaveLoad.savedGames); 
    file.Close(); 
} 

public static void Load() { 
    if(File.Exists(Application.persistentDataPath + "/savedGames.gd")) { 
    BinaryFormatter bf = new BinaryFormatter(); 
    FileStream file = File.Open(Application.persistentDataPath + "/savedGames.gd", FileMode.Open); 
    SaveLoad.savedGames = (List<Game>)bf.Deserialize(file); 
    file.Close(); } 
}
}

Таковы основы работы с игровыми данными в Unity. В файле проекта вы можете найти несколько других скриптов, которые демонстрируют, как я применяю написанные нами функции и как я отображаю данные при помощи Unity GUI.

Комментарии

Нет комментариев. Ваш будет первым!