Архитектура проекта D3D_MouseZones

Проект содержит четыре уникальных файла: main.cpp, main.h, MouseZoneClass.cpp и MouseZoneClass.h. Файлы с кодом класса для реализации активных зон являются основным отличием этого проекта от предыдущего.

Программе необходимы следующие библиотеки: d3d9.lib, dxguid.lib, d3dx9dt.lib, d3dxof.lib, comctl32.lib и winmm.lib.

Заголовочный файл Main.h
Главный заголовочный файл проекта называется main.h. В нем я выполняю следующие действия:

Устанавливаю глобальные переменные Direct3D.
Устанавливаю структуру настраиваемого формата вершин.
Устанавливаю различные глобальные переменные.
Определяю прототипы функций.
Устанавливаю глобальные данные активных зон.
Устанавливаю глобальные данные кнопок мыши.
Глобальные данные активных зон
Большая часть кода в файле main.h аналогична содержимому одноименного файла из предыдущего примера. Главные отличия заключаются в добавлении глобальных переменных и прототипов функций для работы с активными зонами. Поскольку для активных зон в данном примере используется отдельный класс, в файл main.h добавлено совсем немного нового кода. Взгляните на приведенный ниже листинг, обратив особое внимание на строки выделенные полужирным курсивом:

#define STRICT
#include
#include
#include
#include
#include
#include
#include
#include "DXUtil.h"
#include "D3DUtil.h"
#include "MouseZoneClass.h"

// Глобальные переменные
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pVBInterface = NULL;
// Глобальный массив для хранения графики интерфейса
LPDIRECT3DTEXTURE9 g_pTexture[32];
// Смещения видимой области окна
int g_iXOffset = 0;
int g_iYOffset = 0;
// Размеры окна
int g_iWindowWidth = 640;
int g_iWindowHeight = 480;
// Структура данных нашего настраиваемого формата вершин
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; // Местоположение
D3DXVECTOR3 vecNorm; // Нормаль
FLOAT tu, tv; // Координаты текстуры
FLOAT tu2, tv2; // Координаты текстуры
};
// Описание структуры настраиваемого формата вершинwh
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2)

void vDrawInterfaceObject(int iXPos, int iYPos, float fXSize, float fYSize,
int iTexture);
void vInitInterfaceObjects(void);
LRESULT WINAPI fnMessageProcessor(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT InitD3D(HWND hWnd);
void vRender(void);
void vCleanup(void);
void vCheckInput(void);
void vSetupMouseZones(int iMenu);

// Глобальный класс активных зон
MouseZoneClass MZones;
// Идентификатор текущего меню
int g_iCurrentScreen = 0;
// Переменные состояния кнопок мыши
bool g_bLeftButton = 0;
bool g_bRightButton = 0;
// Глобальный дескриптор окна игры
HWND g_hWnd;

Первым отличием приведенного кода является наличие директивы включения заголовочного файла MouseZoneClass.h. Этот файл содержит заголовочные данные класса MouseZoneClass. Я подробно опишу его чуть позже. До этого момента просто помните о необходимости включать этот файл каждый раз, когда вы хотите использовать класс MouseZoneClass.

Теперь обратите внимание на два новых прототипа функций. Первая из функций, называемая vCheckInput(), проверяет состояние мыши и обрабатывает полученные результаты. Вторая функция называется vSetupMouseZones() и устанавливает необходимые горячие точки в зависимости от текущего меню.

Следующий фрагмент нового кода создает глобальный объект MouseZoneClass. Он называется MZones и используется для всех активных зон, присутствующих в игре.

Затем в коде расположилась целочисленная переменная с именем g_iCurrentScreen. Она отслеживает в каком меню пользователь находится в данный момент. Это необходимо для того, чтобы программа знала какие команды меню ей обрабатывать.

Следующие две новых строки содержат переменные состояния кнопок мыши. Кнопка мыши может быть либо нажата, либо отпущена, так что для хранения ее состояния можно воспользоваться логической переменной. Когда значение переменной равно 0, — кнопка отпущена. Когда значение переменной равно 1, — кнопка нажата.

В последней строке кода создается глобальный дескриптор окна. Он необходим для доступа к созданному программой окну из функций, отличных от WinMain().

Файл программы Main.cpp
Следующий уникальный файл проекта называется main.cpp. Он содержит обычный код Windows и некоторый объем нового кода для обработки ввода от мыши и обнаружения активных зон.

Функция WinMain()
Функция WinMain() в рассматриваемой программе изменилась весьма незначительно. Взгляните на приведенный ниже фрагмент кода, в котором выделены главные отличия:

// Инициализация Direct3D
if(SUCCEEDED(InitD3D(hWnd))) {
// Инициализация виртуального буфера для отображения четырехугольников
vInitInterfaceObjects();
// Инициализация активных зон
vSetupMouseZones(0);
// Начало цикла обработки сообщений
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
// Если все нормально, обработать щелчок мыши
if(timeGetTime() > dwInputTimer) {
// Проверка входных данных
vCheckInput();
dwInputTimer = timeGetTime() + 50;
}

// Проверка необходимости выхода из программы
if(g_iCurrentScreen == 3) {
break;
}

// Визуализация сцены
vRender();
}
}
}

Первым изменением является добавленный вызов функции vSetupMouseZones(). Программа только что была запущена и вызов этой функции необходим для того, чтобы установить активные зоны для первого экрана.
Сейчас изображенный на рис. 6.23 экран должен выглядеть для вас очень знакомо. Это титульный экран игры Battle Armor. Поскольку делать на титульном экране почти нечего, я устанавливаю лишь пару активных зон. Давайте перейдем к коду функции vSetupMouseZones(), чтобы увидеть какие зоны используются на титульном экране.

Функция vSetupMouseZones()
Функция vSetupMouseZones() содержит следующий код:

void vSetupMouseZones(int iMenu)
{
// Титульный экран
if(iMenu == 0) {
MZones.vFreeZones();
MZones.vInitialize(2);
MZones.iAddZone("TITLE_SCREEN", 0, 0, 640, 480, 2);
MZones.iAddZone("EXIT_BUTTON", 587, 0, 53, 24, 0);
}
// Главное меню
else if(iMenu == 1) {
MZones.vFreeZones();
MZones.vInitialize(5);
MZones.iAddZone("EXIT_BUTTON", 587, 0, 53, 24, 0);
MZones.iAddZone("MAINMENU_NEWGAME", 192, 64, 256, 64, 0);
MZones.iAddZone("MAINMENU_LOADGAME", 192, 128, 256, 64, 0);
MZones.iAddZone("MAINMENU_SAVEGAME", 192, 192, 256, 64, 0);
MZones.iAddZone("MAINMENU_OPTIONS", 192, 256, 256, 64, 0);
}
// Экран выхода из игры
else if(iMenu == 2) {
MZones.vFreeZones();
MZones.vInitialize(1);
MZones.iAddZone("TITLE_SCREEN", 0, 0, 640, 480, 2);
}
// Меню параметров
else if(iMenu == 7) {
MZones.vFreeZones();
MZones.vInitialize(5);
MZones.iAddZone("EXIT_BUTTON", 587, 0, 53, 24, 0);
MZones.iAddZone("OPTIONS_AUDIO", 192, 64, 256, 64, 0);
MZones.iAddZone("OPTIONS_VIDEO", 192, 128, 256, 64, 0);
MZones.iAddZone("OPTIONS_DIFF", 192, 192, 256, 64, 0);
MZones.iAddZone("OPTIONS_BACK", 192, 256, 256, 64, 0);
}
}

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

Функция MouseZoneClass::vFreeZones()
Как видите, функция проверяет переданный ей номер меню и действует соответствующим образом. WinMain() при вызове функции передает ей номер меню 0. В коде, относящемся к меню с номером 0, первым расположен вызов функции MouseZoneClass::vFreeZones(). Эта функция удаляет все существующие на данный момент активные зоны и выполняет инициализацию класса активных зон. Это необходимо, так как к титульному экрану можно перейти из других меню.

Функция MouseZoneClass::vInitialize()
Затем вызывается функция MouseZoneClass::vInitialize(). Она выделяет память для активных зон, количество которых передано ей в параметре. Вы можете указать число, превышающее максимальное количество активных зон, которые вы реально будете использовать. Соответствие параметра количеству активных зон необходимо только для повышения эффективности программы.

Затем начинается код действительного создания активных зон. Активные зоны, используемые в титульном экране, создаются путем вызова функции MouseZoneClass::iAddZone(). Титульный экран содержит две активных зоны: одну для кнопки выхода из игры и одну для всего остального экрана. Кнопка выхода из игры направляет пользователя к финальному экрану программы, а вторая активная зона переносит игрока к главному меню.

Функция MouseZoneClass::iAddZone()
Прототип добавляющей активную зону функции выглядит следующим образом:

int MouseZoneClass::iAddZone(
char *szZoneName,
short shX,
short shY,
short shWidth,
short shHeight,
short shClickType)

Первый параметр называется szZoneName и в нем передается имя создаваемой активной зоны. Класс активной зоны использует это имя, чтобы сообщить вам какая из зон была активирована, поэтому оно очень важно. Для титульного экрана используются два имени. Кнопке выхода из игры я присваиваю имя EXIT_BUTTON, а остальной части экрана — имя TITLE_SCREEN.

Следующие два параметра, shX и shY, задают местоположение левого верхнего угла активной зоны. Активные зоны являются прямоугольными, так что данные о местоположении одного из углов необходимы. Все координаты указываются в пространстве рабочей области окна, так что вам не надо беспокоиться об их вычислении. Если вы взглянете на код, то увидите, что зона с именем TITLE_SCREEN начинается в левом верхнем углу экрана с координатами (0, 0).

Следующий параметр называется shWidth. Он задает ширину активной зоны. Ширина окна игры в рассматриваемом примере равна 640 точкам, поэтому я задаю ширину охватывающей весь экран зоны равной 640.

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

Последний параметр называется shClickType и определяет тип щелчка мыши, на который будет реагировать зона.
Тип щелчка позволяет управлять поведением активной зоны в зависимости от состояния кнопок мыши. Помимо прочих применений, это очень полезно для зон, реагирующих на наведение указателя мыши. В рассматриваемом примере я указываю, что зона TITLE_SCREEN будет срабатывать при щелчке любой кнопкой мыши, а зона EXIT_BUTTON реагирует только на щелчок левой кнопкой.

Функция vCheckInput()
Мы покинули функцию WinMain() и перешли к функции vSetupMouseZones(), а затем к членам класса активных зон. Настало время вернуться обратно к функции WinMain() и посмотреть где обрабатываются события мыши. В главной функции находится следующий код:

if(timeGetTime() > dwInputTimer) {
// Проверка входных данных
vCheckInput();
dwInputTimer = timeGetTime()+50;
}

Управление щелчками мыши
Внутри этого небольшого фрагмента кода находится вызов функции vCheckInput(). Возможно, вы недоумеваете для чего здесь нужен вызов функции timeGetTime(). Дело в том, что современные компьютеры настолько быстрые, что единственное нажатие на кнопку мыши длится десятки итераций цикла обработки сообщений. В результате одно нажатие на кнопку мыши активирует пункт меню десятки раз. Это приведет к проблемам, поскольку ваш интерфейс окажется слишком чувствительным к сделанным пользователем щелчкам мышью. Чтобы побороть проблему, я установил таймер, который разрешает обработку сообытия мыши не чаще чем раз в 50 милисекунд. Код проверяет текущее время и смотрит прошло ли 50 милисекунд с момента последнего вызова функции vCheckInput(). Если прошло достаточно времени, функция вызывается снова и таймер сбрасывается. Если время еще не истекло, выполнение кода продолжается, но никакой проверки введенных данных не происходит. Величина 50 милисекунд выбрана мной произвольным образом и вы можете изменить ее в соответствии с вашими вкусами. Если вам непонятно, какой эффект она оказывает, установите значение 0 и запустите пример (попробуйте щелкнуть в меню по кнопке Options).

Функция vCheckInput() проверяет текущую позицию указателя мыши и выполняет действия, зависящие от того, какое меню в данный момент показывается пользователю. Вот как выглядит код этой функции:

void vCheckInput(void)
{
bool bRet;
char szZoneHit[64];
POINT Point;
RECT rcWindowRect;
int iMouseX;
int iMouseY;

// Проверка смещения окна
GetWindowRect(g_hWnd, &rcWindowRect);
// Обновление позиции указателя мыши
GetCursorPos(&Point);
// Вычисление реальных координат указателя мыши
iMouseX = Point.x - g_iXOffset - rcWindowRect.left;
iMouseY = Point.y - g_iYOffset - rcWindowRect.top;
// Проверка попадания в активную зону
bRet = MZones.bCheckZones(
(short)iMouseX, (short)iMouseY,
szZoneHit, g_bLeftButton,
g_bRightButton);
if(bRet) {
// ЛОГИКА ДЛЯ ТИТУЛЬНОГО ЭКРАНА
if(g_iCurrentScreen == 0) {
// Переход к главному меню
if(!stricmp(szZoneHit, "TITLE_SCREEN")) {
// Делаем главное меню текущим
g_iCurrentScreen = 1;
// Устанавливаем активные зоны
vSetupMouseZones(1);
}
// Переход к экрану завершения игры
else if(!stricmp(szZoneHit, "EXIT_BUTTON")) {
// Делаем завершающий экран текущим
g_iCurrentScreen = 2;
// Устанавливаем активные зоны
vSetupMouseZones(2);
}
}
// ЛОГИКА ГЛАВНОГО МЕНЮ
else if(g_iCurrentScreen == 1) {
// Переход к экрану завершения игры
if(!stricmp(szZoneHit, "EXIT_BUTTON")) {
// Делаем завершающий экран текущим
g_iCurrentScreen = 2;
// Устанавливаем активные зоны
vSetupMouseZones(2);
}
else if(!stricmp(szZoneHit, "MAINMENU_NEWGAME"))
{
// Добавьте сюда код для начала новой игры
}
else if(!stricmp(szZoneHit, "MAINMENU_LOADGAME"))
{
// Добавьте сюда код для загрузки игры
}
else if(!stricmp(szZoneHit, "MAINMENU_SAVEGAME"))
{
// Добавьте сюда код для сохранения игры
}
else if(!stricmp(szZoneHit, "MAINMENU_OPTIONS"))
{
// Делаем меню параметров текущим
g_iCurrentScreen = 7;
// Устанавливаем активные зоны
vSetupMouseZones(7);
}
}
// ЛОГИКА ЭКРАНА ЗАВЕРШЕНИЯ ИГРЫ
else if(g_iCurrentScreen == 2) {
// Выходим из программы, если пользователь
// нажмет любую кнопку мыши
if(!stricmp(szZoneHit, "TITLE_SCREEN")) {
// Сообщаем WinMain() о завершении программы
g_iCurrentScreen = 3;
}
}
// ЛОГИКА МЕНЮ ПАРАМЕТРОВ
else if(g_iCurrentScreen == 7) {
// Переход к экрану завершения игры
if(!stricmp(szZoneHit, "EXIT_BUTTON")) {
// Делаем экран завершения игры текущим
g_iCurrentScreen = 2;
// Устанавливаем активные зоны
vSetupMouseZones(2);
}
// Возврат к главному меню
else if(!stricmp(szZoneHit, "OPTIONS_BACK")) {
// Делаем главное меню текущим
g_iCurrentScreen = 1;
// Устанавливаем активные зоны
vSetupMouseZones(1);
}
}
}
}

Вычисление смещения клиентской области окна на рабочем столе
Поскольку окна в операционной системе могут перемещаться, программа сначала должна проверить где именно расположено окно в данный момент. Эту задачу выполняет вызов функции GetWindowRect(). Вооружившись координатами окна, код может вычислить где находился указатель мыши в момент щелчка относительно клиентской области окна. Это очень важный момент, поскольку активные зоны определяются в клиентском пространстве, а не в пространстве рабочего стола.
Поэтому его клиентская область сместилась на 10 точек вправо и на 10 точек вниз. Щелчки мышью по активной зоне теперь регистрируются со смещением на 10 точек по обеим осям. Это вызвано тем фактом, что система передает программе данные о положении указателя мыши в пространстве рабочего стола. Проверка активной зоны использует координаты (340, 10) и не беспокоится о смещении клиентской области окна. Это вызывает проблему, поскольку активаня зона на рис. 6.25 в действительности расположена по координатам (350, 20). Для решения этой проблемы мы вычисляем в каком месте рабочего стола расположено окно и вычитаем его координаты из координат указателя мыши в момент щелчка. В результате мы получаем координаты в клиентской области не зависящие от местоположения окна. На рис. 6.25 корректировка значений снова вернет нас в точку (340, 10), поскольку вычисления будут выглядеть следующим образом: (350–10, 20–10).
Вычисление местоположения указателя мыши
Следующим этапом работы кода является вычисление текущего местоположения указателя мыши. Это делается с помощью вызова предоставляемой Windows функции GetCursorPos(). Эта функция проверяет текущее местоположение указателя мыши и сохраняет полученный результат в структуре POINT. Структура POINT содержит координаты указателя мыши по осям X и Y.

Следующие строки кода корректируют полученные координаты указателя мыши. Это достаточно простое вычисление — берутся координаты указателя мыши и из них вычитаются координаты окна и смещение клиентской области окна. В результате будут получены скорректированные координаты, которые можно использовать для проверки попадания по активной зоне.

Функция MouseZoneClass::bCheckZones()
В следующей части кода выполняется вызов функции bCheckZones(). Эта функция получает текущее состояние мыши и сравнивает его с хранящимися в памяти данными активной зоны. Если кнопки мыши находятся в требуемом состоянии, и указатель мыши расположен внутри активной зоны, функция возвращает название активной зоны. Вот как выглядит прототип функции:

bool MouseZoneClass::bCheckZones(
short shX,
short shY,
char *szZoneHit,
bool bLeftDown,
bool bRightDown)

В первых двух параметрах передаются скорректированные координаты указателя мыши по осям X и Y соответственно. Мы передадим координаты, которые вычислили чуть раньше.

Следующим параметром является символьный буфер, куда будет помещено название активированной пользователем зоны. Название копируется в буфер если найдена зона с требуемыми координатами и выполнены условия ее активации.

В последних двух параметрах функции передается состояние кнопок мыши. Если кнопка нажата, передается 1, а если отпущена — 0. Я передаю здесь переменные g_bLeftButton и g_bRightButton.

Для примера предположим, что пользователь запустил программу и щелкнул по рисунку на титульном экране. В результате была активирована зона TITLE_SCREEN. Символьный массив szZoneHit теперь содержит название активированной зоны. Что делать дальше? Вы устанавливаете переменную g_iCurrentScreen, чтобы указать, что теперь пользователь находится в главном меню, а затем устанавливаете активные зоны для главного меню.

Переменная g_iCurrentScreen хранит состояние меню. Это необходимо, чтобы отслеживать местоположение пользователя в мире интерфейсов игры. Число 0 означает, что пользователь находится на титульном экране. Число 1 означает, что пользователь находится в главном меню. В приведенном ниже списке перечислены все используемые в рассматриваемом примере значения.

0 Титульный экран
1 Главное меню
2 Экран завершения игры
3 Выход из программы
7 Меню Options

Чтобы переместить пользователя от одного меню к другому вы должны изменить значение переменной, содержащей номер текущего экрана, а затем установить активные зоны для нового меню. Теперь вы можете завершить перемещение пользователя, отобразив новый экран. Вот и все, что относится к навигации по меню! Взгляните на оставшуюся часть функции vCheckInput() и посмотрите, сможете ли вы следовать за ее логикой.
Обнаружение сообщений кнопок мыши
Работа функции vCheckInput() зависит от состояния кнопок мыши. К счастью, определение состояния кнопок мыши — очень простой процесс. Взгляните на функцию fnMessageProcessor() из программы. Вот как выглядит ее фрагмент:

switch(msg)
{
case WM_LBUTTONDOWN:
g_bLeftButton = 1;
break;
case WM_LBUTTONUP:
g_bLeftButton = 0;
break;
case WM_RBUTTONDOWN:
g_bRightButton = 1;
break;
case WM_RBUTTONUP:
g_bRightButton = 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
break;
}

Первые четыре инструкции case проверяют системные сообщения мыши. Первое из них, WM_LBUTTONDOWN, позволяет узнать что нажата левая кнопка мыши. Следующее, WM_LBUTTONUP, сообщает вам, что левая кнопка мыши была отпущена. То же самое справедливо и для правой кнопки мыши, только используются сообщения WM_RBUTTONDOWN и WM_RBUTTONUP.

Простейший способ хранить состояние мыши — воспользоваться глобальными переменными. Для хранения состояния двух кнопок мыши я использую переменные g_bRightButton и g_bLeftButton. Что может быть проще?

Выход из программы
Давайте на минуту сосредоточим наше внимание на функции WinMain(). Я знаю, что заставляю вас прыгать с места на место, но пожалуйста потерпите. Вернемся к функции WinMain() в которой содержится следующий код:

if(g_iCurrentScreen == 3) {
break;
}

Этот код я использую для выхода пользователя из программы. Нельзя выкинуть пользователя из программы, как только он щелкнул по завершающему экрану. Сперва надо завершить главный цикл обработки сообщений. Лучший способ обнаружения завершения программы состоит в проверке значения специальной переменной. Я проверяю не равно ли значение переменной g_iCurrentScreen числу 3. Это сообщает мне, что пользователь закончил играть и необходимо завершить работу программы. В реальной игре может использоваться более трудоемкий метод, но для простого примера подходит и рассмотренный простой способ.

Динамическое отображение меню
Мы почти закончили исследование кода программы. К данному моменту вы уже знаете как установить активные зоны, определить местоположение указателя мыши, обрабатывать сообщения кнопок мыши и завершить работу пользователя с программой. Осталось узнать только одну вещь — как динамически отображать экраны меню. Это необходимо, поскольку графика меню меняется на каждом экране. Сосредоточьтесь на функции vRender() чтобы увидеть как это делается.

Код этой функции выглядит очень похоже на код предыдущей программы. Главное отличие заключается в появившейся проверке значения переменной g_iCurrentScreen. Цикл визуализации проверяет текущее значение переменной, чтобы определить, какое изображение выводить на экран. Как видите, фоновое оформление каждого меню одно и то же; меняется только изображение в центре экрана. Если вы взглянете на каждый логический блок, то увидите, что отличается только последний вызов функции рисования. Вы, если пожелаете, можете полностью изменить графику. Я же хотел оставить пример максимально простым.

Вот и все, что требуется для простого динамического отображения меню. Главная хитрость — проверять состояние программы и отображать соответствующую графику. Правда, просто?

Заголовочный файл MouseZoneClass.h
Итак, вы увидели класс активных зон в действии, и теперь настало время разобраться как он работает. Класс активных зон состоит из двух файлов: MouseZoneClass.h и MouseZoneClass.cpp. Откройте заголовочный файл MouseZoneClass.h и следуйте дальше.

Структура данных stHotSpot
Первый заслуживающий внимания элемент заголовочного файла класса — структура данных stHotSpot. Вот как выглядит ее код:

struct stHotSpot
{
short m_shZoneXPos;
short m_shZoneYPos;
short m_shZoneWidth;
short m_shZoneHeight;
bool m_bActive;
short m_shClickType;
char *m_szZoneName;
};

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

Член данных m_shClickType хранит информацию о том, какой именно тип щелчка активирует данную зону. Возможные значения перечнслены в таблице 6.10.

Переменная m_szZoneName используется для хранения имени активной зоны.

Закрытые члены данных класса MouseZoneClass
Далее в заголовочном файле расположено объявление класса MouseZoneClass. Ниже приведен его код:

class MouseZoneClass
{
private:
int m_iMaxZones;
stHotSpot *m_HotSpots;

public:
MouseZoneClass(void);
~MouseZoneClass(void);
void vInitialize(int iMaxZones);
void vFreeZones(void);
int iAddZone(char *szZoneName, short shX, short shY,
short shWidth, short shHeight, short shClickType);
int iRemoveZone(char *szZoneName);
bool bCheckZones(short shX, short shY, char *szZoneHit,
bool bLeftDown, bool bRightDown);
};

Есть только две закрытые переменные класса — m_iMaxZones и m_HotSpots. Переменная m_iMaxZones хранит количество активных зон, для которых выделена память. Это очень важные сведения, поскольку количество используемых зон может изменяться. Переменная m_HotSpots является указателем на массив структур данных stHotSpot, представляющих реально существующие активные зоны.