Опубликован: 28.04.2009 | Доступ: свободный | Студентов: 1838 / 107 | Оценка: 4.36 / 4.40 | Длительность: 16:40:00
Специальности: Программист
Лекция 4:

Хранитель экрана

Аннотация: В данной лекции осуществляется проектирование хранителя экрана средствами XNA Framework. Также описываются свойства окна Display Properties и его использование при создании экранного хранителя.

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

Что такое хранитель экрана? Это приложение, автоматически запускаемое Windows по истечению определенного срока бездействия компьютера. Изначально использование хранителя экрана было обусловлено сугубо практическими соображения: первые мониторы обладали весьма ограниченным ресурсом, в результате чего длительное отображение статичного изображения приводило к выгоранию люминофора в определенных местах и, соответственно, "запоминанию" монитором этого изображения1Например, длительное отображение панелей Norton Commander приводило к выжиганию в люминофоре монитора "изображения" данных панелей . Современные мониторы обладают значительно более продолжительным сроком службы, поэтому в настоящее время хранитель экрана скорее выполняет декоративную роль.

Хранитель экрана

увеличить изображение
Рис. 4.1. Хранитель экрана

Технически хранитель экрана представляет собой исполняемый exe -файл с расширением .scr. Запуская хранитель экрана, Windows предает ему один из трех параметров командной строки, перечисленных в таблице 4.1. Разработчики C++ обычно создают хранители экрана с использованием библиотеки Scrnsave.lib (или Scrnsavw.lib ), которые самостоятельно обрабатывают параметры командной строки и реализуют завершение работы хранителя экрана при перемещении мыши, нажатии клавиши и т.п. Фактически разработчику необходимо лишь переопределить обработчик события WM_PAINT и реализовать код диалогового окна.

Примечание

К справедливости следует отметить, что реализация диалогового окна средствами Win32 API является далеко не самой приятной задачей.

Таблица 4.1. Параметры командой строки хранителя экрана
Параметр командной строки Описание
/s Запускает хранитель экрана в обычном режиме
/c{:n} Показывает диалоговое окно конфигурации хранителя экрана. n – дескриптор родительского диалогового окна Display Properties
/p {n} Запускает хранитель экрана в окне предварительного просмотра с дескриптором n.
Нет параметров Показывает диалоговое окно конфигурации хранителя экрана

В настоящее время .NET Framework 2.0 не содержит аналога библиотеки Scrnsave.lib, поэтому нам придется реализовывать всю функциональность самим. Это далеко не такая тривиальная задача, как хотелось бы, но и отнюдь не архисложная. Так что в путь!

Примечание

В состав Visual Studio 2005 Pro входит Screen Saver Starter Kit, позволяющий создать проект готового хранителя экрана буквально одним щелчком мыши. Но, к сожалению, Screen Saver Starter Kit имеет ряд недоделок2Например, не реализована возможность отображения хранителя экрана в окне предварительного просмотра и даже ошибок, поэтому мы не будем его использовать.

4.1. Реализация вращающегося диска

Учитывая сложность стоящей перед нами задачи, мы разобьем ее на несколько этапов. Начнем мы с визуализации вращающегося цветного диска. Создайте новый проект Windows Forms, установив в диалоговом окне New Project флажок Create directory for solution (в последствие мы добавим в решение проект инсталлятора). Нам придется выполнять визуализацию как в полноэкранном режиме, так и в окне предварительного просмотра, поэтому весь код визуализации будет логично разместить в отдельном классе. Добавьте в проект новый класс Firework (Project | Add Class).

Внутри класса Firework могут возникать критические исключения (например, при инициализации графического устройства). Обрабатывать в коде, использующем класс Firework, разношерстные исключения вроде ошибки создания графического устройства дольно утомительно, поэтому класс Firework будет перехватывать низкоуровневые исключения и генерировать свое собственное исключение FireworkException с ясным описанием причины возникновения ошибки.

class FireworkException : Exception 
{
public FireworkException(string message) : base(message) 
{ 
} 
}
Листинг 4.1.

Для визуализации круга классу Firework необходимо загрузить и скомпилировать эффект. Только вот где его хранить? Типовой хранитель экрана обычно состоит из одного файла с расширением .scr, поэтому использование дополнительного fx -файла является далеко не самой лучшей идеей. К счастью Visual Studio позволяет легко внедрить fx -файл непосредственно в exe -файл. Для этого включите в проект файл ColorFill.fx, используемый во всех примерах этой лекции, и присвойте свойству Build Action этого файла значение Embedded Resource (рисунок 4.2).

 Внедрение fx-файла в сборку

Рис. 4.2. Внедрение fx-файла в сборку

Основные фрагменты кода класса Firework приведены в листинге 4.2. Полный текст примера находится в example.zip в каталоге Examples\Ch04\Ex01.

class Firework : IDisposable
{
// Место расположение ресурса с кодом файла эффекта
const string effectFileName = "GSP.XNA.Book.Ch04.Ex01.Data.ColorFill.fx";
// Количество сегментов в диске
const int slices = 64;
// Угловая скорость вращения диска (радиан в секунду)
public const float diskSpeed = 3.0f; 
// Радиус диска
public const float diskRadius = 0.015f;
// Текущий угол поворота float diskAngle = 0;
// Дескриптор окна, на которое будет осуществляться визуализация
...
// Конструктор. Принимает:
// hWnd – дескриптор окна в котором осуществляется визуализация.
// scintillaSize – размер искр (пока игнорируется)
// scintillaInterval – интервал между искрами (пока игнорируется)
public Firework(IntPtr hWnd, float scintillaSize, 
float scintillaInterval)
{ 

// Сохраняем дескриптор окна this.hWnd = hWnd;
presentParams = new PresentationParameters();
presentParams.BackBufferCount = 1;
presentParams.SwapEffect = SwapEffect.Discard; 
presentParams.PresentationInterval = PresentInterval.One;
try {
GraphicsDeviceCapabilities caps = GraphicsAdapter.
DefaultAdapter.GetCapabilities(DeviceType.Hardware);
 CreateOptions options = CreateOptions.SingleThreaded; if
 (caps.DeviceCapabilities.SupportsHardwareTransformAndLight)
options |= CreateOptions.HardwareVertexProcessing; else
options |= CreateOptions.SoftwareVertexProcessing;
device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, 
DeviceType.Hardware, 
hWnd, options, presentParams);
} 
// Это исключение обычно генерируется при отключенном аппаратном 
ускорении в Display 
// Properties
catch (DeviceNotSupportedException)
{ 
// Перехватываем исключение и генерируем 
собственное исключение с более понятным описанием 
// проблемы
throw new FireworkException("Не могу создать устройство Direct3D");
}
decl = new VertexDeclaration(device, VertexPositionColor.VertexElements);
 diskVertices = new VertexPositionColor[slices + 2];
// Загружаем эффект из ресурсов сборки
Stream effectStream = Assembly.GetExecutingAssembly().
GetManifestResourceStream( 
4> effectFileName) ; 
// Выполняем компиляцию эффекта
CompiledEffect compiledEffect = Effect.CompileEffectFromFile
(effectStream, null, 
null, CompilerOptions.None, TargetPlatform.Windows);
if (!compiledEffect.Success)
throw new FireworkException(String.Format( 
"Ошибка при компиляции эффекта: \r\n{0}", compiledEffect.
ErrorsAndWarnings));
effect = new Effect(device, compiledEffect.GetEffectCode(), 
CompilerOptions.NotCloneable, null);
if (!effect.CurrentTechnique.Validate())
throw new FireworkException(String.Format
("Ошибка при валидации техники \"{0}\""+ 
"эффекта \"{1}\"\n\rСкорее всего, 
функциональность шейдера превышает возможности GPU", 
effect.CurrentTechnique.Name, effectFileName));
stopwatch = new Stopwatch();
 stopwatch.Start(); 
}
// Рассчитывает угол, на который должен повернуться диск с
 момента последнего вызова этого 
// метола и выполняет собственно поворот.
public void Update()
{ 
// Так как хранитель экрана может работать часами, значение 
переменной currentTime может 
// достигнуть достаточно большой величины. Поэтому, во избежание
 падения точности вычислений 
// используется тип double
double currentTime = (float)stopwatch.ElapsedTicks / 
(float)Stopwatch.Frequency; 
// Переменная delta принимает очень ограниченный диапазон 
значений, поэтому здесь вполне 
// можно обойтись типом float
float delta = (float)(currentTime - lastTime);
// Корректируем угол поворота диска
diskAngle += diskSpeed * delta;
// Рассчитываем новые координаты вершин диска
diskVertices[0] = new VertexPositionColor(new Vector3(0.0f, 0.0f, 0.0f),
 XnaGraphics.Color.LightGray);
for (int i = 0; i <= slices; i++) {
float angle = (float)i / (float)slices * 2.0f * (float)Math.PI;
float x = diskRadius * (float)Math.Sin(diskAngle + angle);
float y = diskRadius * (float)Math.Cos(diskAngle + angle);
byte red = (byte)(255 * Math.Abs(Math.Sin(angle * 3)));
byte green = (byte)(255 * Math.Abs(Math.Cos(angle * 2)));
diskVertices[i + 1] = new VertexPositionColor(new Vector3(x, y, 0.0f),
 new
 XnaGraphics.Color(red, green, 128)); 
};
lastTime = currentTime; 
}
// Выполняет визуализацию изображения
 public void Paint() 
{
}
// Освобождает ресурсы формы
 public void Dispose() 
{
if (stopwatch != null)
 stopwatch.Stop() ;
if (device != null) 
{
device.Dispose() ; device = null; 
} 
   } 
}
Листинг 4.2.
Андрей Леонов
Андрей Леонов

Reference = add reference, в висуал студия 2010 не могу найти в вкладке Solution Explorer, Microsoft.Xna.Framework. Его нету.