Рис. 9.4. Программа Bumper
Восемь спрайтов, показанных на рисунке, представлены четырьмя разными поверхностями — по каждой поверхности создаются два спрайта. Исходные векторы направления, по которым перемещаются спрайты, определяются случайным образом. В начале своей работы программа «раскручивает» генератор случайных чисел, чтобы результаты ее работы не были всегда одинаковыми. При нажатии клавиши пробела векторы направления пересчитываются заново. Код программы Bumper рассматривается в следующих разделах.
Программа Bumper, как и все остальные программы в этой книге, построена на основе базового класса DirectDrawWin. Производный от него класс BumperWin определяется так:
class BumperWin : public DirectDrawWin {
public:
BumperWin();
protected:
//{{AFX_MSG(BumperWin)
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnDestroy();
//}}
AFX_MSG DECLARE_MESSAGE_MAP()
private:
int SelectDriver();
int SelectInitialDisplayMode();
BOOL CreateCustomSurfaces();
void DrawScene();
void RestoreSurfaces();
BOOL SpritesCollide(Sprite* s1, Sprite* s2);
BOOL SpritesCollideRect(Sprite* s1, Sprite* s2);
BOOL SpritesCollidePixel(Sprite* s1, Sprite* s2);
private:
Sprite* sprite[MAX_SPRITES];
int nsprites;
LPDIRECTDRAWSURFACE text;
};
В нем объявляются два обработчика сообщений. Функция OnKeyDown() обрабатывает нажатия клавиш, а функция OnDestroy() освобождает спрайты в конце работы программы.
Функции SelectDriver(), SelectInitialDisplayMode(), CreateCustomSurfaces(), DrawScene() и RestoreSurfaces() наследуются от класса DirectDrawWin. Вскоре мы подробно рассмотрим каждую из этих функций. Функции SpritesCollide(), SpritesCollideRect() и SpritesCollidePixel() совпадают с одноименными функциями, описанными выше, однако на этот раз они принадлежат классу BumperWin. Поскольку эти функции уже рассматривались, мы не будем обсуждать их снова.
В классе объявлены три переменные: массив указателей на объекты Sprite, целая переменная для хранения общего количества спрайтов и указатель text на интерфейс DirectDrawSurface. Первые две переменные предназначены для хранения спрайтов и последующих обращений к ним. Указатель text используется для отображения меню, находящегося в левом нижнем углу экрана.
При запуске программы Bumper прежде всего вызывается функция SelectDriver(). Чтобы добиться максимальной гибкости, при наличии нескольких драйверов DirectDraw программа Bumper выводит меню. Функция SelectDriver() выглядит так:
int BumperWin::SelectDriver() {
int numdrivers=GetNumDrivers();
if (numdrivers==1) return 0;
CArray<CString, CString> drivers;
for (int i=0;i<numdrivers;i++) {
LPSTR desc, name;
GetDriverInfo(i, 0, &desc, &name);
drivers.Add(desc);
}
DriverDialog dialog;
dialog.SetContents(&drivers);
if (dialog.DoModal()!=IDOK) return -1;
return dialog.GetSelection();
}
С помощью класса DriverDialog программа выводит меню со списком драйверов и использует драйвер, выбранный пользователем. Наши функции проверки столкновений предназначены только для 8-битных поверхностей, поэтому драйверы, не поддерживающие 8-битных видеорежимов (скажем, драйверы 3Dfx), в этой программе не работают. Следовательно, функция SelectInitialDisplayMode() должна правильно реагировать на выбор такого драйвера.
Функция SelectInitialDisplayMode() вызывается после функции SelectDriver(), но перед созданием поверхностей. Функция выглядит так:
int BumperWin::SelectInitialDisplayMode() {
DWORD curdepth=GetDisplayDepth();
int i, nummodes=GetNumDisplayModes();
DWORD w,h,d;
if (curdepth!=desireddepth) ddraw2->SetDisplayMode(640, 480, curdepth, 0, 0);
for (i=0;i<nummodes;i++) {
GetDisplayModeDimensions(i, w, h, d);
if (w==desiredwidth && h==desiredheight && d==desireddepth) return i;
}
ddraw2->RestoreDisplayMode();
ddraw2->Release(), ddraw2=0;
AfxMessageBox('Can't find 8-bit mode on this device');
return -1;
}
Функция SelectInitialDisplayMode() ищет конкретный видеорежим 640x480x8. Если этот режим не найден, она выводит сообщение и возвращает –1, говоря тем самым классу DirectDrawWin о том, что приложение следует завершить. Если режим будет найден, функция возвращает его индекс. По этому индексу класс DirectDrawWin узнает о том, какой видеорежим следует активизировать.
Если функция SelectInitialDisplayMode() находит нужный видеорежим, класс DirectDrawWin вызывает функцию CreateCustomSurfaces(). Она создает поверхности наших восьми спрайтов, а также поверхность меню. Функция CreateCustomSurfaces() приведена в листинге 9.3.
Листинг 9.3. Функция CreateCustomSurfaces()
BOOL BumperWin::CreateCustomSurfaces() {
DDCOLORKEY ddck;
ddck.dwColorSpaceLowValue = 0;
ddck.dwColorSpaceHighValue = 0;
LPDIRECTDRAWSURFACE surf;
srand(time(0));
CString msg='Can't find ';
surf=CreateSurface('diamond.bmp', TRUE);
if (surf==0) {
msg+='diamond.bmp';
Fatal(msg);
}