Уровень – это подгружаемые данные, никто не хранит их всех в памяти как и в основном исходном коде. Будем учиться писать уровень который будет читаться с файла.
Только не нужно пропускать статью со словами «Что нам стоить прочитать с файла…», Читать будем с помощью сериализации. Почему? Потому, что нужно сразу привыкать к хорошему или правильному. На консоле будет сложно прочитать данные старыми методами. Можно, но сложно поэтому вспоминаем, а для кого-то учим XML, структуры в ней, и методы сбора информации.
Среда: Microsoft Visual Studio C# 2008 Express Edition, XNA 3.1
Проект, как это будет реализовано?
Имеем категорию levels, файл level_001.xml
В файле нацарапано:
Position – Координаты объекта
Rotation – Вращение объекта
TypeName – Тип объекта
В программе должен быть описан каждый тип объекта. Я опишу три простейших:
class Land:LevelElement Элемент уровня: земля
class Box:LevelElement Элемент уровня: ящик
class Circle:LevelElement Элемент уровня: круг
Теперь общее представление программной структуры:
Самый главный класс – Игровой менеджер (GameManager), который содержит множество игровых элементов (GameElement). Игровой элемент может быть уровень, игрок, фон, даже элемент уровня в общем все зависит от вашей фантазии. Мы, для краткости, рассмотрим только одну вариацию – уровень (Level). Уровень в свою очередь может содержать множество элементов уровня (LevelElement). Вот они наши вариации Ящик(Box), Земля(Land), Круг(Circle). Создается игровой элемент не просто так, а добавляется в коллекцию учитывая промежуточные значения класса LevelReadElement, куда заноситься данные из файла.
Структура решения
Для большей зрелищности я добавил текстуры к элементам уровня, а так же подключил через библиотеку Farseer Physics физические свойства элементов - будут падать.
Теперь ближе к файлам, комментарии в коде.
Game1.cs
Только не нужно пропускать статью со словами «Что нам стоить прочитать с файла…», Читать будем с помощью сериализации. Почему? Потому, что нужно сразу привыкать к хорошему или правильному. На консоле будет сложно прочитать данные старыми методами. Можно, но сложно поэтому вспоминаем, а для кого-то учим XML, структуры в ней, и методы сбора информации.
Среда: Microsoft Visual Studio C# 2008 Express Edition, XNA 3.1
Проект, как это будет реализовано?
Имеем категорию levels, файл level_001.xml
В файле нацарапано:
Каждый элемент уровня имеет несколько параметров:0 600 0 Land 100 100 1 Box 400 100 0 Box 600 100 0 Circle 700 100 0 Circle
Position – Координаты объекта
Rotation – Вращение объекта
TypeName – Тип объекта
В программе должен быть описан каждый тип объекта. Я опишу три простейших:
class Land:LevelElement Элемент уровня: земля
class Box:LevelElement Элемент уровня: ящик
class Circle:LevelElement Элемент уровня: круг
Теперь общее представление программной структуры:
Самый главный класс – Игровой менеджер (GameManager), который содержит множество игровых элементов (GameElement). Игровой элемент может быть уровень, игрок, фон, даже элемент уровня в общем все зависит от вашей фантазии. Мы, для краткости, рассмотрим только одну вариацию – уровень (Level). Уровень в свою очередь может содержать множество элементов уровня (LevelElement). Вот они наши вариации Ящик(Box), Земля(Land), Круг(Circle). Создается игровой элемент не просто так, а добавляется в коллекцию учитывая промежуточные значения класса LevelReadElement, куда заноситься данные из файла.
Структура решения
Для большей зрелищности я добавил текстуры к элементам уровня, а так же подключил через библиотеку Farseer Physics физические свойства элементов - будут падать.
Теперь ближе к файлам, комментарии в коде.
Game1.cs
using System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Serialization.GameManagers;
using Serialization.Levels;
namespace Serialization
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
GameManager gameManager;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
gameManager = new GameManager(this, graphics);
gameManager.Add(new Level());
Components.Add(gameManager);
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
}
}
}
GameManager.csusing System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
namespace Serialization.GameManagers
{
///
/// Игровой менеджер
///
class GameManager: DrawableGameComponent
{
///
/// Список игровых элементов
///
List elements;
///
/// Инициализирован ли менеджер
///
public bool IsInitialized;
///
/// Чудо рисователь
///
public SpriteBatch spriteBatch;
///
/// Контент загрузчик
///
public ContentManager contentManager;
///
/// Графическое устройство
///
public GraphicsDeviceManager graphicDeviceManager;
///
/// Физический симулятор
///
public PhysicsSimulator physicsSimulator;
///
/// Конструктор
///
///
Игра///
Менеджер Графического устройстваpublic GameManager(Game game, GraphicsDeviceManager graphicDeviceManager) : base(game)
{
elements = new List();
IsInitialized = false;
this.graphicDeviceManager = graphicDeviceManager;
physicsSimulator = new PhysicsSimulator(new Vector2(0, 1000));
}
///
/// Инициализация
///
public override void Initialize()
{
base.Initialize();
this.IsInitialized = true;
}
///
/// Загрузка данных
///
protected override void LoadContent()
{
base.LoadContent();
contentManager = Game.Content;
spriteBatch = new SpriteBatch(GraphicsDevice);
foreach (GameElement element in elements)
element.LoadContent();
}
///
/// Выгрузка данных
///
protected override void UnloadContent()
{
foreach (GameElement element in elements)
element.UnLoadContent();
base.UnloadContent();
}
///
/// Обновление данных
///
///
Игровое времяpublic override void Update(GameTime gameTime)
{
base.Update(gameTime);
foreach (GameElement element in elements)
element.Update(gameTime);
physicsSimulator.Update(gameTime.ElapsedGameTime.Milliseconds * 0.001f);
}
///
/// Отображение данных
///
///
Игровое времяpublic override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
GraphicsDevice.Clear(Color.Black);
foreach (GameElement element in elements)
element.Draw(gameTime);
}
///
/// Добавление Игрового элемента
///
///
Игровой элементpublic void Add(GameElement element)
{
element.gameManager = this;
if (IsInitialized) element.LoadContent();
elements.Add(element);
}
///
/// Удаление Игрового элемента
///
///
Игровой элементpublic void Remove(GameElement element)
{
element.UnLoadContent();
elements.Remove(element);
}
}
}
GameElement.csusing System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
namespace Serialization.GameManagers
{
///
/// Элемент игры (базовый класс)
///
class GameElement
{
///
/// Принадлежность игровому Менеджеру
///
public GameManager gameManager;
///
/// Конструктор
///
public GameElement()
{ }
///
/// Загрузка данных
///
public virtual void LoadContent()
{ }
///
/// Выгрузка данных
///
public virtual void UnLoadContent()
{ }
///
/// Обновление данных
///
///
Игровое времяpublic virtual void Update(GameTime gameTime)
{ }
///
/// Отображение данных
///
///
Игровое времяpublic virtual void Draw(GameTime gameTime)
{ }
}
}
Level.csusing System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
using Serialization.GameManagers;
using Serialization.Levels.LevelElements;
namespace Serialization.Levels
{
///
/// Уровень
///
class Level:GameElement
{
///
/// Список елементов в уровне
///
public List elements;
///
/// Конструктор
///
public Level()
{
elements = new List();
}
///
/// Загрузка ресурсов элементов в уровне
///
public override void LoadContent()
{
//foreach (LevelElement element in elements)
//element.LoadContent();
/*Box box = new Box();
LevelReadElement r = new LevelReadElement();
r.Position = new Vector2(100, 100);
r.Rotation = 1;
r.TypeName = "Box";
box.level = this;
box.LoadContent(r);
Add(box);
Add(box);*/
ReadLevel("level_001.xml");
}
///
/// Выгрузка элементов уровня
///
public override void UnLoadContent()
{
foreach (LevelElement element in elements)
element.UnLoadContent();
}
///
/// Обновление элементов уровня
///
///
Игровое времяpublic override void Update(GameTime gameTime)
{
foreach (LevelElement element in elements)
element.Update(gameTime);
}
///
/// Отображение Элементов уровня
///
///
Игровое времяpublic override void Draw(GameTime gameTime)
{
foreach (LevelElement element in elements)
element.Draw(gameTime);
}
///
/// Добавление элемента к уровню
///
///
Элемент уровняpublic void Add(LevelElement element)
{
element.level = this;
elements.Add(element);
}
///
/// Удаление элемента из уровня
///
///
Элемент уровняpublic void Remove(LevelElement element)
{
element.UnLoadContent();
elements.Remove(element);
}
///
/// Загрузка элементов из файла
///
///
Название файла (путь к файлу)public void ReadLevel(string path)
{
List list = new List();
string filename = System.IO.Path.Combine("levels", path);
FileStream stream = File.Open(filename, FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(List));
list = (List)serializer.Deserialize(stream);
stream.Close();
foreach (LevelReadElement element in list)
{
if (element.TypeName == "Box")
{
Box levEl = new Box();
levEl.level = this;
levEl.LoadContent(element);
Add(levEl);
}
else
if (element.TypeName == "Circle")
{
Circle levEl = new Circle();
levEl.level = this;
levEl.LoadContent(element);
Add(levEl);
}
else
if (element.TypeName == "Land")
{
Land levEl = new Land();
levEl.level = this;
levEl.LoadContent(element);
Add(levEl);
}
}
}
///
/// Запись элементов в файл
///
///
Название файла (путь к файлу)public void SaveLevel(string path)
{
List list = new List();
LevelReadElement el;
foreach (LevelElement element in elements)
{
el = new LevelReadElement();
el.TypeName = element.TypeName;
el.Position = element.body[0].Position;
el.Rotation = element.body[0].Rotation;
list.Add(el);
}
string filename = System.IO.Path.Combine("levels", path);
FileStream stream = File.Open(filename, FileMode.OpenOrCreate);
XmlSerializer serializer = new XmlSerializer(typeof(List));
serializer.Serialize(stream, list);
stream.Close();
}
}
}
LevelElement.csusing System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
namespace Serialization.Levels
{
///
/// Один элемент уровня
///
class LevelElement
{
///
/// Ссылка на принадлежность уровню
///
public Level level;
///
/// Список тел элемента
///
public List body;
///
/// Список геометрий
///
public List geom;
///
/// Текстовое название элемента
///
public string TypeName;
///
/// Констуктор
///
public LevelElement()
{
TypeName = "none";
body = new List();
geom = new List();
}
///
/// Загрузка данных
///
public virtual void LoadContent(LevelReadElement element)
{ }
///
/// Выгрузка данных
///
public virtual void UnLoadContent()
{ }
///
/// Обновление данных
///
///
Игровое времяpublic virtual void Update(GameTime gameTime)
{ }
///
/// Отображение данных
///
///
Игровое времяpublic virtual void Draw(GameTime gameTime)
{ }
}
}
LevelReadElement.csusing System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
namespace Serialization.Levels
{
///
/// Читаемый элемент уровня из файла
///
[Serializable]
public class LevelReadElement
{
///
/// Координатная позиция
///
public Vector2 Position;
///
/// Вращение
///
public float Rotation;
///
/// Текстовое название елемента
///
public string TypeName;
}
}
Box.csusing System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
namespace Serialization.Levels.LevelElements
{
///
/// Элемент уровня: ящик
///
class Box:LevelElement
{
///
/// Тестура ящика
///
public Texture2D texture;
///
/// Масса тела
///
public float Mass=10;
///
/// Конструктор
///
public Box()
: base()
{ }
///
/// Загрузка данных
///
///
Начальные параметрыpublic override void LoadContent(LevelReadElement element)
{
texture = level.gameManager.contentManager.Load("box");
body.Add(BodyFactory.Instance.CreateRectangleBody(level.gameManager.physicsSimulator, texture.Width, texture.Height, Mass));
geom.Add(GeomFactory.Instance.CreateRectangleGeom(level.gameManager.physicsSimulator, body[0], texture.Width, texture.Height));
body[0].Position = element.Position;
body[0].Rotation = element.Rotation;
TypeName = element.TypeName;
}
///
/// Отображение данных
///
///
Игровое времяpublic override void Draw(GameTime gameTime)
{
level.gameManager.spriteBatch.Begin();
level.gameManager.spriteBatch.Draw(texture, body[0].Position, null, Color.White, body[0].Rotation, new Vector2(texture.Width / 2, texture.Height / 2), 1, SpriteEffects.None, 0);
level.gameManager.spriteBatch.End();
}
}
}
Circle.csusing System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
namespace Serialization.Levels.LevelElements
{
///
/// Элемент уровня: круг
///
class Circle:LevelElement
{
///
/// Тестура круга
///
public Texture2D texture;
///
/// Масса тела
///
public float Mass = 2;
///
/// Конструктор
///
public Circle()
: base()
{ }
///
/// Загрузка данных
///
///
Начальные параметрыpublic override void LoadContent(LevelReadElement element)
{
texture = level.gameManager.contentManager.Load("circle");
body.Add(BodyFactory.Instance.CreateCircleBody(level.gameManager.physicsSimulator,texture.Width/2, Mass));
geom.Add(GeomFactory.Instance.CreateCircleGeom(level.gameManager.physicsSimulator, body[0], texture.Width/2, 100));
body[0].Position = element.Position;
body[0].Rotation = element.Rotation;
TypeName = element.TypeName;
}
///
/// Отображение данных
///
///
Игровое времяpublic override void Draw(GameTime gameTime)
{
level.gameManager.spriteBatch.Begin();
level.gameManager.spriteBatch.Draw(texture, body[0].Position, null, Color.White, body[0].Rotation, new Vector2(texture.Width / 2, texture.Height / 2), 1, SpriteEffects.None, 0);
level.gameManager.spriteBatch.End();
}
}
}
Land.csusing System;
using System.Collections.Generic;
using System.Linq;
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.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Interfaces;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;
namespace Serialization.Levels.LevelElements
{
///
/// Элемент уровня: земля
///
class Land:LevelElement
{
///
/// Тестура земли
///
public Texture2D texture;
///
/// Масса тела
///
public float Mass=1000;
///
/// Конструктор
///
public Land()
: base()
{ }
///
/// Загрузка данных
///
///
Начальные параметрыpublic override void LoadContent(LevelReadElement element)
{
texture = level.gameManager.contentManager.Load("land");
body.Add(BodyFactory.Instance.CreateRectangleBody(level.gameManager.physicsSimulator, texture.Width, texture.Height, Mass));
geom.Add(GeomFactory.Instance.CreateRectangleGeom(level.gameManager.physicsSimulator, body[0], texture.Width, texture.Height));
body[0].Position = element.Position;
body[0].Rotation = element.Rotation;
body[0].IsStatic = true;
TypeName = element.TypeName;
}
///
/// Отображение данных
///
///
Игровое времяpublic override void Draw(GameTime gameTime)
{
level.gameManager.spriteBatch.Begin();
level.gameManager.spriteBatch.Draw(texture, body[0].Position, null, Color.White, body[0].Rotation, new Vector2(texture.Width / 2, texture.Height / 2), 1, SpriteEffects.None, 0);
level.gameManager.spriteBatch.End();
}
}
}
Весь файл проэкта.



Комментарии
Отправить комментарий