Основы программирования на GML (GameMaker Language)

Этот курс предназначен для начинающих разработчиков игр, желающих освоить язык GML в среде GameMaker Studio. Вы изучите синтаксис, работу с объектами, событиями и создадите фундамент для разработки собственных игровых проектов.

1. Введение в GML: Синтаксис, переменные и типы данных

Введение в GML: Синтаксис, переменные и типы данных

Добро пожаловать в курс «Основы программирования на GML». Если вы читаете эту статью, значит, вы решили создать свою собственную игру с помощью GameMaker. Это отличный выбор. GameMaker Language (GML) — это мощный, но при этом дружелюбный к новичкам язык программирования. Он позволяет как быстро набросать прототип, так и создать сложную коммерческую игру.

В этой первой статье мы заложим фундамент ваших знаний. Мы разберем, как «говорить» с компьютером на его языке, как хранить информацию и какие виды данных вообще существуют в мире GameMaker.

Что такое синтаксис?

Синтаксис — это набор правил грамматики для языка программирования. Если в русском языке мы ставим точку в конце предложения, то в коде тоже есть свои знаки препинания. Если вы нарушите правила синтаксиса, компьютер вас не поймет и выдаст ошибку.

GML во многом похож на такие языки, как C++, C# или Java, но он гораздо более снисходителен к ошибкам новичков.

Основные правила написания кода

1. Завершение строки Традиционно в программировании каждая команда заканчивается точкой с запятой ;. В GML это не всегда обязательно (код часто будет работать и без нее), но это считается правилом хорошего тона. Использование ; помогает избежать неочевидных ошибок в будущем.

2. Блоки кода Часто нам нужно сгруппировать несколько команд вместе, чтобы они выполнялись при определенном условии (например, «если игрок нажал кнопку»). Для этого используются фигурные скобки { и }.

3. Комментарии Комментарии — это заметки, которые программист оставляет для себя или коллег. Компьютер их игнорирует. В GML есть два вида комментариев: * Однострочные: начинаются с // Многострочные: заключаются между / и */

Переменные: Контейнеры для данных

Представьте, что вы переезжаете. Чтобы перевезти вещи, вы складываете их в коробки и подписываете маркером: «Книги», «Посуда», «Одежда». В программировании переменная — это и есть такая коробка. У нее есть имя (надпись маркером) и содержимое (значение).

!Визуальная метафора переменных как коробок с подписанными именами и разным содержимым.

Чтобы создать переменную (объявить её) и положить в неё значение (присвоить), мы используем знак равенства =. В программировании этот знак означает не «равно», а «присвоить значение справа переменной слева».

Правила именования переменных

Имя переменной может состоять из букв, цифр и знака подчеркивания _. Однако есть важные ограничения: * Имя не может начинаться с цифры. * Имя не может содержать пробелы. * Нельзя использовать зарезервированные слова (например, if, while, return).

Хорошие примеры: ammo_count, playerSpeed, _tempValue. Плохие примеры: 2ndPlayer, my score, if.

Область видимости переменных (Scope)

Это одна из самых важных тем в GML. В зависимости от того, как вы объявите переменную, она будет доступна в разных частях игры. Существует три основных типа:

#### 1. Переменные экземпляра (Instance Variables) Это самый частый тип. Если вы просто напишете hp = 10; внутри объекта, эта переменная будет принадлежать конкретному экземпляру этого объекта. У каждого врага на уровне может быть своя переменная hp, и они не будут мешать друг другу.

#### 2. Локальные переменные (Local Variables) Они создаются с помощью ключевого слова var. Эти переменные существуют только в момент выполнения текущего куска кода (скрипта или события) и исчезают сразу после его завершения. Они нужны для временных вычислений.

#### 3. Глобальные переменные (Global Variables) Эти переменные доступны из любой точки игры. Они принадлежат не объекту, а всему игровому миру. Их используют для хранения очков, настроек или номера уровня. Чтобы создать такую переменную, используют префикс global..

!Диаграмма областей видимости переменных в GML.

Типы данных

В наши «коробки»-переменные можно класть разные вещи. GML определяет тип данных автоматически (динамическая типизация), но вам важно понимать, с чем вы работаете.

1. Вещественные числа (Real Numbers)

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

Пример математической операции с числами: Допустим, мы рассчитываем урон. Формула может выглядеть так:

Где — итоговый урон, — базовый урон оружия, а — множитель критического удара.

В коде это выглядит так:

2. Строки (Strings)

Строка — это текст. Строки всегда заключаются в двойные " или одинарные ' кавычки. Если вы забудете кавычки, GameMaker подумает, что вы пишете имя переменной.

Вы можете складывать строки (конкатенация):

3. Булевы значения (Booleans)

Это логический тип, который может иметь только два значения: истина (true) или ложь (false). Они используются для проверок и переключателей.

В GML true также равно числу , а false равно числу . Но для читаемости кода лучше использовать слова.

4. Массивы (Arrays)

Иногда нужно хранить список предметов, например, инвентарь. Создавать переменные item1, item2, item3 неудобно. Для этого есть массивы — нумерованные списки.

Чтобы получить доступ к элементу, мы используем его индекс в квадратных скобках. Важно: нумерация начинается с нуля.

5. Специальные типы

В GML есть особые значения, которые вы будете встречать часто: * undefined — значение не определено (часто бывает при ошибках или пустых полях). * noone — специальное значение (равное ), которое означает «никого». Например, функция поиска врага может вернуть noone, если врагов рядом нет.

Простые операции

Теперь, когда у нас есть переменные, мы можем производить над ними действия.

Арифметические операции

* + Сложение * - Вычитание Умножение * / Деление

Операции сравнения

Эти операции всегда возвращают true или false. * == Равно ли? (Обратите внимание на двойное равно для сравнения!) * != Не равно * > Больше * < Меньше * >= Больше или равно * <= Меньше или равно

Пример проверки:

Заключение

Сегодня вы узнали, как строится код в GML. Вы поняли, что переменные — это именованные хранилища данных, и что эти данные могут быть разных типов: от простых чисел до текстовых строк и списков. Также вы разобрались с тем, что переменные могут быть временными (var), принадлежать объекту или быть глобальными.

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

2. Управление логикой: Условные операторы if/else и циклы

Управление логикой: Условные операторы if/else и циклы

В предыдущей статье мы научились создавать «коробки» для данных — переменные. Мы узнали, как хранить числа, строки и списки. Но сами по себе данные — это просто статичная информация. Чтобы игра ожила, она должна уметь принимать решения.

Почему враг атакует, когда видит игрока? Почему дверь открывается только при наличии ключа? Почему монетки исчезают, когда мы их касаемся? За всё это отвечает логика.

В этой статье мы разберем два фундаментальных столпа программирования на GML: условные операторы (как научить игру думать) и циклы (как научить игру выполнять рутинную работу).

Условные операторы: Искусство выбора

В основе любой компьютерной логики лежит простая концепция: «Если выполняется условие А, то сделай действие Б». В GML, как и в большинстве языков, для этого используется оператор if (если).

Базовая конструкция if

Синтаксис предельно прост. Мы пишем ключевое слово if, затем в круглых скобках указываем условие, а в фигурных скобках — код, который нужно выполнить.

Компьютер проверяет выражение в скобках. Если оно истинно (true), код внутри выполняется. Если ложно (false), код игнорируется.

!Схема работы условного оператора if.

Конструкция else (иначе)

Часто нам нужно не просто сделать что-то при успехе, но и предусмотреть альтернативный вариант. Например: «Если у игрока есть ключ, открой дверь. Иначе проиграй звук закрытого замка».

Блок else выполняется только в том случае, если условие в if оказалось ложным.

Конструкция else if (иначе если)

Иногда вариантов больше двух. Представьте светофор: если красный — стой, если желтый — готовься, если зеленый — иди. Для таких цепочек используется else if.

Важно помнить: как только одно из условий сработало, остальные проверки в этой цепочке пропускаются. Если у игрока 1200 очков, он получит "Gold", и проверка на "Silver" даже не запустится.

Логические операторы: Сложные условия

Жизнь редко бывает простой. Часто для принятия решения нужно учесть несколько факторов одновременно. Например, чтобы выстрелить, нужно: (нажата кнопка огня) И (есть патроны) И (игрок не перезаряжается).

Для объединения условий в GML используются логические операторы:

1. Логическое И (&& или and)

Результат будет истинным, только если оба условия истинны.

Математически это можно записать как умножение логических значений:

Где — результат (1 или 0), — первое условие, — второе условие. Если хотя бы один множитель равен 0 (ложь), результат будет 0.

2. Логическое ИЛИ (|| или or)

Результат будет истинным, если хотя бы одно из условий истинно.

3. Логическое НЕ (! или not)

Этот оператор инвертирует значение. true превращается в false и наоборот. Это очень удобно для переключателей.

Оператор switch: Когда слишком много «если»

Если вам нужно проверить одну переменную на множество конкретных значений, цепочка if... else if... else if становится громоздкой и трудночитаемой. Здесь на помощь приходит switch.

Представьте, что вы программируете поведение врага, у которого есть состояния: стоит, идет, атакует.

Ключевые элементы switch: * case: Вариант значения. Если переменная state равна этому значению, выполняется код после двоеточия. * break: Команда «выход». Она обязательна. Если её не написать, программа продолжит выполнять код следующего case, даже если он не подходит. Это частая ошибка новичков. * default: Выполняется, если ни один из case не подошел (аналог else).

Циклы: Сила повторения

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

1. Цикл repeat

Это уникальная фишка GML. Самый простой цикл, который просто повторяет код указанное количество раз. Идеально подходит для простых задач, где не нужен счетчик.

2. Цикл while (Пока)

Этот цикл выполняется до тех пор, пока условие остается истинным. Это мощный, но опасный инструмент.

Осторожно: Бесконечный цикл! Если условие в while никогда не станет ложным (например, вы забыли изменять переменную внутри цикла), игра зависнет намертво. Компьютер будет бесконечно крутиться в этом куске кода, игнорируя всё остальное.

3. Цикл do... until (Делай... пока не)

Похож на while, но с одним отличием: проверка условия происходит после выполнения кода. Это гарантирует, что код внутри цикла выполнится хотя бы один раз.

4. Цикл for

Самый универсальный и часто используемый цикл в программировании. Он идеально подходит, когда нам нужно перебрать массив или выполнить действие со счетчиком.

Синтаксис выглядит пугающе, но он логичен: for (инициализация; условие; шаг)

Разберем структуру for:

  • Инициализация (var i = 0): Создаем временную переменную-счетчик . Обычно начинают с 0.
  • Условие (i < 5): Цикл работает, пока это выражение истинно.
  • Шаг (i += 1): Что делать с после каждого круга. Обычно увеличиваем на 1.
  • Математически количество итераций в таком цикле можно выразить как:

    Где — начальное значение счетчика (в примере 0), а — граничное значение (в примере 5), при условии шага равного 1. То есть цикл выполнится раз.

    !Визуализация жизненного цикла переменной-счетчика внутри цикла for.

    Управление потоком внутри циклов

    Иногда нужно вмешаться в работу цикла прямо в процессе.

    * break: Немедленно прерывает цикл и выходит из него. Полезно, если мы искали предмет и нашли его — нет смысла искать дальше. continue: Пропускает оставшуюся часть кода в текущей* итерации и сразу переходит к следующей. Полезно, если мы хотим пропустить обработку конкретного элемента.

    Заключение

    Сегодня мы превратили ваш код из простого хранилища данных в активную систему, способную принимать решения и выполнять сложную работу. Вы узнали: * Как использовать if и else для ветвления сюжета. * Как комбинировать условия с помощью && и ||. * Как использовать switch для управления состояниями. * Как заставить игру делать работу за вас с помощью циклов repeat, while и for.

    Эти инструменты — это 90% логики любой игры. В следующей статье мы научимся организовывать этот код в удобные блоки, которые можно использовать многократно — мы поговорим о функциях и скриптах.

    3. Архитектура игры: Объекты, события и взаимодействие экземпляров

    Архитектура игры: Объекты, события и взаимодействие экземпляров

    Мы уже научились объявлять переменные и писать логические условия. Вы знаете, как сказать компьютеру: «Если здоровье меньше нуля, сделай что-то». Но где именно писать этот код? Куда положить переменную hp? И как сделать так, чтобы один враг не знал о здоровье другого?

    В этой статье мы переходим от теории кода к практике движка GameMaker. Мы разберем архитектуру, на которой строится любая игра: Объекты, Экземпляры и События.

    Объекты и Экземпляры: В чем разница?

    Это самый важный концепт в GameMaker, который часто путает новичков. В редакторе ресурсов (Asset Browser) вы создаете Объекты (Objects). Но в комнату (Room) вы помещаете Экземпляры (Instances).

    Аналогия с чертежом и домом

    Представьте, что вы архитектор.

  • Объект (Object) — это чертеж дома. Это просто бумага с инструкциями. В чертеже написано: «У дома должна быть красная крыша и дверь посередине». Чертеж один, он лежит у вас в столе. В нем нельзя жить.
  • Экземпляр (Instance) — это реальный дом, построенный по этому чертежу. Вы можете построить целую улицу из 50 домов по одному чертежу. В каждом доме живут разные люди, в одном может гореть свет, а в другом — нет.
  • !Визуализация различия между Объектом (чертеж) и Экземплярами (реальные объекты в игре).

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

    Когда вы пишете код внутри объекта obj_enemy, вы пишете инструкцию для всех будущих врагов. Но когда игра запускается, каждый враг становится независимым экземпляром.

    Если вы напишете hp = 100; в событии создания, у каждого врага будет свое личное здоровье. Если вы нанесете урон одному врагу, здоровье остальных не изменится, хотя они созданы из одного объекта.

    Система событий (The Event System)

    Код в GameMaker не выполняется сплошным текстом сверху вниз, как в простой консольной программе. Он работает на основе событий. Объект просто «ждет», пока произойдет что-то конкретное, и реагирует на это куском кода.

    Вот основные события, которые вы будете использовать в 90% случаев:

    1. Create Event (Событие создания)

    Это событие срабатывает один раз — в тот самый момент, когда экземпляр появляется в игровом мире. Это идеальное место для инициализации переменных.

    2. Step Event (Событие шага)

    Это сердце вашей игры. Step Event срабатывает каждый кадр игры. Если ваша игра работает со скоростью 60 кадров в секунду (60 FPS), то код в Step Event выполнится 60 раз за одну секунду.

    Здесь пишется вся логика, которая требует постоянной проверки: движение, гравитация, проверка нажатия клавиш, таймеры.

    Математически перемещение персонажа в этом событии можно описать так:

    Где — новая позиция (координата), — текущая позиция, а — скорость (смещение за один кадр).

    3. Draw Event (Событие отрисовки)

    GameMaker рисует спрайт объекта автоматически. Но если вы добавите код в Draw Event, автоматическая отрисовка отключится, и вы получите полный контроль. Здесь можно рисовать полоски здоровья над головой, текст или спецэффекты.

    Важно: Никогда не пишите игровую логику (движение, изменение переменных) в Draw Event. Это событие предназначено только для визуализации.

    4. Destroy Event (Событие уничтожения)

    Срабатывает в последний момент жизни экземпляра, прямо перед тем, как он исчезнет из памяти (например, когда его убили). Здесь можно создать эффект взрыва или начислить очки игроку.

    Взаимодействие между экземплярами

    Изолированный объект скучен. Игра начинается, когда объекты взаимодействуют: пуля попадает во врага, игрок подбирает монету. В GML есть несколько способов «достучаться» до другого экземпляра.

    Точечная нотация (Dot Notation)

    Если вы знаете имя объекта, вы можете обратиться к его переменным через точку. Допустим, у нас есть obj_player.

    Опасность: Этот код сработает идеально, если obj_player в комнате один. Но если вы напишете obj_enemy.hp = 0, GameMaker выберет случайного врага (обычно самого старого) и убьет его. Чтобы взаимодействовать с конкретным экземпляром, нам нужен его уникальный ID.

    Уникальный ID экземпляра

    У каждого экземпляра в комнате есть уникальный номер (например, 100052). Все функции столкновений возвращают именно этот ID.

    Конструкция with

    Это мощнейший инструмент GML. Он позволяет временно перенести выполнение кода «внутрь» другого экземпляра или всех экземпляров определенного объекта.

    Когда срабатывает with (obj_enemy), код внутри фигурных скобок выполняется для каждого активного врага в комнате. Внутри этих скобок переменная hp относится к конкретному врагу, а не к тому, кто вызвал код.

    Ключевое слово other

    Внутри конструкции with или в событии столкновения (Collision Event) часто нужно обратиться к тому, кто инициировал действие. Для этого используется other.

    Пример: Пуля сталкивается с врагом.

    Коллизии: Как мы чувствуем мир

    Чтобы объекты не проходили сквозь стены и друг друга, используются проверки коллизий. В GML есть два основных подхода:

    1. События столкновения (Collision Events)

    Вы выбираете событие «Collision with obj_wall» в редакторе. Это просто, но дает мало контроля. Подходит для простых действий (подбор монетки).

    2. Проверки в коде (Code Checks)

    Профессионалы чаще используют функции в Step Event. Самая популярная — place_meeting.

    Функция place_meeting(x, y, obj) виртуально помещает ваш объект в указанные координаты и возвращает true (истина), если там есть перекрытие с объектом obj, или false (ложь), если чисто.

    Если вам нужно не просто узнать «есть ли там кто-то», а получить ID этого «кого-то», используйте instance_place.

    Родительские объекты (Parenting)

    Представьте, что у вас в игре 50 видов врагов: орки, гоблины, драконы. У всех есть здоровье, все получают урон от пуль. Писать код столкновения с пулей 50 раз — ужасная идея.

    Вы можете создать объект obj_enemy_parent. В нем вы пропишете логику получения урона. Затем для всех реальных врагов вы укажете obj_enemy_parent как Родителя (Parent).

    Преимущества наследования:

  • Наследование кода: Дети автоматически выполняют код родителя (если вы не перепишете его).
  • Группировка: Если вы напишете with (obj_enemy_parent), это подействует и на орков, и на гоблинов, и на драконов.
  • Заключение

    Мы разобрали фундамент архитектуры GameMaker. Теперь вы понимаете, что: * Объект — это шаблон, а Экземпляр — живая копия. * Игра живет циклами Событий (Create -> Step -> Draw). * Для управления группой объектов используется with и родительские объекты.

    В следующей статье мы займемся визуальной частью: спрайтами, анимацией и тем, как заставить вашу игру выглядеть красиво.

    4. Визуализация и аудио: Работа со спрайтами, функциями рисования и звуком

    Визуализация и аудио: Работа со спрайтами, функциями рисования и звуком

    В предыдущих статьях мы создали «мозг» нашей игры: научились писать логические условия, циклы и настроили архитектуру объектов. Но пока что наша игра — это лишь набор невидимых правил и математических вычислений. Пришло время дать игроку глаза и уши.

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

    Анатомия спрайта: Больше, чем просто картинка

    Вы уже знаете, что Спрайт (Sprite) — это графический ресурс, который мы назначаем объекту. Однако в GML спрайт — это не просто статичное изображение, а сложная структура, которой можно управлять в реальном времени.

    Основные переменные отрисовки

    У каждого объекта есть встроенные переменные, отвечающие за то, как именно отображается его спрайт. Изменяя их, вы можете создавать анимации и эффекты без создания новых графических файлов.

    1. sprite_index Эта переменная хранит ссылку на текущий спрайт объекта. Вы можете менять её в любой момент, например, когда персонаж начинает бежать или прыгать.

    2. image_index и image_speed Спрайты часто состоят из нескольких кадров (субкадров). * image_index — это номер текущего кадра (начинается с 0). Это число может быть дробным. * image_speed — это множитель скорости анимации. Если он равен 1, анимация играет с нормальной скоростью. Если 0 — анимация замирает.

    3. image_xscale и image_yscale Эти переменные отвечают за масштаб по горизонтали и вертикали. По умолчанию они равны 1.

    Математически новый размер объекта рассчитывается так:

    Где — итоговый размер в пикселях, — исходный размер спрайта, а — коэффициент масштабирования (image_xscale или image_yscale).

    Трюк с разворотом: Если установить image_xscale = -1, спрайт зеркально отразится по горизонтали. Это классический способ заставить персонажа смотреть влево без рисования отдельного спрайта.

    4. image_angle Угол поворота спрайта в градусах. 0 — это право, 90 — вверх, 180 — влево, 270 — вниз. Вращение происходит вокруг точки привязки (Origin) спрайта.

    5. image_alpha Прозрачность спрайта. Значение варьируется от 0 (полностью прозрачный) до 1 (полностью видимый). Это идеально подходит для эффектов исчезновения или призраков.

    6. image_blend Цвет смешивания. По умолчанию это c_white (белый), что означает «рисовать как есть». Если вы установите c_red, спрайт окрасится в красный оттенок. Это полезно, чтобы показать, что враг получил урон.

    !Визуализация влияния основных переменных трансформации на спрайт.

    Событие Draw Event: Художник внутри кода

    До сих пор мы полагались на то, что GameMaker сам рисует спрайты объектов. Он делает это автоматически в скрытом процессе. Но иногда нам нужно больше контроля: нарисовать полоску здоровья над головой, вывести текст очков или отобразить инвентарь.

    Для этого существует событие Draw Event.

    Главное правило Draw Event

    Как только вы добавляете любой код в событие Draw, GameMaker перестает рисовать спрайт объекта автоматически. Он считает, что раз вы взяли управление на себя, то вы сами решите, что рисовать.

    Если вы хотите, чтобы объект по-прежнему рисовал свой спрайт, но с дополнениями, вы должны явно приказать ему это сделать:

    Функции рисования

    В GML сотни функций рисования. Все они начинаются с префикса draw_. Вот самые важные:

    * draw_sprite(sprite, subimg, x, y) — нарисовать конкретный спрайт в конкретных координатах. * draw_text(x, y, string) — вывести текст. * draw_rectangle(x1, y1, x2, y2, outline) — нарисовать прямоугольник. * draw_circle(x, y, r, outline) — нарисовать круг. * draw_line(x1, y1, x2, y2) — нарисовать линию.

    Настройка кисти

    Функции рисования используют текущие настройки «кисти» — цвета, прозрачности и шрифта. Эти настройки глобальны: если вы поменяли цвет на красный, всё последующее будет рисоваться красным, пока вы не поменяете его обратно.

    Хорошим тоном считается сброс настроек (цвет, прозрачность, выравнивание текста) после того, как вы закончили рисовать свой элемент.

    Плавность и математика: Функция lerp

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

    Формула линейной интерполяции выглядит так:

    Где — итоговое значение, — начальное значение, — конечное значение, а — коэффициент интерполяции от 0 до 1 (процент пути).

    В GML это записывается проще: val = lerp(val, target, 0.1);

    Пример плавного изменения прозрачности при смерти:

    Каждый кадр прозрачность будет приближаться к нулю на 5% от оставшейся разницы. Это создает приятный эффект затухания.

    Аудио: Звуковое оформление

    Звук в GameMaker работает по схожему принципу с графикой. Вы загружаете звуковые файлы (Sound Assets) и воспроизводите их по имени.

    Воспроизведение звука

    Основная функция — audio_play_sound. Она требует три аргумента:

  • Sound ID: Имя ресурса звука (например, snd_jump).
  • Priority: Приоритет (число). Если системе не хватит каналов для воспроизведения всех звуков, она выключит звуки с низким приоритетом. Обычно для музыки ставят 100, для важных эффектов 10, для фоновых шумов 1.
  • Loop: Зацикливание (true или false). Музыка должна быть true, выстрел — false.
  • Изменение параметров звука

    Вы можете менять громкость и высоту тона (pitch) звуков в реальном времени. Это добавляет вариативности.

    * audio_sound_gain(index, volume, time) — изменение громкости. time позволяет сделать плавное затухание (в миллисекундах). * audio_sound_pitch(index, pitch) — изменение скорости/высоты. 1 — норма, 0.5 — низкий бас (замедление), 2 — писк (ускорение).

    Пример: Двигатель машины звучит выше, когда скорость больше.

    Координаты и привязка (Origin)

    При работе с визуализацией критически важно понимать, где находится точка у вашего спрайта. Это свойство называется Origin (Начало координат).

    * Если Origin в левом верхнем углу (0, 0), то при вращении спрайт будет «махать» вокруг этого угла. * Если Origin в центре (Middle Centre), спрайт будет вращаться вокруг своей оси.

    Это особенно важно при использовании image_xscale = -1. Если точка привязки не по центру, спрайт при развороте «улетит» в сторону на свою ширину.

    Заключение

    Теперь ваша игра не только работает, но и выглядит живой. Вы умеете: Управлять анимацией и трансформацией спрайтов через переменные image_. * Использовать Draw Event для создания интерфейсов и спецэффектов. * Применять математику (lerp) для плавности. * Работать со звуковым окружением.

    В следующей части курса мы объединим все полученные знания, чтобы создать полноценную игровую систему — мы поговорим о камерах, комнатах и управлении игровым состоянием.

    5. Структурирование кода: Скрипты, пользовательские функции и массивы

    Структурирование кода: Скрипты, пользовательские функции и массивы

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

    Код начинает разрастаться. Возможно, вы заметили, что копируете одни и те же строки из объекта игрока в объект врага. Или ваш список переменных стал настолько длинным, что в нем сложно ориентироваться. Это называется «спагетти-код» — он запутанный, сложный для поддержки и склонный к ошибкам.

    В этой статье мы займемся генеральной уборкой. Мы изучим инструменты, которые превращают хаос в порядок: функции, скрипты и продвинутую работу с массивами и структурами.

    Принцип DRY: Не повторяй себя

    В программировании есть золотое правило: DRY (Don't Repeat Yourself) — «Не повторяйся». Если вы пишете один и тот же код дважды, значит, пора создать функцию.

    Представьте, что у вас есть 10 разных врагов. У каждого есть здоровье, и каждый должен получать урон. Копировать код получения урона 10 раз — плохая идея. Если вы захотите изменить формулу урона, вам придется править её в 10 местах. Функция позволяет написать логику один раз и вызывать её одной строкой.

    Функции и Скрипты в GML

    В современном GameMaker (версии 2.3 и выше) функции являются основой всего. Скрипт (Script) — это просто файл-контейнер, в котором вы пишете свои глобальные функции.

    Объявление функции

    Чтобы создать свою команду, мы используем ключевое слово function. Давайте напишем функцию, которая рассчитывает урон.

    Разберем анатомию функции:

  • Имя: calculate_damage. По этому имени мы будем вызывать функцию.
  • Аргументы: _base_damage и _armor. Это данные, которые мы «скармливаем» функции. Принято начинать имена локальных аргументов с нижнего подчеркивания _, чтобы не путать их с переменными объекта.
  • Тело функции: Код внутри { }. Здесь происходит магия.
  • Возврат значения (return): Это результат работы функции. Когда код доходит до return, функция останавливается и «отдает» значение тому, кто её вызвал.
  • !Визуализация потока данных через функцию: входные параметры обрабатываются логикой и возвращают результат.

    Использование функции

    Теперь в любом объекте (игрок, враг, босс) мы можем использовать эту логику:

    Математика функций

    Функции в программировании очень похожи на функции в математике. Рассмотрим формулу расчета урона с учетом критического удара:

    Где — итоговый урон, — базовый урон оружия, — броня цели, а — множитель критического удара (например, 2.0 для крита или 1.0 для обычного удара).

    В коде это выглядит так:

    Продвинутая работа с массивами

    Мы уже касались массивов в первой статье. Это нумерованные списки. Но массивы в GML гораздо мощнее, чем просто список покупок.

    Функции для работы с массивами

    Вам не обязательно знать размер массива заранее. Вы можете менять его динамически.

    * array_create(size, default_value) — создает массив заданного размера, заполненный значением по умолчанию. * array_push(array, value) — добавляет новое значение в конец массива. * array_pop(array) — удаляет последнее значение из массива и возвращает его. * array_length(array) — сообщает текущую длину массива. * array_contains(array, value) — проверяет, есть ли значение в массиве (возвращает true или false).

    Пример инвентаря:

    Многомерные массивы (2D Arrays)

    Иногда одного списка недостаточно. Представьте шахматную доску или карту уровня, состоящую из клеток. Здесь нам нужна сетка. Это называется двумерным массивом.

    В GML это реализуется как «массив массивов». Вы обращаетесь к ячейке по двум координатам: grid[x][y].

    !Структура двумерного массива, где доступ к данным осуществляется по номеру строки и столбца.

    Структуры (Structs): Новый лучший друг

    До версии GML 2.3, если вы хотели создать сложную структуру данных (например, описание предмета с именем, весом и ценой), вам приходилось использовать громоздкие объекты или параллельные массивы. Теперь у нас есть Структуры.

    Структура — это «легкий» объект. У неё нет событий (Create, Step, Draw), нет координат x и y (если вы их сами не создадите), и она почти не занимает памяти. Это просто контейнер для именованных данных.

    Создание структуры

    Конструкторы (Constructors)

    Чтобы не писать фигурные скобки каждый раз вручную, мы можем создать функцию-конструктор. Это «шаблон» для создания структур. В объявлении функции нужно добавить слово constructor.

    Обратите внимание на ключевое слово new. Оно говорит GameMaker: «Используй функцию Item как чертеж и построй мне новую структуру».

    Практический пример: Система квестов

    Давайте объединим массивы и структуры, чтобы создать простой журнал заданий.

  • Мы создадим конструктор для квеста.
  • Мы создадим массив для хранения списка квестов.
  • Мы напишем функцию для добавления квеста.
  • Теперь global.quest_log — это массив, внутри которого лежат структуры. Мы можем перебрать его циклом for и вывести названия всех квестов на экран.

    Область видимости (Scope) в функциях

    Важно помнить о том, какие переменные видит функция.

  • Локальные (var): Если вы объявите переменную через var внутри функции, она исчезнет сразу после return.
  • Переменные экземпляра: Если функция вызвана внутри объекта (без global.), она имеет доступ к переменным этого объекта (x, y, hp).
  • Глобальные: Функция всегда видит global.variable.
  • Заключение

    Сегодня вы перешли на новый уровень программирования. Вы научились: * Использовать функции, чтобы не повторять код и упростить логику. * Понимать разницу между аргументами (вход) и возвращаемым значением (выход). * Работать с динамическими массивами и создавать сетки данных. * Использовать структуры и конструкторы для создания сложных типов данных.

    Ваш код стал чище, модульнее и профессиональнее. Теперь вы можете создавать сложные системы инвентаря, диалогов и RPG-характеристик, не утопая в хаосе переменных.

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