Невозможно пройти тесты, в окне с вопросами пусто |
Сохранение и загрузка игр, сериализация, работа с файлами
Цель работы: Научиться сохранять и загружать состояния игр, используя изолированное хранилище и механизмы сериализации
19.1. О жизненном цикле приложений
Сохранение состояния игры и загрузка сохранённых состояний – это обычная, необходимая для многих игр процедура. Игры для Windows Phone могут использовать для хранения файлов так называемое изолированное хранилище (Isolated Storage), работа с которым осуществляется с помощью пространства имён System.IO.IsolatedStorage. Каждое приложение имеет собственное изолированное хранилище, таким образом, данные, хранимые приложениями, защищены от случайного или злонамеренного изменения другими программами. В свою очередь, приложение не может получить доступ к данным других приложений.
Можно организовать сохранение либо загрузку каких-либо данных, ориентируясь на произвольные события внутри программы, однако, часто эти операции выполняют при запуске (Launching), закрытии (Closing), активации (Activated), деактивации (Deactivated). Прежде чем рассматривать способы работы с изолированным хранилищем, поговорим о жизненном цикле XNA-приложения.
Жизненный цикл приложения начинается с запуска его пользователем (Launching). Приложение исполняется, после чего, например, по нажатию кнопки перехода на домашний экран, приложение деактивируется (Deactivated). Здесь возможны два варианта развития событий. При первом, когда мы не позаботились о том, чтобы сохранить состояние приложения, оно будет некоторое время находиться в памяти, при попытке возврата к нему (нажатием кнопки Назад), оно будет открыто в том же состоянии, в котором предстаёт перед пользователем сразу после запуска – активировано (Activated). При втором варианте можно выполнить так называемое захоронение (Tombstoning) приложения, сохранив важные данные и позаботившись о том, чтобы эти данные были восстановлены при активации приложения.
Если мы нажмём кнопку Назад во время исполнения приложения, оно будет закрыто (Closing), при этом возврат к приложению невозможен (система не будет сохранять его в памяти, пытаться сохранить промежуточные состояния, как в случае с деактивацией), однако, при возникновении этого события можно сохранить данные, которые будут использованы при следующем запуске того же приложения.
В XNA есть собственные механизмы для обработки состояний приложения. Так, вам уже известен метод Initialize, который выполняется при запуске игры и используется для инициализации игровых параметров. Базовый класс Game предоставляет и другие методы для организации реакции пользователя на изменение состояния игры. В частности, это методы OnActivation (происходит при активации игры), OnDeactivation (при деактивации), Exiting (при выходе из игры).
Для организации работы по загрузке и сохранению игры нам понадобится воспользоваться классом PhoneApplicationService. Обработчики его событий удобно использовать для работы с данными, которые должны быть сохранены при завершении работы игры и загружены при запуске игры. Для сохранения данных, которые должны сохраняться после выхода из игры, нам понадобится пользоваться изолированным хранилищем.
Как будет видно из примера, которым мы займёмся ниже, при запуске XNA-приложения, выполняются методы Initialize и OnActivation. При использовании обработчиков событий из PhoneApplicationService присутствует чёткое разделение при запуске приложения или при его повторной активации. Собственно говоря, это – одна из причин использования PhoneApplicationService в XNA-приложении.
19.2. О сериализации
Сериализация объектов - это, другими словами – сохранение объекта в виде файла. Сериализованный объект можно восстановить в ходе операции десериализации. В случае с компьютерными играми сериализацию удобно использовать для сохранения игровой информации. Для того, чтобы эффективно использовать инструменты сериализации в играх, нужно заранее уяснить, какие именно объекты будут сериализованы. Желательно выделить для сериализации особые объекты, которые будут содержать лишь ту информацию, сохранение которой необходимо для успешного продолжения игрового процесса при десериализации соответствующего объекта, то есть – загрузке игры.
Игра, предусматривающая возможность сохранения игрового состояния, должна предусматривать как минимум два режима работы. Первый – когда игра начинается сначала – в таком случае поля класса, предназначенного для сериализации, заполняются некоторыми заранее заданными значениями. В ходе игры поля класса меняются, отражая изменения, произошедшие в ходе игрового процесса. Далее, после того, как объект сериализован, игра может либо начинаться сначала, либо – восстанавливать свое состояние на момент сериализации. Второй режим – это загрузка ранее сохранённой игры.
Когда объект сериализован, результаты этой операции можно записать в файл. На данном этапе нам понадобится работа с изолированным хранилищем.
При этом, есть возможность сохранять в хранилище любые файлы, работать с каталогами, читать файлы, то есть – пользоваться обычными возможностями системы управления файлами. Эта функциональность обеспечивается при помощи объектов System.IO.IsolatedStorage.IsolatedStorageFile (работа с хранилищем) System.IO.IsolatedStorage.IsolatedFileStream (работа с файлами в хранилище). Кроме того, в хранилище можно размещать данные в специальном словаре (содержащем ключи и значения). Эта структура данных называется изолированное хранилище настроек (Isolated Settings Storage), работа с хранилищем настроек осуществляется с помощью объекта System.IO.IsolatedStorage.IsolatedStorageSettings.
19.3. Работа с изолированным хранилищем и механизмами сериализации
Напишем простое приложение, позволяющее отслеживать выполнение этих событий и реализующее механизм сохранения состояния приложения. Создадим новый проект P13_1, подключим библиотеки Microsoft.Phone, System.Windows, System.Xml.Serialization. Добавим в состав контента проекта шрифт для вывода сообщений, новый класс SavedParam.cs. В итоге окно Обозреватель решений для нашего проекта будет выглядеть так, как показано на рис. 19.1.
В листинге 19.1 приведен код класса SavedParams
using System; using System.Collections.Generic; using System.IO; using System.IO.IsolatedStorage; using System.Xml.Serialization; using Microsoft.Xna.Framework; //Класс для хранения данных игры, сериализуется namespace P13_1 { public class SavedParam { const string filename = "SavedParam.xml"; //Игровые данные //Количество касаний public int touchCount; //Позиция касания public Vector2 touchPos; public SavedParam() { //Установим в значения по умолчанию touchCount = 0; touchPos = new Vector2(10, 450); } //Процедура сохранения состояния объекта public void Save() { //Получим изолированное хранилище для приложения IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication(); //Создадим файл для последующей работы IsolatedStorageFileStream stream = storage.CreateFile(filename); //Подготовим механизм сериализации для данного объекта XmlSerializer xml = new XmlSerializer(GetType()); //Сериализуем текущий объект, направив вывод в ранее созданный файл xml.Serialize(stream, this); //Закроем файл stream.Close(); //Освободим ресурсы stream.Dispose(); } //Статический метод для восстановления состояния объекта (десериализации, загрузки игры) public static SavedParam Load() { //Получим изолированное хранилище IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication(); //Для хранения ссылки на объект типа SavedParam SavedParam savedParam; //Если в хранилище существует файл с нужным именем if (storage.FileExists(filename)) { //Откроем файл IsolatedStorageFileStream stream = storage.OpenFile("SavedParam.xml", FileMode.Open); //Подготовим механизм для сериализации XmlSerializer xml = new XmlSerializer(typeof(SavedParam)); //Извлечем данные из файла savedParam = xml.Deserialize(stream) as SavedParam; stream.Close(); stream.Dispose(); } else { //Если такого файла нет (при первом запуске в нашем случае) //Запишем в savedParams ссылку на новый объект типа SavedParams savedParam = new SavedParam(); } //Вернём в точку вызова либо результат десериализации, либо - новый объект. return savedParam; } } }Листинг 19.1. Код класса SavedParams
Класс SavedParam имеет статический метод Load(). Мы вызываем этот метод при запуске или активации игры. В методе делается попытка прочесть файл SavedParam.xml, если файл найден, это означает, что ранее, при выходе из игры или при её деактивации мы сохранили в него результат сериализации объекта SavedParam. В таком случае мы десереализуем этот объект и возвращаем его в точку вызова. Если ранее объект сохранён не был, мы создаём новый объект и так же возвращаем его. При создании нового объекта (это происходит при самом первом запуске игры) переменные touchCount и touchPos устанавливаются в значения по умолчанию.
В методе Save() мы сохраняем объект в файл.
Обратите внимание на то, что класс, подлежащий сериализации, должен иметь атрибут [Serializable], в нашем случае это условие исполняется по умолчанию.