Китай |
Программирование простой игры в DirectX
Другие заготовки для создания препятствий
Создание одного препятствия
Вначале решим задачу для правильного отображения одного препятствия. Поскольку сгенерированное препятствие должно быть неподвижным относительно движущейся дороги, необходимо организовать его перемещение при рисовании с такой-же скоростью, что и дороги.
// Вычисление положения препятствия public void LocationObstacle(float elapsedTime, float speed) { position.Z += (speed * elapsedTime); }Листинг 17.40. Функция LocationObstacle() вычисления положения препятствия в классе Obstacle
- Добавьте к классу Obstacle свойство только для чтения с именем Depth, которое будет возвращать координату глубины препятствия в мировых координатах
// Свойство глубины класса Obstacle public float Depth { get { return position.Z; } }Листинг 17.41. Добавление к классу Obstacle свойства глубины
// Рисование препятствия public void DrawObstacle(Device device) { if (isTeapot) { device.Transform.World = Matrix.Scaling(OBJECT_RADIUS, OBJECT_RADIUS, OBJECT_RADIUS) * Matrix.Translation(position); } else { device.Transform.World = Matrix.Translation(position); } device.Material = obstacleMaterial; device.SetTexture(0, null); obstacleMesh.DrawSubset(0); }Листинг 17.42. Рендеринг препятствия функцией DrawObstacle()
В этой функции, если сгенерирован объект заварочного чайника, то выполняется его масштабирование перед отображением, поскольку простыми способами, как это можно сделать с типовыми примитивами, его размер не изменить.
Теперь нужно позаботиться об освобождении закрепленных за Mesh -препятствиями ресурсов при их удалении. Для этого создадим в классе Obstacle соответствующую секцию #region
#region Освобождение ресурсов для сборщика мусора // Для принудительного вызова при смене препятствий public void Dispose() { obstacleMesh.Dispose(); System.GC.SuppressFinalize(this); } // Для автоматического вызова при завершении игры ~Obstacle() // Деструктор { this.Dispose(); } #endregionЛистинг 17.43. Секция для освобождения памяти, занятой объектами препятствий
Здесь мы вызываем метод Dispose() класса Mesh, а затем инициируем работу сборщика мусора для возвращения системе освобожденной памяти. Игра может продолжаться долго, а сгенерированные Mesh -препятствия требуют много ресурсов. Поэтому, такая мера будет своевременно возвращать системе память, исключив ее возможную утечку. То же самое происходит автоматически и при завершении игры, когда вызывается деструктор класса препятствий.
Создание группы препятствий
Теперь рассмотрим вопрос создания нескольких разных препятствий на движущейся дороге. Для этого нам понадобится простой метод добавления и удаления препятствий. Можно использовать массив для одновременного хранения нескольких препятствий. Но тогда придется делать его динамическим, поскольку количество хранимых элементов заранее определить невозможно. Воспользуемся более эффективным библиотечным средством - создадим класс-коллекцию для хранения данных о препятствиях.
- Добавьте в файл Obstacle.cs после класса Obstacle новый класс с именем Obstacles, наследующий интерфейс System.Collections.IEnumerable библиотечной сборки mscorlib
// Класс-коллекция для хранения данных о препятствиях public class Obstacles : IEnumerable { // Создать объект динамического массива // с автоматическим изменением длины private ArrayList obstacleList = new ArrayList(); // Создание свойства возможности адресации // элементов коллекции по индексу public Obstacle this[int index] { get { return (Obstacle)obstacleList[index]; } } // Переопределение интерфейсного метода сброса курсора // нумератора в начало динамического массива public IEnumerator GetEnumerator() { return obstacleList.GetEnumerator(); } // Добавление препятствия в динамический массив public void Add(Obstacle obstacle) { obstacleList.Add(obstacle); } // Удаление препятствия из динамического массива public void Remove(Obstacle obstacle) { obstacleList.Remove(obstacle); } // Очистка коллекции препятствий public void Clear() { obstacleList.Clear(); } }Листинг 17.44. Класс-коллекция Obstacles в файле Obstacle.cs
В данном коде мы используем библиотечный класс ArrayList для создания динамического массива и упаковки его в класс-коллекцию. Коллекции в C# означают то же самое, что шаблоны в C++. Мы создали класс-коллекцию на основе интерфейсного класса IEnumerable. В нашем классе мы создали свойство для прямого индексированного доступа к хранимым в коллекции объектам препятствий, а также заложили с помощью интерфейсного класса IEnumerable возможность доступа к препятствиям методом перебора с использованием цикла foreach языка C#. Мы также упаковали в своем классе три виртуальных метода поэлементного добавления ( Add ), поэлементного удаления ( Remove ) и групповой очистки ( Clear ) класса ArrayList.
- Для доступности сокращенных имен ArrayList и IEnumerable известите компилятор об используемом библиотечном пространстве имен System.Collections соответствующей using -директивой в начале файла Obstacle.cs
using System; using System.Drawing; using System.Windows.Forms; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using System.Collections; namespace Dodger { ................................................... }Листинг 17.45. Размещение using-директивы в файле Obstacle.cs
Интерфейс - это не более чем именованный набор членов абстрактного класса, чисто виртуальные функции которого устанавливают стандарт их обязательного переопределения в ближайшем наследнике. Интерфейсы - это еще один способ реализации полиморфизма в приложениях. Поскольку в разных наследниках члены одних и тех же интерфейсов будут реализованы по-разному, и реагировать на одни и те же вызовы они будут по-своему. Для узнаваемости в библиотеке FCL все интерфейсные классы начинаются с символа I.
Интерфейс - это чистая синтаксическая конструкция, которая предназначена для установки правил взаимодействия отдельных частей программы. Интерфейсы никогда не содержат поля данных и в них не бывает реализаций методов по умолчанию. Каждый член интерфейса (будь то свойство или метод) автоматически становится абстрактным. Кроме того, в C# множественное наследование классов не поддерживается, зато множественное наследование интерфейсов - обычное дело.
Мы не можем выбирать, какие методы в интерфейсе нам реализовывать. В классе, наследующем интерфейсный класс, должны быть реализованы все методы без исключения, либо этот интерфейс вообще не реализуется - половинчатого решения быть не может.