Опубликован: 13.07.2010 | Доступ: свободный | Студентов: 891 / 20 | Оценка: 4.40 / 4.20 | Длительность: 77:34:00
Самостоятельная работа 17:

Программирование простой игры в DirectX

Создание устройства в главном классе

  • Добавьте в конец конструктора класса DodgerGame код определения стиля окна формы
public DodgerGame()
    {
      this.Size = new Size(800, 600); // Размер окна формы
      this.Text = "Lab37. Игра Dodger"; // Заголовок окна
      this.SetStyle(ControlStyles.AllPaintingInWmPaint 
        | ControlStyles.Opaque, true);
    }
Листинг 17.4. Добавление стиля окна в конструктор DodgerGame()
  • Создайте в начале класса DodgerGame перед конструктором секцию для размещения переменных-членов класса и объявите в ней ссылку на устройство
public class DodgerGame : Form
  {
    #region Секция переменных-членов класса DodgerGame
  
    // Ссылка на устройство
    Device device;
      
    #endregion
  
    public DodgerGame()
    {
    ....................................
    }
  }
Листинг 17.5. Создание секции для переменных класса DodgerGame
  • Добавьте в класс DodgerGame функцию InitializeGraphics() для инициализации объектов DirectX
// Функция создания и инициализации объектов DirectX
    public void InitializeGraphics()
    {
      // Установить параметры представления
      PresentParameters presentParams = new PresentParameters();
      presentParams.Windowed = true;
      presentParams.SwapEffect = SwapEffect.Discard;
  
      // Включить буфер глубины устройства
      presentParams.AutoDepthStencilFormat = DepthFormat.D16;
      presentParams.EnableAutoDepthStencil = true;
        
      // Установить адаптер программную поддержку по умолчанию
      int adapterOrdinal = Manager.Adapters.Default.Adapter;
      CreateFlags flags = CreateFlags.SoftwareVertexProcessing;
        
      // Проверка возможности использования прямой аппаратной поддержки
      Caps caps = Manager.GetDeviceCaps(adapterOrdinal, DeviceType.Hardware);
        
      // Возможна ли прямая аппаратная поддержка вершинного буфера
      if(caps.DeviceCaps.SupportsHardwareTransformAndLight)
        flags = CreateFlags.HardwareVertexProcessing;
          
      // Возможна ли прямая аппаратная поддержка устройства
      if(caps.DeviceCaps.SupportsPureDevice)
        flags |= CreateFlags.PureDevice;
          
      // Создать устройство
      device = new Device(adapterOrdinal, DeviceType.Hardware, this, flags, presentParams);
    }
Листинг 17.6. Код функции инициализации InitializeGraphics()
  • Добавьте в функцию Main() класса AppEntry вызов функции инициализации объектов DirectX
public class AppEntry
  {
    static void Main()
    {
      using (DodgerGame frm = new DodgerGame())
      {
        frm.InitializeGraphics();
        System.Windows.Forms.Application.Run(frm);
      }
    }
  }
Листинг 17.7. Добавление в функцию Main() вызова функции InitializeGraphics()

Сначала мы создаем и устанавливаем параметры представления устройства, в том числе включаем буфер глубины. Затем извлекаем и сохраняем свободный порядковый номер устройства и устанавливаем изначально программный способ обработки вершин. Затем мы выясняем, способна ли поддерживать видеокарта текущего компьютера прямое аппаратное вычисление вершин графических объектов. При обнаружении возможностей аппаратной обработки вершин проверяем возможность создания реального устройства и настраиваем переменную flags с помощью поразрядной операции OR. Способы определения возможностей аппаратной поддержки приведены в лабораторной работе №11.

  • Для восстановления состояния устройства после его сброса добавьте в конец функции InitializeGraphics() код регистрации события DeviceReset объекта устройства ( код добавляйте вручную, чтобы оболочка создала правильный обработчик)
// Функция создания и инициализации объектов DirectX
    public void InitializeGraphics()
    {
      .........................................................
            // Создать устройство
            device = new Device(adapterOrdinal, DeviceType.Hardware, this, flags, presentParams);
    
            // Подписаться на событие регистрации сброса устройства
            device.DeviceReset += new EventHandler(device_DeviceReset);
    }
Листинг 17.8. Добавление кода регистрации события DeviceReset

Оболочка создаст обработчик события с автоматически сгенерированным именем. Переименуйте обработчик на OnDeviceReset

// Функция создания и инициализации объектов DirectX
    public void InitializeGraphics()
    {
      .......................................................
      device.DeviceReset += new EventHandler(OnDeviceReset);
    }
  
    // Обработчик события сброса устройства
    private void OnDeviceReset(object sender, EventArgs e)
    {

    }
Листинг 17.9. Изменяем имя обработчика
  • Добавьте в конец функции InitializeGraphics() принудительный вызов обработчика при запуске приложения
public void InitializeGraphics()
    {
      .........................................................
            // Подписаться на событие регистрации сброса устройства
            device.DeviceReset += new EventHandler(OnDeviceReset);
    
            // Прямой вызов обработчика
            this.OnDeviceReset(device, null);
    }
Листинг 17.10. Вызов обработчика восстановления устройства при запуске приложения

Установка камеры и освещения сцены

  • Заполните обработчик события сброса устройства следующим кодом
// Обработчик события сброса устройства
    private void OnDeviceReset(object sender, EventArgs e)
    {
      // Установка камеры
      device.Transform.Projection = Matrix.PerspectiveFovLH(
        (float)Math.PI / 4, // Угол зрения равен 45 градусов
        (float)this.Width / (float)this.Height, // Форматное соотношение сторон
        1.0F, // Ближний план
        1000.0F); // Дальний план
      device.Transform.View = Matrix.LookAtLH(
        new Vector3(0.0F, 9.5F, 17.0F), // Положение камеры
        new Vector3(), // Положение объекта текущее
        new Vector3(0, 1, 0)); // Направление камеры
  
      // Установка освещения
      // Поддерживаются ли несколько полноценных источников освещения
      if((device.DeviceCaps.VertexProcessingCaps.SupportsDirectionAllLights) &&
        (device.DeviceCaps.MaxActiveLights > 1))
      {
        // Устанавливаем первый источник
        device.Lights[0].Type = LightType.Directional;
        device.Lights[0].Diffuse = Color.White;
        device.Lights[0].Direction = new Vector3(1, -1, -1);
        device.Lights[0].Commit();
        device.Lights[0].Enabled = true;
  
        // Устанавливаем второй источник
        device.Lights[1].Type = LightType.Directional;
        device.Lights[1].Diffuse = Color.White;
        device.Lights[1].Direction = new Vector3(-1, 1, -1);
        device.Lights[1].Commit();
        device.Lights[1].Enabled = true;
      }
      else 
      {
        // Источники не поддерживаются, устанавливаем общий свет
        device.RenderState.Ambient = Color.White;
        // Настраиваем общий свет
        device.Lights[0].Type = LightType.Directional;
        device.Lights[0].Diffuse = Color.White;
        device.Lights[0].Direction = new Vector3(1, -1, -1);
        device.Lights[0].Commit();
        device.Lights[0].Enabled = true;
      }
  
      // Включить освещение сцены
      device.RenderState.Lighting = true;
    }
Листинг 17.11. Код обработчика сброса устройства

В этом коде обработчика вначале устанавливается камера и определяется вид и преобразование проекции устройства. Для нашей игры устанавливается неподвижная камера, параметры которой мы задаем заново после каждого сброса устройства, поскольку все параметры устройства после его сброса теряются. Далее мы определяем возможность установки двух источников направленного света. Если данная видеокарта их не поддерживает, то устанавливаем общее освещение. Включение освещения сцены установлено по умолчанию, но на всякий случай пропишем это явно.

Добавление кода запуска рендеринга

Переопределим в классе DodgerGame наследуемый из Form метод OnPaint() и поместим в него код организации рендеринга. Операцию переопределения метода OnPaint() выполните вручную!!! и начните с ввода ключевого слова override

  • Добавьте вручную (чтобы оболочка правильно сгенерировала аргументы метода) в класс DodgerGame код переопределения метода OnPaint()
// Код организации рендеринга
        protected override void OnPaint(PaintEventArgs e)
        {
            // Очистка экрана
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
                Color.CornflowerBlue, 1.0F, 0);
    
            // Формирование о отображение сцены
            device.BeginScene();
            device.EndScene();
        
            device.Present();
            this.Invalidate();
        }
Листинг 17.12. Код организации рендеринга в методе OnPaint() формы
  • Запустите приложение, должно получиться пустое окно с голубым фоном.

Приступим к созданию первого игрового объекта "Дорога" (road).