Опубликован: 05.08.2010 | Уровень: специалист | Доступ: свободно
Самостоятельная работа 3:

Windows Forms и XNA 3.0

Аннотация: В этой работе мы изучим совместное применение технологий Windows Forms и XNA для построения оконных приложений с обычным графическим интерфейсом GUI и трехмерным ускорителем видеокарты. В качестве упражнений мы построим приложения разного типа и познакомимся с тонкостями совместного применения указанных средств.

Все необходимые для выполнения данной работы программы можно найти в прилагаемом каталоге.

Работа выполнена на основе материалов С.А. Гайдукова, приведенных в источнике http://www.intuit.ru/department/se/intxna

Упражнение 1. Вывод графической информации XNA на стандартную форму

Создание заготовки приложения

  • Создайте новое решение и новый проект командой File/New/Project, заполнив окно мастера так

  • Переименуйте файл формы Form1.cs в MainForm.cs
  • Выделите форму в конструкторе форм и в панели Properties в свойстве Text установите заголовок окна " Упражнение 1 "
  • Командой Project/Add Reference вызовите окно добавления ссылок и на вкладке .NET выберите библиотечную сборку Microsoft.Xna.Framework.dll версии 3.0


В этой библиотеке находится множество компонентов 3D -графики, размещенные в нескольких пространствах имен, в том числе в интересующем нас пространстве имен Microsoft.Xna.Framework.Graphics


Прежде всего воспользуемся классом GraphicsDevice, инкапсулирующим средства управления графическим устройством - видеокартой компьютера. Конструктор этого класса описывается так

public GraphicsDevice(Microsoft.Xna.Framework.Graphics.GraphicsAdapter adapter,
  Microsoft.Xna.Framework.Graphics.DeviceType deviceType, System.IntPtr renderWindowHandle,
Microsoft.Xna.Framework.Graphics.PresentationParameters presentationParameters)
  • adapter - статическое свойство класса GraphicsAdapter, соответствующее выбранной для работы видеокарте. Современные компьютеры содержат несколько видеокарт, поэтому проще всего выбрать свойства GraphicsAdapter.DefaultAdapter
  • deviceType - тип устройства, задаваемый перечислением DeviceType, которое имеет значения
    • Hardware - реальное аппаратное устройство
    • Reference - реальное устройство эмулируется средствами DirectX SDK. Обычно используется для эмуляции функциональности, не поддерживаемой текущей видеокартой. Такую эмуляцию реально использовать лишь в отладочных целях, так как производительность приложений в этом режиме редко превышает один кадр в секунду
    • NullReference - игнорируются все поступающие команды и используется для оценки производительности приложения с имитацией бесконечно быстрой видеокарты
  • renderWindowHandle - дескриптор окна или элемента управления (например, Panel ), который будет использоваться для вывода графических изображений
  • presentationParameters - экземпляр класса PresentationParameters, свойства которого определяют настройки представления изображения на экране

Для создания и настройки графического устройства нужно объявить в классе MainForm поле, зарегистрировать для события Load формы обработчик, в котором создать экземпляр устройства и настроить его с помощью свойств соответствующих объектов.

  • Вызовите контекстное меню формы MainForm, щелкнув на ней правой кнопкой мыши, и выполните команду View Code
  • Подключите иснструкцией using к файлу MainForm.cs пространство имен Microsoft.Xna.Framework.Graphics
  • Объявите в классе MainForm поле типа GraphicsDevice с именем device
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Graphics;
    
namespace Application1
{
    public partial class MainForm : Form
    {
        // Объявим поле графического устройства для видимости в методах
        GraphicsDevice device;
    
        public MainForm()
        {
            InitializeComponent();
    
            // Подпишемся на событие Load формы 
            this.Load += new EventHandler(MainForm_Load);
        }
    
        void MainForm_Load(object sender, EventArgs e)
        {
            // Создаем объект представления для настройки графического устройства
            PresentationParameters presentParams = new PresentationParameters();
            // Настраиваем объект представления через его свойства
            presentParams.IsFullScreen = false; // Включаем оконный режим
            presentParams.BackBufferCount = 1;  // Включаем задний буфер 
                                                // для двойной буферизации 
            // Переключение переднего и заднего буферов 
            // должно осуществляться с максимальной эффективностью
            presentParams.SwapEffect = SwapEffect.Discard;
            // Устанавливаем размеры заднего буфера по клиентской области окна формы
            presentParams.BackBufferWidth = this.ClientSize.Width;
            presentParams.BackBufferHeight = this.ClientSize.Height;
    
            // Создадим графическое устройство с заданными настройками
            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,
                this.Handle, presentParams);
        }
    }
}

Прежде, чем создать объект графического устройства, мы создаем вспомогательный объект presentParams, через свойства которого устанавливаем настройки для будущего устройства. Размеры заднего буфера можно не задавать, поскольку они по умолчанию в объекте presentParams установлены по умолчанию в значение 0, а это значит, что устройство device само вычислит их. Передним буфером считается экран, а задний буфер добавляется для устранения мерцания, в итоге при добавлении одного заднего буфера обеспечивается режим двойной буферизации.

Таким образом, в приведенном коде мы создали графическое устройство со следующими характеристиками:

  • Устройство будет использовать видеоадаптер по умолчанию
  • Устройство будет аппаратным
  • Вывод будет осуществляться в оконном режиме на клиентскую поверхность текущей формы
  • Обработка вершин будет осуществляться средствами GPU - графического процессора видеокарты с двойной буферизацией

Очистка буферов графического устройства

Теперь нужно прописать код очистки графических буферов перед рисованием нового изображения. Для этого существует метод Clear() графического устройства, имеющий несколько перегрузок. Пока воспользуемся упрощенной перегрузкой этого метода, которая очищает только экранный буфер заданным цветом:

public void Clear(Microsoft.Xna.Framework.Graphics.Color color)

Куда же вставить вызов функции очистки графического устройства? Когда происходит разрушение экрана за счет сворачивания окна или заслонения другими окнами, то при необходимости его восстановления операционная система генерирует событие Paint унаследованного формой класса Control и мы можем либо подписать обработчик на это событие и туда вставить очистку, либо перекрыть виртуальный метод диспетчеризации OnPaint() формы и в него вставить вызов очистки. Воспользуемся вторым вариантом...

  • В классе MainForm как член класса наберите ключевое слово override, после введения символа пробела в открывшемся окне подсказчика кода IntalliSense выберите метод диспетчеризации OnPaint() и завершите набор нажатием клавиши Enter
  • В перекрытом методе диспетчеризации OnPaint() введите код вызова метода очистки устройства
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Graphics;
    
namespace Application1
{
    public partial class MainForm : Form
    {
        // Объявим поле графического устройства для видимости в методах
        GraphicsDevice device;
    
        public MainForm()
        {
            InitializeComponent();
    
            // Подпишемся на событие Load формы 
            this.Load += new EventHandler(MainForm_Load);
        }
    
        void MainForm_Load(object sender, EventArgs e)
        {
            // Создаем объект представления для настройки графического устройства
            PresentationParameters presentParams = new PresentationParameters();
            // Настраиваем объект представления через его свойства
            presentParams.IsFullScreen = false; // Включаем оконный режим
            presentParams.BackBufferCount = 1;  // Включаем задний буфер 
                                                // для двойной буферизации 
            // Переключение переднего и заднего буферов 
            // должно осуществляться с максимальной эффективностью
            presentParams.SwapEffect = SwapEffect.Discard;
            // Устанавливаем размеры заднего буфера по клиентской области окна формы
            presentParams.BackBufferWidth = this.ClientSize.Width;
            presentParams.BackBufferHeight = this.ClientSize.Height;
    
            // Создадим графическое устройство с заданными настройками
            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,
                this.Handle, presentParams);
        }
    
        protected override void OnPaint(PaintEventArgs e)
        {
            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);
    
            base.OnPaint(e);
        }
    }
}

Обратите внимание, что мы были вынуждены прописать полное имя структуры Color во избежании конфликта имен, поскольку точно такая же структура находится в подключенном к файлу пространстве имен System.Drawing.

При завершении работы нашего приложения сборщик мусора GC среды исполнения CLR попытается автоматически освободить графическое устройство. Но сделать это корректно ему не удастся. Дело в том, классы XNA являются тонкой оболочкой API DirectX, а сам DirectX освобождает только те объекты, которые были созданы в его потоке. Сборщик мусора же для своей работы создает новый отдельный поток, который не сможет корректно удалить объекты DirectX, в том числе созданный нами объект device. В связи с этим нам нужно самим позаботиться об удалении таких критичных объектов в нашем приложении.

  • В конструкторе MainForm() одноименного класса подпишитесь на обработчик события FormClosed и заполните обработчик кодом удаления устройства device
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Graphics;
    
namespace Application1
{
    public partial class MainForm : Form
    {
        // Объявим поле графического устройства для видимости в методах
        GraphicsDevice device;
    
        public MainForm()
        {
            InitializeComponent();
    
            // Подпишемся на событие Load формы 
            this.Load += new EventHandler(MainForm_Load);
    
            // Попишемся на событие FormClosed формы
            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);
        }
    
        void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            //  Удаляем (освобождаем) устройство
            device.Dispose();
            // На всякий случай присваиваем ссылке на устройство значение null 
            device = null;        
        }
    
        void MainForm_Load(object sender, EventArgs e)
        {
            // Создаем объект представления для настройки графического устройства
            PresentationParameters presentParams = new PresentationParameters();
            // Настраиваем объект представления через его свойства
            presentParams.IsFullScreen = false; // Включаем оконный режим
            presentParams.BackBufferCount = 1;  // Включаем задний буфер 
                                                // для двойной буферизации 
            // Переключение переднего и заднего буферов 
            // должно осуществляться с максимальной эффективностью
            presentParams.SwapEffect = SwapEffect.Discard;
            // Устанавливаем размеры заднего буфера по клиентской области окна формы
            presentParams.BackBufferWidth = this.ClientSize.Width;
            presentParams.BackBufferHeight = this.ClientSize.Height;
    
            // Создадим графическое устройство с заданными настройками
            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,
                this.Handle, presentParams);
        }
    
        protected override void OnPaint(PaintEventArgs e)
        {
            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);
    
            base.OnPaint(e);
        }
    }
}
  • Запустите приложение на выполнение

Пока ничего не произошло и мы не видим графического окна в клиентской области формы, хотя событие Paint исправно срабатывает, в чем можно убедиться, если установить точку останова (Breakpoint) в перекрытом методе диспетчеризации OnPaint().


Всe дело в том, что мы используем двойную буферизацию, то есть наше приложение выполняет все графические построения в заднем невидимом вспомогательном буфере. После окончания визуализации необходимо скопировать информацию из этого вспомогательного буфера на форму. Эту операцию выполняет метод Present() класса GraphicsDevice.

  • Добавьте вызов метода Present() в перекрытый метод диспетчеризации OnPaint()
protected override void OnPaint(PaintEventArgs e)
{
    device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);
    
    // Копируем задний буфер на экран
    device.Present();
    
    base.OnPaint(e);
}
  • Снова запустите программу на выполнение - на экране появится окно, клиентская область которого закрашена синим цветом, что нам и требовалось


Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?

Юрий Макушин
Юрий Макушин
Россия, Москва, РЭА им. Плеханова, 2004