Гра “Шукаємо пари”, Різне, Програмування, статті

Автор: Петро Смирнов, королівство 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). Скачайте його та розпакуйте в будь-яку папку.
Оскільки рівні в грі можуть мати досить складну форму, я вирішив, що найпростіше, що я можу зробити для їх створення – написати простенький редактор рівнів. У самій папці, куди Ви розпакували архів, ви побачите наступні файли:

Коли ви запустите Editor.exe, то Ви побачите вікно, схоже на те, що Ви бачите на малюнку:

Зовнішній вигляд вікна може відрізнятися в залежності від використовуваних скінів, і розширень оболонки Windows.
Якщо Ви відкриєте вихідний код редактора рівнів з підпапки Source папки, куди Ви розпакували архів, то побачите як мінімум наступні файли:
У папці також можуть перебувати інші файли, якщо Ви вже відкривали проект в середовищі Delphi або намагалися його компілювати – середа створює ряд тимчасових файлів, які допомагають в роботі і зберігають Ваші настройки, але вони не обов’язкові. Як я вже казав, основна логіка гри закладена саме в моделі, тобто у файлі common.pas. Цей же файл знаходиться і в проекті гри (див. нижче), скопійований він виключно для зручності. У цьому файлі Ви знайдете ряд службових оголошень і оголошення головного об’єкту – TGameField. Всі методи докладно прокоментовані (навіть, напевно, занадто докладно), тому для початківця програміста не буде складностей розібратися в тому, для чого ж призначений той чи інший метод. Вид знаходиться в файлах з ім’ям MainEd.* – Там знаходиться власне форма та її реалізація. Код також прокоментовано. Хочеться зупинитися трохи на взаємодії між моделлю і виглядом, так докладно описується в будь-якому керівництві по паттерну MVS. На початку Вид створює екземпляр класу TGameField (Він називається GameField) і намагається завантажити в нього дані за замовчуванням, навіть якщо це не вийшло – GameField просто прийме всі значення за замовчуванням, тобто не буде містити жодного рівня. Далі Вид реєструє свій оброблювач FieldChange у Віда. Це є дуже важливим моментом. Вплив з боку користувача на Вид здійснюється за допомогою миші, управління з клавіатури не передбачено. Вид перетворює клацання миші по ігровому полю в два числа – координати клітинки, за якою був здійснений клацання і передає ці координати в GameField через метод Click. GameField розраховує зміна стану ігрового поля (для редактора це зміна полягає в тому, що стан клітини переключається між активним і неактивним) і викликає своє подія OnChange, Тобто зареєстрований обробник FieldChange. Завдяки цьому, Виду немає потреби підтримувати себе актуальним де-небудь, крім процедури FieldChange – при будь-яких змінах моделі FieldChange буде викликаний і налаштує Вид відповідно до моделі. Це виражається в деякій втрати продуктивності, так як у випадку з повним ручним контролем ми повинні змінювати тільки один параметр форми – наприклад, заголовок, тоді як зараз ми не знаємо, що конкретно було змінено (хоча цю функціональність при бажанні можна передбачити і це нескладно) і змушені перезавантажувати Вид повністю, зате це з успіхом компенсується гнучкістю – при зміні ширини або висоти нам немає потреби пам’ятати, що потрібно перемалювати поле – все буде виконано повністю автоматично без нашого відома! Ви ніколи нічого не забудете – про це подбає Модель! Обробка оповіщення Моделі в редакторі досить складна – він змінює доступність цілого ряду елементів меню в залежності від наявності рівнів, змінює заголовки елементів меню, розміри ігрового поля і, нарешті, перемальовує саме ігрове поле.
Кожна з цих операцій проводиться при будь-якій зміні моделі – навіть якщо ми змінили заголовок рівня, ігрове поле все-одно перемалюють. Це можна обійти, якщо доповнити FieldChange спеціальним безліччю прапорів, що показує, яке конкретно подія саме сталося. Але це ускладнить реалізацію, тоді як Редактор взагалі є внутрішнім продуктом “для службового користування” і невелика неоптимальність і “гальма” цілком (для мене) припустимі і “оптимізацією заради оптимізації” я займатися не буду.

А тепер давайте розглянемо другий проект – саму гру. Гра знаходиться в архіві Doubles.zip (36 kb). Скачайте архів і розпакуйте його в будь-яку папку. У папці Ви знайдете наступні файли:
Принцип роботи гри повністю ідентичний принципу роботи Редактора – тільки інтерфейс відрізняється. Після запуску гри, Ви побачите вікно, схоже на наведене на малюнку:

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

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

Це призводить до деформації таблиці при введенні імен занадто довгих, або занадто коротких, але вирішувати цю проблему я (поки) не буду. Можливо, потім я доповню статтю версіями програм, виконаних на основі цієї: наприклад, з гарним інтерфейсом, плавно перевертається картами та іншими речами, але це буде вже занадто складно для першого навчального проекту. Цей підхід цілком працездатний, а початківцям програмістам слід назавжди запам’ятати правило: спершу зроби, щоб працювало, потім зроби, щоб працювало добре, потім зроби, щоб це було красиво. Порушення цього правила призводить до незрозумілого вихідного коду, незрозумілим програмами, які важко налагоджувати і модернізувати. Дана програма є всього лише першою сходинкою – щоб працювало. Відкривши підкаталог Source каталогу, куди ви розпакували архів, ви побачите наступні файли:

У папці також можуть перебувати інші файли, створені середовищем Delphi в процесі роботи. При запуску програми, вона створює в першу чергу Модель – об’єкт GameField класу TGameField. Далі програма намагається завантажити карту рівнів з ресурсів. Потім програма намагається завантажити таблицю рекордів в глобальний об’єкт RecordTable класу TRecordTable – Це прихований файл, що має те ж ім’я, що і програма, але з розширенням Dat.

При всій моїй нелюбові до глобальних об’єктів, тут я не зміг придумати осудною архітектури, що дозволяє двом формам одночасно користуватися одним і тим же об’єктом, окрім як перекриття конструктора з одним параметром – цим самим об’єктом.
Потім програма порівнює, від тієї чи гри у нас є рекорди, покладаючись тільки на кількість рівнів – інакше просто виникне помилка при зверненні до рівня, якого немає або в таблиці рекордів, або на ігровому полі! Після всього, я створюю меню із заголовків рівнів, що дозволяє мені вибирати рівень через меню. Нарешті, робимо перший рівень активним і встановлюємо оповіщувач на Модель. Оповіщувач спрацює автоматично, тому Вид буде оновлено автоматично. Обробка оповіщення моделі дуже проста, на відміну від Редактора – тут тільки змінюються розміри ігрового поля, розмір форми, якщо це необхідно і перемальовується ігрове поле.

Зміна розміру форми докладно прокоментовано. Суть всього коду полягає тільки в тому, щоб при зміні розміру всі осередки мали однаковий розмір, але при цьому справа не залишалося порожнього місця. Також там забезпечений мінімальний розмір комірки – 25 пікселів.

Це обмеження можна зменшити, але чи сподобається Вам гра на швидкість, де треба буквально Вицеливать кожну карту мишкою, або запам’ятовувати абсолютно не запам’ятовуються комбінації розміру 3×3 пікселя? Мені б така гра точно не сподобалася.
Таймер забезпечує регулярне відображення часу на Статусної рядку, в тому числі і статус переможця. Далі всі функції носять самодокументіруемие імена – малювання вписаною в клітку карти, обробка (Переадресація Моделі) клацання миші по ігровому полю, зміна поточного рівня і зовсім прості функції – відображення довідки і таблиці рекордів. В кінці роботи програми ігрове поле GameField і таблиця рекордів RecordTable знищуються, що призводить до автоматичного збереження таблиці рекордів.

Ось коротко і все взаємодія між Моделлю і Видом – воно дуже прозоро і самоочевидно. Я думаю, що, розібравшись з даними прикладом, ви зможете розробити власний аналог, скажімо, Сапера, або шахів. Спочатку я не раджу займатися навороченним штучним інтелектом, якщо гра вимагає відповідної реакції комп’ютера – достатньо буде довільного допустимого ходу. Зміни в штучному інтелекті надалі торкнуться тільки моделі, і Вам доведеться переписати тільки маленьку ділянку коду, а не всю програму цілком. Мало того. Ви можете взяти мою програму за основу, як ігрове поле, і, змінюючи Модель, перетворювати її то в Сапера, то в шашки.

Можливо, пізніше я викладу свою реалізацію гри Crazy Minesweeper з використанням даного інтерфейсу, але на даний момент у мене немає достатньо вільного часу.

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


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

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*