Игра "Ищем пары"Источник: delphikingdom Петр Смирнов
Автор: Петр Смирнов, королевство DelphiМногие начинающие программисты начинают свои программы даже не с написания программы Hello, world, а начинают с уже довольно серьезных программных продуктов - компьютерных игр. Но, не имея достаточного опыта программирования сразу же написать хорошую игру практически невозможно. Давать советы вроде "Попробуй сперва написать что-то попроще", как правило, не достигают цели, особенно если программировать человек начинает в юном возрасте со свойственным этому возрасту максимализмом. Столкнувшись с неодолимыми трудностями, такой человек нередко бросает программировать. Но что посоветовать таким программистам? Читать теорию правильного построения программ, их архитектуры? Как правило, это только усугубляет ситуацию - почти все мои знакомые предпочитают больше практических экспериментов, нежели теоретических разглагольствований. Может быть, попробовать таким программистам дать пример простой игры, такой, с каких начинают программировать - игра на клеточном поле? Таких игр можно назвать очень много - от шашек и шахмат до сапера и морского боя. Все они объединены единой идеей - клеточное поле (или два - для морского боя), на котором разворачиваются события. Такие поля по своей сути плоские, но я встречал и трехмерные реализации с превосходной анимацией и детально проработанной графикой, но смысл от этого не изменяется - игрок кликает по определенным клеткам, а доска преобразуется в соответствии с правилами игры. Если вспомнить тот же морской бой, то мы поймем, что правила игры абсолютно не зависят от того, на чем мы играем - в шахматы на реальной доске, в плоскую компьютерную версию, где фигуры "перескакивают" с клетки на клетку без отрисовки промежуточного положения, или где трехмерные фигуры наносят удары по противнику и величественно перемещаются на новую позицию. Поэтому вполне очевидным является отдельная реализация правил игры и, возможно, алгоритма ответа со стороны компьютера и собственно отображения. Этот подход является стандартным в программировании и называется паттерн Модель - Вид - Контроллер.
Я постараюсь объяснить в меру своего понимания. Модель - это нечто, что реализует правила игры и алгоритм обсчета ответного хода, Вид - то, что показывает пользователю содержимое Модели в удобном для пользователя виде, а Контроллер - нечто, что принимает команды пользователя и влияет на Модель и Вид . Данный подход очень важен при разработке Web приложений, в этом практически в один голос уверяют авторы многочисленных статей по Web программированию. Для Windows данная технология не обязательно, но также возможна (как инструмент для облегчения жизни программиста административными мерами). Нередко я экспериментирую с правилами и прихожу к выводу, что им можно следовать не совсем в том виде, как описано в книжках, а другим - более удобным в конкретном случае способом. Если взглянуть на Delphi, а точнее, на средства, предоставляемые средой программирования и языком Object Pascal, то можно прийти к выводу, что наиболее удобным для реализации такого паттерна является сокращенный паттерн Модель - Вид.
Кстати, разработчики языка Oberon в статье Oberon: перспективы эволюции даже ставят концепцию Модель/Вид вперед Модель/Вид/Контроллер , тогда как разработчики и приверженцы языка Java с пеной у рта доказывают, что Модель/Вид/Контроллер всегда было первично, а Модель/Вид это упрощенный, а потому неполноценный подход. Мне лично ближе подход оберонщиков. Обычно, я реализую Модель как отдельный объект, а Вид - как отдельную форму (в ней будет совмещаться как Вид , так и Контроллер ). В дальнейшем в качестве сокращения для паттерна Модель/Вид/Контроллер и Модель/Вид я буду использовать одно обозначение MVC, что означает Model/View/Controller, не делая на данном этапе разницы между этими паттернами. А теперь давайте рассмотрим применение MVC на примере игры "Ищем пары".
Проект игры выполнен полностью в среде Delphi 6.0 с использованием ее стандартных инструментов: компилятора ресурсов brc32 и редактора ресурсов Image Editor. Пакет состоит из двух частей: редактора уровней и собственно игры. Давайте приступим к рассмотрению редактора . Его исходный текст хранится в файле Editor.zip (30 kb). Скачайте его и распакуйте в любую папку.
Внешний вид окна может отличаться в зависимости от используемых скинов, и расширений оболочки Windows. Если Вы откроете исходный код редактора уровней из подпапки Source папки, куда Вы распаковали архив, то увидите как минимум следующие файлы:
Каждая из этих операций производится при любом изменении модели - даже если мы сменили заголовок уровня, игровое поле все-равно перерисуется. Это можно обойти, если дополнить FieldChange специальным множеством флагов, показывающим, какое конкретно событие именно произошло. Но это усложнит реализацию, тогда как Редактор вообще является внутренним продуктом "для служебного пользования" и небольшая неоптимальность и "тормоза" вполне (для меня) допустимы и "оптимизацией ради оптимизации" я заниматься не стану. А теперь давайте рассмотрим второй проект - саму игру. Игра находится в архиве Doubles.zip (36 kb). Скачайте архив и распакуйте его в любую папку. В папке Вы найдете следующие файлы:
Внешний вид окна может отличаться в зависимости от используемых скинов и расширений оболочки Windows. В процессе игры, вы можете увидеть и другие окна. Все они выполнены в максимально аскетичном стиле стандартных диалогов VCL, чтобы не отвлекать Вас от основной задачи - понимания принципа работы игры. Например, для ввода имени победителя я применил стандартный InputQuery : Также для отображения таблицы рекордов я использовал обычный TMemo с разделителями-табуляциями: Это приводит к деформации таблицы при вводе имен слишком длинных, или слишком коротких, но решать эту проблему я (пока) не буду. Возможно, потом я дополню статью версиями программ, выполненных на основе этой: например, с красивым интерфейсом, плавно переворачивающимися картами и другими вещами, но это будет уже слишком сложно для первого обучающего проекта. Этот подход вполне работоспособен, а начинающим программистам следует навсегда запомнить правило: сперва сделай, чтобы работало, потом сделай, чтобы работало хорошо, потом сделай, чтобы это было красиво. Нарушение этого правила приводит к непонятному исходному коду, непонятным программам, которые трудно отлаживать и модернизировать. Данная программа является всего лишь первой ступенью - чтобы работало. Открыв подкаталог Source каталога, куда вы распаковали архив, вы увидите следующие файлы:
В папке также могут находиться другие файлы, созданные средой Delphi в процессе работы. При запуске программы, она создает в первую очередь Модель - объект GameField класса TGameField . Далее программа пытается загрузить карту уровней из ресурсов. Затем программа пытается загрузить таблицу рекордов в глобальный объект RecordTable класса TRecordTable - это скрытый файл, имеющий то же имя, что и программа, но с расширением Dat. При всей моей нелюбви к глобальным объектам, здесь я не смог придумать вменяемой архитектуры, позволяющей двум формам одновременно пользоваться одним и тем же объектом, кроме как перекрытия конструктора с одним параметром - этим самым объектом.Затем программа сравнивает, от той ли игры у нас имеются рекорды, полагаясь только на количество уровней - иначе просто возникнет ошибка при обращении к уровню, которого нет либо в таблице рекордов, либо на игровом поле! После всего, я создаю меню из заголовков уровней, что позволяет мне выбирать уровень через меню. Наконец, делаем первый уровень активным и устанавливаем оповещатель на Модель . Оповещатель сработает автоматически, поэтому Вид будет обновлен автоматически. Обработка оповещения модели очень простая, в отличие от Редактора - здесь только изменяются размеры игрового поля, размер формы, если это необходимо и перерисовывается игровое поле. Изменение размера формы подробно прокомментировано. Суть всего кода заключается только в том, чтобы при изменении размера все ячейки имели одинаковый размер, но при этом справа не оставалось пустого места. Также там обеспечен минимальный размер ячейки - 25 пикселей. Это ограничение можно уменьшить, но понравится ли Вам игра на скорость, где надо буквально выцеливать каждую карту мышкой, или запоминать совершенно незапоминающиеся комбинации размера 3x3 пикселя? Мне бы такая игра точно не понравилась.Таймер обеспечивает регулярное отображение времени на Статусной строке, в том числе и статус победителя. Далее все функции носят самодокументируемые имена - рисование вписанной в клетку карты, обработка (переадресация Модели ) щелчка мыши по игровому полю, изменение текущего уровня и совсем простые функции - отображения справки и таблицы рекордов. В конце работы программы игровое поле GameField и таблица рекордов RecordTable уничтожаются, что приводит к автоматическому сохранению таблицы рекордов. Вот вкратце и все взаимодействие между Моделью и Видом - оно весьма прозрачно и самоочевидно. Я думаю, что, разобравшись с данным примером, вы сможете разработать собственный аналог, скажем, Сапера, или шахмат. Поначалу я не советую заниматься навороченным искусственным интеллектом, если игра требует ответной реакции компьютера - достаточно будет произвольного допустимого хода. Изменения в искусственном интеллекте в дальнейшем коснутся только модели, и Вам придется переписать только маленький участок кода, а не всю программу целиком. Мало того. Вы можете взять мою программу за основу, как игровое поле, и, меняя Модель , превращать ее то в Сапера, то в шашки. Возможно, позднее я выложу свою реализацию игры Crazy Minesweeper с использованием данного интерфейса, но на данный момент у меня нет достаточно свободного времени. Ну и напоследок хочется пожелать удачного игростроения! Доставляйте удовольствие не только себе, но и другим. Все примеры откомпилированы в среде Delphi 6.0 с использованием RunTime пакетов vcl60.bpl, rtl60.bpl.
|