Разработка игры "Гонка на время с препятствиями"

Разработка объектно-ориентированной программы на языке С++, которая реализирует инкапсуляцию, наследование и полиморфизм. Выделение компонентов - абстрактных единиц, выполняющих определенный набор действий. Реализация основного алгоритма функционирования.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык русский
Дата добавления 24.11.2010
Размер файла 273,7 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Московский энергетический институт

(технический университет)

Институт Автоматики и Вычислительной Техники

Кафедра ВМСиС

Курсовой проект

по дисциплине

"Объектно-ориентированное программирование"

Разработка игры "Гонка на время с препятствиями"

Москва, 2006

Содержание

  • Содержание
  • Введение
  • 1. Формулировка прикладной задачи
  • 2. Выделение компонентов задачи
  • 3. Выбор объектов и способов связи между ними
  • 4. Проектирование структуры классов
    • 4.1 Класс CRoadObject
    • 4.2 Класс CBar
    • 4.3 Класс СFuelBar
    • 4.4 Класс CMarking
    • 4.5 Класс CCar
    • 4.6 Класс CRoad
    • 4.7 Класс CUser
    • 4.8 Класс CMyTime
    • 4.9 Класс CRace
  • 5. Разработка алгоритма функционирования программы
  • 6. Отладка и тестирование программы
  • Заключение
  • Список использованных источников
  • Приложение Листинг программы

Введение

В ходе курсового проектирования будет разрабатываться программа на языке C++, реализующая игру "Гонка на время с препятствиями". Основное требование, предъявляемое к программе: программа должна быть полностью объектно-ориентированной. Программа должна реализовывать три основные идеи объектно-ориентированного программирования (ООП): инкапсуляция, наследование и полиморфизм, демонстрировать плюсы ООП, одним из которых является легкая наращиваемость программы. Для разработки следует использовать среду Microsoft Visual C++ версии 6.0.

Проектирование можно разбить на несколько этапов:

1. Формулировка четких требований к программе, определение сценария работы программы: что делает пользователь и что в ответ делает программа?

2. Выделение компонентов - абстрактных единиц, выполняющих определенный набор действий.

3. Построение иерархии объектов, выбор связи между ними.

4. Проектирование структуры классов

5. Реализация основного алгоритма функционирования.

6. Отладка и тестирование программы.

Курсовое проектирование будет построено по данной схеме.

программа инкапсуляция полиморфизм алгоритм

1. Формулировка прикладной задачи

Необходимо разработать алгоритмы функционирования и их реализацию на языке С++ для игрового приложения "Гонки на время с препятствиями". Программа должна являться оконным приложением ОС MS Windows. С этой целью в качестве среды разработки будет использоваться MS Visual Studio 6.0.

Пользователь запускает программу и видя на мониторе диалоговое окно регистрации, вводит свое имя, выбирает гоночную машину и начинает гонку. Видя на мониторе главное окно приложения, с изображенным в нем гоночной трассой (вид сверху) с препятствиями, пользователь управляет машиной, меняя полосу ее движения и увеличивая/уменьшая скорость движения, с целью избежать столкновения с препятствием и прийти к финишу вовремя. С течением времени, на гоночной машине, управляемой пользователем, может закончиться топливо. Чтобы избежать этого, пользователь должен время от времени совершать наезды на топливные канистры, расположенные на гоночной трассе, тем самым "заправляя" машину топливом. Элементы дорожной разметки разного типа на трассе не дают машине пользователя с нее сойти (отбойники), а также наглядно показывают финишную и разделительную линии.

Программа, в свою очередь регистрирует пользователя, отображая на экране специальное диалоговое окно, и начинает гонку. Рисуя на мониторе окно, с изображенной в нем гоночной трассой с дорожными объектами (препятствия, канистры, элементы дорожной разметки), программа меняет координаты этих объектов на экране (в окне), в соответствии со скоростью движения гоночного автомобиля, имитируя, тем самым, движение автомобиля навстречу им. Анализируя действия пользователя, программа меняет полосу движения машины или изменяет скорость ее движения. Программа также анализирует взаимное расположение машины и дорожных объектов для определения момента столкновения (повреждения машины) или пополнения топливного бака гоночной машины.

При истечении времени, отведенного на прохождение трассы, программа прерывает гонку и информирует пользователя о неудачном результате. Программа также завершает гонку, если машина пользователя окончательно повреждена или у нее закончилось топливо. Программа может завершить гонку и в любой другой момент при нажатии пользователем клавиши "Е", или приостановить ее - при нажатии клавиши "P".

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

2. Выделение компонентов задачи

Объектно-ориентированный анализ прикладной задачи всегда должен начинаться с декомпозиции "Кто. Что делает?". Исходя из некоего обобщенного сценария работы приложения, представленного в п.1, можно выделить две главных компоненты, осуществляющих все действия в задаче:

· Пользователь

· Программа

Составим полный и более подробный набор ответов на вопрос "кто.что_делает?":

· Пользователь.вводит_информацию_о_себе

· Пользователь.выбирает_машину

· Пользователь.меняет_расположение_машины_на_трассе ( или "машина.меняет_свое_расположение_на_трассе" под действием пользователя)

· Пользователь.меняет_скорость_машины (машина.меняет_свою_скорость)

· Программа.регистрирует_пользователя

· Программа.увеличивает_время_пользователя_в_игре (время.увеличивает_себя)

· Программа.рисует_окно (окно.рисует_себя)

· Программа.рисует_трассу_в_окне (трасса.рисует_себя_в_окне)

· Программа.генерирует_новые_дорожные_объекты_на_трассе (трасса.генерирует_новые_дорожные_объекты)

· Программа.удаляет_пройденные_дорожные_объекты_на_трассе (трасса.удаляет_пройденные_дорожные_объекты)

· Программа.рисует_препятсвия_в_окне (препятсвие.рисует_себя_в_окне)

· Программа.рисует_топливные_канистры_в_окне (топливная_канистра.рисует_себя_в_окне)

· Программа.рисует_элементы_дорожной_разметки_в_окне (элемент_дорожной_разметки.рисуют_себя_в_окне)

· Программа.рисует_машину_в_окне (машина.рисует_себя_в_окне)

· Программа.меняет_координаты_препятствия_на_экране (препятсвие.меняет_свои_координаты)

· Программа.меняет_координаты_топливной_канистры_на_экране (топливная_канистра.меняет_свои_координаты)

· Программа.меняет_координаты_элемента_дорожной_разметки_на_экране (элемент_дорожной_разметки.меняет_свои_координаты)

· Программа.меняет_координаты_машины_на_экране (машина.меняет_свои_координаты)

· Программа.анализирует_взаимное_расположение_препятствия_и_машины (препятствие.анализирует_взаимное_расположение_препятствия_и_себя)

· Программа.анализирует_взаимное_расположение_канистры_и_машины (канистра.анализирует_взаимное_расположение_препятствия_и_себя)

· Программа.сравнивает_время_пользователя_в_игре_с_макисмально_ _допустимым (время.сравнивает_себя_с_максимально_допустимым)

· Программа.выводит_результаты_в_окне

· Программа.приостанавливает_игру

· Программа.завершает_игру

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

1. Пользователь. Выполняет действия:

§ вводит свое имя

§ выбирает гоночную машину

2. Машина. Выполняет действия:

§ отображает себя в окне

§ меняет свои координаты на экране

§ меняет свою скорость

§ меняет свой уровень повреждения

§ меняет свой уровень топлива

3. Препятствие. Выполняет действия:

§ отображает себя в окне

§ меняет свои координаты на экране

§ реагирует на столкновение с машиной (увеличивает уровень повреждения машины)

4. Канистра. Выполняет действия:

§ отображает себя в окне

§ меняет свои координаты

§ реагирует на столкновение с машиной (увеличивает уровень топлива машины)

5. Элемент дорожной разметки. Выполняет действия:

§ Отображает себя в окне

§ Реагирует на столкновение с машиной (если элемент - отбойник - меняет координаты машины)

6. Трасса. Выполняет действия:

§ Отображает дорогу на экране

§ Отображает все дорожные объекты на дороге

§ Генерирует новые дорожные объекты и заполняет ими новый участок дороги

§ Удаляет дорожные объекты с пройденных невидимых участков дороги

7. Управляющий компонент - Гонка

§ Регистрирует Пользователя

§ Создает гоночную Трассу

§ Запускает игровой процесс

§ Анализирует информацию, введенную с клавиатуры

§ Осуществляет необходимые периодические действия игрового процесса

§ Выводит информацию об игровом процессе на экран

8. Компонент- Время

§ Инкрементирует свое значение на заданную величину

§ Высчитывает разницу с другим моментом времени

9. Компонент - Окно приложения

§ Отображает себя на экране

§ Обрабатывает сообщения от клавиатуры и от таймера

10. Компонент - Диалоговое окно регистрации

§ Отображает себя на экране

§ Выдает информацию, введенную пользователем

3. Выбор объектов и способов связи между ними

Каждому выделенному в п.2. компоненту сопоставим свой объект. Диаграмма взаимодействия объектов представлена на рисунке 3.1.

Рисунок 3.1 Диаграмма взаимодействия объектов.

Как было сказано выше, основные действия игрового процесса (запуск, приостановка, периодические действия игрового процесса и т.д.) выполняются Управляющим объектом. Соответственно, ему должны быть доступны две основные составляющие игры, две соревнующиеся стороны: Пользователь, с его машиной, и Трасса, с ее дорожными объектами. Для организации этой связи будем использовать механизм внедрения. Внедрение- это один из способов организации связи между объектами, при котором в объекте существует объектное поле другого типа. При этом не рекомендуется пользоваться вложением статических экземпляров объектов, поэтому в Управляющем объекте будем иметь по крайней мере 2 поля-указателя: указатель на объект-пользователя и указатель на объект-трассу.

Объект Машина является составной частью объекта Пользователя. Поэтому, для обеспечения их связи, здесь тоже логично применить механизм внедрения. Для выполнения управляющих действий над объектом Машиной со стороны Управляющего объекта (а такие действия необходимы, например: анализ уровня повреждения машины, для принятия решения о преждевременном окончании гонки) можно организовать связь еще и между Управляющим объектом и объектом Машиной. Однако мы поступим следующим образом: просто добавим в интерфейс объекта Пользователя метод доступа, который будет возвращать Управляющему объекту указатель, на объект-машину.

Объект Трасса должен управлять (перемещать, отображать на экране и т.д) множеством дорожных объектов разных типов. Поэтому в поля объекта Трасса будет включен специальный статический массив дорожных объектов. Очевидно, что размер этого массива равен максимально возможному числу дорожных объектов, отображаемых на экране. Применение в этом массиве именно указателей на дорожные объекты, а не статических экземпляров объектов очевидно: отображаемые на экране дорожные объекты будут создаваться и уничтожаться во время выполнения программы. С типом элементов-указателей данного массива определимся позднее, когда будет определена цепочка наследования дорожных объектов и будет выделен корневой объект в этой цепочке.

Связь между дорожными объектами и объектом Машиной необходима для осуществления реакции на столкновение с машиной. Препятствие должно знать, какую машину ему надо повредить, канистра должна знать, кого надо заправить топливом, а отбойнику нужна информация, какую машину необходимо отбросить назад, на трассу. Эту связь организуем при помощи механизма передачи по параметру. То есть при вызове метода, осуществляющего эту реакцию у дорожного объекта, в списке фактических параметров будем передавать указатель на машину, на которую необходимо "отреагировать". Этот указатель будет храниться в одном из полей объекта Трасса, осуществляющего управление всем дорожными объектами, т.е связь между объектом Машиной и дорожными объектами будет осуществляться путем внедрения в объект Трасса поля-указателя на объект Машину.

Согласно сценарию работы программы, она должна иметь возможность вести счет времени пользователя в игре. Можно было бы в объекты, которым необходимо хранить значения времени, просто ввести дополнительные поля, но гораздо удобнее, эти поля вывести в отдельный класс и определить необходимый набор операций над ними. После этого, экземпляры данного класса можно внедрять в какие нам нужно объекты. Сделаем объект Время, частью объекта Пользователь.

Ну и наконец, все взаимодействие Управляющего объекта с внешним миром осуществляется через объект Окно приложения. Именно этот объект отображает трассу и машину пользователя на экране, именно он преобразует нажатия клавиш на клавиатуре в управляющие воздействия на машину и на игровой процесс в целом, именно он обеспечивает инициацию периодических действий игрового процесса. Кроме того, Окно приложения также создает и объект Диалоговое окно регистрации, которое определяет, какими значениями будут проинициализированы поля объекта Пользователь. Отсюда следует, что объекту Окно приложения должны быть доступны объекты Пользователь, Диалоговое окно регистрации и Управляющий объект. Можно было бы сделать эти 3 объекты глобальными, но это может привести к нежелательным последствиям ( любой сможет получить доступ к этим трем объектам). Поэтому свяжем объект Окно приложения и объекты Пользователь, Управляющий объект и Диалоговое окно регистрации отношением внедрения.

Анализируя список действий объектов Препятствие, Канистра, Разметка и Машина, можно увидеть что все они могут отображаться на экране и менять свои координаты, и у всех них есть по крайней мере один общий атрибут - координаты корневой точки. То есть их можно объединить в одну цепочку наследования. Однако объекты Препятствие, Канистра и Разметка могут помимо описанных действий еще и реагировать на столкновение с машиной. Следовательно, при объединении всех этих 4-ех объектов в одну цепочку наследования, объект Машина пришлось бы сделать родителем (т.к у него не может быть метода реакции на столкновение с самим собой), а остальные объекты- сделать его потомками (т.к они "расширяют" список возможных поведений родителя). Однако очевидно, что нельзя назвать канистру - частным случаем автомобиля, как и наоборот. Поэтому создадим абстрактный класс Дорожный объект. Его прямыми наследниками будут объект Препятствие и объект Машина. Объекты Канистра и Разметка сделаем наследниками Препятствия, т.к. у них совершенно точно должны быть дополнительные поля, характеризующие емкость канистры и тип дорожной разметки соответственно.

Полная диаграмма классов изображена на рисунке 3.2

Рисунок 3.2 Иерархия классов.

4. Проектирование структуры классов

4.1 Класс CRoadObject

Этот класс является базовым классом, для классов CCar, CBar, CFuelBar и CMarking. Класс имеет следующую структуру:

classCRoadObject

protected:

WORD x,y;

virtual void draw(CDC* pDC) = 0;

public:

CRoadObject(WORD xx=0 , WORD yy =0): x(xx), y(yy) {};

void show(CDC* pDC) {draw(pDC);}

void move(int nx, int ny, bool fromOldPlace)

{

if (fromOldPlace) { x += nx; y += ny;}

else { x = nx; y = ny;}

}

WORD getX() {return x;}

WORD getY() {return y;}

bool operator==(CRoadObject& a)

{

if ((y == a.getX())&& (x == a.getY()))

return 1;

else

return 0;

}

};

Данный класс будет абстрактным, то есть для него не будет создано ни одного экземпляра. По сути, этот класс описывает свойства и методы простейшего дорожного объекта, который можно отобразить на экране. Для инициализации полей объекта будем использовать инициализирующий конструктор с параметрами по умолчанию и списком инициализации. Инициализирующий конструктор с параметрами по умолчанию - самый экономичный вид конструкторов, который можно использовать и как обычный инициализирующий конструктор, и как конструктор по умолчанию

Заметим, что координаты корневой точки вынесены в секцию protected. Это позволяет ограничить видимость полей объекта (поля объекта будут доступны для самого объекта и для всех его наследников). Будем делать так и в дальнейшем. Таким образом реализуется хороший стиль программирования, когда доступ к полям объектов может осуществляться только при помощи специальных методов доступа. Примером такого метода является метод move, который позволяет изменить координаты объекта. Причем, введена возможность указать лишь необходимое смещение объекта, а не значения новых абсолютных координат. Это очень удобно, так как избавляет от необходимости запрашивать у объекта старые координаты и вычислять их новые значения с учетом изменений. Функции getX и getY служат методами доступа, позволяющими узнать координаты объекта извне.

Все методы доступа определяются внутри класса, а следовательно, автоматически становятся inline. Эти методы доступа будут активно использоваться в программе, а следовательно, и выигрыш в быстродействии (из-за избавления от затрат на вызовы этих методов) может оказаться значительным.

В программе постоянно будут сравниваться координаты дорожных объектов между собой (например, чтобы проверить, не наехала ли гоночная машина на какой-нибудь другой дорожный объект). Поэтому перегрузим оператор сравнения == для класса CRoadObject и всех его наследников. Оператор == будет возвращать значение true, если координаты правого и левого его операндов совпадают.

Пара методов show и draw - служит для реализации классической схемы полиморфизма: статический метод show внутри себя вызывает виртуальный метод draw. Таким образом, вызов метода show в зависимости от типа объекта-потомка, для которого он будет вызван, будет производить разные действия, то есть отображать на экране разные объекты. Параметр метода draw - pDC - указатель на контекст устройства, на котором будет отображаться объект.

Гарантией того, что ни одного экземпляра данного класса не будет создано, является объявление функции draw чисто виртуальной. Если при дальнейшей модификации программы кто-нибудь попытается создать экземпляр класса CRoadObject, компилятор пресечет эту попытку.

4.2 Класс CBar

Этот класс является прямым наследником класса CRoadObject. Описание класса выглядит следующим образом:

class CBar :public CRoadObject

{

protected:

bool damagedByCar;

virtual void draw(CDC* pDC);

virtual void onCarCollision(PCCar car);;

public:

CBar(WORD xx=0 , WORD yy =0):CRoadObject(xx, yy),damagedByCar(false) {}

void checkForCarCollision( PCCar car) {

if ((*car) == (*this ))

onCarCollision( car ); }

virtual ~CBar() { }

};

Экземпляр данного класса представляет собой особым образом закрашенный прямоугольник, изображающий препятствие на дороге. Особенность объекта препятствия в том, что помимо всех свойств и методов дорожного объекта, препятствие может вступить во взаимодействие с машиной. Поэтому к унаследованным полям добавляется признак повреждения машиной damageByCar и статический метод сheckForCarCollision, который сравнивает координаты объекта и машины, и в случае их совпадения, вызывает виртуальный метод onCarCollision, который описывает реакцию препятствия на наезд машины и его воздействие на нее. Указатель, на объект - машину передается в параметре метода. Таким образом, здесь, как и в случае с show-draw, реализуется полиморфизм: вызов статического непереопределяемого метода сheckForCarCollision, в зависимости от типа объекта, для которого он будет вызван, будет приводить к разной реакции на наезд машины.

Для сокращения затрат на частый вызов метода (метод будет вызываться для каждого дорожного объекта при каждом отображении трассы), метод сheckForCarCollision определен внутри класса и, соответственно, является inline.

4.3 Класс СFuelBar

Класс является наследником от CBar. Описание класса выглядит следующим образом:

class CFuelBar : public CBar

{

private:

WORD capacity;

virtual void draw(CDC* pDC);

virtual void onCarCollision( PCCar car );

public:

CFuelBar(WORD xx=0, WORD yy=0,WORD cap=0): CBar(xx,yy),

capacity(cap) {}

WORD getCap();

virtual ~CFuelBar() { }

};

Экземпляр данного класса представляет с собой канистру с топливом. У класса появляется новое поле - capacity - емкость канситры, и новый метод доступа getCap. Переопределяемый виртуальный метод draw отрисовывает на экране кружок с буквой "F", а описывающий реакцию на "пересечение" с машиной, виртуальный метод onCarCollison реализует процесс пополнения запаса топлива машины.

4.4 Класс CMarking

Класс является наследником от CBar. Описание класса выглядит следующим образом:

class CMarking: public CBar

{

private:

int markingType;

virtual void draw(CDC* pDC);

virtual void onCarCollision(PCCar car );;

public:

CMarking(WORD xx = 0,WORD yy = 0,WORD markType = 0):

CBar(xx, yy), markingType(markType) {}

virtual ~CMarking() { }

};

Экземпляр данного класса представляет собой элемент дорожной разметки. Тип элемента определяется полем markingType. Введение дополнительно поля классификатора типа было сделано для того, чтобы не плодить множество потомков. Например, вместо того чтобы заводить 4 разных объекта, каждый со своим переопределяемым виртуальным методом draw, был введен единственный виртуальный метод draw, который в зависимости от значения markingType, отображает на экране либо разделительную полосу (markingType = 0), либо отбойник (markingType = 1 или 2), либо финишную черту (markingType = 3). Реакция на столкновение с машиной, определяемая виртуальным методом onCarCollision, также зависит от значения markingType: например, наезд машины на правый отбойник отбросит машину влево, на левый - наоборот, вправо.

4.5 Класс CCar

Класс является прямым наследником от CRoadObject. Описание класса выглядит следующим образом:

classCCar: public CRoadObject

{

private:

WORD speed;

WORD fuelLevel;

WORD damageLevel;

WORD maxSpeed;

WORD maxFuelLev

WORD maxDamageLev;

WORD color;

char* speedStr;

char* fuelLevelStr;

char* damageLevelStr;

virtual void draw(CDC* pDC);

public:

CCar(WORD xx=0, WORD yy=0, WORD nmaxSpeed=0, WORD nmaxFuelLev=0,

WORD nmaxDamageLev=0, WORD ccolor=0): CRoadObject(xx,yy),

maxFuelLev(nmaxFuelLev), maxSpeed(nmaxSpeed),

maxDamageLev(nmaxDamageLev), color(ccolor),

speed(10), fuelLevel(nmaxFuelLev), damageLevel(0) {};

WORD getSpeed() { return speed; }

WORD getDamage() { return damageLevel; }

WORD getFuel() { return fuelLevel; }

void changeSpeed(int speedChange);

void changeFuelLev(int fuelChange);

void changeDamageLev(int damgeChange);

bool isDestroyed() {

return( damageLevel >= maxDamageLev ? 1 : 0); }

bool isEmpty() {

return( fuelLevel >= maxFuelLev ? 1 : 0); }

CString getSpeedStr() {

CString s1;

_ultoa(speed,speedStr,10);

return (s1 = speedStr );

}

CString getDamageStr() {

CString s1;

_ultoa(damageLevel,damageLevelStr,10);

return (s1 = damageLevelStr );

}

CString getFuelStr() {

CString s1;

_ultoa(fuelLevel,fuelLevelStr,10);

return (s1 = fuelLevelStr );

}

virtual ~CCar() { }

};

Объект данного типа представляет собой гоночный болид. Виртуальный метод draw рисует на экране закрашенный прямоугольник определенного цвета и четыре черных прямоугольника по бокам, изображающие колеса. Как и у любой гоночной машины, у экземпляра данного класса есть свойства скорости, уровня топлива, уровня повреждения и значения пределов этих параметров, отраженные в соответствующих полях объекта speed, fuelLevel, damageLevel,maxSpeed, maxFuelLev, maxDamageLev. Значения этих полей инициализируются конструктором при создании объекта. Интерфейсные методы getSpeed, getDamage, getFuel позволяют получить значения текущей скорости, уровня повреждения и уровня топлива соответственно. Для случая, когда программе просто нужно проверить, может ли машина продолжать движение, предусмотрены методы isDesrtoyed и isEmpty, которые возвращают значение true, если она окончательно повреждена или у нее закончилось топливо. Эти методы доступа, как и у выше описанных объектов, определены как inline.

Некоторые объекты (препятствия, канистры, отбойники) могут воздействовать на машину, изменяя значения скорости, уровня топлива и уровня повреждения. Поэтому необходимо наличие специальных методов доступа changeSpeed, changeFuelLev, changedamageLev, которые обеспечивают возможность этого воздействия.

Чтобы облегчить жизнь программисту, использующему объект, предусмотрены строковые поля speedStr, fuelLevelStr, damageLevelStr, хранящие значения скорости, уровня топлива и уровня повреждения в строковом виде, а также методы доступа к этим полям (getSpeedStr, getFuelStr и getDamageStr соответственно)

4.5 Класс CRoad

Объект типа CRoad введен для систематизированного управления всеми "пассивными" дорожными объектами (то есть препятствиями, канистрами и элементами разметки). Он представляет собой некую "базу данных" всех видимых объектов на трассе. Класс имеет следующее описание:

class CRoad

{

private:

WORD x,y;

CBar* objs[ MAX_ROAD_OBJECTS ];

CCar* carOnRoad ;

WORD width,height;

WORD length;

int passed;

CMyTime maxRaceTime;

void removeOldObjs();

void generateNewObjs();

int findFirstNil();

public:

CRoad(WORD xx=0,WORD yy=0,WORD widthInGridX=10,WORD heightInGridY=20,

WORD lenghtInGridY=100,CCar* car=NULL);

void showRoad(CDC* pDC);

void listDown();

bool isFinished(){ return ((passed==lenght) ? true : false); }

bool isRaceTimeElapsed(CMyTime userTime){

if ( (maxRaceTime - userTime) < 100 ) return true;

else return false;

}

CString getMaxRaceTimeStr() { return maxRaceTime.getTimeStr(); }

~CRoad();

};

Экземпляр данного класса представляет собой закрашенный прямоугольник серого цвета, изображающий дорожное полотно, на котором могут отображаться всевозможные дорожные объекты. Список указателей на эти дорожные объекты хранится в массиве objs. Тип элементов данного массива был выбран по правилу совместимости типов объектов, согласно которому указателю на экземпляр объекта-предка можно присвоить указатель на экземпляр любого из его потомков. Соответственно, тип элемента массива должен быть тип-указатель на объект-родитель, т.е указатель на CBar.

Как говорилось выше, связывание объекта Трасса и объекта Машина отношением агрегации необходимо, для обеспечения взаимодействия объектов типа Препятствия (и наследников) и Машины (препятствия должны знать, какую машину им необходимо повредить при столкновении). Этим объясняется наличие у объекта Трасса поля carOnRoad типа указатель на объект-машину. Это поле инициализируется конструктором при создании трассы (то есть объект Машина должен быть создан до момента создания объекта Трасса). Кроме того, конструктор инициализирует следующие поля:

· width, height - ширина и высота отображаемой на экране части трассы, указанная в неких условных единицах сетки - GRIDX(ширина клетки сетки) и GRIDY(высота клетки сетки) Значения констант GRIDX и GRIDY указаны в модуле consts.pas

· length - длина трассы в единицах GRIDY. В соответствии со значением length инициализируется значение максимально возможного времени прохождения трассы - maxRaceTime.

· passed - длина пройденной части трассы в единицах GRIDY

Как упоминалось при формулировке требований к программе, движение гоночной машины по трассе должно имитироваться путем движения окружающих ее дорожных объектов навстречу ей. Это действие осуществляется методом listDown, который выполняет 3 действия:

1. изменяет координаты всех дорожных объектов так, чтобы при следующем отображении все они переместились на экране вниз на GRIDY

2. добавляет в массив objs регулярные элемент разметки, объекты-препятствия и объекты-канистры. Число и координаты препятствий и канистр генерируется случайным образом так, чтобы при следующем отображении они, в случайном порядке, заполнили освободившееся после шага 1 место экрана.

3. удаляет из массива objs невидимые больше элементы

Действия 2), 3) выведены во вспомогательные методы generateObjs и removeOldObjs соответственно. Они объявлены в секции private и вызываются только из метода listDown.

Метод findFirstNil также является вспомогательным и используется для того, чтобы производить поиск в массиве objs свободных элементов. Метод возвращает либо индекс свободного элемента, либо значение "-1", если таковых не найдено.

Видимое перемещение объектов на экране не произойдет до тех пор, пока не будет вызван метод showRoad. Алгоритм данного метода достаточно прост:

1. На экране рисуется закрашенный прямоугольник серого цвета, изображающий дорожное полотно. При этом закрашиваются все старые положения дорожных объектов. Этим и объясняется то, что у дорожных объектов нет отдельного метода, который бы "стирал" объект с экрана - это действие выполняется сразу для всех в методе showRoad.

2. в цикле для элементов массива objs происходит вызов методов show и checkForCarCollision. И, несмотря на то, что формальный тип элементов этого массива один, различие фактических типов элементов и применение механизма позднего связывания приводит к тому, что в итоге вызываются различные версии виртуальных методов draw и onCarCollision.

Для того чтобы управляющий объект имел возможность проверить, пересекла ли машина финишную черту, к интерфейсу объекта типа CRoad добавлен метод isFinished. Проверка на истечение времени гонки осуществляется вызовом метода isRaceTimeElapsed - в метод передается объект-время пользователя в гонке, которое сравнивается с максимально допустимым временем прохождения всей трассы.

Деструктор ~CRoad введен для того, чтобы корректно освобождать память, динамически выделенную под дорожные объекты, в момент завершения работы программы. Тип элементов массива дорожных объектов objs - указатель на CBar. Для того, чтобы при применении оператора delete к элементу данного массива вызывалась нужная версия деструктора, все деструкторы дорожных объектов определены как виртуальные (см. выше).

Метод getMaxRaceTimeStr позволяет управляющему объекту получить значение максимально возможного времени прохождения трассы в строковом виде для вывода его на экран.

4.6 Класс CUser

Экземпляр данного класса представляет пользователя в игре. Описание класса выглядит следующим образом:

class CUser

{

private:

CString name; //eiy iieuciaaoaey

CMyTime timeInGame;//a?aiy iieuciaaoaey a ea?a

PCCar car; //aiii?iay iaoeia iieuciaaoaey

public:

CUser(CString uname = "Без имени",int carNum = 0);

CString getName() { return name; }

PCCar getUserCar(){return car;}

void onSysTick(WORD tickPeriod) {

timeInGame+=tickPeriod;

}

CMyTime getTimeInGame(){ return timeInGame; }

CString getTimeStr() {return timeInGame.getTimeStr(); }

~CUser();

};

Поля данного объекта отражают всю ту информацию, которая характеризует пользователя в игре: это его имя (поле name), гоночная машина, которую он выбрал (поле car), и время, проведенное в игре (поле timeInGame).

Методы getName, getTime, getTimeStr - методы доступа к полям name и timeInGame. С помощью метода getUserCar вызвавший его код может получить доступ к машине пользователя и вызывать ее методы. Эта возможность необходима, например, для Управляющего объекта, чтобы он имел возможность изменять положение/скорость машины в соответствии с нажатыми клавишами клавиатуры. Для сокращения затрат на вызовы методов, эти методы определены как inline.

Метод onSysTick описывает действия, которые необходимо совершать периодически (например, по прерыванию от системного таймера). Это, как минимум, инкрементация времени, проведенного в игре. В параметре tickPeriod указывается, через какие промежутки времени вызывается метод в миллисекундах.

Деструктор ~CUser вводится для того, чтобы по окончании гонки иметь возможность освободить память, выделенную при жизни объекта Пользователь (память выделяется под объект Машина).

4.7 Класс CMyTime

Экземпляры данного класса служат для хранения значений времени в игре. В частности, в объекте Пользователь хранится значение времени, проведенного им в гонке, а в объекте Трасса хранится значение максимально возможного времени прохождения гоночной трассы игроком. Эти два значения постоянно сравниваются Управляющим объектом для принятия решения о неудачном результате игрока. Класс CMyTime имеет следующее описание:

class CMyTime

{

public:

WORD msec; //ieeeenaeoiau

WORD sec; //naeoiau

WORD mins; //ieioou

WORD hour; //?anu

// no?iee aey o?aiaiey no?ie-cia?aiee a?aiaie

char* hstr;

char* mstr;

char* sstr;

char* msstr;

public:

CMyTime(WORD nhour = 0,WORD nmin = 0,WORD nsec = 0,

WORD nmsec = 0):hour(nhour), mins(nmin),sec(nsec), msec(nmsec)

{

hstr=(char*)malloc(3);

mstr=(char*)malloc(3);

sstr=(char*)malloc(3);

msstr=(char*)malloc(4);

}

CMyTime(CMyTime& oldObj);

void set(WORD nhour = 0,WORD nmin = 0,WORD nsec = 0,

WORD nmsec = 0)

{

msec = nmsec; sec = nsec; mins = nmin; hour = nhour;

}

CMyTime& operator+=(WORD millisecs);

long int operator-(CMyTime& rightTime);

void getTime(WORD* rhour, WORD* rmin, WORD* rsec, WORD* rmsec);

CString getTimeStr();

~CMyTime();

};

Аналогично классу CCar, числовые поля объекта hour, mins, sec и msec, значения которых выводятся на экран, дублируются их строковыми аналогами hstr, mstr,sstr и msstr соответственно. Это сделано для того, чтобы избавить код, использующий объект, от необходимости заниматься преобразованием числа в строку.

Как и во всех выше описанных классах, для инициализации полей объекта используется инициализирующий конструктор с параметрами по умолчанию. Инициализация числовых полей объекта происходит в списке инициализации конструктора, а в теле конструктора происходит выделение памяти под heap-поля объекта. Установить время уже после создания объекта можно с помощью метода доступа set.

В отличие от других классов, для этого класса определен еще и собственный копирующий конструктор. Это обусловлено двумя причинами: во-первых, объекты данного класса будут передаваться в параметрах функции, а во-вторых, в объекте содержаться heap-поля, и, соответственно, введение собственного копирующего конструктора вместо стандартного становится обязательным.

Для инкрементирования значения времени на определенную величину был переопределен оператор += (правый его операнд задает величину, на которую инкрементируется значение времени, в миллисекундах).

В игре также будет часто использоваться операция сравнения времени, которое прошло с момента старта гонки, с максимально допустимым временем проведения гонки. Для этой цели переопределим бинарный оператор вычитания - . Он будет давать разницу между значениями времени двух объектов класса CMyTime в миллисекундах.

Методы getTime и getTimeStr позволяют получить значение времени в числовом и строковом форматах соответственно.

4.8 Класс CRace

Как было решено на этапе выделения компонентов приложения, программе необходим главный управляющий объект. Таким объектом является экземпляр класса CRace. Описание класса выглядит следующим образом:

class CRace

{

private:

PCUser player;

PCRoad road;

bool started;

bool finished;

bool canceled;

bool paused;

bool timeElapsed:

bool carBroken;

bool carEmpty;

int currentTick;

public:

CRace(): player(NULL), road(NULL), started(false), finished(false),canceled(false),

paused(false), timeElapsed(false), carBroken(false),

carEmpty(false),currentTick(false) {};

void registerPlayer(PCUser plr);

void onSysTick();

void onKeyDown(UINT keyCode);

void showElements(CDC* pDC);

void start() {started = true; }

bool isStarted() {return started; }

bool isFinished() {return finished; }

bool isPaused() {return paused; }

bool isCanceled() {return canceled; }

bool isTimeElapsed() {return timeElapsed; }

bool isCarBroken() {return carBroken; }

bool isCarEmpty() {return carEmpty; }

CString getMaxRaceTimeStr() { return road->getMaxRaceTimeStr(); }

~CRace();

};

Две основные составляющие игры - это Пользователь, с его машиной, и Трасса, с ее дорожными объектами. Информация о них (указатели на объекты) хранится в полях player и road. Инициализация поля player происходит в методе registerPlayer, который передает Управляющему объекту указатель на созданный ранее объект Пользователь. Создание объекта Трасса и инициализация поля road происходит в этом же методе, (то есть сразу после того, как становится доступным указатель на объект- Машину пользователя).

Группа полей-флагов описывает состояние игрового процесса в текущий момент времени. Флаг finished будет иметь значение true, если гоночная машина успешно финиширует, флаг canceled - когда пользователь нажмет клавишу отмены гонки. Флаг paused будет установлен при желании пользователя приостановить игру. Флаг timeElapsed выставляются при истечении времени, отведенного пользователю на гонку. Флаги carBroken и carEmpty будут равны true, если машина пользователя не может продолжать движение по причине повреждения или отсутствия топлива. Инициализация полей флагов состояния происходит в конструкторе в списке инициализации. Для доступа ко всем флагам состояния игрового процесса предусмотрены соответствующие методы.

Основа любой гонки - это движение. Движение одних объектов относительно других. В данной игре дорожные объекты на экране движутся на встречу неподвижной (по вертикальной оси) машине. Причем движение это (то есть изменение координат дорожных объектов и их отрисовка на новом месте) происходит через дискретные промежутки времени, величина которых находится в обратной зависимости от скорости машины: чем быстрее машина движется, тем чаще меняют свои координаты дорожные объекты. Один "такт" такого движения реализует метод onSysTick. Этот метод вызывается через равные промежутки времени. Алгоритм метода следующий:

1. На основе значения скорости автомобиля и значения поля currentTick, которое является счетчиком тактов, принимается решение, будет ли на этом такте происходить движение дорожных объектов. Если да, то 2, иначе 3.

2. для объекта Трасса вызывается метод listDown, который осуществляет изменение координат дорожных объектов соответствующее их движению навстречу машине. Уровень топлива машины понижается. Счетчик тактов сбрасывается. Переход к 4.

3. инкрементируется счетчик тактов

4. происходит проверка состояния объекта Трасса (не финишировала ли машина на данном такте движения и не закончилось ли отведенное для гонки время) и объекта Машина (не получила ли машина на данном такте движения окончательное повреждение и не закончилось ли у нее топливо). На основе данной проверки происходит установка соответствующих флагов состояния игрового процесса: finished, timeElapsed, carBroken и carEmpty.

Чтобы предоставить внешнему обработчику событий от таймера возможность вызывать метод onSysTick только тогда, когда игра стартовала ( пользователь прошел процедуру инициализации, и были созданы объекты Машина и Трасса ) и, соответственно, избежать ошибок (не вызывать методов не существующих пока объектов), был введен флаг started и соответствующие методы доступа к нему - start и isStarted.

Управляющий объект преобразует внешние управляющие воздействия (нажатия пользователем определенных клавиш на клавиатуре) во внутренние (перемещение машины пользователя, приостановка игрового процесса и т.д.) с помощью метода onKeyDown. В списке параметров этому методу передается код нажатой клавиши.

В паре с методами onSysTick и onKeyDown должен вызываться метод showElements, который сделает изменения, произведенные этим методами, видимыми пользователю. В списке параметров этому методу передается указатель на контекст устройства, в котором необходимо произвести рисование.

Деструктором ~CRace освобождает память, выделенную при создании экземпляра класса CRoad в методе registerPlayer.

5. Разработка алгоритма функционирования программы

Управление нашими объектами в программе будет событийно-ориентированным, т.е. вызовы всех методов будут обусловлены наступлением какого-нибудь внешнего события (нажатием пользователем клавиши, истечением периода таймера и т.д.). В связи с этим, можно выделить несколько типов событий, наступающих во время работы нашей программы:

1. Выбор пользователем пункта меню "Start"

2. Сообщение от таймера

3. Сообщение от клавиатуры

4. Сообщение о перерисовке окна

5. Обработчик сообщения о закрытии окна приложения

Таким образом, весь алгоритм функционирования программы должен быть реализован посредством обработчиков этих событий. MS Visual C++ и MFC предоставляют нам возможность добавить свои программные коды в вышеописанные обработчики. Алгоритмы функционирования обработчиков 1) и 2) представлены на рисунках 5.1 и 5.2.

Рисунок 5.1. Схема алгоритма обработчика выбора пункта меню Start

Рисунок 5.2. Схема алгоритма обработчика события от таймера

Обработка сообщения от клавиатуры состоит в последовательном вызове игрового ( CRace::onKeyDown ) и стандартного обработчиков.

Обработка сообщения о перерисовке окна состоит в вызове метода showElements, который отобразит в окне 2 основных составляющих игрового процесса: трассу и гоночную машину, и выводе основных параметров игрового процесса, таких как: время пользователя в гонке, максимально возможное время прохождения трассы, уровень повреждения, скорость, а также уровень топлива машины пользователя.

В обработчик сообщения о закрытии окна приложения поместим удаление динамически созданного во время работы программы объекта Пользователь.

6. Отладка и тестирование программы

Отладка и тестирование программы производились в среде разработки Microsoft Visual C++ версии 6.0. На этапе первоначальной отладки были исправлены все неточности и ошибки. Исходный текст программы состоит из автоматически сгенерированных средой разработки файлов и файлов, в которых описаны и реализованы пользовательские классы. (Roadobj.h, Roadobj.cpp - описание и реализация класса CRoadObject и всех его наследников; Road.h, Road.cpp - описание и реализация класса CRoad; User.h, User.cpp - описание и реализация классов CUser и CMyTime; CRace.h, CRace.cpp - описание и реализация класса CRace: Frace_defs.h - файл, содержащий объявления констант ). Общий объем исходных кодов - 115 Кбайт. В результате тестирования, было доказано, что программа является полностью работоспособной, и ее работа соответствует сценарию, описанному в задании. Размер исполняемого файла программы - 44 Кбайт.

При запуске программы на экране появляется окно приложения размером 530 x 530 пикселей. Для старта гонки пользователь должен выбрать в главном меню приложения пункт "Start". После этого на экране появляется диалоговое окно (см. рис. 6.1 ) с полем для ввода имени игрока и кнопками, для выбора гоночной машины. После нажатия на кнопку "OK" стартует сама гонка и в окне приложения появляется гоночная трасса с препятствиями. Внизу расположена машина пользователя, которой ему необходимо управлять. При нажатии пользователем клавиш курсора машина меняет ряд движения или изменяет свою скорость. Значение скорости, наряду с другими параметрами машины и игры, отображается правом верхнем углу окна. Нажатием клавиши "P" игру можно приостановить (повторное нажатие возобновит игру). Вид окна приложения во время игрового процесса изображен на рисунке 6.2

Рисунок 6.1 Диалоговое окно регистрации

Рисунок 6.2. Окно приложения во время игрового процесса

После окончания гонки пользователю выводится сообщение, в котором указан его результат. После его закрытия игроку остается лишь завершить работу с приложением, закрыв основное окно программы.

При тестировании программы пользователями были высказаны следующие предложения по развитию и модернизации программы:

· Можно разнообразить структуру дорожных объектов, введя новый объект - "ремонтный бокс" (машина может понизить свой уровень повреждения остановившись в нем)

· Желательно ввести в программу возможность считывания и записи результатов игроков в файл.

Разработанная программа является легко модифицируемой, и добавление новых объектов в программу не является сложной задачей.

Заключение

В результате курсового проектирования была написана и отлажена объектно-ориентированная программа, реализующая игру "Гонка на время с препятствиями". Программа является полностью объектно-ориентированной и демонстрирует основные идеи ООП: инкапсуляцию, наследование и полиморфизм.

Принцип инкапсуляции проявляется в том, что манипулирование с объектом в программе идет как с единым неделимым целым. Объекты создаются, уничтожаются, выполняют по запросу определенные действия, но при этом их внутренняя структура остается невидимой для внешнего мира.

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

Одно из определений, которое можно дать понятию Полиморфизм, следующее: различные по внутренней структуре, свойствам и назначению объекты могут выполнять одинаковые по смыслу (с точки зрения программиста) действия по-разному, в зависимости от своей природы и внутреннего устройства. Таким образом, если объекты полиморфны, можно описать некий шаблон действий с использованием определенных методов, а потом применять к разным объектам этот шаблон действий, при этом результаты будут разными. Это позволяет легко добавлять новые объекты в программу: достаточно будет сделать их членами цепочки наследования и переопределить виртуальные методы, характеризующие их поведение - что самое главное, шаблон работы с объектами при этом не изменится!

Список использованных источников

1. Вышеславцев А.П. Курс лекций "Объектно-ориентированное программирование", 10-й семестр 2006 г.

2. Bruce Eckel. "Thinking in С++.2nd ed. Volume 1". 2000г.

3. Мешков А. Тихомиров Ю. "Visual C++ и MFC", БХВ-Петербург, 2004.

4. Справочная система Microsoft Developer Network - April 2003.

Приложение

Листинг программы

/**********************************************************

*

* Oaee: roadobj.h

* Iienaiea:

* oaee niaa??eo iienaiea eeanna NRoadObject e anao aai ianeaaieeia

*

************************************************************/

#ifndef _ROADOBJ_H_

#define _ROADOBJ_H_

#include "frace_defs.h"

/************************************************************

* Aano?aeoiue eeann - ai?i?iue iauaeo

***********************************************************/

class CRoadObject

{

protected:

WORD x,y; //eii?aeiaou ei?iaaie oi?ee iauaeoa

/*-------------------------------------------------------------

- Eeann CRoadObject. Ae?ooaeuiue iaoia draw

- i?ioaao?a, io?eniauaa?uay iauaeo ia ye?aia

---------------------------------------------------------------*/

virtual void draw(CDC* pDC) = 0;

public:

/*-------------------------------------------------------------

- Eeann CRoadObject.

- eieoeaeece?o?uee eiino?oeoi? n def ia?aiao?aie

- xx,yy - ia?aeuiua eii?aeiaou ei?iaaie oi?ee iauaeoa

-------------------------------------------------------------*/

CRoadObject(WORD xx=0 , WORD yy =0): x(xx), y(yy) {};

/*-------------------------------------------------------------

- Eeann CRoadObject. Iaoia show

- i?ioaao?a, ioia?a?aao iauaeo ia ye?aia

-------------------------------------------------------------*/

void show(CDC* pDC) {draw(pDC);}

/*-------------------------------------------------------------

- Eeann CRoadObject. Iaoia move

- i?ioaao?a, eciaiyao eii?aeiaou iauaeoa

- nx,ny - eciaiaiea eii?aeiao

- fromOldPlace - a neo?aa =true,nx,ny ?anoaieaa?ony eae

niauaiea ioiineoaeuii noa?uo

=false, nx ny ?anoaieaa?ony eae

iiaua aanie?oiua eii?aeiaou iauaeoa

-------------------------------------------------------------*/

void move(int nx, int ny, int fromOldPlace)

{

if (fromOldPlace) { x += nx; y += ny;}

else { x = nx; y = ny;}

}

/*-------------------------------------------------------------

- Eeann CRoadObject. Iaoia getX

- ooieoey, aica?auaao oaeouo? eii?aeiaoo O

-------------------------------------------------------------*/


Подобные документы

  • Введение в объектно-ориентированное программирование. Постановка задачи. Описание алгоритма решения в псевдокоде (команды в виде текста на русском языке). Исходный текст программы на С. Тестирование программы. Модификация программы. Полиморфизм.

    курсовая работа [294,0 K], добавлен 08.09.2008

  • Разработка объектно-ориентированной модели информационной подсистемы учета студентов университета во время экзаменационной сессии с помощью программы Rational Rose 2000, с использованием языка UML. Порядок генерации программного кода на языке С++.

    курсовая работа [689,9 K], добавлен 21.06.2011

  • Разработка программы логической игры в "крестики-нолики" пять в ряд на поле размера 15х15 клеток с применением графики на языке Pascal с использованием объектно-ориентированного программирования. Структура алгоритма программы и описание ее работы.

    курсовая работа [821,5 K], добавлен 13.02.2012

  • Разработка объектно-ориентированной модели животного, которая объясняется построением модели игры Terrarium. Модель построена на базе концепций объектно-ориентированного программирования. Разработка компонента, моделирующего поведение животного.

    курсовая работа [23,2 K], добавлен 30.11.2008

  • Разработка и реализация компьютерной игры "Змейка" с помощью языка программирования Pascal и модуля CRT. Составление общего алгоритма программы, выделение ее функциональных частей. Разработка тестовых примеров. Использование типизированных файлов.

    курсовая работа [2,1 M], добавлен 23.02.2011

  • Что такое класс в объектно-ориентированном программировании. Какую структуру имеет модуль в С++. Какими средствами осуществляется консольный ввод данных в языке Си, С++. Инкапсуляция, полиморфизм, наследование. Использование библиотеки "".

    контрольная работа [1,9 M], добавлен 13.11.2016

  • Разработка объектно-ориентированной подсистемы складского учета для фирмы "КавказЮгАвто". Краткая характеристика предметной области. Построение диаграмм размещения, прецедентов, последовательности, компонентов и классов. Генерация программного кода C++.

    курсовая работа [6,6 M], добавлен 26.06.2011

  • Принципы и алгоритмы обработки прерываний. Набор действий по реализации этапов обработки прерываний микропроцессора. Разработка структуры и алгоритма резидентной программы. Реализация программы на языке Ассемблер, методы её отладки и тестирования.

    курсовая работа [348,7 K], добавлен 22.12.2014

  • Изучение принципов объектно-ориентированного программирования. Понятие класса в Delphi, в основе которых лежат три фундаментальные принципы - инкапсуляция, наследование и полиморфизм. Разработка классов транспортных средств и структур классов (кошки).

    курсовая работа [29,7 K], добавлен 29.10.2011

  • Разработка программы с использованием принципов объектно-ориентированного программирования на языке высокого уровня С средствами Microsoft Visual Studio 2010. Построение алгоритма реализации. Класс программы, инструкция по использованию программы.

    курсовая работа [1,0 M], добавлен 26.12.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.