Я и некоторые другие инсайдеры из Microsoft начали работать над игрой для Windows Phone 7 (конечно, на XNA). После некоторого мозгового штурма мы решили разработать игру, которая будет чем-то напоминать игру Worms старого стиля, но в то же время мы подумали об ее адаптации к зимнему сезону. Так что это будет Snowfight — дети, бросающие снежки друг в друга.
Проект размещен на CodePlex, и вы можете проверить его здесь .
Задачи были разделены между членами команды, и каждому из нас был назначен компонент. Сейчас я занимаюсь разработкой системы частиц и решил поделиться своим подходом к проблеме.
ПРИМЕЧАНИЕ. Это очень базовая реализация, и я буду расширять ее по пути.
Прежде всего, вот структура, которую я решил использовать:
Суперкласс здесь — это менеджер частиц, который может управлять несколькими системами. Система частиц может содержать несколько частиц, и в то же время каждая частица может иметь индивидуальные свойства, которые определяют ее поведение.
Для базового «скелетона» я создал простую структуру классов, разделенную на папки:
Если вы заинтересованы в реальной диаграмме классов, прежде чем я объясню каждый класс в отдельности, вот он:
Основной Частицей здесь является фундаментальный атом — сама частица (не требует объяснений). Его структура определяется этими свойствами:
public class BasicParticle
{
public Vector2 CurrentPosition { get; set; }
public Vector2 Velocity { get; set; }
public float Size { get; set; }
}
Обратите внимание, что скорость также определяется вектором в двумерном пространстве, который определяет, насколько быстро частица будет двигаться по осям X и Y.
Теперь, когда есть основная частица, пришло время объединить их в систему. Система будет непосредственно управлять частицами, которые будут удерживаться в ней. Я начал с набора основных свойств:
public class ParticleSystem
{
public int SystemID { get; set; }
public Texture2D Texture { get; set; }
public List<BasicParticle> Particles { get; set; }
private SpriteBatch SpriteManager {get; set;}
private Vector2 CenterPosition { get; set; }
}
SystemID — это уникальный идентификатор, который в дальнейшем используется для идентификации системы частиц в менеджере частиц. В игре могут одновременно работать системы с несколькими частицами — например, может идти снег, и в то же время снежный ком падает на дерево, разбивающееся на несколько частей снега. Как вы можете видеть, в одной системе сейчас я предполагаю, что все частицы будут иметь одинаковую текстуру. Позже, это, безусловно, возможно расширить, но для «элементарного» компонента, это нормально, чтобы положить все в одно ведро.
Теперь, учитывая тот факт, что частицы могут иметь разные размеры и свойства, я добавляю их в общую коллекцию, которая будет использоваться для рисования каждого из них. Переданный экземпляр SpriteBatch будет фактически представлять механизм рисования, а вектор CenterPosition будет представлять, где именно система будет размещена на экране.
В конструкторе класса ParticleSystem я прошу пользователя указать некоторые детали:
- Что будет SpriteBatch?
- Какой идентификатор системы?
- Где разместить систему изначально?
- Какую текстуру наносить на каждую частицу?
Вот как это выглядит на самом деле:
public ParticleSystem(SpriteBatch SpriteManager, int SystemID, Vector2 CenterPosition, Texture2D Texture)
{
this.SystemID = SystemID;
this.Texture = Texture;
this.Particles = new List<BasicParticle>();
this.SpriteManager = SpriteManager;
this.CenterPosition = CenterPosition;
}
Классная вещь. Для целей тестирования давайте наполним коллекцию рядом базовых частиц прямо здесь, в конструкторе:
for (int i = 0; i < 200; i++)
{
BasicParticle particle = new BasicParticle();
particle.CurrentPosition = CenterPosition;
particle.Size = new Random().Next(128);
float randomX = (float)new Random().NextDouble() - 0.5f;
float randomY = (float)new Random().NextDouble() - 0.5f;
particle.Velocity = new Vector2(randomX,randomY);
Particles.Add(particle);
}
У меня будет ровно 200 частиц. Размер генерируется случайным образом, так же как и скорость. Обратите внимание на тот факт, что скорость также может быть отрицательной — я не хочу, чтобы все частицы двигались в одном направлении.
Как только основание установлено, пришло время рисовать частицы. На данный момент я решил оставить эту задачу в менеджере, который содержит эти:
public void StartDrawing()
{
SpriteManager.Begin();
foreach (BasicParticle particle in Particles)
{
SpriteManager.Draw(Texture, new Rectangle((int)particle.CurrentPosition.X, (int)particle.CurrentPosition.Y,
(int)particle.Size, (int)particle.Size), Color.White);
particle.CurrentPosition += particle.Velocity;
}
SpriteManager.End();
}
Одним из недостатков здесь является то, что я не определяю мертвые частицы. Таким образом, даже если одна частица покидает видимую часть экрана, теоретически она все равно будет двигаться. Когда я собираюсь расширить эту часть, я позабочусь, чтобы нейтрализовать эти частицы — либо обновить их, либо удалить из коллекции.
Менеджер частиц (определенный классом ParticleManager) на данный момент является самым простым элементом в движке:
public class ParticleManager
{
private List<ParticleSystem> Systems { get; set; }
public void Initialize()
{
Systems = new List<ParticleSystem>();
}
public void AddSystem(ParticleSystem SystemToAdd)
{
Systems.Add(SystemToAdd);
}
public ParticleSystem GetSystem(int SystemID)
{
return (from c in Systems where c.SystemID == SystemID select c).FirstOrDefault();
}
}
Все, что он может сделать в данный момент, это инициализировать себя, добавить новую систему в коллекцию и вернуть систему, в которой выполняется поиск по ее идентификатору. Это было бы основной функциональностью.
Теперь, если вы хотите поэкспериментировать с этим движком, попробуйте сделать это.
1) Объявите экземпляр Texture2D и ParticleManager в основном классе:
2) Загрузите текстуру в методе LoadContent:
ПРИМЕЧАНИЕ: здесь я использую простое растровое изображение 16 × 16 красной формы. Вы можете загрузить свою собственную текстуру.
3) В том же методе инициализируйте экземпляр ParticleManager и добавьте в него новую систему:
4) В методе Draw найдите нужную систему и начните рисовать ее:
Теперь, если вы запустите игру (либо на эмуляторе, либо на телефоне), вы должны увидеть результаты, подобные этим:
Из-за не очень случайного механизма формы летят во многих направлениях, но не точно вокруг центра, а скорее по диагональным векторам. Это изменится, как только я осуществлю генерацию вектора на основе триггерных функций, но сейчас (чтобы продемонстрировать возможные возможности) это то, что у нас есть.







