Главная » C++, C++ Builder » Совершенствуем игру Match Game

0

Несмотря на то что первый образец программы Match Game, несомненно, работает, он требует определенной эстетической доработки. Во-первых,  пользователю  приходится  довольствоваться тем изображением, которое мы положили в управляющий элемент Image, когда проектировали форму. Было бы куда лучше, если бы родители, например, могли загрузить любую, на свой вкус, картинку, перед тем как давать своему ребенку поиграть в нашу игру. Также было бы неплохо, если бы  кнопки могли появляться при каждом новом запуске программы в новом порядке. Во второй версии Match Game мы попробуем расправиться с этими проблемами.

Замечание

Исходный текст второй версии программы Match Game можно найти на прилагаемом компакт- диске.

Давайте решать проблемы по мере их поступления — сначала позаботимся, чтобы пользователь мог динамически загружать картинки в элемент Image  во время исполнения. Добавьте в форму главное меню (main menu), положив его на форму, открытую в редакторе форм.  Добавьте  в главное меню пункт Файл с двумя подпунктами — Выбрать картинку и Выход. В завершение перейдите на вкладку Dialogs в палитре компонентов (Component Palette) и перетащите оттуда на форму компонент Open Dialog (окно диалога открытия файла). Это тот самый диалог, который мы собираемся использовать, чтобы позволить пользователю выбирать файл с растровым рисунком для отображения в игровом поле.

Добавьте обработчик для команды меню Выбрать картинку и добавьте в него следующие строки: void __fastcall TForm1::SelectImageClick(TObject *Sender)

{

OpenDialog->Filter =

" Файлы растровых рисунков (*.bmp)|*.bmp"; if ( OpenDialog->Execute() )

{

TPicture *pPicture = new TPicture;

pPicture->LoadFromFile( OpenDialog->FileName ); Image1->Picture = pPicture;

}

}

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

Name|Type

где Name — это тот текст, который увидит пользователь в выпадающем списке фильтров. Свойство Name обычно имеет структуру Описание (*.ext), где ext — это расширение нужных файлов. В  нашем случае описание будет выглядеть, как «Файлы растровых рисунков», а расширение — bmp.

Type — это то действительное расширение, которое вы хотите ввести. Символ `|’ (трубопровод)

между этими двумя частями необходим, его обязательно надо ввести.

Следующая интересная вещь касается использования объекта TPicture. Мы еще не сталкивались с TPicture, так что стоит познакомиться с ним поближе. Объект TPicture используется для хранения растровых рисунков, иконок и других графических образов. TPicture может быть отображен так же, как мы отображали растровые рисунки во второй главе. Кроме того, вы можете загрузить объект TPicture непосредственно с диска, используя метод LoadFromFile, а также загрузить его из файла ресурсов, о чем мы до сих пор не говорили.

После того как вы получили объект TPicture с загруженной из файла, выбранного пользователем, картинкой, просто присвойте эту картинку свойству Picture управляющего элемента Image. Вот, собственно, и все. Управляющий элемент сам отобразит картинку в своем поле.

Итак, первая проблема разрешена. Разрешение второй проблемы чуть менее очевидно. Мы хотим, чтобы заголовки на кнопках появлялись в случайном порядке. На наше счастье библиотека функций CBuilder содержит функцию, называемую random, которой  передается  некоторое значение, а она возвращает случайным образом выбранное число из промежутка от 0 до заданного значения. Мы используем эту функцию для выполнения нашей задачи. Вот код для нового метода FormCreate:

void __fastcall TForm1::FormCreate(TObject *Sender)

{

char strings_1[MaxStringCount*2][ 20 ]; char strings_2[MaxStringCount*2][ 20 ];

// Сначала копируем все строки в массив

for ( int i=0; i<MaxStringCount*2-1; ++i )

{

strcpy ( strings_1[i], strNames[i]);

strcpy ( strings_1[i+MaxStrCount], strNames[i]);

}

// Теперь запомним их в произвольном порядке

for ( int i=MaxStringCount*2-1; i>=0; –i )

{

int nInd = random( i);

// Кладем эту строку во второй массив

strcpy ( strings_2[i], strings_1[nInd] );

// Копируем последнюю строку в эту позицию

strcpy ( strings_1[nInd], strings_1[i] );

}

int nStringIndex = 0;

for ( int i=0; i<ControlCount; ++i )

{

TButton *pButton = dynamic_cast<TButton *>(Controls[i]); if ( pButton )

{

pButton->Caption = strings_2[ nStringIndex ]; if ( nStringIndex < MaxStringCount*2-1 ) nStringIndex ++;

}

}

}

Кроме  того,  поскольку  я   все  время  твержу  о   недопустимости  использования  в  программе

«магических» (ключевых) значений, добавьте в начало файла следующее описание:

const MaxStringCount = 8;

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

Как это все работает?

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

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

Заключительная часть — это уже знакомое нам по прошлой версии программы  присвоение каждой кнопке нового заголовка. Если вы теперь запустите программу Match Game 2, вы увидите нечто похожее на окно, изображенное на рис. 3.3.

Итак, теперь у вас есть вполне работоспособная игра, в которую ваши дети могут играть до умопомрачения. Правда, если вы, конечно, не хотите вырастить из детей фанатов шоу Star Trek, вам лучше изменить текст заголовков на что-нибудь более подходящее им по возрасту.

Несмотря на то что мы неплохо развлеклись, а попутно и узнали кое-что о TPicture и TImageControls, вы пока не много узнали о графике, встроенной в CBuilder. В следующем небольшом примере вы кое-что узнаете о некоторых особенностях внутреннего устройства системы.

Пример номер два: крестики-нолики

Рис. 3.3. Окно обновленной программы Match Game

Замечание

Хотя в примере мы и говорили все время об использовании TButton, в примере, находящемся на прилагаемом компакт-диске, я, на самом деле, использовал TBitBtn. TBitBtn — класс, наследующий от TButton, который может отображать (нести на кнопке) не только текст, но и изображение. Если у вас дети настолько малы, что не умеют читать, можете попробовать приложить руку к изменению программы Match Game 2, так чтобы использовать рисунки вместо текста. Все будет написано на самом деле практически так же, только вместо свойства Caption (заголовок) вам придется использовать свойство TBitBtn, которое называется Glyph (характерный рисунок, «иконка» кнопки). Кроме того, вам придется создать объекты TPicture для каждого изображения, которое собираетесь использовать (так же, как мы это делали в процедуре загрузки картинки для игрового поля). Вместе с CBuilder поставляются неплохие картинки, которые вы можете найти в директории CBuilder\Images вашего дерева каталогов.

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

  • Комментарии