Как выполнять сохранение и загрузку игры в Unity
47К открытий48К показов
Совсем недавно мы опубликовали серию уроков (1 часть, 2 часть, 3 часть, 4 часть) по созданию простой игры, используя очень распространенный игровой движок — Unity. В этой статье мы покажем, как организовать систему управления сохраненными играми в Unity. Мы будем писать меню как в Final Fantasy, где игроку предоставляется возможность создать новое уникальное сохранение или же продолжить уже существующее. Итак, к концу урока вы научитесь:
- сохранять игру и загружать уже имеющуюся, используя сериализацию;
- использовать статические переменные для сохранения данных при изменении сцены.
Подготовка к сериализации
Первое, что нам нужно сделать, это сериализовать наши данные, которые мы сохраним, а затем в нужное время восстановим. Для этого нам потребуется создать скрипт (в качестве языка программирования будем использовать C#). Давайте назовем его SaveLoad
. Этот сценарий будет обрабатывать все, что связано с сохранением и восстановлением данных.
Мы будем ссылаться на этот сценарий из других скриптов, а потому сделаем класс статичным, добавив ключевое слово static
между public
и class
. Также не забудем удалить два автоматически созданных метода, поскольку нам не потребуется крепить скрипт ни к какому игровому объекту.
Полученный сценарий должен выглядеть вот так:
Мы хотим добавить немного функциональных возможностей нашему скрипту, а потому давайте пропишем несколько директив:
Первая строка позволит нам использовать динамические списки. Вторая — даст нужный функционал операционной системы по сериализации данных. А последняя директива позволяет нам работать с потоками ввода и вывода. Иными словами, она используется для создания и чтения файлов.
Отлично, теперь мы готовы к сериализации данных!
Создание класса, с возможностью сериализации
Игры, похожие на Final Fantasy (то есть многие RPG), предлагают игроку выбрать класс персонажа. Например, рыцарь, разбойник или маг. Создайте новый скрипт, назовите его Game
и объявите в нем переменные:
Обратите внимание на строчку [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
. Его еще у нас нет — давайте создадим новый скрипт с таким названием:
Ничего странного не заметили? Да, мы действительно создали новый класс, внутри которого есть ровно одна строковая переменная. Мы, конечно же, могли использовать в скрипте Game переменные типа string
вместо экземпляров класса Character
. Но целью нашей статьи является не то, как сделать лучше, а рассказать, как можно решить нашу проблему.
Теперь, когда наши классы настроены, перейдем обратно к скрипту SaveLoad и добавим возможность сохранения игры.
Сохранение игры
Что происходит по нажатию на кнопку «Загрузить игру»? Правильно — показывается список уже сохраненных игры, которые мы можем восстановить. Так давайте создадим список игр и назовем его savedGames:
А теперь напишем статическую функцию сохранения игры:
Разберемся с тем, что же выполняет эта функция. Сначала мы добавляем в ранее созданный список текущую игру. Так как в скрипте Game
мы использовали статическую переменную current, то мы можем достаточно просто обратиться к ней: Game.current
. После этого нам следует сериализовать список, для чего и создается экземпляр класса BinaryFormatter
.
Далее при помощи класса FileStream
мы создаем новый файл под названием savedGames.gd
в специальной директории, в которой и должны храниться все игровые данные. Для названия файла сохранений будем использовать savedGames
, а расширением будет gd
(от словосочетания game data).
Примечание автора В качестве расширения файла вы можете использовать все, что угодно. Так, например, в играх The Elder Scrolls используется .esm
.
Далее вызывается метод Serialize
экземпляра класса BinaryFormatter
, который и сохраняет в файл сериализованные данные. И на этом все — файл закрывается. Наша игра сохранена!
Загрузка игры
А здесь все довольно просто. Так как при сохранении игры мы создали файл и записали в него сериализованный список, то сейчас нам придется открыть его и десериализовать имеющиеся в нем данные:
Алгоритм предельно прост. Для начала мы проверяем, существует ли файл сохранений. Если существует, то мы создаем новый экземпляр класса BinaryFormatter
, открываем файл savedGames.gd
и записываем в список savedGames
десериализованные данные, считанные из файла.
Обратите внимание на 5 строчку. Метод Deserialize
вернет нам битовую последовательность и, присвоив списку savedGames
то, что возвращает функция Deserialize
, мы ни к чему хорошему не придем. И поэтому нам следует полученную битовую последовательность преобразовать (привести) к типу List <Game>
.
Примечание автора Тип данных, к которому вы приводите десериализованные данные, может быть совершенно разным. Например: Player.lives = (int)bf.Deserialize(file);
.
Вывод
Итак, теперь вы знаете, как реализовать систему сохранения и загрузки игровых данных. Наш скрипт готов, и в окончательном виде он выглядит вот так:
Таковы основы работы с игровыми данными в Unity. В файле проекта вы можете найти несколько других скриптов, которые демонстрируют, как я применяю написанные нами функции и как я отображаю данные при помощи Unity GUI.
Перевод статьи «How to Save and Load Your Players’ Progress in Unity»
47К открытий48К показов