Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Hello, World!
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  20:41[Войти] | [Зарегистрироваться]

Игра «Ищем пары»

Петр Смирнов
дата публикации 27-02-2008 08:07

Игра "Ищем пары"

Многие начинающие программисты начинают свои программы даже не с написания программы Hello, world, а начинают с уже довольно серьезных программных продуктов - компьютерных игр. Но, не имея достаточного опыта программирования сразу же написать хорошую игру практически невозможно. Давать советы вроде "Попробуй сперва написать что-то попроще", как правило, не достигают цели, особенно если программировать человек начинает в юном возрасте со свойственным этому возрасту максимализмом. Столкнувшись с неодолимыми трудностями, такой человек нередко бросает программировать. Но что посоветовать таким программистам? Читать теорию правильного построения программ, их архитектуры? Как правило, это только усугубляет ситуацию - почти все мои знакомые предпочитают больше практических экспериментов, нежели теоретических разглагольствований. Может быть, попробовать таким программистам дать пример простой игры, такой, с каких начинают программировать - игра на клеточном поле?

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

Подробнее про то, что же представляет собой этот подход, Вы можете прочитать в статье Wikipedia.

Здесь и далее я буду использовать следующие условные обозначение: жирным шрифтом я буду выделять новые термины, или важные имена файлов, курсивом я буду выделять термины далее по тексту, чтобы Вы могли легко ориентироваться по тексту. По моему опыту, не имеющий никаких "опорных" точек текст плохо читается, а эта статья все-таки первый мой опыт, так что не судите строго, если что не так. Мелким шрифтом будут выделены участки, которые можно пропустить - они не несут информации, необходимой для понимания текста, являются, по сути, комментариями.
Я постараюсь объяснить в меру своего понимания. Модель - это нечто, что реализует правила игры и алгоритм обсчета ответного хода, Вид - то, что показывает пользователю содержимое Модели в удобном для пользователя виде, а Контроллер - нечто, что принимает команды пользователя и влияет на Модель и Вид. Данный подход очень важен при разработке Web приложений, в этом практически в один голос уверяют авторы многочисленных статей по Web программированию. Для Windows данная технология не обязательно, но также возможна (как инструмент для облегчения жизни программиста административными мерами). Нередко я экспериментирую с правилами и прихожу к выводу, что им можно следовать не совсем в том виде, как описано в книжках, а другим - более удобным в конкретном случае способом. Если взглянуть на Delphi, а точнее, на средства, предоставляемые средой программирования и языком Object Pascal, то можно прийти к выводу, что наиболее удобным для реализации такого паттерна является сокращенный паттерн Модель - Вид.
Кстати, разработчики языка Oberon в статье Oberon: перспективы эволюции даже ставят концепцию Модель/Вид вперед Модель/Вид/Контроллер, тогда как разработчики и приверженцы языка Java с пеной у рта доказывают, что Модель/Вид/Контроллер всегда было первично, а Модель/Вид это упрощенный, а потому неполноценный подход. Мне лично ближе подход оберонщиков.
Обычно, я реализую Модель как отдельный объект, а Вид - как отдельную форму (в ней будет совмещаться как Вид, так и Контроллер). В дальнейшем в качестве сокращения для паттерна Модель/Вид/Контроллер и Модель/Вид я буду использовать одно обозначение MVC, что означает Model/View/Controller, не делая на данном этапе разницы между этими паттернами. А теперь давайте рассмотрим применение MVC на примере игры "Ищем пары".

Проект игры выполнен полностью в среде Delphi 6.0 с использованием ее стандартных инструментов: компилятора ресурсов brc32 и редактора ресурсов Image Editor. Пакет состоит из двух частей: редактора уровней и собственно игры. Давайте приступим к рассмотрению редактора. Его исходный текст хранится в файле Editor.zip (30 kb). Скачайте его и распакуйте в любую папку.
Поскольку уровни в игре могут иметь весьма сложную форму, я решил, что самое простое, что я могу сделать для их создания - написать простенький редактор уровней. В самой папке, куда Вы распаковали архив, вы увидите следующие файлы:

  • Editor.exe - собственно исполняемый файл, его надо будет запускать
  • Editor.txt - справка для редактора, она может быть открыта с помощь клавиши F1 из редактора, или вручную
  • Example.dat - пример уровней
Когда вы запустите Editor.exe, то Вы увидите окно, похожее на то, что Вы видите на рисунке:

Внешний вид окна может отличаться в зависимости от используемых скинов, и расширений оболочки Windows.
Если Вы откроете исходный код редактора уровней из подпапки Source папки, куда Вы распаковали архив, то увидите как минимум следующие файлы:
  • Common.pas - в этом файле расположена модель игры TGameField - класс, осуществляющий изменение своего состояния в ответ на команды. Действия пользователя преобразуются в команды в форме frmEditor
  • Editor.dpr - главный файл проекта
  • Editor.res - иконка к программе, вы можете заменить ее на любую другую, по умолчанию она содержит стандартную иконку приложения, написанного на Delphi 6.0
  • MainEd.dfm - главная форма
  • MainEd.pas - главная программа
  • WinXP.res - ресурс, обеспечивающий поддержку тем Windows XP в написанном приложении.
В папке также могут находиться другие файлы, если Вы уже открывали проект в среде Delphi или пытались его компилировать - среда создает ряд временных файлов, которые помогают в работе и хранят Ваши настройки, но они не обязательны. Как я уже говорил, основная логика игры заложена именно в модели, то есть в файле common.pas. Этот же файл находится и в проекте игры (см. ниже), скопирован он исключительно для удобства. В этом файле Вы найдете ряд служебных объявлений и объявление главного объекта - TGameField. Все методы подробно прокомментированы (даже, наверное, слишком подробно), поэтому для начинающего программиста не будет сложностей разобраться в том, для чего же предназначен тот или иной метод. Вид находится в файлах с именем MainEd.* - там находится собственно форма и ее реализация. Код также прокомментирован. Хочется остановиться немного на взаимодействии между моделью и видом, столь подробно описываемом в любом руководстве по паттерну MVS. В начале Вид создает экземпляр класса TGameField (он называется GameField) и пытается загрузить в него данные по умолчанию, даже если это не получилось - GameField просто примет все значения по умолчанию, то есть не будет содержать ни одного уровня. Далее Вид регистрирует свой обработчик FieldChange у Вида. Это является очень важным моментом. Воздействие со стороны пользователя на Вид осуществляется с помощью мыши, управление с клавиатуры не предусмотрено. Вид преобразует щелчки мыши по игровому полю в два числа - координаты ячейки, по которой был осуществлен щелчок и передает эти координаты в GameField через метод Click. GameField рассчитывает изменение состояния игрового поля (для редактора это изменение состоит в том, что состояние клетки переключается между активным и неактивным) и вызывает свое событие OnChange, то есть зарегистрированный обработчик FieldChange. Благодаря этому, Виду нет нужды поддерживать себя актуальным где-либо, кроме процедуры FieldChange - при любых изменениях модели FieldChange будет вызван и настроит Вид в соответствии с моделью. Это выражается в некоторой потере производительности, так как в случае с полным ручным контролем мы должны изменять только один параметр формы - например, заголовок, тогда как сейчас мы не знаем, что конкретно было изменено (хотя эту функциональность при желании можно предусмотреть и это несложно) и вынуждены перезагружать Вид полностью, зато это с успехом компенсируется гибкостью - при изменении ширины или высоты нам нет нужды помнить, что требуется перерисовать поле - все будет выполнено полностью автоматически без нашего ведома! Вы никогда ничего не забудете - об этом позаботится Модель! Обработка оповещения Модели в Редакторе весьма сложна - он изменяет доступность целого ряда элементов меню в зависимости от наличия уровней, изменяет заголовки элементов меню, размеры игрового поля и, наконец, перерисовывает само игровое поле.
Каждая из этих операций производится при любом изменении модели - даже если мы сменили заголовок уровня, игровое поле все-равно перерисуется. Это можно обойти, если дополнить FieldChange специальным множеством флагов, показывающим, какое конкретно событие именно произошло. Но это усложнит реализацию, тогда как Редактор вообще является внутренним продуктом "для служебного пользования" и небольшая неоптимальность и "тормоза" вполне (для меня) допустимы и "оптимизацией ради оптимизации" я заниматься не стану.

А теперь давайте рассмотрим второй проект - саму игру. Игра находится в архиве Doubles.zip (36 kb). Скачайте архив и распакуйте его в любую папку. В папке Вы найдете следующие файлы:
  • Doubles.exe - исполняемый файл игры
  • Doubles.txt - справка по игре, может быть вызвана либо из игры по нажатию F1 из самой игры, либо вручную.
Принцип работы игры полностью идентичен принципу работы Редактора - только интерфейс отличается. После запуска игры, Вы увидите окно, похожее на приведенное на рисунке:

Внешний вид окна может отличаться в зависимости от используемых скинов и расширений оболочки Windows.
В процессе игры, вы можете увидеть и другие окна. Все они выполнены в максимально аскетичном стиле стандартных диалогов VCL, чтобы не отвлекать Вас от основной задачи - понимания принципа работы игры. Например, для ввода имени победителя я применил стандартный InputQuery:

Также для отображения таблицы рекордов я использовал обычный TMemo с разделителями-табуляциями:

Это приводит к деформации таблицы при вводе имен слишком длинных, или слишком коротких, но решать эту проблему я (пока) не буду. Возможно, потом я дополню статью версиями программ, выполненных на основе этой: например, с красивым интерфейсом, плавно переворачивающимися картами и другими вещами, но это будет уже слишком сложно для первого обучающего проекта. Этот подход вполне работоспособен, а начинающим программистам следует навсегда запомнить правило: сперва сделай, чтобы работало, потом сделай, чтобы работало хорошо, потом сделай, чтобы это было красиво. Нарушение этого правила приводит к непонятному исходному коду, непонятным программам, которые трудно отлаживать и модернизировать. Данная программа является всего лишь первой ступенью - чтобы работало. Открыв подкаталог Source каталога, куда вы распаковали архив, вы увидите следующие файлы:
  • Common.pas - копия Модели из проекта Редактор. Копия сделана для удобства, чтобы программы можно было держать в отдельных каталогах и отдельно компилировать. Вы можете скопировать любой из Common.pas в отдельный каталог и прописать к нему путь в свойствах проекта, параметр Search Path.
  • Doubles.dpr - главный файл проекта
  • Doubles.res - файл ресурса с главной иконкой приложения. Сейчас в нем содержится стандартная для любого приложения, написанного на Delphi 6.0 иконка
  • Field.dat - карта уровней, это результат работы редактора, Вы можете открыть этот файл для того, чтобы отредактировать, или заменить его любым своим файлом. Если вы редактируете этот файл, или заменяете на другой, не забудьте откомпилировать файл ресурса заново - автоматически это сделано не будет. Как компилировать, вы узнаете чуть ниже.
  • Field.rc - исходники ресурса. Содержит только инструкцию включить в выходной файл ресурса один файл Field.dat. Этот файл является файлом уровня. Если Вы меняете файл Field.dat, вы должны перекомпилировать Field.rc, автоматически это не делается, иначе последняя версия игрового поля не будет вставлена в программу. Для компиляции, наберите в командной строке brc32 field.rc. Проще всего это делать из Total Commander или Far. Если программа сообщает о том, что исполняемый файл не найден, попробуйте указать полный путь к компилятору ресурсов: C:\Program Files\Borland\Delphi6\Bin\brc32.exe Field.rc. Вы также должны находиться в текущем каталоге. Если Вы установили Delphi в папку, отличную от указанной, укажите реальный путь. Вы заметите, что дата файла Field.res обновится - это значит, что компиляция прошла успешно. В Far вы также можете посмотреть отчет о компиляции - лишний повод пользоваться именно им при компиляции.
  • Field.res - готовый, скомпилированный ресурс, содержащий в себе карты игровых полей. При изменении файла Field.dat Вы должны его перекомпилировать - чуть выше указаны инструкции, как это сделать.
  • Glyphs.res - готовый файл ресурсов с картинками. Создан с использованием стандартной программы Image Editor из поставки Delphi 6.0. Вы можете его редактировать. Я плохой дизайнер, потому карты в оригинальной версии выглядят довольно непритязательно. При желании, вы можете это исправить.
  • Result.dfm - форма отображения результатов.
  • Result.pas - исходный код формы для отображения рекордов. Внутри исходного кода реализована еще одна мини-модель - обертка к файлу, хранящему рекорды. Его реализует класс TRecordTable. Здесь реализована минимальная абстракция от средств отображения - процедуры работают с TStrings, что позволяет, например, передавать им не только TMemo в качестве параметра, но и TListBox. Но вот для экспорта в TStringGrid вместо TMemo придется немного подумать. В принципе, ничего сложного нет, ну разве что стоит сказать про расчет мест: они рассчитываются по минимальному штрафу, который равен сумме секунд плюс штраф за каждый ход, составляющий 3 секунды. Я думаю, что это справедливо, чтобы человек, много кликавший мышкой, стоял дальше от начала турнирной таблицы, нежели человек, который много думал, но мало кликал :-)
  • Unit1.dfm - файл главной формы.
  • Unit1.pas - исходный текст главной формы. Алгоритм работы я опишу чуть ниже.
  • WinXP.res - ресурс, включаемый в программу для поддержки скинов Windows XP.

В папке также могут находиться другие файлы, созданные средой Delphi в процессе работы. При запуске программы, она создает в первую очередь Модель - объект GameField класса TGameField. Далее программа пытается загрузить карту уровней из ресурсов. Затем программа пытается загрузить таблицу рекордов в глобальный объект RecordTable класса TRecordTable - это скрытый файл, имеющий то же имя, что и программа, но с расширением Dat.

При всей моей нелюбви к глобальным объектам, здесь я не смог придумать вменяемой архитектуры, позволяющей двум формам одновременно пользоваться одним и тем же объектом, кроме как перекрытия конструктора с одним параметром - этим самым объектом.
Затем программа сравнивает, от той ли игры у нас имеются рекорды, полагаясь только на количество уровней - иначе просто возникнет ошибка при обращении к уровню, которого нет либо в таблице рекордов, либо на игровом поле! После всего, я создаю меню из заголовков уровней, что позволяет мне выбирать уровень через меню. Наконец, делаем первый уровень активным и устанавливаем оповещатель на Модель. Оповещатель сработает автоматически, поэтому Вид будет обновлен автоматически. Обработка оповещения модели очень простая, в отличие от Редактора - здесь только изменяются размеры игрового поля, размер формы, если это необходимо и перерисовывается игровое поле.

Изменение размера формы подробно прокомментировано. Суть всего кода заключается только в том, чтобы при изменении размера все ячейки имели одинаковый размер, но при этом справа не оставалось пустого места. Также там обеспечен минимальный размер ячейки - 25 пикселей.

Это ограничение можно уменьшить, но понравится ли Вам игра на скорость, где надо буквально выцеливать каждую карту мышкой, или запоминать совершенно незапоминающиеся комбинации размера 3x3 пикселя? Мне бы такая игра точно не понравилась.
Таймер обеспечивает регулярное отображение времени на Статусной строке, в том числе и статус победителя. Далее все функции носят самодокументируемые имена - рисование вписанной в клетку карты, обработка (переадресация Модели) щелчка мыши по игровому полю, изменение текущего уровня и совсем простые функции - отображения справки и таблицы рекордов. В конце работы программы игровое поле GameField и таблица рекордов RecordTable уничтожаются, что приводит к автоматическому сохранению таблицы рекордов.

Вот вкратце и все взаимодействие между Моделью и Видом - оно весьма прозрачно и самоочевидно. Я думаю, что, разобравшись с данным примером, вы сможете разработать собственный аналог, скажем, Сапера, или шахмат. Поначалу я не советую заниматься навороченным искусственным интеллектом, если игра требует ответной реакции компьютера - достаточно будет произвольного допустимого хода. Изменения в искусственном интеллекте в дальнейшем коснутся только модели, и Вам придется переписать только маленький участок кода, а не всю программу целиком. Мало того. Вы можете взять мою программу за основу, как игровое поле, и, меняя Модель, превращать ее то в Сапера, то в шашки.

Возможно, позднее я выложу свою реализацию игры Crazy Minesweeper с использованием данного интерфейса, но на данный момент у меня нет достаточно свободного времени.

Когда Вы поймете, что данная статья для Вас слишком проста, Вы сможете перейти к другим, более сложным статьям, написанным более опытными авторами. Я рекомендую прочитать также статью обзорного характера гибкость и масштабируемость компьютерных игр про правильный подход к проектированию сложных компьютерных игр. Ну и, конечно, никогда не забывайте про своих верных помощников www.rambler.ru, www.yandex.ru, www.google.ru

Ну и напоследок хочется пожелать удачного игростроения! Доставляйте удовольствие не только себе, но и другим.

Все примеры откомпилированы в среде Delphi 6.0 с использованием RunTime пакетов vcl60.bpl, rtl60.bpl.
Специально для Королевства Delphi


К материалу прилагаются файлы:


Смотрите также материалы по темам:


 Обсуждение материала [ 15-03-2008 11:57 ] 8 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования