Китай |
Программирование простой игры в DirectX
Начало новой игры
Игра останавливается, если автомобиль врезается в препятствие. Нам нужно задать возможность начать новую игру. Задействуем для этих целей клавишу пробела Space. Прежде всего, когда игра остановлена, нужно прекратить выполнение функции OnFrameUpdate() класса DodgerGame.
- Поместите в начало функции OnFrameUpdate() класса DodgerGame признак остановки игры
// Функция обновления кадра, привязанная // к таймеру повышенной точности private void OnFrameUpdate() { // Если игра остановлена, не выполнять функцию if(isGameOver || !hasGameStarted) return; // Извлекаем точное время elapsedTime = Utility.Timer(DirectXTimer.GetElapsedTime); ..................................................... }Листинг 17.64. Признак остановки игры в функции OnFrameUpdate()
- Добавьте в переопределение функции OnKeyDown() класса DodgerGame код перезапуска игры по нажатию клавиши пробела Space
// Переопределение виртуального метода OnKeyDown() в классе DodgerGame protected override void OnKeyDown(KeyEventArgs e) { ......................................................... // Space - перезапуск игры if (e.KeyCode == Keys.Space && isGameOver) { LoadDefaultGameOptions(); isGameOver = false; hasGameStarted = true; } }Листинг 17.65. Перезапуск игры в функции OnKeyDown()
При желании, можно удалить вызов функции LoadDefaultGameOptions() из метода InitializeGraphics(), поскольку игра все-равно начнется только после нажатия клавиши Space, где вызов этой функции теперь тоже размещен.
Давайте просто для тренировки создадим небольшую паузу между окончанием текущей игры и возможностью начала новой, чтобы все состояния игры физически успели восстановиться. Для этого поместим в функцию OnKeyDown() код, который на время 1000 миллисекунд (1 секунда) сделает недоступными все клавиши, кроме Escape.
- Поместите в начало функции OnKeyDown() класса DodgerGame сразу после секции завершения игры следующий код
// Переопределение виртуального метода OnKeyDown() в классе DodgerGame protected override void OnKeyDown(KeyEventArgs e) { // Выход по Esc - завершение работы приложения if (e.KeyCode == Keys.Escape) this.Close(); // Блокировка клавиатуры if (System.Environment.TickCount - gameOverTick < 1000) return; // Перемещение влево ..................................................... }Листинг 17.66. Блокировка клавиатуры на 1 секунду в функции OnKeyDown()
- Запустите приложение и убедитесь, что все работает как надо
Разработка табло результатов
Игра функционирует полностью, но не хватает механизма отображения очков, максимального выигрыша, комментариев и пр. атрибутов настоящей игры. Исправим это.
В дополнительной библиотеке Microsoft.Directx.Direct3DX.dll есть класс Microsoft.DirectX.Direct3D.Font, который можно использовать для отображения текста (классы одного и того же пространства имен могут физически быть размещены в разных библиотечных файлах). Но и в библиотеке System.Drawing.dll также есть класс System.Drawing.Font. Посколько в нашем приложении подключены ссылки на обе библиотеки, то без указания полного имени класса (с указанием пространства имен), компилятор не сможет самостоятельно определить, какой класс имеется ввиду. Но полное имя класса получится длинным, что может быть не всегда практично. Лучший способ - ввести короткий псевдоним нужного пространства имен, и использовать его для идентификации класса вместо длинной цепочки пространств имен.
Поскольку мы собираемся использовать в нашей игре для отображения текста класс Microsoft.DirectX.Direct3D.Font, то для однозначного указания именно этого класса введем сокращенный псевдоним его пространства имен. Назовем псевдоним именем Direct3D
- Добавьте в начало файла DodgerGame.cs после блока подключения пространств имен следующий код объявления псевдонима
using System; using System.Drawing; using System.Windows.Forms; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Direct3D = Microsoft.DirectX.Direct3D; namespace Dodger { class DodgerGame : Form { .............................................. } }Листинг 17.67. Объявление псевдонима пространства имен
Теперь класс Font можно однозначно определять в нашем приложении, если использовать для него сокращенный псевдоним, который мы только что создали.
Создаваемые шрифты могут быть различного цвета, но должны имет один размер и семейство (стиль). При желании можно отображать несколько различных типов текста. Выберем для нашей игры два типа текста, которые будут поддерживаться двумя экземплярами класса Font.
#region Секция переменных-членов класса ............................................... // Ссылки на шрифты private Direct3D.Font scoreFont = null; private Direct3D.Font gameFont = null; #endregionЛистинг 17.68. Добавление ссылок на шрифты в классе DodgerGame
Ссылки на объекты шрифтов мы заготовили, теперь нужно создать сами объекты.
- Добавьте в конец функции InitializeGraphics() ( чтобы этот код размещался после создания устройства ) класса DodgerGame следующий код, используя псевдоним пространства имен Direct3D для однозначной адресации класса Font
// Функция создания и инициализации объектов DirectX public void InitializeGraphics() { ............................................................ // Создать устройство device = new Device(adapterOrdinal, DeviceType.Hardware, this, flags, presentParams); ............................................................ // Создание шрифтов scoreFont = new Direct3D.Font(device, new System.Drawing.Font("Arial", 12.0f, FontStyle.Bold)); gameFont = new Direct3D.Font(device, new System.Drawing.Font("Arial", 36.0f, FontStyle.Bold | FontStyle.Italic)); }Листинг 17.69. Создание объектов шрифтов в функции InitializeGraphics()
Теперь у нас имеются два шрифта различных размеров одного и того же семейства " Arial ". Осталось только отобразить на экране нужный текст. Отображать текст с результатами нужно в конце игры.
- Добавьте в метод OnPaint() класса DodgerGame следующий код
// Код организации рендеринга protected override void OnPaint(PaintEventArgs e) { // Очистка экрана device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0F, 0); // Вычисление новых параметров рисования дороги, // автомобиля и препятствий OnFrameUpdate(); // Формирование о отображение сцены device.BeginScene(); { // Отображение двух секций дороги с разной глубиной DrawRoad(0.0F, 0.0F, RoadDepth0); DrawRoad(0.0F, 0.0F, RoadDepth1); // Отображение автомобиля car.DrawCar(device); // Отображение препятствий foreach (Obstacle o in obstacles) { o.DrawObstacle(device); } // Визуализация табло if (hasGameStarted) { // Отображение очков scoreFont.DrawText(string.Format("Текущие очки: {0}", score), new Rectangle(5, 5, 0, 0), DrawTextFormat.NoClip, Color.Yellow); } if (isGameOver) { // Если игра закончена, уведомить игрока if (hasGameStarted) { gameFont.DrawText("Вы проиграли. Ха-ха-ха!", new Rectangle(25, 45, 0, 0), DrawTextFormat.NoClip, Color.Red); } if ((System.Environment.TickCount - gameOverTick)>= 1000) { // Вывести подсказку - как начать игру, если после // остановки предыдущей игры прошло более 1 секунды gameFont.DrawText("Нажмите клавишу Space", new Rectangle(25, 130, 0, 0), DrawTextFormat.NoClip, Color.WhiteSmoke); } } } device.EndScene(); device.Present(); this.Invalidate(); }Листинг 17.70. Отображение результатов игры после ее завершения в функции OnPaint()
- Запустите игру и полюбуйтесь на выполненную работу