Программирование простой игры в DirectX
Добавление текстовых надписей и запись лучших результатов в системный реестр
Теперь у нас есть работоспособный вариант игры. Но Родина должна знать своих героев. Желательно для этого предусмотреть возможность сохранения нескольких игроков с лучшими достижениями.
- Для сохранения лучших результатов добавьте в главное пространство имен Dodger файла DodgerGame.cs (перед или после класса DodgerGame ) простую структуру
namespace Dodger { public class DodgerGame : Form { ............................................... } // Структура для сохранения лучших достижений игры public struct HighScore { private int realScore; // Результат private string playerName; // Игрок // Свойства для доступа к закрытым членам public int Score { get { return realScore; } set { realScore = value; } } public string Name { get { return playerName; } set { playerName = value; } } } }Листинг 17.71. Добавление структуры для сохранения победителей в файл DodgerGame
После объявления структуры нужно в движке игры создать массив (список) для требуемого количества игроков. Мы будем сохранять результаты трех лучших игроков.
#region Секция переменных-членов класса DodgerGame ............................................... // Ссылки на шрифты private Direct3D.Font scoreFont = null; private Direct3D.Font gameFont = null; // Информация о трех лучших достижениях private HighScore[] highScores = new HighScore[3]; private string defaultHighScoreName = string.Empty; #endregionЛистинг 17.72. Добавление информации о лучших игроках в класс DodgerGame
Теперь создадим три отдельных функции. Первая функция CheckHighScore() проверит текущий счет и сравнит его с имеющимся, чтобы внести при необходимости новое лучшее значение в список. Вторая функция SaveHighScores() сохранит информацию о наилучших результатах в системный реестр перед завершением работы приложения, а третья функция LoadHighScores() будет вызывать эти значения из системного реестра при запуске приложения.
- Добавьте код перечисленных функций в конец класса DodgerGame
public class DodgerGame : Form { ............................................................... // Функции поддержки лучших результатов в истории игры // Если текущий игрок побеждает, то запоминаем его // индекс и просим ввести свое имя private void CheckHighScore() { int index = -1;// Сбрасываем текущий индекс for (int i = highScores.Length - 1; i>= 0; i--) { if (score>= highScores[i].Score) // Нашли, кого заменить { index = i; } } // Делаем замену, если индекс отличен от -1 if (index>= 0) { // Освобождаем найденную позицию for (int i = highScores.Length - 1; i>index ; i--) { highScores[i] = highScores[i-1]; } // Сохраняем на освободившемся месте текущий результат highScores[index].Score = score; highScores[index].Name = Input.InputBox("Вы получили высокий счет!!!", "Просим ввести ваше имя:", defaultHighScoreName); } } // Сохранение лучших результатов в системном реестре public void SaveHighScores() { Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey( "Software\\MDXBoox\\Dodger"); try // Делаем попытку { for(int i = 0; i < highScores.Length; i++) { key.SetValue(string.Format("Player{0}", i), highScores[i].Name); key.SetValue(string.Format("Score{0}", i), highScores[i].Score); } key.SetValue("PlayerName", defaultHighScoreName); } finally // Выполняем при любом исходе { if (key != null) { key.Close(); // Обязательно закрываем ключ } } } // Загрузка списка лучших результатов из системного реестра private void LoadHighScores() { Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey( "Software\\MDXBoox\\Dodger"); try // Делаем попытку { for(int i = 0; i < highScores.Length; i++) { highScores[i].Name = (string)key.GetValue( string.Format("Player{0}", i), string.Empty); highScores[i].Score = (int)key.GetValue( string.Format("Score{0}", i), 0); } defaultHighScoreName = (string)key.GetValue( "PlayerName", System.Environment.UserName); } finally // Выполняем при любом исходе { if (key != null) { key.Close(); // Обязательно закрываем ключ } } } }Листинг 17.73. Добавление функций поддержки лучших результатов игры в класс DodgerGame
Мы пытаемся сохранить сведения в узле HKEY_LOCAL_MACHINE системного реестра. Если к нему нет доступа, измените код для записи в HKEY_CURRENT_USER.
Если теперь попытаться откомпилировать приложение, то компилятор скажет, что не найден класс Input в подключенных к проекту пространствах имен. Его нет среди библиотечных классов C#. Это дополнительный пользовательский класс (делали умные люди - проклятые капиталисты), который находится в отдельном файле InputBox.cs каталога Source, прилагаемого к данной лабораторной работе. Он имитирует работу одноименного класса библиотеки для Visual Basic фирмы Microsoft.
- Выполните команду Project/Add Existing Item меню оболочки, найдите в прилагаемом каталоге Source файл InputBox.cs и присоедините его к проекту. Оболочка физически скопирует его в место размещения проекта вместе с файлом ресурсов
Теперь главной задачей является правильное размещение вызовов наших функций поддержки лучших достижений. Проверка того, входит ли текущий результат в тройку лучших, должна производится сразу после завершения текущей игры.
- Добавьте в секцию кода проверки столкновения и остановки игры функции OnFrameUpdate() класса DodgerGame следующий код
// Функция обновления кадра, привязанная // к таймеру повышенной точности private void OnFrameUpdate() { ...................................................... // Удалить миновавшие препятствия с помощью временного списка foreach (Obstacle o in removeObstacles) { obstacles.Remove(o); o.Dispose();// Вызвать сборщик мусора (упаковать) } removeObstacles.Clear();// Очистить временный список // Проверка столкновения и остановка игры foreach (Obstacle o in obstacles) { if (o.IsHittingCar(car.Location, car.Diameter)) { // Если обнаружено столкновение - закончить игру! isGameOver = true; // Стоп-игра, перекур! gameOverTick = System.Environment.TickCount; // Останавливаем таймер Utility.Timer(DirectXTimer.Stop); // Проверить результат среди лучших игроков CheckHighScore(); } } // Вычисление координаты нового положения автомобиля car.LocationCar(elapsedTime); // Вычисление координаты нового положения препятствий foreach (Obstacle o in obstacles) { o.LocationObstacle(elapsedTime, RoadSpeed); } }Листинг 17.74. Проверка кандидатуры в тройку лучших в OnFrameUpdate()
Запись рекордов в системный реестр должна выполняться перед завершением работы приложения непосредственно после выхода из цикла сообщений Windows.
- Откройте через панель Solution Explorer на редактирование файл AppEntry.cs приложения и после цикла сообщений добавьте вызов функции сохранения результатов
using System; namespace Dodger { class AppEntry { static void Main() { using (DodgerGame frm = new DodgerGame()) { frm.InitializeGraphics(); System.Windows.Forms.Application.Run(frm); // Запись результатов в системный реестр frm.SaveHighScores(); } } } }Листинг 17.75. Сохранение достижений в системном реестре из кода файла AppEntry.cs
Чтение результатов из системного реестра нужно выполнять сразу после запуска приложения, поэтому вызов функции чтения разместим в конструкторе класса DodgerGame.
- Поместите в конец конструктора DodgerGame() класса DodgerGame код чтения достижений из системного реестра
public DodgerGame() { this.Size = new Size(800, 600); // Размер окна формы this.Text = "Lab37. Игра Dodger"; // Заголовок окна this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true); // Чтение лучших результатов из системного реестра this.LoadHighScores(); }Листинг 17.76. Код чтения из системного реестра в конструкторе класса DodgerGame
И последнее действие в данной марафонской лабораторной работе - вывести лучшие достижения на экран.
- Добавьте в функцию OnPain() класса 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); } // Показ лучших достижений gameFont.DrawText("Лучшие результаты: ", new Rectangle(25, 205, 0, 0), DrawTextFormat.NoClip, Color.Red); for (int i = 0; i < highScores.Length; i++) { gameFont.DrawText(string.Format("Игрок: {0} : {1}", highScores[i].Name, highScores[i].Score), new Rectangle(25, 270 + (i * 55), 0, 0), DrawTextFormat.NoClip, Color.Yellow); } } } device.EndScene(); device.Present(); this.Invalidate(); }Листинг 17.77. Отображение лучших достижений в функции OnPaint()
- Запустите игру и проверьте ее функциональные возможности
Должно внешне быть примерно такое