D:\sideБлогThe Game Maker Language: действия

⚠️ Обращайте внимание на даты.
Этот блог больше не ведётся с 17 января 2023, и на тот момент с написания этой страницы (10.06.2013) прошло 9 лет.

Надеюсь, с первым уроком вы уже ознакомились. Или уже знакомы с его материалом.

В этом уроке мы обсудим новое понятие в GML, использующее выражения. Называют их по-разному, лично я придерживаюсь перевода “действия”. Поскольку все действия-значки в коде выражаются именно с помощью них.

Справка же даёт иной термин - высказывание (statement).

Вообще, любая программа на GML - это последовательность этих самых “действий”. Любой скрипт и фрагмент кода - это несколько действий, записанных подряд. Лично я пользуюсь синтаксисом стиля С (если не знаете, что это - не обращайте внимания), поэтому действия разделяю между собой знаком ;. Строго говоря, это необязательно. Достаточно каждое действие писать на новой строке. Но если необходимо записать несколько действий в одной - может возникнуть проблема. Давайте придерживаться единых правил, и всё же ставить ;.

Вспомните, что такое “выражение”. Сейчас нам это понадобится.

Присваивания

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

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

ПЕРЕМЕННАЯ ЗНАК ВЫРАЖЕНИЕ

Обсудим каждый из элементов.

  1. ПЕРЕМЕННАЯ - просто название переменной. Это может быть как системная, так и ваша собственная. Если она системная, то сверьтесь со справкой - возможно, запись в эту переменную запрещена или бесполезна. А если такой переменной в игре ещё нет (она не системная и вы её не делали), она будет создана! И будет иметь тип доступа “поле”, то есть, будет принадлежать тому и только тому объекту, в котором её записали. Другие типы видимости переменной необходимо определить до её использования. Чтобы переменная исчезла, как только кончится скрипт, нужно написать подобную конструкцию: var название;; чтобы переменная стала глобальной, и была единой для всей игры, нужна похожая конструкция: globalvar название;.

  2. ЗНАК - знак присваивания. Обычно это просто =. С ним значение выражения будет просто записано в переменную, указанную слева. Но есть ряд случаев, когда удобнее использовать и другие. На втором месте после = по надобности стоит знак +=. Его проще всего объяснить вот такими двумя абсолютно одинаковыми по эффекту фрагментами: x = x + 5; и x += 5;

  3. ВЫРАЖЕНИЕ - любое корректное выражение, что взбредёт вам в голову. Их мы уже обсуждали в первом уроке, и мне к этому добавить пока нечего. Более сложные выражения мы рассмотрим далее - здесь их тоже можно будет применить.

Выполняется присваивание просто, как пылесос: вычисляется выражение, а полученное из него значение записывается в переменную по правилу, определённому знаком. Если используется знак =, то будет записано ровно это самое значение без изменений (а записанное там сейчас просто потеряется). Все прочие знаки присваивания выполняют с текущим значением переменной слева действие, записанное перед знаком = (если это +=, выполнится сложение, и так далее), как если бы текущее значение было слева от знака действия, а значение выражения - справа.

Как очень весело накосячить: x += x + 5;, x = 5;

Пробелы в GML можно расставлять довольно свободно. Главное - не разделить пробелом название переменной или ключевой знак. x + 5 и x+5 - совершенно равнозначные выражения, и работают одинаково. Что я рекомендую: окружать операторы, вроде +, пробелами. В длинных выражениях это позволяет лучше разглядеть, что происходит, а также выделить отдельные выражения в круглых скобках (содержимое), содержимое которых от самих скобок пробелами отделять не принято. Сложно звучит, вот вам чуть изменённый пример из прошлого урока: (2 + 2) * 2. Выглядит немножко лучше, чем (2+2)*2. Неочевидно, почему? Когда у вас возникнет код, в котором операции занимают больше одного символа - думать будет уже поздно.

Присваивание не является выражением! Однако, попытка вычислить результат присваивания может привести к осмысленному результату. На самом деле, это всегда будет ноль, за исключением случая, когда используется =, а значение переменной равно значению выражения. В этом случае значение выражения - 1. Почему так? Догадайтесь. Но имейте в виду: присваивание при этом не произойдёт!

Если вы пользуетесь GameMaker: Studio, то в вашем арсенале есть ещё пара причудливых операторов: ++ и --. Почему причудливых? Они работают очень интересно, в зависимости от того, где их написать. Просто написанный возле переменной, оператор ++ увеличит её на единицу. Это, по сути, самодостаточное действие. Однако оно является и выражением! Тут уже начинается магия. Предположим, что у нас есть переменные a и b, и в них записаны нули.

a = b++; /*b++ сразу вернёт значение b, затем добавит к нему единицу*/
	/*В "a" окажется ноль. Теперь у нас a=0, b=1*/
a = ++b; /*Здесь уже наоборот - сначала прибавится единица, и результат будет возвращён уже с ней*/
	/*В "a" окажется 2.*/

Оператор -- работает идентично, но вычитает единицу, а не прибавляет.

Вот, теперь у вас в арсенале есть одно действие, с помощью которых вы можете писать простейшие скрипты. Но написать с этими знаниями полезный скрипт будет тяжеловато. Так что поехали дальше.

Функции

Осторожно. Здесь начинается веселье. Вы предупреждены.

Функции - это… некий участок программного кода, который вы можете вызвать. Зачем? А я почём знаю. Но обычно у вызова функции есть четыре возможных причины:

  1. Вам нужен результат функции, который она просто вычисляет
  2. Вам нужен побочный эффект от выполнения этой функции (скажем, вызвал - и игра выключилась)
  3. Вам нужен побочный эффект и результат (исполнения требуемого эффекта, например)
  4. Вам нужен отдых (здесь только доля шутки, позже объясню)

У вызова функции есть вполне конкретная структура, но её тяжело описать без особой разметки, свойственной программистам, поэтому приведу несколько примеров, чтобы была понятна суть:

название_функции(выражение, ещё_одно+выражение, ещё*какое-нибудь/выражение);
название_функции(0, "Привет", 3.14159);
название_второй_функции();

Отметьте - в числах используется точка для ограничения дробной части. А использовать плюс для склеивания кусочков текста можно сколько влезет. Сейчас это не очень важно, но заметить стоит.

Где вызов функции - там всегда круглые скобки. Поэтому будем описывать структуру вызова вокруг них. Слева от них находится название функции, которую вы вызываете. Внутри через запятую перечисляются выражения (аргументы функции). Для каждой функции можно прочитать, какие ей нужны аргументы, в каком порядке и зачем. Обычно эту информацию можно найти в справке. Перед вызовом функции эти выражения вычисляются, а полученные значения передаются непосредственно в код функции.

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

Вызов функции может быть выражением, т. е., её выполнение может оставить после себя значение, прямо как после вычисления выражения. В таких случаях говорят, что “функция возвращает значение”. Скажем, есть функции, которая исключительно вычисляет результат. Синус, например:

result = sin(100 / room_speed) * radius; //Написано от балды

Но так бывает не всегда. Серьёзно, какая вам разница, с каким результатом завершилось действие “быстро выруби игру”, если игра всё равно завершилась? Она разве что зависнет, если вы ошиблись где-то дальше, но это вы заметите. Однако, это (как любил выражаться мой преподаватель) “паталогический случай”. Большинство функций всё же возвращают какое-то значение.

Наиболее важно то, что почти все действия в GameMaker выполняются при помощи вызова различных функций. Нарисовать спрайт? draw_sprite. Выключить игру? game_end. Запоминать их все - бесполезно. Они все есть в справке, и для каждой из них строго написано, какие им нужны аргументы и зачем. Если не написано - вас надули, это не функция, а переменная.

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

Эксперимент II

Здесь вам придётся поработать самостоятельно! Приготовления:

А теперь задание:

  1. Заставить obj_spinner стоять прямо на курсоре мыши.
  2. Заставить obj_spinner стоять в 50 пикселях от курсора мыши, причём в стороне, указанной в его direction. Если там ноль - то справа, если 90, то сверху, и так далее. Чтобы проверить, получилось ли - потыкайте сделанные на приготовлениях клавиши.
  3. У системной переменной direction есть собственное назначение, и использовать её для таких действий нехорошо. Избавьтесь от использования встроенной переменной direction, заменив её на свою собственную переменную. Придумайте ей имя и заботьтесь о ней.
  4. Задание-бонус: с помощью image_angle и вашей новой переменной заставить спрайт у obj_spinner всегда смотреть на курсор мыши.
  5. Ещё один бонус: при помощи point_direction заставить obj_spinner всегда смотреть в левый верхний угол комнаты.

Как вы понимаете, присылать мне выполненное задание бесполезно, я не стану его проверять. Просить подсказки тоже бесполезно, поскольку выше изложено всё, что вам надо. Если то, что у вас получилось, работает как написано - значит, вы справились. В первую очередь, успех в выполнении этих экспериментов нужен вам самим, если вы решили изучить GML. Я пишу этот цикл статей лишь в надежде на то, что новички перестанут прилипать с глупыми вопросами вроде “Научишь писать скрипты?”, потому что этим никогда не хотел заниматься ни я сам, ни кто-либо из знакомых мне опытных разработчиков в GameMaker. Равно как не люблю давать сразу готовые решения не самых сложных задач при помощи кода. Пусть попробует сам! Не получилось? Научить искать ошибки.

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

Китайская пословица