Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Шахматы настольная логическая игра, сочетающая в себе элементы искусства, науки и спорта. Одна из древнейших игр на Земле, сохранившихся до нашего времени; долгое время считалась игрой королей и аристократов. Играется двумя игроками по определённым правилам. Ежегодно в мире проводятся тысячи различных соревнований по шахматам. Широко распространено мнение, что шахматы, как и другие настольные логические игры, способствуют развитию умственных способностей, памяти, творческого мышления. (материал из Википедии свободной энциклопедии)
С момента своего появления, шахматы завоевали огромную популярность и множество фанатов. К этому моменту уже написано большое количество программ, так или иначе связанных с шахматами. Но эта тема не теряет актуальности, и поэтому написание программы, реализующей игру в шахматы между двумя людьми является хорошей задачей.
Целью проекта является написание игры «Шахматы» с 3D интерфейсом и возможностью игры по сети.
Для достижения этой цели были поставлены следующие задачи:
Типы, определяемые с ключевыми словами struct (структура), class (класс) и union (объединение), являются классами. Отличия их сводятся к следующему:
Элементы, не помеченные никаким из спецификаторов, в структурах имеют доступ public (открытый); в классах private (закрытый).
Спецификации доступа
Конструктор и деструктор
Конструктор отвечает за создание представителей данного класса. Его объявление записывается без типа возвращаемого значения и ничего не возвращает, а имя должно совпадать с именем класса. Конструктор может иметь любые параметры, необходимые для конструирования, т. е. создания, нового представителя класса. Если конструктор не определен, компилятор генерирует конструктор по умолчанию, который просто выделяет память, необходимую для размещения представителя класса.
Деструктор отвечает за уничтожение представителей класса. Это происходит либо в случае, когда автоматический объект данного класса выходит из области действия, либо при удалении динамических объектов, созданных операцией new.
Деструктор объявляется без типа возвращаемого значения, ничего не возвращает и не имеет параметров. Если деструктор не определен, генерируется деструктор по умолчанию, который просто возвращает системе занимаемую объектом память.
Элементы-функции (методы)
Функция-элемент класса объявляется внутри определения класса. Там же может быть расположено и определение тела функции. В этом случае функцию-элемент называют встроенной и компилятор будет генерировать ее встроенное расширение на месте вызова. Если определение функции располагается вне тела класса, то к ее имени добавляется префикс, состоящий из имени класса и операции разрешения области действия. В этом случае функцию-элемент также можно определить как встроенную с помощью ключевого слова inline.
Наследование (inheritance) - это процесс, посредством которого один объект может приобретать свойства другого. Точнее, объект может наследовать основные свойства другого объекта и добавлять к ним черты, характерные только для него. Наследование является важным, поскольку оно позволяет поддерживать концепцию иерархии классов (hierarchical classification). Применение иерархии классов делает управляемыми большие потоки информации.
Без использования иерархии классов, для каждого объекта пришлось бы задать все характеристики, которые бы исчерпывающи его определяли. Однако при использовании наследования можно описать объект путём определения того общего класса (или классов), к которому он относится, с теми специальными чертами, которые делают объект уникальным. Наследование играет очень важную роль в OOП.
Инкапсуляция (encapsulation) - это механизм, который объединяет данные и код, манипулирующий этими данными, а также защищает и то, и другое от внешнего вмешательства или неправильного использования. В объектно-ориентированном программировании код и данные могут быть объединены вместе.
Внутри объекта коды и данные могут быть закрытыми (private). Закрытые коды или данные доступны только для других частей этого объекта. Таким образом, закрытые коды и данные недоступны для тех частей программы, которые существуют вне объекта. Если коды и данные являются открытыми, то, несмотря на то, что они заданы внутри объекта, они доступны и для других частей программы. Характерной является ситуация, когда открытая часть объекта используется для того, чтобы обеспечить контролируемый интерфейс закрытых элементов объекта.
Полиморфизм (polymorphism) (от греческого polymorphos) - это свойство, которое позволяет одно и то же имя использовать для решения двух или более схожих, но технически разных задач. В ООП полиморфизм играет основополагающую роль. Он даёт программисту большие возможности по увеличению абстракции и гибкости программы. Смысл его в том, что при обращении к объекту через указатель, активируется механизм, который разрешает, к какому типу принадлежит данный объект на этапе выполнения, и вызывает соответствующий метод. Этот механизм организуется при помощи виртуальных (virtual) функций. При добавлении к классу виртуальных функций, автоматически создается таблица виртуальных функций (Virtual Functions Table), благодаря которой и происходит вызов «правильного» метода.
Следует отметить наличие в C++ такого механизма, как RTTI (Real Time Type Identification) идентификация типов во время выполнения. Этот механизм тесно связан с полиморфизмом. Для использования RTTI в C++ реализованы функция typeid и структура type_info.
Подобно тому, как класс является схемой для создания своих представителей-объектов, шаблон класса в C++ является схемой для образования конкретных представителей-классов шаблона, или шаблонных классов. Шаблоны классов называют иногда параметризованными типами, поскольку действительный тип (класс) создается посредством спецификации конкретных параметров шаблона.
Шаблон в C++ определяется следующим образом:
template<class T> class A{…};
template<class T> void F(…){…};
Процесс генерации кода из шаблоны с определёнными параметрами называется инстанцированием:
A<int> a;
F<float>();
3.6.Исключения
Исключение это аномальное поведение во время выполнения, которое программа может обнаружить, например: деление на 0, выход за границы массива или истощение свободной памяти. Такие исключения нарушают нормальный ход работы программы, и на них нужно немедленно отреагировать. В C++ имеются встроенные средства для их возбуждения и обработки. С помощью этих средств активизируется механизм, позволяющий двум несвязанным (или независимо разработанным) фрагментам программы обмениваться информацией об исключении.
Для генерации исключения используется оператор throw(). В качестве параметров он принимает какой-либо объект, это может быть, например, строка. Чтобы перехватить исключение используется следующая конструкция:
try
{
…
}
…
catch()
{
}
…
Если код в блоке try сгенерирует исключение, оно будет перехвачено блоком catch, аргумент которого объект того же типа, что и объекта, который передается в оператор throw() при генерации исключения. Если исключение не будет перехвачено, выполнение программы будет аварийно прервано. Для перехвата всех исключений всех типов можно использовать catch(…). После блока try может идти произвольное количество блоков catch() с различными типами аргументов.
Отмечу, что основными критериями при выборе библиотек были: кроссплатформенность и бесплатность.
OpenGL (Open Graphics Library открытая графическая библиотека) спецификация, определяющая независимый от языка программирования кросс-платформенный программный интерфейс для написания приложений, использующих двумерную и трехмерную компьютерную графику. Включает более 250-ти функций для рисования сложных трехмерных сцен из простых примитивов. Используется при создании видео-игр, САПР, виртуальной реальности, визуализации в научных исследованиях. Под Windows конкурирует с Direct3D. OpenGL ориентируется на следующие две задачи:
Основным принципом работы OpenGL является получение наборов векторных графических примитивов в виде точек, линий и полигонов с последующей математической обработкой полученных данных и построением растровой картинки на экране и/или в памяти. Векторные транформации и растеризация выполняются графическим конвейером (graphics pipeline), который по сути представляет из себя дискретный автомат. Абсолютное большинство команд OpenGL попадают в одну из двух групп: либо они добавляют графические примитивы на вход в конвейер, либо конфигурируют конвейер на различное исполнение трансформаций.
OpenGL является низкоуровневым, процедурным API, что вынуждает программиста диктовать точную последовательность шагов, чтобы построить результирующую растровую графику (императивный подход). Это является основным отличием от дескрипторных подходов, когда вся сцена передается в виде структуры данных (чаще всего дерева), которое обрабатывается и строится на экране. С одной стороны императивный подход требует от программиста глубокого знания законов трёхмерной графики и математических моделей, с другой стороны даёт свободу внедрения различных инноваций. Стандарт OpenGL, с появлением новых технологий, позволяет отдельным производителям добавлять в библиотеку функциональность через механизм расширений. Расширения распространяются с помощью двух составляющих: заголовочный файл, в котором находятся прототипы новых функций и констант, а также драйвер устройства, поставляемого разработчиком. Каждый производитель имеет аббревиатуру, которая используется при именовании его новых функций и констант. Например, компания NVIDIA имеет аббревиатуру NV, которая используется при именовании ее новых функций, как, например, glCombinerParameterfvNV(), а также констант, GL_NORMAL_MAP_NV. Может случиться так, что определенное расширение могут реализовать несколько производителей. В этом случае используется аббревиатура EXT, например, glDeleteRenderbuffersEXT. В случае же, когда расширение одобряется Консорциумом ARB, оно приобретает аббревиатуру ARB и становится стандартным расширением. Обычно, расширения, одобренные Консорциумом ARB включаются в одну из последующих спецификаций OpenGL. Список зарегистрированных расширений можно найти в официальной базе расширений (www.opengl.org).
Расширение VBO (Vertex Buffer Object). Одним из самых "узких" мест в работе с современным графическим ускорителем является передача ему данных - текстур, вершин, нормалей и т.п. Для повышения быстродействия следует уменьшить количество запросов на передачу данных и передавать их как можно большими частями.
Если с текстурами все достаточно просто - они один раз загружаются в память графического ускорителя и больше не изменяются, то с геометрическими данными (координаты вершин, нормали, текстурные координаты) дело обстоит значительно хуже.
Стандартный способ передачи данных через команды glVertex, glNormal и т.п. является крайне неэффективным, поскольку передача данных осуществляется очень маленькими частями и через очень большое количество вызовов.
Можно заметно повысить эффективность передачи данных при помощи использования так называемых вершинных массивов (vertex arrays). При этом данные хранятся в памяти CPU и ускорителю передается только указатель на них и размер. При использовании этого способа передача осуществляется сразу большими блоками, количество обращений к графическому ускорителю (GPU) заметно сокращается. Это приводит к гораздо более эффективному использованию GPU и общему повышению быстродействия программы. Однако этот способ требует постоянной передачи массивов данных от CPU к GPU - каждый вызов, в котором используется указатель на массив приводит к передаче его графическому ускорителю и именно эта постоянная необходимость передачи большого объема данных ограничивает эффективность приложения. В то же время далеко не все передаваемые данные действительно изменяются для каждого их использования, достаточно большой объем информации или вообще не изменяется (данные для статических объектов, текстурные координаты и т.п.) или изменяется сравнительно редко или небольшими частями. Поэтому было бы гораздо эффективнее сразу загрузить такие данные в память GPU, после чего использование этих данных графическим ускорителем не будет требовать их передачи от CPU. Именно подобную возможность и предоставляет пользователю расширение ARB_vertex_buffer_object. Использование данного расширения позволяет кэшировать (хранить) различные типы данных (как информацию о вершинах, так и индексы в массив вершин) в быстрой памяти графического ускорителя. Для этого блоки данных помещаются в так называемые вершинные буфера (vertex buffer objects), при этом каждый такой буфер представляет из себя просто массив байт.
HawkNL кросс-платформенная библиотека для работы с сокетами. Поддерживает работу с протоколами TCP/IP, UDP.
Обёртка над библиотекой pthreads, полностью совместима с набором стандартов POSIX (Portable Operating System Interface for uniX), используется для управления потоками.
CEGUI кросс-платформенная библиотека для создания GUI (Graphic User Interface). Поддерживает различные графические API (Application Programming Interface): OpenGL, DirectX и т.д. Для хранения данных используется XML (eXtensible Markup Language). Предоставляет большинство необходимых визуальных компонентов: кнопки, списки, формы и т.д.
zlib свободная кроссплатформенная библиотека для сжатия данных, созданная Жан-лу Галли (фр. Jean-loup Gailly) и Марком Адлером (англ. Mark Adler). Является обобщением алгоритма сжатия данных DEFLATE, используемого в их компрессоре данных gzip. Первая публичная версия 0.9, выпущена 1 мая, 1995 для использования вместе с библиотекой libpng. Распространяется под лицензией zlib.
TinyXML маленькая, но очень удобная библиотека для анализа XML-файлов.
SDL (Simple DirectMedia Layer) это кроссплатформенная мультимедийная библиотека, реализующая единый программный интерфейс к графической подсистеме, звуковым устройствам и средствам ввода для широкого спектра платформ. Данная библиотека активно используется при написании мультемедийных программ (в основном игр) для операционной системы Linux.
Официально поддерживаются операционные системы: Linux, Windows, BeOS, MacOS, MacOS X, FreeBSD, OpenBSD, BSD/OS, Solaris, IRIX и QNX. Так же есть неофициальная поддержка для Windows CE, AmigaOS, MorphOS, Dreamcast, Atari, NetBSD, AIX, OSF/Tru64, RISC OS и SymbianOS.
SDL API доступны для языков: C, C++, Ada, Eiffel, Java, Lua, ML, Perl, PHP, Pike, Python и Ruby.
Техника «слот-сигнал» подразумевает передачу сообщений объектам напрямую, путём вызова соответствующих функций. При этом объект сам должен привязать функции к нужным сообщениям. Этот подход во многом превосходит стандартный «Оконный цикл» операционных систем семейства Windows. Из наиболее распространённых реализаций этой техники можно отметить boost::signal (www.boost.org). В данном проекте используется собственная реализация:
class SLOT
{
public:
SLOT(){};
~SLOT(){};
template<typename T>
void Connect(void (T::*function)(void *args),T *obj){ slot = new __Slot<T>(function,obj); };
void Call(void *args) { slot->Call(args); };
private:
struct __SlotBase
{
virtual void Call(void *args) = 0;
} *slot;
template<typename T>
struct __Slot : public __SlotBase
{
typedef void (T::*Function)(void *args);
__Slot(Function fn,T *Obj):function(fn),obj(Obj){};
void Call(void *args) { (obj->*function)(args); };
private:
Function function;
T *obj;
};
};
Техника «синглтон» (Singleton) это шаблон, гарантирующий, что класс имеет только один экземпляр, и обеспечивающий глобальный доступ к этому экземпляру. Другими словами, синглтон - это усовершенствованный вариант глобальной переменной. Преимущества синглтонов перед глобальными переменными:
1) Синглтон гарантирует, что будет создан только один экземпляр класса.
Например, классы Log, SceneManager, ResourceManager (обычно любой менеджер в системе по сути синглтон) и т.п. могут существовать только в единственном экземпляре . Глобальная переменная не удовлетворяет этому требованию, так как раз мы можем создать один экземпляр класса, значит мы можем "наплодить" хоть сколько таких же экземпляров, что в большинстве случаев приведёт к краху программы. При использовании синглтона, мы просто делаем конструктор класса private и объявляем синглтон-контейнер другом этого класса.
2) При использовании синглтонов почти не нужно заботиться о порядке создания глобальных объектов.
Например, класс SceneManager при конструировании использует класс Log, который в свою очередь при конструировании использует класс ResourceManager.
В 99% случаев если классы созданы не в том порядке (надо ResourceManager, Log и последним SceneManager), программа упадёт ещё до входа в функцию main(). При использовании глобальных переменных, приходится вникать в код каждого из "глобальных" классов, чтобы понять как они взаимосвязаны и инициализировать их в правильном порядке. Причем, если глобальные переменные находятся в разных модулях программы, то порядок их создания не специфицирован! Эта проблема с лёгкостью решается при помощи синглтонов: объект в них конструируется только при первом обращении к нему. Проблемы могут возникнуть только если конструкторы нескольких классов "зациклены" друг на друга.
3) Не нужно заботиться об удалении глобальных объектов.
А что если при уничтожении, SceneManager захочет сообщить что-либо архиважное классу Log, а того уже и нет в помине? Иногда надо, чтобы некоторые уничтоженные объекты снова "оживали" при надобности. С помощью обычных глобальных переменных эта проблема не решаема. У синглтонов и с этим всё в порядке: достаточно лишь "сказать" контейнеру, что если объект уже уничтожен, а к нему кто-то обращается, его нужно восстановить.
В данном проекте используется собственная реализация синглтонов:
template<typename T> class Singleton
{
public:
Singleton()
{
assert(!ms_Singleton);
ms_Singleton = (T *)this;
};
~Singleton()
{
assert(ms_Singleton);
ms_Singleton = 0;
};
static T &getSingleton()
{
assert(ms_Singleton);
return *ms_Singleton;
}
static T *getSingletonPtr()
{
return ms_Singleton;
}
protected:
static T *ms_Singleton;
};
16