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


