Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 17:

Программирование простой игры в 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 следующий код
#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 и присоедините его к проекту. Оболочка физически скопирует его в место размещения проекта вместе с файлом ресурсов

  • Откомпилируйте проект командой Project/Build Dodger, теперь компилятор успокоился

Теперь главной задачей является правильное размещение вызовов наших функций поддержки лучших достижений. Проверка того, входит ли текущий результат в тройку лучших, должна производится сразу после завершения текущей игры.

  • Добавьте в секцию кода проверки столкновения и остановки игры функции 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()
  • Запустите игру и проверьте ее функциональные возможности

Должно внешне быть примерно такое

Уважаемые Студенты-программисты (и примкнувшие к ним КОЧЕГАРЫ)!!! Сделайте все своими ручками. Попытайтесь разобраться в коде. У вас впереди будет много блестящих программ, но эти "кривые" - самые первые, поэтому и запомнятся вам как самые родные ( или двоюродные ).


Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ