Создадим новый проект P10_1. Реализуем в нем демонстрацию следующих операций с файлами:
Создание нового файла.
Открытие файла и чтение информации из него.
Копирование файла.
Переименование файла
Просмотр директории
Удаление файла
Для работы с файлами в XNA нужно выполнить следующие шаги:
Во-первых – получить объект типа IAsyncResult – как результат запроса о выборе устройства хранения информации.
Во-вторых – переменная типа IAsyncResult используется для создания переменной типа StorageDevice, которая представляет устройство для хранения данных.
В-третьих – переменная типа StorageDevice используется при создании объекта StorageContainer, который используется для доступа к месту хранения файлов. В случае с Windows-играми это – папка SavedGames, расположенная в папке Мои Документы текущего пользователя.
На рис. 14.1. вы можете видеть данную папку для проекта P10_1.
(рис 14.1) Папка для сохранения игрыВ листинге 14.1. вы можете найти код класса Game1.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
//Классы для работы с устройствами ввода-вывода
using System.IO;
namespace P10_1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Для информации об используемом устройстве хранения информации
StorageDevice sDev;
//Для хранения результата асинхронной операции доступа к устройству
IAsyncResult res;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
//Процедура для создания нового файла
void FileCreate(StorageDevice device)
{
//Массив байтов для записи в файл
byte[] a = new byte[10];
for (byte i = 0; i < 10; i++)
{
a[i] = i;
}
//Открываем контейнер для хранения файлов
//В случае с Windows-играми это - папка с соответствующим
//именем в папке Мои документы текущего пользователя
StorageContainer container =
device.OpenContainer("P10_1");
//Соединяем имя файла и имя контейнера
string filename = Path.Combine(container.Path, "savegame.sav");
//Создаем новый файл
if (!File.Exists(filename))
{
FileStream file = File.Create(filename);
//Записываем в файл массив байтов, сгенерированный выше
file.Write(a, 0, 10);
//закрываем файл
file.Close();
this.Window.Title = "Файл создан";
}
else
{
this.Window.Title = "Файл уже создан";
}
//Уничтожаем контейнер - только после этого файл будет
//сохранен
container.Dispose();
}
//Процедура для открытия существующего файла
void FileOpen(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string filename = Path.Combine(container.Path, "savegame.sav");
if (File.Exists(filename))
{
FileStream file = File.Open(filename, FileMode.Open);
//Выведем данные, считанные из файла, в заголовок игрового окна
this.Window.Title = "Содержимое файла: ";
for (int i = 1; i < file.Length + 1; i++)
this.Window.Title = this.Window.Title + file.ReadByte().ToString();
file.Close();
}
else
{
this.Window.Title = "Отсутствует файл для чтения";
}
container.Dispose();
}
//Процедура для копирования файла
void FileCopy(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string filename = Path.Combine(container.Path, "savegame.sav");
if (File.Exists(filename))
{
string copyfilename = Path.Combine(container.Path, "copysave.bak");
File.Copy(filename, copyfilename, true);
this.Window.Title = "Файл скопирован";
}
else
{
this.Window.Title = "Файл не существует";
}
container.Dispose();
}
//Процедура для переименования файла
void FileRename(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
//Старое имя файла
string oldfilename = Path.Combine(container.Path, "copysave.bak");
//Новое имя файла
string newfilename = Path.Combine(container.Path, "rencopysave.sav");
//Если в папке нет файла с новым именем файла и есть файл
//со старым именем - переименовываем
if (!File.Exists(newfilename)File .Exists (oldfilename ))
{
File.Move(oldfilename, newfilename);
this.Window.Title = "Скопированный файл переименован";
}
else
{
this.Window.Title = "Такой файл уже есть или отсутствует файл для переименования";
}
container.Dispose();
}
//Просмотр содержимого папки
void FileEnumerate(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string FNForComp="";
//Получить список файлов
ICollection<string> FileList = Directory.GetFiles(container.Path);
//Для каждого имени файла проверим, соответствует ли оно
//заданному имени, предположим, что папка не содержит заданного файла
this.Window.Title = "Папка не содержит файл rencopysave.sav";
foreach (string filename in FileList)
{
//Извлечем из строки с полным путем к файлу
//имя файла
FNForComp = Path.GetFileName(filename);
//Если имя файла равно искомому - выведем
//соответствующее сообщение
if (FNForComp == "rencopysave.sav")
{
this.Window.Title = "Папка содержит файл rencopysave.sav";
}
}
container.Dispose();
}
//удаление файла
void FileDelete(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string filename = Path.Combine(container.Path, "rencopysave.sav");
//Удаляем файл если он существует
if (File.Exists(filename))
{
File.Delete(filename);
this.Window.Title = "Файл rencopysave.sav удален";
}
container.Dispose();
}
protected override void Update(GameTime gameTime)
{
KeyboardState kb = Keyboard.GetState();
//Инициируем процедуру выбора устройства, в случае с Windows
//автоматически выбирается папка SavedGames в папке Мои документы
//текущего пользователя
res = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
//Сохраняем найденное устройство - позже мы используем его
//в процедурах работы с файлами
sDev = Guide.EndShowStorageDeviceSelector(res);
//При нажатии на клавишу Q вызываем процедуру сохранения файла
if (kb.IsKeyDown(Keys.Q))
{
FileCreate(sDev);
}
//При нажатии на W вызываем процедуру открытия файла
if (kb.IsKeyDown(Keys.W))
{
FileOpen(sDev);
}
//При нажатии на E вызываем процедуру копирования файла
if (kb.IsKeyDown(Keys.E))
{
FileCopy(sDev);
}
//При нажатии на A вызываем процедуру переименования файла
if (kb.IsKeyDown(Keys.A))
{
FileRename(sDev);
}
//При нажатии на S вызываем процедуру просмотра каталога
if (kb.IsKeyDown(Keys.S))
{
FileEnumerate(sDev);
}
//При нажатии D вызываем процедуру удаления файла
if (kb.IsKeyDown(Keys.D))
{
FileDelete(sDev);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
В обычных файлах можно хранить различные данные, в том числе – информацию об уровнях, информацию для сохранения игры и т.д. Однако при таком подходе вся работа по восстановлению необходимых данных ложится на программиста. Удобнее было бы сохранять и восстанавливать не отдельные переменные или поля объектов, а целые объекты, которые хранят множество полей, используемых в игре. Сделать это позволяет
Игра, предусматривающая возможность сохранения игрового состояния, должна предусматривать как минимум два режима работы. Первый – когда игра начинается сначала – в таком случае поля класса, предназначенного для сериализации, заполняются некоторыми заранее заданными значениями. В ходе игры поля класса меняются, отражая изменения, произошедшие в ходе игрового процесса. Далее, после того, как объект сериализован, игра может либо начинаться сначала, либо – восстанавливать свое состояние на момент сериализации. Рассмотрим пример сохранения и восстановления состояния игры на примере проекта P10_2.
Игровой процесс приводимого здесь проекта заключается в том, что пользователь должен кликать мышью по плиткам, которые исчезают после щелчка по ним. Задача – создать механизм, который позволит сохранять состояние игрового экрана в файле и восстанавливать это состояние. Мы не будем приводить здесь разработку игровой логики, меню и других подсистем игры. Сосредоточимся на сохранении и загрузке игры.
На рис. 14.2. вы можете видеть окно Project Explorer для проекта P10_2.
(рис 14.2) Окно Project Explorer для проекта P10_2Класс Game1 – это стандартный игровой класс.
GameData – класс, содержащий игровые данные и предназначенный для сериализации.
gBaseClass, Wall – классы для визуализации игровых объектов.
В листинге 14.2. вы можете видеть код класса GameData.
using System;
using System.Collections.Generic;
using System.Text;
namespace P10_2
{
//Атрибут, указывающий на то, что класс может быть сериализован
[Serializable]
class GameData
{
//Поле класса, которое содержит важные игровые данные
public byte [,] array;
//Конструктор класса
public GameData (byte [,] arr)
{
array = new byte [8,10];
array = arr;
}
}
}
Обратите внимание на атрибут Serializable – он указывает на то, что класс может быть сериализован. От набора полей этого класса и от данных, хранящихся в них, зависит размер файла, в котором будет храниться игровая информация. В нашем случае класс содержит массив типа Byte, который будет представлять собой состояние игрового окна на момент сохранения игры.
Рассмотрим классы gBaseClass и Wall (листинг 14.3., 14.4.) Эти классы мы уже использовали в проекте P5_1. Класс gBaseClass содержит механизмы для рисования компонента и расположения его на экране, а класс Wall – потомок класса gBaseClass – используется для управления поведением объекта.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P10_2
{
public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
{
Texture2D sprTexture;
public Vector2 sprPosition;
public Rectangle sprRectangle;
public gBaseClass(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle)
: base(game)
{
sprTexture = _sprTexture;
//Именно здесь производится перевод индекса элемента массива
//в координаты на игровом экране
sprPosition = _sprPosition * 64;
sprRectangle = _sprRectangle;
}
public override void Initialize()
{
base.Initialize();
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
SpriteBatch sprBatch =
(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
sprBatch.Draw(sprTexture, sprPosition, Color.White);
base.Draw(gameTime);
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P10_2
{
/// <summary>
/// This is a game component that implements IUpdateable.
/// </summary>
public class Wall : gBaseClass
{
public Wall(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle)
{
}
}
}
Механизмы сериализации и игровая логика реализованы в коде класса Game1. Его вы можете видеть в листинге 14.5.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
//Классы для работы с устройствами ввода-вывода
using System.IO;
//Классы для работы механизмов сериализации
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace P10_2
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Для текстуры стены
Texture2D txtWall;
//Прямоугольник для создания объектов Wall
Rectangle recSprite = new Rectangle(0, 0, 64, 64);
//Массив объектов Wall
Wall [,] arrayOfWalls;
//Объект, хранящий игровые данные и подлежащий сериализации
//Массив в этом объекте используется при загрузке игры и
//при сохранении игры, в игровом процессе используется
//массив объектов Wall
GameData MyObj;
//Переменная для хранения состояния мыши
MouseState mouse;
//устройство для хранения информации
StorageDevice sDev;
//результат операции доступа к устройству
IAsyncResult res;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
//Разрешение экрана 640х512
graphics.PreferredBackBufferWidth = 640;
graphics.PreferredBackBufferHeight = 512;
graphics.ApplyChanges();
//Сделать указатель мыши видимым
this.IsMouseVisible = true;
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
txtWall = Content.Load<Texture2D>("wall");
//В этом массиве лишь единицы - это значит
//что при запуске все игровое поле заполнено
//изображениями стен
byte [,] arrayForGame = new byte[8, 10] {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
};
//Новый массив стен
arrayOfWalls = new Wall[8, 10];
//Создаем объект для хранения данных,
//передавая ему массив
MyObj = new GameData(arrayForGame);
//вызываем процедуру создания и
//вывода на экран игровых объектов
AddSprites ();
}
//Создание игровых объектов
void AddSprites()
{
//Просматриваем массив array в объекте типа GameData
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
if (MyObj.array[i, j] == 1)
{
//Если в массиве единица - создаем новый объект в текущей ячейке
//массива arrayOfWalls и добавляем в список игровых компонентов
arrayOfWalls[i, j] = new Wall(this, ref txtWall, new Vector2(j, i), recSprite);
Components.Add(arrayOfWalls[i, j]);
}
}
}
}
//Процедура очистки массива игровых компонентов
void ClearAll()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
if (arrayOfWalls[i, j] != null)
{
arrayOfWalls[i, j].Dispose();
arrayOfWalls[i, j] = null;
}
}
}
}
//Процедура проверки массива игровых компонентов и отражения
//его состояния в массиве объекта типа GameData
//в нашем случае, если ячейка массива игровых компонентов содержит
//объект, в массив объекта GameData записывают 1, если нет - 0
void SetWallToArray()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
if (arrayOfWalls[i, j] == null)
{
MyObj.array[i, j] = 0;
}
else
{
MyObj.array[i, j] = 1;
}
}
}
}
//Процедура сериализации объекта
void serializ()
{
//Контейнер для хранения данных
StorageContainer container = sDev.OpenContainer("P10_2");
//Полное имя файла - комбинация адреса контейнера и имени
string filename = Path.Combine(container.Path, "savegame.sav");
//Объект, предназначенный для сериализации и десериализации других объектов
IFormatter formatter = new BinaryFormatter();
//Создаем новый поток для записи файла
Stream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
//Сериализуем объект MyObj в поток stream
formatter.Serialize(stream, MyObj);
//Закрываем поток
stream.Close();
//Уничтожаем контейнер
container.Dispose();
//выводим сообщение в заголовок игрового окна
this.Window.Title = "Игра сохранена";
}
//Процедура десериализации объекта
//похожа на процедуру десериализации
//при десериализации файл открывают для чтения и
//десериализуют в объект
void deserializ()
{
StorageContainer container = sDev.OpenContainer("P10_2");
string filename = Path.Combine(container.Path, "savegame.sav");
IFormatter formatter = new BinaryFormatter();
//Новый поток для чтения файла
Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
//Получаем данные из потока и приводим их к типу MyObj
MyObj = (GameData)formatter.Deserialize(stream);
stream.Close();
container.Dispose();
this.Window.Title = "Игра загружена";
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//получим состояние клавиатуры
KeyboardState kb = Keyboard.GetState();
//Сохраняем игру при нажатой клавише A
if (kb.IsKeyDown(Keys.A))
{
//Отразим состояние игровго окна в массиве объекта типа GameData
SetWallToArray();
//Получим устройство для хранения данных
res = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
sDev = Guide.EndShowStorageDeviceSelector(res);
//Вызовем процедуру сериализации
serializ ();
}
//Загружаем игру при нажатой клавише W
if (kb.IsKeyDown (Keys .W ))
{
//Очищаем игровой экран
ClearAll();
//Получим устройство для хранения данных
res = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
sDev = Guide.EndShowStorageDeviceSelector(res);
//Десериализуем объект из файла
deserializ();
//Сконструируем игровой экран на основе десериализованного
//объекта MyObj
AddSprites();
}
//Работа с мышью
mouse = Mouse.GetState();
if (mouse.LeftButton == ButtonState.Pressed)
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
//Если в текущей ячейке массива есть "стена"
//проверим, находился ли указатель мыши в пределах объекта
//соответствующего текущей ячейке. Если да - уничтожим объект
if (arrayOfWalls[i, j] != null)
{
if (arrayOfWalls[i, j].sprPosition.X + arrayOfWalls[i, j].sprRectangle.Width > mouse.X
arrayOfWalls[i, j].sprPosition.X < mouse.X
arrayOfWalls[i, j].sprPosition.Y + arrayOfWalls[i, j].sprRectangle.Height > mouse.Y
arrayOfWalls[i, j].sprPosition.Y < mouse.Y)
{
arrayOfWalls[i, j].Dispose();
arrayOfWalls[i, j] = null;
}
}
}
}
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
base.Draw(gameTime);
spriteBatch.End();
}
}
}
На рис. 14.3. вы можете видеть игровой экран проекта P10_2.
(рис 14.3) Игровой экран проекта P10_2 Создадим новый проект P10_1. Реализуем в нем демонстрацию следующих операций с файлами:
Создание нового файла.
Открытие файла и чтение информации из него.
Копирование файла.
Переименование файла
Просмотр директории
Удаление файла
Для работы с файлами в XNA нужно выполнить следующие шаги:
Во-первых – получить объект типа IAsyncResult – как результат запроса о выборе устройства хранения информации.
Во-вторых – переменная типа IAsyncResult используется для создания переменной типа StorageDevice, которая представляет устройство для хранения данных.
В-третьих – переменная типа StorageDevice используется при создании объекта StorageContainer, который используется для доступа к месту хранения файлов. В случае с Windows-играми это – папка SavedGames, расположенная в папке Мои Документы текущего пользователя.
На рис. 14.1. вы можете видеть данную папку для проекта P10_1.
(рис 14.1) Папка для сохранения игрыВ листинге 14.1. вы можете найти код класса Game1.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
//Классы для работы с устройствами ввода-вывода
using System.IO;
namespace P10_1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Для информации об используемом устройстве хранения информации
StorageDevice sDev;
//Для хранения результата асинхронной операции доступа к устройству
IAsyncResult res;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
//Процедура для создания нового файла
void FileCreate(StorageDevice device)
{
//Массив байтов для записи в файл
byte[] a = new byte[10];
for (byte i = 0; i < 10; i++)
{
a[i] = i;
}
//Открываем контейнер для хранения файлов
//В случае с Windows-играми это - папка с соответствующим
//именем в папке Мои документы текущего пользователя
StorageContainer container =
device.OpenContainer("P10_1");
//Соединяем имя файла и имя контейнера
string filename = Path.Combine(container.Path, "savegame.sav");
//Создаем новый файл
if (!File.Exists(filename))
{
FileStream file = File.Create(filename);
//Записываем в файл массив байтов, сгенерированный выше
file.Write(a, 0, 10);
//закрываем файл
file.Close();
this.Window.Title = "Файл создан";
}
else
{
this.Window.Title = "Файл уже создан";
}
//Уничтожаем контейнер - только после этого файл будет
//сохранен
container.Dispose();
}
//Процедура для открытия существующего файла
void FileOpen(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string filename = Path.Combine(container.Path, "savegame.sav");
if (File.Exists(filename))
{
FileStream file = File.Open(filename, FileMode.Open);
//Выведем данные, считанные из файла, в заголовок игрового окна
this.Window.Title = "Содержимое файла: ";
for (int i = 1; i < file.Length + 1; i++)
this.Window.Title = this.Window.Title + file.ReadByte().ToString();
file.Close();
}
else
{
this.Window.Title = "Отсутствует файл для чтения";
}
container.Dispose();
}
//Процедура для копирования файла
void FileCopy(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string filename = Path.Combine(container.Path, "savegame.sav");
if (File.Exists(filename))
{
string copyfilename = Path.Combine(container.Path, "copysave.bak");
File.Copy(filename, copyfilename, true);
this.Window.Title = "Файл скопирован";
}
else
{
this.Window.Title = "Файл не существует";
}
container.Dispose();
}
//Процедура для переименования файла
void FileRename(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
//Старое имя файла
string oldfilename = Path.Combine(container.Path, "copysave.bak");
//Новое имя файла
string newfilename = Path.Combine(container.Path, "rencopysave.sav");
//Если в папке нет файла с новым именем файла и есть файл
//со старым именем - переименовываем
if (!File.Exists(newfilename)File .Exists (oldfilename ))
{
File.Move(oldfilename, newfilename);
this.Window.Title = "Скопированный файл переименован";
}
else
{
this.Window.Title = "Такой файл уже есть или отсутствует файл для переименования";
}
container.Dispose();
}
//Просмотр содержимого папки
void FileEnumerate(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string FNForComp="";
//Получить список файлов
ICollection<string> FileList = Directory.GetFiles(container.Path);
//Для каждого имени файла проверим, соответствует ли оно
//заданному имени, предположим, что папка не содержит заданного файла
this.Window.Title = "Папка не содержит файл rencopysave.sav";
foreach (string filename in FileList)
{
//Извлечем из строки с полным путем к файлу
//имя файла
FNForComp = Path.GetFileName(filename);
//Если имя файла равно искомому - выведем
//соответствующее сообщение
if (FNForComp == "rencopysave.sav")
{
this.Window.Title = "Папка содержит файл rencopysave.sav";
}
}
container.Dispose();
}
//удаление файла
void FileDelete(StorageDevice device)
{
StorageContainer container =
device.OpenContainer("P10_1");
string filename = Path.Combine(container.Path, "rencopysave.sav");
//Удаляем файл если он существует
if (File.Exists(filename))
{
File.Delete(filename);
this.Window.Title = "Файл rencopysave.sav удален";
}
container.Dispose();
}
protected override void Update(GameTime gameTime)
{
KeyboardState kb = Keyboard.GetState();
//Инициируем процедуру выбора устройства, в случае с Windows
//автоматически выбирается папка SavedGames в папке Мои документы
//текущего пользователя
res = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
//Сохраняем найденное устройство - позже мы используем его
//в процедурах работы с файлами
sDev = Guide.EndShowStorageDeviceSelector(res);
//При нажатии на клавишу Q вызываем процедуру сохранения файла
if (kb.IsKeyDown(Keys.Q))
{
FileCreate(sDev);
}
//При нажатии на W вызываем процедуру открытия файла
if (kb.IsKeyDown(Keys.W))
{
FileOpen(sDev);
}
//При нажатии на E вызываем процедуру копирования файла
if (kb.IsKeyDown(Keys.E))
{
FileCopy(sDev);
}
//При нажатии на A вызываем процедуру переименования файла
if (kb.IsKeyDown(Keys.A))
{
FileRename(sDev);
}
//При нажатии на S вызываем процедуру просмотра каталога
if (kb.IsKeyDown(Keys.S))
{
FileEnumerate(sDev);
}
//При нажатии D вызываем процедуру удаления файла
if (kb.IsKeyDown(Keys.D))
{
FileDelete(sDev);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
В обычных файлах можно хранить различные данные, в том числе – информацию об уровнях, информацию для сохранения игры и т.д. Однако при таком подходе вся работа по восстановлению необходимых данных ложится на программиста. Удобнее было бы сохранять и восстанавливать не отдельные переменные или поля объектов, а целые объекты, которые хранят множество полей, используемых в игре. Сделать это позволяет
Игра, предусматривающая возможность сохранения игрового состояния, должна предусматривать как минимум два режима работы. Первый – когда игра начинается сначала – в таком случае поля класса, предназначенного для сериализации, заполняются некоторыми заранее заданными значениями. В ходе игры поля класса меняются, отражая изменения, произошедшие в ходе игрового процесса. Далее, после того, как объект сериализован, игра может либо начинаться сначала, либо – восстанавливать свое состояние на момент сериализации. Рассмотрим пример сохранения и восстановления состояния игры на примере проекта P10_2.
Игровой процесс приводимого здесь проекта заключается в том, что пользователь должен кликать мышью по плиткам, которые исчезают после щелчка по ним. Задача – создать механизм, который позволит сохранять состояние игрового экрана в файле и восстанавливать это состояние. Мы не будем приводить здесь разработку игровой логики, меню и других подсистем игры. Сосредоточимся на сохранении и загрузке игры.
На рис. 14.2. вы можете видеть окно Project Explorer для проекта P10_2.
(рис 14.2) Окно Project Explorer для проекта P10_2Класс Game1 – это стандартный игровой класс.
GameData – класс, содержащий игровые данные и предназначенный для сериализации.
gBaseClass, Wall – классы для визуализации игровых объектов.
В листинге 14.2. вы можете видеть код класса GameData.
using System;
using System.Collections.Generic;
using System.Text;
namespace P10_2
{
//Атрибут, указывающий на то, что класс может быть сериализован
[Serializable]
class GameData
{
//Поле класса, которое содержит важные игровые данные
public byte [,] array;
//Конструктор класса
public GameData (byte [,] arr)
{
array = new byte [8,10];
array = arr;
}
}
}
Обратите внимание на атрибут Serializable – он указывает на то, что класс может быть сериализован. От набора полей этого класса и от данных, хранящихся в них, зависит размер файла, в котором будет храниться игровая информация. В нашем случае класс содержит массив типа Byte, который будет представлять собой состояние игрового окна на момент сохранения игры.
Рассмотрим классы gBaseClass и Wall (листинг 14.3., 14.4.) Эти классы мы уже использовали в проекте P5_1. Класс gBaseClass содержит механизмы для рисования компонента и расположения его на экране, а класс Wall – потомок класса gBaseClass – используется для управления поведением объекта.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P10_2
{
public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
{
Texture2D sprTexture;
public Vector2 sprPosition;
public Rectangle sprRectangle;
public gBaseClass(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle)
: base(game)
{
sprTexture = _sprTexture;
//Именно здесь производится перевод индекса элемента массива
//в координаты на игровом экране
sprPosition = _sprPosition * 64;
sprRectangle = _sprRectangle;
}
public override void Initialize()
{
base.Initialize();
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
SpriteBatch sprBatch =
(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
sprBatch.Draw(sprTexture, sprPosition, Color.White);
base.Draw(gameTime);
}
}
}
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P10_2
{
/// <summary>
/// This is a game component that implements IUpdateable.
/// </summary>
public class Wall : gBaseClass
{
public Wall(Game game, ref Texture2D _sprTexture,
Vector2 _sprPosition, Rectangle _sprRectangle)
: base(game, ref _sprTexture, _sprPosition, _sprRectangle)
{
}
}
}
Механизмы сериализации и игровая логика реализованы в коде класса Game1. Его вы можете видеть в листинге 14.5.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
//Классы для работы с устройствами ввода-вывода
using System.IO;
//Классы для работы механизмов сериализации
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace P10_2
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Для текстуры стены
Texture2D txtWall;
//Прямоугольник для создания объектов Wall
Rectangle recSprite = new Rectangle(0, 0, 64, 64);
//Массив объектов Wall
Wall [,] arrayOfWalls;
//Объект, хранящий игровые данные и подлежащий сериализации
//Массив в этом объекте используется при загрузке игры и
//при сохранении игры, в игровом процессе используется
//массив объектов Wall
GameData MyObj;
//Переменная для хранения состояния мыши
MouseState mouse;
//устройство для хранения информации
StorageDevice sDev;
//результат операции доступа к устройству
IAsyncResult res;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
//Разрешение экрана 640х512
graphics.PreferredBackBufferWidth = 640;
graphics.PreferredBackBufferHeight = 512;
graphics.ApplyChanges();
//Сделать указатель мыши видимым
this.IsMouseVisible = true;
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
txtWall = Content.Load<Texture2D>("wall");
//В этом массиве лишь единицы - это значит
//что при запуске все игровое поле заполнено
//изображениями стен
byte [,] arrayForGame = new byte[8, 10] {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
};
//Новый массив стен
arrayOfWalls = new Wall[8, 10];
//Создаем объект для хранения данных,
//передавая ему массив
MyObj = new GameData(arrayForGame);
//вызываем процедуру создания и
//вывода на экран игровых объектов
AddSprites ();
}
//Создание игровых объектов
void AddSprites()
{
//Просматриваем массив array в объекте типа GameData
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
if (MyObj.array[i, j] == 1)
{
//Если в массиве единица - создаем новый объект в текущей ячейке
//массива arrayOfWalls и добавляем в список игровых компонентов
arrayOfWalls[i, j] = new Wall(this, ref txtWall, new Vector2(j, i), recSprite);
Components.Add(arrayOfWalls[i, j]);
}
}
}
}
//Процедура очистки массива игровых компонентов
void ClearAll()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
if (arrayOfWalls[i, j] != null)
{
arrayOfWalls[i, j].Dispose();
arrayOfWalls[i, j] = null;
}
}
}
}
//Процедура проверки массива игровых компонентов и отражения
//его состояния в массиве объекта типа GameData
//в нашем случае, если ячейка массива игровых компонентов содержит
//объект, в массив объекта GameData записывают 1, если нет - 0
void SetWallToArray()
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
if (arrayOfWalls[i, j] == null)
{
MyObj.array[i, j] = 0;
}
else
{
MyObj.array[i, j] = 1;
}
}
}
}
//Процедура сериализации объекта
void serializ()
{
//Контейнер для хранения данных
StorageContainer container = sDev.OpenContainer("P10_2");
//Полное имя файла - комбинация адреса контейнера и имени
string filename = Path.Combine(container.Path, "savegame.sav");
//Объект, предназначенный для сериализации и десериализации других объектов
IFormatter formatter = new BinaryFormatter();
//Создаем новый поток для записи файла
Stream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
//Сериализуем объект MyObj в поток stream
formatter.Serialize(stream, MyObj);
//Закрываем поток
stream.Close();
//Уничтожаем контейнер
container.Dispose();
//выводим сообщение в заголовок игрового окна
this.Window.Title = "Игра сохранена";
}
//Процедура десериализации объекта
//похожа на процедуру десериализации
//при десериализации файл открывают для чтения и
//десериализуют в объект
void deserializ()
{
StorageContainer container = sDev.OpenContainer("P10_2");
string filename = Path.Combine(container.Path, "savegame.sav");
IFormatter formatter = new BinaryFormatter();
//Новый поток для чтения файла
Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
//Получаем данные из потока и приводим их к типу MyObj
MyObj = (GameData)formatter.Deserialize(stream);
stream.Close();
container.Dispose();
this.Window.Title = "Игра загружена";
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//получим состояние клавиатуры
KeyboardState kb = Keyboard.GetState();
//Сохраняем игру при нажатой клавише A
if (kb.IsKeyDown(Keys.A))
{
//Отразим состояние игровго окна в массиве объекта типа GameData
SetWallToArray();
//Получим устройство для хранения данных
res = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
sDev = Guide.EndShowStorageDeviceSelector(res);
//Вызовем процедуру сериализации
serializ ();
}
//Загружаем игру при нажатой клавише W
if (kb.IsKeyDown (Keys .W ))
{
//Очищаем игровой экран
ClearAll();
//Получим устройство для хранения данных
res = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
sDev = Guide.EndShowStorageDeviceSelector(res);
//Десериализуем объект из файла
deserializ();
//Сконструируем игровой экран на основе десериализованного
//объекта MyObj
AddSprites();
}
//Работа с мышью
mouse = Mouse.GetState();
if (mouse.LeftButton == ButtonState.Pressed)
{
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 10; j++)
{
//Если в текущей ячейке массива есть "стена"
//проверим, находился ли указатель мыши в пределах объекта
//соответствующего текущей ячейке. Если да - уничтожим объект
if (arrayOfWalls[i, j] != null)
{
if (arrayOfWalls[i, j].sprPosition.X + arrayOfWalls[i, j].sprRectangle.Width > mouse.X
arrayOfWalls[i, j].sprPosition.X < mouse.X
arrayOfWalls[i, j].sprPosition.Y + arrayOfWalls[i, j].sprRectangle.Height > mouse.Y
arrayOfWalls[i, j].sprPosition.Y < mouse.Y)
{
arrayOfWalls[i, j].Dispose();
arrayOfWalls[i, j] = null;
}
}
}
}
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
base.Draw(gameTime);
spriteBatch.End();
}
}
}
На рис. 14.3. вы можете видеть игровой экран проекта P10_2.
(рис 14.3) Игровой экран проекта P10_2 Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.