Game Development

индекс
199,34

Дружим XNA и WPF

Вначале отвечу на вопрос «Зачем их дружить». Ответ прост — WPF хорош для пользовательских интерфейсов, XNA для сложной 3D графики и если вы делаете клиентское приложение со сложным интерфейсом и 3D элементами в нем, то связка XNA & WPF как раз для вас.
Статью я буду иллюстрировать на примере простенького медиаплеера, который я сейчас пишу.

Итак, у нас есть скроллер обложек написанный на XNA. Выглядит он так:



А еще у нас есть WPF проект будущего медиаплеера, вот его скриншот:



Наша задача состоит в том, чтобы поместить первое во второе. Поступим также, как поступали ранее с приложениями на Windows Forms: передадим в конструктор класса Game хендл на элемент управления, поверх которого мы будем рисовать. Но есть одна небольшая проблема: в WPF хендлами обладают только окна. Впрочем, эта проблема легко решаема: добавим в окно элемент управления WindowsFormsHost и поместим в него панель (Panel) из пространства имен System.Windows.Forms. И вот хендл этой панели мы и передадим в конструктор игры. Итак, действуем по порядку:
  1. Объединим XNA и WPF проекты в одном решении
  2. Удаляем у XNA проекта файл Program.cs и в свойствах проекта меняем тип с Windows приложения на библиотеку классов.
  3. Добавим Microsoft.Xna.Framework.Game в References WPF проекта
  4. Добавим в объявление конструктора игры параметр IntPtr handle
  5. В конструкторе формы WPF создадим экземпляр класса игры и передаем ему хендл на панель и вызовем у игры метод Run() в отдельном потоке
  6. Во время инициализации устройства присвоим наш хендл

Поясню последний пункт:
IntPtr Handle;
public MainGame(IntPtr handle)
{
  Handle = handle;
  graphics = new GraphicsDeviceManager(this);
  graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(PreparingDeviceSettings);
}

public void PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
  e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = Handle;
}


* This source code was highlighted with Source Code Highlighter.


Отлично, наша «игра» отрисовывается в отведенную для нее панель, но есть несколько пунктов, по которым меня не устраивает данное решение:
  • При запуске приложения параллельно создается какое-то левое окно. Его конечно можно скрыть, но все равно это нехорошо.
  • При изменении размеров формы у нас происходит тупое растягивание/сжатие картинки. Хотелось бы реинициализировать устройство для обеспечения наилучшего качества картинки.
  • На мой взгляд, самое главное: игра и приложение живут в разных потоках. Это приводит к ошибкам при попытке работы с WPF-контролами из XNA кода.
  • Панель предательски моргает при изменении ее размеров.

Будем решать эти проблемы. Для этого я предлагаю прекратить использование класса игры и написать небольшой менеджер устройства. Нам придется вручную инициализировать устройство, по таймеру перерисовывать сцену, откликаться на изменение размеров контрола и его уничтожение. В менеджер мы передаем контрол, на котором будем рисовать, подписываемся на события ресайза и отрисовки контрола, создаем устройство и таймер, по которому мы будем вызывать инициировать события Update и Draw. Вы можете посмотреть исходный код графического менеджера здесь или в прилагаемом проекте. Нам осталось лишь разобраться с панелью. Оказывается нужно лишь применить соответствующие стили и панель перестанет моргать. Неудобство лишь в том, что модификатор доступа метода SetStyle — protected. Ну ничего, унаследуемся от панели и в конструкторе применим нужные нам стили. Исходный код оптимизированной панели прост до безобразия:

public class OptimizedPanel : Panel
{
  public OptimizedPanel()
   : base()
  {
   this.SetStyle(ControlStyles.UserPaint, true);
   this.SetStyle(ControlStyles.Opaque, true);
  }
}

* This source code was highlighted with Source Code Highlighter.


Итак, у нас все готово, запускаем проект и видим, что все работает как надо: панелька не моргает, корректно меняются размеры, приложение не открывает дополнительных окон.
Здесь вы можете скачать начальный проект приложения со связкой WPF & XNA. Ну а вот что получилось у меня:

+26
6 августа 2010, 17:20
43

комментарии (20)

+2
olegafx #
Где-то я уже такое видел…
+4
Logonoff #
Привет, не знал что ты на хабре есть. В твиттере моем ты это видел, я туда постил идеи с неделю-две назад)
–2
Mort #
Блин, я про iTunes подумал :)
+3
DiverOfDark #
Спасибо за полезную статью!
+1
Hoobert #
Интересно было бы посмотреть обратный вариант. Добавить в игру на XNA интерфейс на WPF. Релизовать в XNA с нуля списки с прокруткой, слайдеры и других контролов с поддержкой скинов нетривиальная задача. Однако, подозреваю, что встроить WPF в XNA практически невозможно :).
+1
Logonoff #
Видел использование контролов WinForms в играх на XNA. Плюс есть несколько готовых библиотек контролов на XNA (например XNA Simple Gui
+2
Vladek #
А может вам FluidKit попробовать? Там есть подходящий контрол ElementFlow.
+1
Logonoff #
Спасибо, поразбираю на досуге)
+1
Sergun #
1) В WPF уже есть свой механизм рендеринга 3D — родной и понятный. В большом классе задач его может быть достаточно.
2) В вашем случае при использовании WindowsFormsHost вы получаете проблему «разделения воздушного пространства». Т.е. вы элементарно не сможете положить WPF-кнопочку поверх рендера XNA. И как правило это может создавать огромные неприятности.

Ну а так — забавно, мы аналогичным образом встраивали рендер Direct3D в приложение на WPF.
0
Logonoff #
1. Знаком с ним, но все-же бывают случаи когда нужно что-то помощнее)
2. Тут можно схитрить — делать рендер в невидимое окно, получать с него картинку и пихать в Image.
+1
Sergun #
2. Тогда тормозить будет это раз, а во-вторых проблема с вводом. Проще тут нативный 3D-движок от WPF использовать.
0
Logonoff #
Всякое бывает, не настаиваю на единственноверности своего решения)
–1
Sergun #
Я думаю идеальный способ как подружить WPF с XNA трудно найти.
Но все равно спасибо за статью.
+1
make_luv #
Когда плеер скачать можно будет?
+1
Logonoff #
Я сейчас только начал изучать WPF и этот медиаплеер скорее тестовый полигон, так что не скоро(
+1
make_luv #
Тем не менее ждем)
+1
Madcyree #
msdn.microsoft.com/en-us/magazine/cc163328.aspx — относительно threading в WPF. Поможет побороть вам некоторые проблемы (такие как обращение к WPF контролам из другого потока например)
+1
Logonoff #
Спасибо! Интересный материал.
+1
Agent_Smith #
Вот еще есть статья по интеграции XNA 4.0 в WPF:
blogs.msdn.com/b/nicgrave/archive/2010/07/25/rendering-with-xna-framework-4-0-inside-of-a-wpf-application.aspx
0
Logonoff #
Ну да, тут по сути то, что я предложил во второй части этого коммента: habrahabr.ru/blogs/XNA/101165/#comment_3132979

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.