Створення ігор-головоломок на Puzzle Script

Puzzle Script — це мінімалістичний ігровий движок для створення головоломок для HTML5, має відкриті вихідні коди. Приклади готових ігор можна подивитися тут.

Частина 1. Створюємо першу гру на Puzzle Script.

Puzzle Script — це безкоштовна онлайн-програма, яка використовується для створення ігор-головоломок. Найбільш відомий вона завдяки створенню головоломок з штовханням блоків на кшталт моєї The Nodus. У цій частині ми створимо гру, вивчивши базові функції Puzzle Script, а в наступній приступимо до програмування.


Перейдіть на веб-сайт движка. Натисніть Make a Game, щоб відкрити редактор Puzzle Script.

Завантаження прикладів

Для початку давайте розглянемо кілька прикладів. У верхній частині екрану відкрийте список «Load Example» і виберіть перший приклад під назвою «Basic». Тепер натисніть на «Run».



З’явиться екран гри. Клацніть всередині його вікна і натисніть Enter на клавіатурі.


Спробуйте зіграти в гру. Ваша мета — дотолкать помаранчеві ящики до чорних квадратів-цілей. Коли на кожній цілі буде стояти по ящику, рівень буде пройдений. Можна натискати на клавіатурі Z для відміни ходу або R, щоб перезавантажити рівні.

Робимо першу гру

Тепер ми створимо кілька рівнів. Один я створю разом з вами, а інші раджу придумати самостійно. Натисніть на «Level Editor» у верхньому меню.


Якщо ви не бачите екрана редактора рівнів, то натисніть кнопку «Run» і запустіть гру. Потрапивши на рівень, знову натисніть на кнопку «Level Editor». Це дозволить редагувати рівень, на якому ви тільки що знаходилися.

Створюємо новий рівень

Вгорі знаходяться ігрові об’єкти. При лівому клацанні отрісовиваємих вибраний об’єкт. Правий клік розмальовує «фоновий» об’єкт. Лівий клік на краю карти збільшує її розмір, правий клік його зменшує.


Щоб пройти рівень, потрібно поставити по ящику на кожну з цілей, тому на кожному рівні повинні бути мінімум:

  • 1 ящик
  • 1 мета
  • 1 гравець

Поки правим клацанням перетворимо все в траву. Потім клацнемо лівою клавішею на краю рівня, щоб зробити його більше, і нарешті намалюємо рівень, який схожий на показаний нижче.

Додаємо його в список рівнів

Закінчивши створення рівня, ми додамо його до списку рівнів. В редакторі рівнів натисніть на білу букву S поруч зі списком ігрових об’єктів, щоб зберегти створений рівень.


Під редактором рівнів повинно з’явитися повідомлення про успішної компіляції та сітка з кумедних символів, як показано нижче.


Ці забавні символи позначають тільки що створений нами рівень. Кожен символ є окремий об’єкт. У лівій частині екрану опустіться вниз і знайдіть LEGEND. У легенді представлено пояснення кожного з символів:

. = Background
# = Wall
P = Player
* = Crate
@ = Crate and Target
O = Target
Все, що знаходиться в лівій частині екрану — це код гри, розділений на різні частини, такі як OBJECTS або LEGEND. Опустіться вниз до LEVELS. Тут ми бачимо рівні, які використовуються в прикладі.


Щоб додати новий рівень, створимо нову порожню рядок у нижній частині розділу рівнів. Потім скопіюємо символи, згенеровані для нашого рівня і вставити їх туди. Все, рівень доданий.


Протестуємо його. Після створення нового рівня потрібно знову натиснути кнопку «Run» у верхній частині екрана, щоб перезавантажити гру з новим рівнем. Іноді це спрацьовує, і тоді потрібно натиснути кнопку «Rebuild», а потім знову натиснути «Run».

Збереження і завантаження гри

Спробуйте створити ще кілька нових рівнів. Коли будете готові зберегти гру, підніміться вгору до початку коду і введіть власна назва, ім’я автора та домашню сторінку, а потім натисніть на кнопку «Save».



Обмежена кількість збережень зберігається в меню «Load» на комп’ютері, який ви використовуєте. Однак у верхній частині екрана існує кнопка «Share». При натисканні на неї генерується повідомлення з двома веб-посиланнями.


Одне з посилань — вихідний код вашого проекту. Друга — це посилання на іграбельну версію гри, якою можна поділитися з друзями. Рекомендую періодично створювати нову посилання на вихідний код і зберігати її в який-небудь текстовий документ, щоб у вас залишалася постійно збережена версія проекту.

Експорт ігор

Також можна експортувати гру як файл html5, який ви потім зможете завантажити на ігрові портали, наприклад на itch.io, Kongregate або Newgrounds. Просто натисніть «Export» завантажити скачаний файл html на ігровий портал.

Наш приклад проекту можна подивитися тут.

Частина 2. Починаємо програмувати в Puzzle Script

У цій частині ми дізнаємося, як почати програмувати в Puzzle Script.

Код

Відкрийте приклад проекту. Код програми знаходиться в лівій частині екрана, він розділений на частини: Objects, Legend, Sounds і т. д. У розділі Rules задаються правила взаємодії об’єктів. Зайдіть до нього. Тут повинна бути тільки один рядок коду:

[ > Player | Crate ] -> [ > Player | > Crate ]
Цей рядок означає, що якщо гравець знаходиться поруч з ящиком і переміщається в його сторону, то гра переносить гравця і штовхає ящик. Щоб пояснити, як це працює, потрібно зрозуміти, що код Puzzle Script слід такій структурі:

[ Умова ] -> [ Подія ]
Це означає наступне:

[ Якщо ця умова виконується ] -> тоді [ Робити це ]
Puzzle Script перевіряє істинність умов зліва від стрілки, наприклад, знаходиться об’єкт гравця поруч з об’єктом скриньки. Якщо умова істинно, то ми щось робимо, наприклад, штовхаємо ящик.

Приклади умов

Ось приклад умови:

[ object1 | object2 ]
Дана подія перевіряє, чи object1 поруч з object2. Можна перевірити, чи знаходяться два об’єкти поруч один з одним, помістивши між ними пряму | лінію, що вводиться натисканням shift + . Умови завжди укладені в квадратні [ ] дужки.

[ crate | crate ]
Наведений вище код перевіряє, чи знаходяться поруч один з одним два ящики.

[ crate | crate | crate ]
Це умова перевіряє, чи знаходяться поруч три ящика.

[ crate target ]
Дана умова перевіряє, чи ящик поверх мети, тому що прямий | лінії між двома об’єктами немає. Об’єкти можуть перебувати один на одного, якщо вони розташовані в різних шарах колізій, які ми розглянемо в наступних частинах туториала.

Зберігаємо рівність

Правила повинні бути врівноважені. І перевірка умови і наступне за ним подія повинні описуватися однаковим чином. Я покажу, що це означає.

[ player | crate ] -> [ player | ]
Цей рядок коду знищує ящик, якщо поруч з ним знаходиться гравець. Можна записати:

[ player | crate ] -> [ player ]
тому що умова зліва перевіряє наявність сусідніх об’єктів у двох окремих просторах сітки, але подія описує тільки один простір сітки, яка займає гравець. Puzzle Script повинен знати, що робити з перевіреними їм просторами. Правильний код для знищення ящика повинен повідомляти наступне:

[ Якщо гравець | поряд з ящиком ] -> тоді [ гравець не робить нічого | ящик видаляється ]
[ player | crate ] -> [ player | ]
Тобто навіть порожні простору в коді мають значення. Однак наступна запис допустима:

[ player target ] -> [ player ]
Так як в умові ми говоримо тільки про одному просторі сітки, то подія описує те ж простір сітки.

Як рухати ящики

Повернемося до вихідної рядку коду.

[ > Player | Crate ] -> [ > Player | > Crate ]
Іншими словами:

[ Якщо гравець рухається до скриньки | і знаходиться поруч з ящиком ] -> тоді [ перемістити гравця | штовхнути ящик ]
Стрілка > підкреслює рух.

Іноді нам потрібно писати коментарі, щоб пам’ятати, що робить код. Puzzle Script ігнорує коментарі — вони призначені тільки для користувача. Щоб записати коментар, потрібно помістити текст в дужках. Напишемо над нашим правилом коментар, описує те, що воно робить:

(Гравець штовхає ящик)
[ > Player | Crate ] -> [ > Player | > Crate ]

Тепер під кодом штовхання скриньки напишемо наступне:

(Гравець тягне ящик)
[ < Player | Crate ] -> [ < Player | < Crate ]

Перевернута стрілка означає, що якщо гравець рухається від ящика, то він тягне ящик. Натисніть кнопку «Run», щоб протестувати цю дію. У вас повинна з’явитися можливість штовхати і тягнути ящики. У програмуванні навіть при опечатке в одну букву комп’ютер може не зрозуміти код тому позбавляйтеся від усіх помилок. При внесенні змін в код знову натискайте «Run», щоб завантажити зміни. Якщо гра веде себе не так, як потрібно, спробуйте натиснути на «Rebuild», щоб очистити пам’ять програми, а потім натисніть кнопку «Run».

Іноді буває потрібно деактивувати рядок коду. Просто перетворіть її в коментар, щоб зберегти на майбутнє. Давайте закомментіруем код штовхання ящиків, щоб гравець міг тільки тягати ящики:

(Гравець штовхає ящик)
([ > Player | Crate ] -> [ > Player | > Crate ])
(Гравець тягне ящик)
[ < Player | Crate ] -> [ < Player | < Crate ]

Якщо це спрацювало, то закоментуйте код волочіння ящиків і спробуйте зробити наступне:

[ < Player | Crate ] -> [ < Player | > Crate ]
Якщо гравець відсувається від ящика, то гравець і ящик перемістяться в протилежних напрямках. Стрілки визначають, у якому напрямку об’єкт або рухається, або буде рухатися. Тепер закомментіруем це і спробуємо наступне:

[ > Player | Crate ] -> [ Player | > Crate ]
Ящик рухається, але гравець залишається на місці. Поекспериментуйте з ^ і v (буква v), щоб подивитися, як будуть рухатися об’єкти.

Помилки

Давайте навмисно напишемо невірне правило і подивимося, що станеться. Введіть таку команду:

[ < Player | Crate ] -> [ < Player ]
Спробуйте запустити програму. Ви повинні побачити таке повідомлення:

line 81: In a rule, each pattern to match on the left must have a corresponding pattern on the right of equal length (number of cells).
Зазвичай Puzzle Script дуже добре описує помилку. Однак іноді Puzzle Script помиляється сам. У таких випадках вам потрібно самим пройтися по коду і розібратися, де помилка.

Ще трохи експериментів

Спробуйте поекспериментувати і самостійно писати правила. Ось кілька прикладів.

[ > Player | ... | Crate ] -> [ > Player | ... | > Crate ]
У наведеному вище прикладі гравець буде штовхати ящик, якщо вони знаходяться в будь-якому місці одного рядка рівня, і гравець рухається до скриньки.

[ > Player | Crate ] -> [ Crate | Player ]
Цей код змінює гравця і ящик місцями.

Читайте також  Розширення мережевих можливостей програмованого реле за допомогою WI-FI

[ > Player | Crate ] -> [ Player | Target ]
У цьому коді якщо гравець поруч з ящиком і рухається до нього, то гравець перестане рухатися, але ящик перетвориться в ціль. Найкраще в Puzzle Script — простота створення нового і можливість експериментів.

Частина 3. Створення об’єктів

Всі графічні фрагменти в іграх на Puzzle Script позначають об’єкти. Для створення ігор на Puzzle Script необхідно створювати власні об’єкти. У цій частині я розповім, як створювати і додавати в свій код.

Загальні відомості

Відкрийте приклад проекту. Процес створення об’єкта складається з таких кроків:

  • Створення списку об’єктів
  • Додавання об’єкта у легенду
  • Додавання в шар колізій

Зробивши всі ці кроки можна почати використовувати об’єкт.

Створення об’єкта

Кілька об’єктів вже існує. У кожній грі повинен бути фоновий об’єкт. Всі об’єкти створюються з сітки розміром 5 x 5 пікселів і мають хоча б один колір. Нижче показаний фоновий об’єкт.

Background
LIGHTGREEN GREEN
11111
01111
11101
11111
10111

Числа позначають пікселі зображення. Кожне число відповідає своїм кольором. Перший колір має число 0, другий — 1, і так до 9. Може бути до десяти кольорів. В нашому випадку кожна 1 забарвлює піксель в світло-зелений (Green Light), а 0 — зелений (Green). Результат виглядає так:


Об’єкти завжди створюються наступним чином:

  • Назва
  • Кольори
  • Зображення

Назва завжди знаходиться у верхньому рядку. Кольори завжди у другому рядку, а зображення займає наступні 5 рядків, по 5 символів на рядок, що формує сітку 5 x 5. Чи можна зробити наступне:

Background
LIGHTGREEN

Цей код створить об’єкт з назвою «Background», який буде сіткою 5 x 5 пікселів світло-зеленого кольору. Якщо не описати сітку зображення, то ми отримаємо блок суцільного кольору, що іноді може бути корисно.

Даємо назви об’єктів

Об’єкти можна називати як завгодно, але назва не може починатися з символу і воно повинно бути одним словом без пробілів. Давайте об’єктів зрозумілі назви, але не переборщіть. PlayerStill — гарна назва, PlayerThatIsStandingStill — занадто довге і багатослівне.

Кольори

Необхідно оголосити кольори, які ви хочете використовувати для об’єкта, і розділити їх пропуском. Puzzle Script має заздалегідь задані кольори:

  • black
  • white
  • grey
  • darkgrey
  • lightgrey
  • gray
  • darkgray
  • lightgray
  • red
  • darkred
  • lightred
  • brown
  • darkbrown
  • lightbrown
  • orange
  • yellow
  • green
  • darkgreen
  • lightgreen
  • blue
  • lightblue
  • darkblue
  • purple
  • pink
  • transparent

Можна також задавати кольору в шістнадцятковому вигляді, що дає нам набагато більший діапазон кольорів. Для вибору шістнадцяткових квітів можна використовувати веб-сайти, наприклад такий. Виберіть потрібний колір, а потім перепишіть код кольору, вказаний над зображенням. Шістнадцяткові коди кольорів записуються в Puzzle Script наступним чином:

Назва
#51A2BD #ff0000 #ffffff

Коду кольору завжди передує символ #.

Додаємо об’єкт у легенду

Створивши об’єкт, потрібно додати його в легенду. Легенда виглядає так:

. = Background
# = Wall
P = Player
* = Crate
@ = Crate and Target
O = Target

Кожен символ позначає об’єкт на рівні. Тобто коли ми бачимо таку сітку символів:

#p.*.##
#.**.##
#..#..#
##....#
##...o#
#######

то насправді вона описує наш рівень:


Кожного створеного об’єкту потрібно присвоїти букву, символ або число, що позначають цей об’єкт на рівні. Ось так:

P = player

Групуємо об’єкти в легенді

Також ми можемо створювати в легенді групи об’єктів. Наприклад, якщо у нас є кілька різнокольорових ящиків, то можна зробити так:

O = OrangeCrate
B = BlueCrate
G = GreenCrate

Що дозволить нам використовувати ящики в редакторі рівні. Але для створення коду можна в Legend згрупувати їх разом, ось так:

Crates = OrangeCrate or GreenCrate or BlueCrate
І все разом це буде виглядати так:

=======
LEGEND
=======
O = OrangeCrate
B = BlueCrate
G = GreenCrate

Crates = OrangeCrate or GreenCrate or BlueCrate
Навіщо це робити? Тому що тоді замість створення таких правил:

[ > Player | OrangeCrate ] -> [ > Player | > OrangeCrate ]
[ > Player | BlueCrate] -> [ > Player | > BlueCrate ]
[ > Player | GreenCrate] -> [ > Player | > GreenCrate]

можна просто записати:

[ > Player | Crates ] -> [ > Player | > Crates ]
І цей код буде працювати для всієї групи об’єктів.

Крім того, в розділі Шари колізій можна буде посилатися на шар, в якому знаходиться група, а не вводити кожен окремий об’єкт.

Шари колізій

За замовчуванням розділ шарів колізій виглядає наступним чином:

Background
Target
Player, Wall, Crate

Кожна рядок виділяє об’єкти в свій власний шар. Порядок розташування шарів об’єктів визначає, які об’єкти будуть поверх інших. Об’єкти у верхньому рядку будуть перебувати на нижньому шарі, наступний рядок буде в шарі над ним, і так далі. Фон завжди повинен перебувати у верхньому рядку, щоб розташовуватися на нижньому шарі. Об’єкти в одному шарі не можуть перебувати один поверх іншого. Тобто такого бути не може:

[ player wall ] -> [ player wall ]
Можна зробити так, щоб об’єкти в різних шарах взаємодіяли один з одним. Наприклад, можна написати:

[ > Player | Target ] -> [ > Player | > Target ]

Експеримент

Створіть кілька нових об’єктів. Створіть тип ящиків, які можна тільки штовхати. Створіть інший ящик, який можна тільки тягнути. Створіть ящик, який зникає, коли його торкаєшся. Продовжуючи експерименти, ви будете краще запам’ятовувати, як все робиться.

Частина 4. Умови перемоги

Всі люблять перемагати. Ми, як гравці, хочемо перемагати. У цій частині ми дізнаємося, як запрограмувати умови перемоги в грі.

Умови перемоги

Завантажте приклад проекту і перейдіть до розділу коду Win Conditions. Ви повинні побачити наступне:

All on Target Crate
Гра виграна, якщо на кожній цілі є ящик. Якщо у вас є 3 шухляди і 2 цілі, то виграєте, поставивши на цілі всього 2 ящика. Якщо поміняти місцями:

All crate on target
то кожен ящик повинен буде знаходитися на цілі.

Умов може бути одна або декілька. У випадку множинних умов вони повинні виконуватися всі. Наприклад, у нас може бути таке:

All on Target Crate
All Target2 on Crate2

Якщо на рівні є target і target2, то на них, відповідно, crate і crate2. Якщо на рівні немає ні одного з об’єктів, необхідних для виконання певної умови перемоги, наприклад, немає target2, то ця умова виконується автоматично.

Різні типи умов перемоги

Існує кілька різних типів умов перемоги.

No Object
В цьому випадку перемога настає тоді, коли на рівні немає ні одного такого об’єкта.

Some Object
Ви виграєте, коли на рівні є хоча б один об’єкт зазначеного типу.

Some Object1 on Object2
Потрібно, щоб хоча б один з об’єктів Object1 перебував на Object2.

No Object1 On Object2
Це умова протилежно All on Target Crate. В даному випадку нам потрібно, щоб всі зазначені об’єкти перебували окремо один від одного, а не один на одному. Також можна комбінувати один з одним різні умови перемоги.

Експеримент

Поекспериментуємо з різними умовами перемоги. Спробуйте створити гру, в якій виграш настає, якщо всі ящики знаходяться не на цілях. Або зробіть гру, в якій потрібно знищити всі ящики певного типу, але на вашому шляху знаходяться інші штовхнув ящики.

Мій завершений приклад проекту можна подивитися тут.

Частина 5. Команда late

У PuzzleScript є дуже корисна команда під назвою «late». Порядок походження подій в грі важливий, і іноді для отримання потрібних результатів потрібен код, який виконується пізніше. У цій частині я розповім про використання команди late.

Навіщо вона нам знадобиться

Відкрийте приклад проекту, після чого вставте в гру наступний код і запустіть її:

[ player | target ] -> [ player | ]
Можна очікувати, що як тільки гравець постане поруч із метою, мета буде знищена, проте цього не відбувається. Замість цього мета зникає в ході після того, як поруч з нею постало гравець. Тому спробуємо інший код:

late [ player | target ] -> [ player | ]
Як тільки ви встанете поряд з метою, вона зникне. Так вийшло тому, що все, позначене як late, відбувається після виконання всього іншого коду. Іноді таке буває потрібно.

Порядок подій

Ось як виконується код в Puzzle Script при кожному переміщенні.

  1. Puzzle Script виявляє, що гравець хоче переміститися
  2. По можливості правила зчитуються і виконуються зверху вниз
  3. Гравець переміщується, якщо це можливо
  4. Застосовуються Late-правила

Комп’ютер починає з верхнього рядка коду і зчитує вниз, рядок за рядком. перевіряючи кожне умова. Тому коли ви намагаєтеся переміститися, Puzzle Script зчитує всі правила і перевіряє, чи не істинні умови, і якщо це так, то він щось робить. Наприклад, перший рядок може бути такою:

[ player | spikeTrap ] -> [ | spikeTrap ]
Якщо гравець не стоїть поряд з пасткою колу, то код продовжує виконання. Це означає, що важливий порядок написання рядків коду. Можна застосовувати команду late в деяких випадках, які ви дізнаєтеся з часом на практиці.

Способи використання Late на практиці

З мого досвіду, краще всього використовувати команду late, коли ви перевіряєте, чи знаходяться об’єкти поверх один одного або поруч один з одним, але бувають і інші випадки. Якщо ви перевіряєте, чи знаходиться один об’єкт на іншому, то подія не зареєструється до наступного ходу, якщо тільки не використовувати команду late:

[ player spikeTrap ] -> [ spikeTrap ]
У наведеному вище випадку гравець не буде убитий пасткою з кілками до наступного ходу після переміщення в пастку. Щоб гравець помер миттєво, просто додамо команду late,

late [ player spikeTrap ] -> [ spikeTrap ]
Щоб перезапустити весь рівень при смерті персонажа, можна зробити наступне:

late [ player spikeTrap ] -> restart
І рівень перезапуститься, коли гравець потрапить в пастку з кілками.

Готовий приклад проекту можна подивитися тут.

Частина 6. Робота зі звуковими ефектами

Ми зробили відмінну гру Puzzle Script, але тепер потрібно додати в неї звуки. Як це зробити? Зараз я вам розповім!

Генерація звуків

Відкрийте приклад проекту. Додамо в нього звуків. Під екраном гри можна побачити чорні квадрати з білими символами. Вони використовуються для генерації звуку. Кожен символ генерує унікальний тип звуку, а хрестик видаляє створені звуки. Спробуйте натискати на квадрати і послухайте звуки.


Жовті числа — це унікальні коди, які потрібно копіювати і вставляти в ті місця коду, де необхідні звуки.

Як використовувати звуки

Знайшовши відповідний звук, ви повинні вставити його в розділ Sounds коду гри.


Звуки можна використати кількома способами. Найбільш простий — створення нового звукового ефекту (sfx). Вони повинні бути пронумеровані. Ми створюємо новий sfx, призначаючи йому число від 0 до 10 при вставці числового ID звуку. У списку Sounds створимо новий sfx під назвою sfx0 і привласнимо йому згенерований звук:

sfx0 36301705
Для використання звуку потрібно вставити його в правила після події. Давайте прикріпимо тільки що створений sfx0 до події знищення скриньки (подія вже присутній в прикладі проекту):

(The player destroys a crate)
[ > Player | CrateVanish ] -> [ Player | ] sfx0

Також звуки можна оголошувати для відтворення з певними подіями, наприклад:

Crate MOVE 36772507
В цьому випадку звук відтворюється при переміщенні Crate. Згенеруємо новий звуковий ефект для перетягування об’єктів CratePull і змусимо його відтворюватися при переміщенні CratePull:

CratePull MOVE 12735307
Звуки подій потрібно оголошувати тільки в розділі Sounds: їх не потрібно згадувати в правилах.

Список способів відтворення звуків

Нижче представлений список різних звуків подій, які можна використовувати, взятий з документації Puzzle Script.

Object Action 541566 — відтворюється, коли об’єкт під час ходу піддається дії (action).

Create Object 641667 — відтворюється при створенні певного об’єкта.

EndGame 5416789 — відтворюється при завершенні гри.

EndLevel 6417822 — відтворюється після завершення рівня.

Object CantMove 781673 — відтворюється, коли об’єкт безуспішно намагався зрушити в будь-якому напрямку.

Player CantMove Down Left 464674 — відтворюється, коли об’єкт безуспішно намагався зрушити вниз або вліво.

CloseMessage 344456 — відтворюється, коли гравець закриває вікно з повідомленням.

Object Destroy 187975 — відтворюється при знищенні об’єкта.

Object Move 264567 — відтворюється, коли об’єкт успішно переміщається в будь-якому напрямку.

Object Move Down Left 765432 — відтворюється, коли об’єкт успішно зсунувся вниз або вліво.

Object Move Horizontal 345367 — відтворюється, коли об’єкт успішно перемістився по горизонталі. Можна також використовувати Vertical.

Restart 7865435 — відтворюється, коли гравець натискає кнопку перезапуску R.

SFX0 765743 — може бути що завгодно від SFX0 до SFX10. Це особливі звукові події, які можна виконувати з правил.

ShowMessage 478483 — відтворюється при відображенні повідомлення.

StartGame 234626 — відтворюється на початку нової гри.

Startlevel 765436 — відтворюється при початку кожного рівня.

TitleScreen 876543 — відтворюється після завантаження заставки екрану.

Undo 436234 — відтворюється, коли гравець натискає клавішу скасування (Z).

Для move і cantmove можна вказувати напрямки, щоб при переміщенні в різних напрямках відтворювалися різні звуки.

Готовий приклад проекту знаходиться тут.

Частина 7. Відтворення музики

Ми навчилися створювати ігри Puzzle Script, але хіба не здорово буде додати до них музику? Це можливо, і зараз я розповім, як це робиться.

Примітка: схоже, на даний момент ця функція в Puzzle Script зламана, так що переходьте до наступної частини.

Що потрібно робити

Відкрийте заготівлю проекту. Музика працює в Puzzle Script наступним чином: у гру можна вставити посилання на одне відео з Youtube PuzzleScript автоматично буде відтворювати всі звуки цього відео. Відкрийте Youtube і оберіть будь-яке відео, або використовуйте наступне:

youtube.com/watch?v=CKAc3nYEatw

Щоб відтворити музику з відео, нам потрібно отримати унікальний ID відео. Виділена зеленим частина в рядку вище і є унікальний ID.


Під міткою домашньої сторінки автора на початку проекту додайте мітку youtube, а після неї унікальний ID відео, наприклад, так:

youtube CKAc3nYEatw

Щоб переконатися в правильності роботи натисніть на «Share» і клацніть по посиланню гри (не за посиланням вихідного коду). Під час тестування всередині редактора музику Puzzle Script відтворювати неможливо. Якщо ви хочете, щоб у грі була музика, то потрібно завантажити її як відео на Youtube і вставити в свій проект унікальний ID. В кожному проекті може бути тільки одне відео Youtube.

Приклад проекту

Приклад проекту Puzzle Script можна знайти тут.

Частина 8. Як використовувати команду Action

Ми вже знаємо, як переміщатися, штовхати і тягнути блоки, але що якщо нам потрібно зробити що-то при натисканні певної клавіші, наприклад пробілу?

Можливості введення в PuzzleScript досить обмежені, в основному це клавіші-стрілки, Z для відміни, R для перезавантаження, і ми не можемо їх змінити. Але движок дає нам додаткову клавішу для дій — натискання на пробіл або клавішу X.

Робота з командою Action слід описаному вище формату. Ми використовували базовий формат коду PuzzleScript:

[ Умова ] -> [ Подія ]
Якщо умова істинно, то ми виконуємо подія. Команда action використовується таким же чином, але у неї є власні правила. Вона виглядає ось так:

[ Action додаткові умови ] -> [ Подія ]
Ось приклад використання команди Action:

[ Action Player ] -> [ Crate ]
По-перше, Action завжди є першим словом умови.

По-друге, якщо ми хочемо впливати на певний об’єкт в грі, то потрібно згадати цей об’єкт і в умові, і в подію (якщо згадати інший об’єкт, то програма видалить вихідний і замінить його на новий об’єкт, а якщо об’єкт не вказати, то він просто піде).

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

Перевіримо показаний вище код в проекті гри. Можна почати з цього прикладу. Ви побачите, що при натисканні на пробіл або X гравець стає ящиком.

Умова повідомляє [ Якщо ми натискаємо клавішу Action і на рівні є об’єкт Player ] -> тоді [ замінити об’єкт гравця на скриньку ]

Тепер спробуємо на прикладі проекту наступний код:

[ Action Player | Crate ] -> [ Player | > Crate ]

Зробіть так, щоб вас зі всіх боків оточували ящики, як на зображенні вище. Ви помітите, що впливає тільки на один ящик. Не знаю точно, в чому причина, але якщо ви хочете впливати з допомогою команди Action на декілька об’єктів, то потрібно вказати її і в умові, і в подію.

Замінимо код оновленою версією:

[ Action Player | Crate ] -> [ Action Player | > Crate ]

Тепер ми можемо штовхати всі ящики одночасно. Якщо вам потрібно застосувати дію до кількох об’єктів, то помістіть Action і умова, і подія.

Частина 9. Перевірка множинних умов

Давайте навчимося перевіряти кілька умов, наприклад, наявність бомби І руйнується блоку.


Відкрийте приклад проекту. Тепер додайте в нього наступний код:

late [Player Switch][DoorClosed] -> [Player Switch][DoorOpen]
Код відповідає такого формату:

[ Умова 1 ] [ Умова 2 ] -> [ Крок 1 ] [ Крок 2 ]
Якщо умова 1 істинно і умова 2 істинна, то виконуються крок 1 і крок 2. У нашому випадку в умові 1 перевіряється, перебуває Player на Switch. Якщо так, то перевіряється умова 2, тобто наявність на рівні закритій двері? Якщо умова істинно, то об’єкт DoorClosed перетворюється в об’єкт DoorOpen, відкриваючи двері.


Припустимо, нам потрібно, щоб двері зачинялися, коли герой йде з перемикача, тому що ми хочемо, щоб для відкривання дверей гравець штовхнув ящик на перемикач. Можна написати щось подібне:

late [Player | Switch][DoorOpen] -> [Player | Switch][DoorClosed]
Якщо гравець стоїть поряд з перемикачем і десь на рівні є відкрита двері, то ми закриваємо двері. Нарешті, нам потрібно, щоб двері залишалася відкритою, якщо ми толкнем ящик на перемикач:

late [Crate Switch][DoorClosed] -> [Crate Switch][DoorOpen]
Тепер двері буде залишатися відкритим, поки на перемикачі стоїть ящик.

Готовий приклад проекту можна подивитися тут.

Частина 10. Створення контрольних точок

Можливо, у вас є гарна ідея для гри на Puzzle Script, але для неї потрібні контрольні точки (чекпоінти), щоб гравець в разі смерті відновлювався на них. Як це зробити? Досить просто, і зараз я поясню, як.

Створення контрольних точок

Відкрийте приклад проекту. Тепер нам потрібно запрограмувати контрольну точку. Для цього достатньо всього одного рядка коду:

late [ Player FlagRed ] -> CHECKPOINT
FlagRed — це контрольна точка. Коли гравець знаходиться поверх прапора, цей код створює контрольну точку (CHECKPOINT). Якщо не використовувати late, то функція контрольної точки не спрацює.

Протестуйте гру. Пройдіть над контрольною точкою, а потім трохи далі та натисніть R. Ви повинні будете почати з контрольної точки.

Кілька контрольних точок

При наявності декількох контрольних точок гра використовує останню, активовану гравцем.

Щоб уникнути повторної активації вже використаної контрольної точки, потрібно змінити її на інший об’єкт. Для цього створимо в розділі OBJECTS коду під червоним прапором його білу копію.

FlagWhite
White Orange
.1...
.00..
.000.
.1...
.1...

Тепер перепишемо цю рядок в легенді:

Flag = FlagRed or FlagWhite
Ми можемо створити групу об’єктів. У такому разі Flag буде або FlagRed, або FlagWhite. Поки хоча б одному з згрупованих об’єктів присвоєно символ (ми присвоїли FlagRed символ F), нам не потрібно буде присвоювати символи іншим об’єктам групи, при цьому отримати доступ до ним ви зможете тільки в коді, але не в редакторі рівнів. Потім можна присвоїти групі шари колізій, що ми зробили. Звернення до групи об’єктів, наприклад, Flag, звертається до всієї групи. Тому:

[ > Player | Flag ] -> [ > Player | ]
Цей код вплине і на червоний, і білий прапор.

Зміна об’єкта прапора

Ось як поміняти FlagRed на FlagWhite:

late [ Player FlagRed ] -> [ Player FlagWhite ]
Якщо в кінці ходу гравець знаходиться на червоному прапорі, то ми перетворюємо його в білий. Однак потрібно зробити так, щоб графіка прапора змінювалася після створення контрольної точки, тому що код читається зверху вниз. Протестуйте програму.

Зміна другого прапора

У проекті є два прапора. Давайте зробимо так, щоб при активації другого прапора старий білий прапор перетворювався в чорний, щоб його можна було використати. Запишемо наступне:

late [ Player FlagRed ][ FlagWhite] -> [ Player FlagRed ][FlagBlack]
Код повідомляє: якщо гравець знаходиться на червоному прапорі і де-то в грі є білі прапори, то потрібно зробити білі прапори чорними. Так як код зчитується зверху вниз, нам потрібно виконати це в наступному порядку:

late [ Player FlagRed ] -> CHECKPOINT
late [ Player FlagRed ][ FlagWhite] -> [ Player FlagRed ][FlagBlack]
late [ Player FlagRed ] -> [ Player FlagWhite ]

Якщо ви не розумієте, чому код повинен бути в такому порядку, то спробуйте поміняти порядок рядків і протестуйте програму. Щоб вирішувати власні завдання, вам потрібно навчитися робити крок назад і обдумувати те, що робить код. Але я дам вам підказку — коли ви граєте, де знаходяться червоні прапори під час останньої рядки коду в наступному неправильному прикладі?

[ > Player | Crate ] -> [ > Player | > Crate ]
[ > Crate | Flag ] -> [ Crate | Flag ]
late [ Player FlagRed ] -> CHECKPOINT
late [ Player FlagRed ] -> [ Player FlagWhite ]
late [ Player FlagRed ][ FlagWhite] -> [ Player FlagRed ][FlagBlack]

Переглянути готовий приклад проекту можна тут. Крім того, я тільки що продемонстрував вам найпростіший спосіб анімації. У наступній частині я розповім про створення більш складних анімацій, наприклад вибухів.

Частина 11. Анімації

У PuzzleScript є два способи створення анімацій. В одному з них використовується реальний час, але в цій частині я не буду про неї говорити. Інший використовується для швидких одноразових анімацій, таких як вибух або спускається по вертикальній драбині персонаж.

Відкрийте приклад проекту. Ми створимо бомбу і серію кадрів вибуху, а потім анимируем їх.

Анімація об’єктів

Для 2D-анімації необхідно кілька малюнків об’єкта, переходить з одного стану в інше, наприклад, спрайт біжить Маріо. Він містить 4 кадру анімації.


Для симуляції переходу одного кадру анімації в іншій ми створимо PuzzleScript кілька об’єктів, які будуть кадрами анімації, а потім використовуємо код для перемикання між ними.

Бомба

Створимо такий об’єкт-бомбу.

Bomb
black yellow grey
..1..
..1..
.000.
00020
.000.

Не забудьте додати його в шари і у легенду.

Щоб анімувати вибух, нам потрібно створити кожен кадр анімації як окремий об’єкт, а потім перемикатися між ними в коді. Давайте створимо об’єкти вибуху.

Explosion1
black yellow grey red
..1..
..1..
.000.
00320
.000.

Explosion2
black yellow grey red
..1..
..1..
.333.
03330
.333.

Explosion3
black yellow grey red
..1..
.333.
33333
33333
.333.

Explosion4
black yellow grey red
.333.
33333
33333
33333
.333.

Explosion5
black yellow grey red
.333.
33333
33.33
33333
.333.

Explosion6
black yellow grey red
.333.
3...3
3...3
3...3
.333.

Explosion7
black yellow grey red
.....
.....
.....
.....
.....

Правильно буде пронумерувати об’єкти, щоб знати, який кадр анімації нам потрібен. Після додавання їх в шари і у легенду ми можемо дописати кілька рядків коду.

[Explosion7] -> []
[Explosion6] -> [Explosion7]
[Explosion5] -> [Explosion6]
[Explosion4] -> [Explosion5]
[Explosion3] -> [Explosion4]
[Explosion2] -> [Explosion3]
[Explosion1] -> [Explosion2]
[Bomb] -> [Explosion1]

Спробуйте додати цей код, а потім додайте на рівні бомбу і запустіть гру. Ви помітите, що при кожному переміщенні анімація бомби змінюється на один кадр. Перемикаючись між об’єктами, ми створюємо анімацію.

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

Використання команди Again

Щоб анімувати всі разом, нам потрібна команда again. Again означає, що після зчитування всього коду PuzzleScript зробить паузу, а потім вважає код знову, виконуючи всі команди again. Її можна використовувати для гравітації, ковзання по льоду, а в нашому випадку і для анімацій. Все, що потрібно — це переписати код наступним чином:

[Explosion7] -> []
[Explosion6] -> [Explosion7] again
[Explosion5] -> [Explosion6] again
[Explosion4] -> [Explosion5] again
[Explosion3] -> [Explosion4] again
[Explosion2] -> [Explosion3] again
[Explosion1] -> [Explosion2] again
[Bomb] -> [Explosion1] again

Протестуйте гру. Ви побачите, що вся анімація бомби відтворюється відразу. Якщо на ваш погляд вона дуже повільна або швидка, то швидкість можна змінити. На самому початку тексту програми, під homepage напишіть наступний код:

again_interval 0.1
Це частина того, що в PuzzleScript називається prelude. Це місце, в якому до іншої частини коду можна задати додаткові правила, які визначають поведінку гри. Тепер анімація повинна відтворюватися швидше. Спробуйте змінити число після again_interval і перевірте, що змінилося.

Готовий приклад проекту можна подивитися тут.

Частина 12. Гравітація

Зазвичай PuzzleScript використовується для створення ігор з виглядом зверху (top down), але насправді можна симулювати деякі елементи сайдскроллеров, хоч і з обмеженими правилами PuzzleScript, тобто вони все одно будуть покроковими. У цій частині я розповім, як реалізувати гравітацію.

Приклад проекту

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

Падіння, етап 1

Ось перша частина коду, який ми будемо використовувати:

down [ Player | no Object ] -> [ | Player ]
down [ Crate | no Object no Player ] -> [ | Crate ]

Спробуйте зіштовхнути ящик з краю. Ви помітите, що після переміщення ящик зависає в повітрі, але після ще одного ходу він падає на землю. Тут відбувається кілька речей.

По-перше, давайте поговоримо про ключовому слові down. Додаючи down, ми обмежуємо правило тим, що воно застосовується тільки в напрямку «вниз». Саме тому ящик зсувається вниз. Якщо замінити down на right, то ви побачите, що ящик застрягне в правій стіні, як у грі з антигравитацией. Спробуйте так зробити.

Далі зробимо що-небудь незвичайне. Замість того, щоб перевіряти, чи знаходиться гравець поряд з певним об’єктом (наприклад, ящиком), ми перевіряємо, чи знаходиться він поруч з узагальненим object. Якщо подивитися на легенду в коді прикладу, то ви побачите, що ми визначили object як групу об’єктів, тобто при кожному використанні слова object ми маємо на увазі групу об’єктів. Тобто ми перевіряємо, чи є якісь з цих об’єктів під гравцем. Якщо ні, то ми наказуємо гравцеві зайняти це порожній простір і залишити попереднє простір, з-за слова down слідуючи в напрямку вниз.

Але також можна помітити, що після зіштовхування з уступу ящик не падає до наступного ходу, і що він миттєво досягає землі. Щоб усунути це відкладене падіння, можна використовувати ключове слово late:

late down [ Player | no Object ] -> [ | Player ]
late down [ Crate | no Object no Player ] -> [ | Crate ]

Але як зробити так, щоб він падав поступово, кадр за кадром?

Падіння, етап 2

Тепер ми використовуємо ключове слово random. Перепишемо код наступним чином:

random down [ Player | no Object ] -> [ | Player ]
random down [ Crate | no Object no Player ] -> [ | Crate ]

Запустіть код. Він працює дуже схоже на попередній код, але однією важливою відмінністю. Ящик зависає в повітрі, але при кожному переміщенні гравця він падає вниз на одну клітинку. Це відбувається завдяки слову random. Строго кажучи, random призначене для створення ігор з випадковими елементами, але нам воно згодилося тут. Воно змушує виконуватися відповідний рядок коду по одному разу за хід. PuzzleScript виконує за один хід кожне правило скільки може раз, і тільки потім гравець бачить графічні зміни. Саме тому здається, що ящик падає на землю миттєво. Але при використанні слова random ми дозволяємо йому падати тільки на одну клітинку за раз.

Падіння, етап 3

Тепер ми додамо ключове слово again:

random down [ Player | no Object ] -> [ | Player ] again
random down [ Crate | no Object no Player ] -> [ | Crate ] again

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

Останній етап

І останній штрих. Щоб ящик падав до землі відразу після виштовхування з краю, нам потрібно додати ще один рядок коду над тією, яку ми тільки що написали:

[moving Player] -> [moving Player] again
random down [ Player | no Object ] -> [ | Player ] again
random down [ Crate | no Object no Player ] -> [ | Crate ] again

Ключове слово moving, використане в квадратних дужках [] перед player означає, що ми перевіряємо, чи рухається гравець. Це означає, що якщо гравець рухається, то ми наказуємо йому продовжувати рухатися і запускати команду again. Тоді відразу ж спрацює все, що використовує ключове слово again; у нашому випадку це анімації падіння ящика.

Готовий приклад проекту можна подивитися тут.

Частина 13. Перевірка напрямки руху

 

Вказівка напрямку

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

Як би те ні було, ми можемо вказувати, щоб події відбувалися тільки в певних напрямках.

Відкрийте PuzzleScript, створіть гру на основі прикладу Basic і замініть його Rules наступним кодом:

[ Left Player ] -> [ Crate ]
Запустіть гру і подивіться, що станеться.

Якщо спробувати піти вліво, то гравець перетвориться в ящик. Це хороший приклад того, як працює зазначення напрямків.

Ми вже звикли до формату, в якому я пояснював код PuzzleScript:

[ Умова ] -> [ Подія ]
Якщо подія істинне, то відбувається подія.

Але тепер, коли нам потрібно перевіряти напрямку, він буде дотримуватися нових правил:

[ Movement Direction Object Affected ] -> [ New State of Object ]
Тобто наш попередній код:

[ Left Player ] -> [ Crate ]
перевіряє, чи рухається гравець ліворуч (Left). Якщо так, то ми замінюємо гравця на об’єкт скриньки.

Типи напрямків

Можна вибирати з наступних напрямків:

  • Up
  • Down
  • Left
  • Right
  • Horizontal (перевіряє, чи є рух по горизонталі)
  • Vertical (перевіряє, чи є рух по вертикалі)

Частина 14. Створення настроюваних переміщень

З якихось причин в грі може знадобитися додаткова переміщення. Качки можуть ходити тільки вліво, ящики можна штовхати тільки по горизонталі, і так далі. Зробити це дуже просто. Давайте ще раз поглянемо на базовий формат коду PuzzleScript:

[ Умова ] -> [ Подія ]
Якщо умова істинно, то відбувається подія. Щоб створити настроюваний переміщення, потрібно зробити наступне:

[ Condition ] -> [ Movement Direction Object To Move]
Ось як буде виглядати приклад:

[ Player | Crate ] -> [ Player | Left Crate ]
Завантажте PuzzleScript, відкрийте приклад Basic, вставте цей код і подивіться, що станеться. Підходьте до скриньки, навколо якого немає стін.

Ви побачите, що коли поруч із скринькою знаходиться гравець, то ящик штовхається вліво. Однак так як ключового слова Late немає, це відбувається в ході після того, як ви зробили перший крок до нього.

При вказівці в Event крім об’єкта ще й напрямку руху, наприклад Left, PuzzleScript намагатиметься перемістити об’єкт у вказаному напрямку. Саме тому скриньку, а не гравець рухається вліво — Left знаходиться поруч з Crate.

Пам’ятаєте цей код з попередньої частини?

[ Напрямок руху Об'єкт] -> [ Новий стан об'єкта]
Якщо вказати напрямок руху в умови поруч з об’єктом, то воно перевіряє, рухається цей об’єктів даному напрямку. Це важлива відмінність. Можна переписати так:

[ Перевіряємо це ] -> [ Робимо це ]

Допустимі настроювані переміщення

Настроювані руху можна створювати з будь-яким з цих слів:

  • Up
  • Down
  • Left
  • Right

Не можна використовувати Horizontal або Vertical, тому що PuzzleScript не зрозуміє, в якому напрямку ви хочете перемістити об’єкт, або щоб зробити це, потрібно багато обхідних дій. Потрібно вказувати конкретний напрямок.

Частина 15. Перевірка осередків поряд з об’єктами

Іноді буває необхідно перевірити, що знаходиться поруч з об’єктом. Зробити це досить просто. Давайте ще раз подивимося на формат коду PuzzleScript:

[ Умова ] -> [ Подія ]
тобто

[ Перевіряємо це ] -> якщо це істинно [ То робимо це ]
Щоб перевірити по сторонам від об’єкта, нам потрібно додати один елемент. Формат виглядає так:

Перевіряється сторона [ перевіряється object1 | object2 поряд ] -> [ object1 | object2]
Перед умовою ми перевіряємо, в якій стороні від об’єкта ми хочемо виконати перевірку.

Всередині умови ми припускаємо, що потрібно не менше двох осередків. Комірка — це, по суті, будь-які об’єкти, що знаходяться в одному просторі і не поруч один з одним.

[ Це одна клітинка ]

[ Це перша комірка | це друга комірка | це третя клітинка ]
Перша клітинка — це об’єкт, сторони якого ми перевіряємо. Друга клітинка — це об’єкт, наявність якого ми перевіряємо. Спробуйте наступні приклади:

Left [ Player | Crate ] -> [ Player | ]
Показаний вище код видаляє ящики, якщо вони знаходяться в квадраті ліворуч від гравця.

Left [ Crate | Crate ] -> [ Player | Crate ]
Цей код перевіряє, чи ящик ліворуч від іншого ящика. Якщо так, то ящик, сторона якого перевіряється, стане новим об’єктом гравця.

Left [ Crate | Crate ] -> [ Crate | Player ]
У наведеному вище коді є така ж перевірка, але об’єктом гравця стає ящик ліворуч.

Left [ Crate | Crate | Crate ] -> [ Crate | Player | Crate ]
У цьому коді теж використовується аналогічний патерн. Ящик безпосередньо зліва від стає гравцем, якщо 3 скриньки знаходяться поруч один з одним по горизонталі.

Left [ Crate | Crate | Crate ] -> [ Crate | Crate | Player ]
Якщо 3 скриньки знаходяться поруч один з одним по горизонталі, то самий лівий стає гравцем.

Якщо ви протестуєте кожен з цих прикладів, то почнете розуміти закономірність. Перша клітинка — це об’єкт, який ми перевіряємо, щоб побачити, що знаходиться поруч. Такі осередки, від найближчої до далекої, є об’єктами, наявність яких ми перевіряємо.

Ключові слова

 

  • Up — перевіряє над об’єктом
  • Down — перевіряє під об’єктом
  • Left — перевіряє зліва від об’єкта
  • Right — перевіряє праворуч від об’єкта
  • Horizontal — перевіряє ліворуч і праворуч від об’єкта
  • Vertical — перевіряє над і під об’єктом




Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

Вам також сподобається...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *