Программа
Программа состоит из набора инструкций, вызванных выражениями. Программа должна начинаться с символа '{' и заканчиваться символом '}'. Между этими символами должны помещаться выражения. Выражения должны быть разделены символом ';'. Так выглядит глобальная структура каждой программы:
{
<инструкция>;
<инструкция>;
...
}
Переменные
Как и любой другой язык программирования, GML содержит переменные. Переменные могут хранить любые реальные значения (values) или строки (strings). Переменные не должны быть объявлены. Существует большое количество встроенных переменных. Некоторые общие (general), такие как mouse_x и mouse_y, указывают текущую позицию мыши, тогда как все другие являются локальными к образцу объекта, для которого выполняется программа, подобно x и y, которые указывают текущую позицию образца. Переменная имеет название, которое должно начинаться с символа и может содержать только символы, числа и символ подчёркивания '_'. (Максимальная длина должна составлять 64 символа). Когда Вы используете новую переменную - она локальна к текущему образцу и не известна в программах для других образцов (даже того же самого объекта). Хотя Вы можете обращаться к переменным в других образцах; смотрите ниже.
Присваивания
Присваивания хранят величину в переменной. Присваивание имеет форму:
Прежде чем присваивать значение для переменной, Вы можете его также прибавить, используя +=; вычитать, используя -=; умножать, используя *= или делить, используя /= или используйте поразрядное назначение операторов |=, &\, или ^=.
Выражения
Выражениями могут являться вещественными числами (например, 3.4) и/или строками, между одиночными или двойными кавычками (например 'hello' или “hello”) или более сложными выражениями. Для выражений существуют следующие бинарные операторы (в порядке приоритета):
&& || ^^: комбинация Булевых значений (&& = and, || = or, ^^ = xor)
< <= == != > >=: сравнение, результат истинный (1) или ложный (0)
| & ^: поразрядные операторы (| = поразрядный или, & = поразрядный and, ^ = поразрядный xor)
<< >>: поразрядные операторы (<< = shift left, > > = shift right)
+ -: сложение, вычитание
* / div mod: умножение, деление, целочисленное деление и по модулю
Отметьте, что величина деления x на y является величиной x/y, округлённой к направлению нуля к ближайшему целому. Оператор модуля возвращает разность, полученную делением операндов. Другими словами, x модуль y = x - (x поделить y) * y. Также, существуют следующие одноместные операторы:
!: нет, поворачивает истину в ложь и ложь в истину
-: отрицает следующее значение
~: отрицает следующее поразрядное значение
В качестве значений Вы можете использовать числа, переменные или функции, которые возвращают значение. Суб-выражения могут быть помещены между скобками. Все операторы работают для реальных значений. Сравнение также работает для строк, и + связывает строки. (Пожалуйста, отметьте, что вопреки определённым языкам, оба аргумента в Булевой операции всегда будут вычислены, даже когда первый аргумент уже определяет результат.)
Пример
{
x = 23;
color = $FFAA00;
str = 'hello world';
y += 5;
x *= y;
x = y << 2;
x = 23*((2+4) / sin(y));
str = 'hello' + " world";
b = (x < 5) && !(x==2 || x==4);
}
Дополнительные переменные
Вы создаёте новые переменные, определяя значение для них (объявлять их сначала не требуется). Если Вы просто используете имя переменной, то она будет сохранена только с образцом текущего объекта. Так что, не дожидаясь его нахождения, она будет работать со следующим объектом (или другим образцом этого объекта). Вы можете устанавливать и считывать переменные в других объектах, помещая название объекта с точкой перед именем этой переменной.
Чтобы создать глобальные переменные, которые являются видимыми для всех образцов объекта, перед переменной пишите слово global и точку. Вы, например, можете записать так:
{
if (global.doit)
{
// сделаем что-нибудь
global.doit = false;
}
}
Иногда Вы можете захотеть, чтобы переменные были только в пределах текущей части кода или сценария. Этим методом Вы избегаете потерю памяти и можете быть уверены в невозникновении конфликта в присваивании имён. Это быстрее, чем использование глобальных переменных. Чтобы определить данное действие, Вы должны назначить переменные в начале части кода, используюя ключевое слово var. Это выглядит следующим образом.
var <varname1>,<varname2>,<varname3>, ...
Например, Вы можете написать:
{
var xx,yy;
xx = x+10;
yy = y+10;
instance_create(xx,yy,ball);
}
Адресация переменных в других образцах
Как описано выше, Вы можете устанавливать переменные в текущих инструкциях, используя подобный оператор
Но в большинстве случаев Вы захотите адресовать переменные в другом образце. Например, Вы захотели остановить движение всех шариков, или Вы захотели переместить основного персонажа в специфическую позицию; или в событии столкновения, Вы бы захотели установить спрайт для другого взаимодействующего образца. Это может быть достигнуто, если перед именем переменной поставить название объекта и точку. Так, например, Вы можете написать
ball.speed = 0;
Это изменит скорость всех образцов объекта шарика. Существует множество специальных объектов "objects".
· self: Текущий образец, для которого мы выполняем действие
· other: Другой образец, вовлечённый в событие столкновения
· all: Все образцы
· noone: Вообще нет образцов (звучит странно, но оказывается полезным при дальнейшем рассмотрении GML)
· global: Вообще нет образцов, но контейнер сохраняет глобальные переменные
На примере, Вы можете использовать инструкции следующего вида:
other.sprite_index = sprite5;
all.speed = 0;
global.message = 'A good result';
global.x = ball.x;
Вы могли бы задаться вопросом, что же делает последнее присваивание, когда существует множество шариков. Что-ж - первое принимает значение x и назначает его глобальному значению.
Но если Вы хотите установить скорость одного определённого шарика, а не сразу всех шаров - сделать это немного сложнее. Каждый образец имеет уникальный идентификатор. Когда Вы помещаете образцы в редактор комнат, то данный идентификатор образца будет отображаться при наведении на образец курсора мыши. Он представляется в виде числа, которое больше или равно 100000. Это число Вы можете использовать дополнительно при размещении его слева от точки. Но будьте осторожны. Точка интерпретируется как десятичная точка в числе. Чтобы избежать путаницы, поместите по краям числа круглые скобки. Например: допустим идентификатор шарика имеет значение 100032, его Вы можете записать так:
Когда Вы создаёте образец в программе, запрос возвращает идентификатор. Вот конкректная часть подпрограммы:
{
nnn = instance_create(100,100,ball);
nnn.speed = 8;
}
Она создаёт шарик и устанавливает его скорость. Обратите внимание, что мы назначили идентификатор образца как переменную и использовали эту переменную как указание перед точкой. Данное использование полностью допустимо. Позвольте попробовать сделать это более точно. Точка фактически считается оператором. Она берёт значение как левый операнд и переменную (addres) как правый операнд, и возвращает адрес этой специфической переменной в указанном объекте или образце. Все имена объекта и специальные объекты, указанные выше, просто представляют значения, и с ними можно иметь дело, как и с любым значением. Например, следующая программа является правильной:
{
obj[0] = ball;
obj[1] = flag;
obj[0].alarm[4] = 12;
obj[1].id.x = 12;
}
Последняя инструкция должна читаться следующим образом. Мы берём идентификатор первого флага. Для образца с этим идентификатором мы устанавливаем x координату на 12.
Название объекта, специальные объекты и идентификаторы образца могут также использоваться во множестве функций. Они действительно рассмотрены в программах как константы.
Массивы
Вы можете использовать 1 и 2-мерные массивы в GML. Просто поместите индекс между квадратными скобками для 1-мерного массива и второй индекс с запятой между ними для 2-мерных массивов. В настоящее время Вы можете использовать индекс сгенерированного массива. Каждый массив начинается с индекса 0. Но будьте внимательны при использовании больших индексов, потому что память будет зарезервирована для большого массива. Никогда не используйте отрицательные индексы. Система устанавливает ограничение 32000 для каждого индекса и 1000000 для общего размера. Так, например, Вы можете записать следующее:
Оператор If
Оператор If имеет форму
или
Инструкция также может быть блоком. Выражение будет оценено. Если значение округлённое - <=0 (ложь) - выполняется инструкция после else, в противном случае (истина) - выполняется другая инструкция. Лучше всего, взять за правило заключать инструкцию в условном операторе всегда в фигурные скобки. Вот пример правильного использования
if (<выражение>)
{
<инструкция>
}
else
{
<инструкция>
}
Пример Следующая программа перемещает объект в середину экрана.
Оператор Repeat
Оператор Repeat имеет форму
Оператор повторяется определённое количество раз, соответствующее значению, указанному выражением, заключённым в круглые скобки.
Пример Следующая программа создаёт пять шариков в произвольных позициях.
{
repeat (5) instance_create(random(400),random(400),ball);
}
Оператор While
Оператор While имеет форму
Пока выражение истина, выполняется оператор (который также может быть блоком). Будьте осторожны с использованием 'while' циклов. Вы легко можете сделать такие циклы бесконечными, и тогда Ваша игра зависнет и не будет реагировать ни на какие действия пользователя.
Пример Следующая подпрограмма попробует поместить текущий объект в свободную позицию (аналогично действию перемещения объекта в случайную позицию).
{
while (!place_free(x,y))
{
x = random(room_width);
y = random(room_height);
}
}
Оператор Do
Оператор Do имеет форму
Этот оператор (может также быть блоком) будет выполнен, пока выражение истинно. Оператор выполняется, по крайней мере, один раз. Будьте внимательны при построении своих циклов. Вы можете их навсегда зациклить, при этом Ваша игра зависнет и не будет реагировать на любой ввод пользователя.
Пример Следующая программа пытается установить текущий объект в свободную позицию (это почти то же, что и действие для перемещения объекта в случайную позицию).
{
do
{
x = random(room_width);
y = random(room_height);
}
until (place_free(x,y))
}
Оператор For
Оператор For имеет форму
Он работает следующим образом. Сначала выполняется инструкция1. Затем оценивается выражение. Если оно истинно, выполняется инструкция3; затем инструкция2 и затем выражение оценивается снова. Это продолжается, пока выражение не будет ложно.
Возможно, всё это звучит несколько запутанно. Всё вышеизложенное следует интерпретировать следующим образом. Первая инструкция инициализирует цикл for. Выражение проверяет, должен ли цикл быть завершён. Инструкция2 - шаговая (step) инструкция, которая ведёт к следующему оценочному циклу.
Наиболее часто используется запуск счётчика, проходящий через некоторый диапазон.
Пример Следующая программа инициализирует массив длиной 10 со значениями 1-10.
Орератор Switch
Во многих ситуациях Вам понадобиться, чтобы Ваше действие зависело от конкретной величины. Вы можете использовать для этого многие операторы, но проще использовать оператор переключения. Оператор Switch имеет следующую форму:
switch (<выражение>)
{
case <выражение1>: <выражение1>; ... ; break;
case <выражение2>: <инструкция2>; ... ; break;
...
default: <инструкция>; ...
}
Это работает следующим образом. Сначала выполняется выражение. Затем это всё сравнивается с результатами других выражений после выполнения их операторами. Выполнение продолжается после первых действий операторов с правильными значениями до тех пор, пока не будет задействован оператор прерывания (break). Если никакое действие оператора не имеет правильного значения, то выполнение будет продолжаться после встроенного оператора (оператор потребуется не по умолчанию). Отметьте, что многочисленные операторы действий могут быть установлены для того же оператора. Также не потребуется прерывание. Если нет оператора прерывания, то выполнение просто продолжает код для следующего действия оператора.
Пример Следующая программа базирует свои действия при нажатой клавише.
switch (keyboard_key)
{
case vk_left:
case vk_numpad4:
x -= 4; break;
case vk_right:
case vk_numpad6:
x += 4; break;
}
Оператор Break
Оператор Break имеет следующую форму
Если использовать в течение цикла for, цикла while, цикла repeat, оператора switch или оператора with, то заканчивает этот цикл или оператор. Если использовать этот оператор за пределами вышеупомянутых операторов, то заканчивается подпрограмма (не игра).
Оператор Continue
Оператор Continue имеет следующую форму
При использовании в течение цикла for, цикла while, цикла repeat, оператора switch или оператора with, продолжает следующую величину для цикла или оператора.
Оператор Exit
Оператор Exit имеет форму
Он просто завершает выполнение данной подпрограммы. (Он не завершает выполнение игры! Для этого Вам необходима функция game_end(); смотрите ниже).
Функции
Функция имеет форму - имя функции, сопровождаемое нулём или большим количеством параметров между скобками, отделённых запятыми.
<функция>(<arg1>,<arg2>,...)
Существуют два типа функций. Во-первых, доступна большая коллекция встроенных функций, для контроля всех аспектов игры. Во-вторых, любой сценарий, который Вы определяете в своей игре, может использоваться как функция.
Обратите внимание, что для функции без параметров Вы также должны использовать скобки. Некоторые функции возвращают значения и могут использоваться в выражениях. Другие просто выполняют команды.
Отметьте, что невозможно использовать функцию как "левую" сторону назначения. Например, Вы не можете записать instance_nearest(x,y,obj).speed = 0. Взамен Вы должны записать (instance_nearest(x,y,obj)).speed = 0.
Сценарии
Когда Вы создаёте сценарий, то захотите иметь доступ к пройденным параметрам из него (или при использовании действия сценария, или при вызове сценария в качестве функции из программы (или из другой, или даже того же самого сценария). Эти параметры сохраняются в переменных argument0, argument1, ..., argument15. Таким образом, могут быть определены максимум 16 параметров. (Обратите внимание, что при вызове сценария из действия, определены могут быть только первые 5 параметров). Вы можете также использовать argument[0] и т.д.
Сценарии также могут возвращать значения, так чтобы они могли быть использованы в выражениях. Для этого Вы используете инструкцию return:
Выполнение сценария заканчивается оператором return!
Пример Вот определение для небольшого сценария, который вычисляет квадрат параметра:
Для вызова сценария изнутри части кода, следует действовать точно так же, как и при вызове функций. То есть, написать название сценария со значениями параметра (argument values) в круглых скобках.
Конструкция With
Как было сказано выше, имеется возможность читать и изменять значения переменных в других образцах. Но в большинстве случаев Вы захотите сделать с другими образцами что-то более существенное. Например, предположим, что Вы хотите переместить все шарики вниз на 8 пикселей. Вы могли бы предположить, что добиться этого можно следующей частью кода
Но это не верно. Правая сторона присваивания получает значение y-координаты первого шарика и добавляется к нему 8. Затем это новое значение устанавливается как y-координата для всех шариков. Таким образом, в результате все шарики получают ту же самую y-координату. Инструкция
будет иметь точно такой же эффект, потому как это просто сокращение первой инструкции. Так, каким же образом сделать это? Для этой цели имеется инструкция with. Её глобальная форма
<выражение> указывает один или большее количество образцов. Для этого Вы можете использовать идентификатор образца, имя объекта (чтобы указать все образцы этого объекта) или один из специальных объектов (all, self, other, noone). <инструкция> теперь выполняется для каждого из обозначенных образцов, как будто этот образец - текущий (self) образец. Таким образом, чтобы переместить все шарики на 8 пикселей вниз, Вы можете напечатать.
Если Вы хотите выполнить множество инструкций - поместите их в фигурные скобки. Например, чтобы переместить все шарики в случайную позицию, Вы можете использовать
with (ball)
{
x = random(room_width);
y = random(room_height);
}
Обратите внимание, что в пределах инструкции(ий), обозначенный образец становится самостоятельным (self) образцом. В пределах инструкций оригинал самостоятельного образца становится другим (other) образцом. Например, чтобы переместить все шарики в позицию текущего образца, Вы можете напечатать
with (ball)
{
x = other.x;
y = other.y;
}
Использование инструкции with является чрезвычайно мощным инструментом. Позвольте привести ещё несколько примеров. Чтобы уничтожить все шарики, Вы печатаете
Если взрывается бомба, и Вы хотите уничтожить все находящиеся рядом с ней образцы, можете использовать
Коментарии
Вы можете добавлять в свои программы комментарии. Всё что находится в строке после символа // не читается программой. Вы можете создать множество линий для комментариев, устанавливая текст между /* и */. (Цветная кодировка, вероятно, правильно в этом случае работать НЕ может! Нажмите F12, чтобы отключить цветную кодировку при возникновении ошибок).
Функции и переменные
GML содержит большое количество встроенных функций и переменных. С их помощью Вы можете управлять любой частью игры. Для всех действий имеются соответствующие функции, так что Вам не понадобится применять какие-либо действия, если Вы предпочитаете использовать код. С использованием функций и переменных, для управления игрой, можно добиться значительно большего, нежели при использовании только одних действий. Так что, если Вы хотите расширить возможности своей игры, настоятельно рекомендуем Вам просмотреть следующие главы, чтобы получить общее представление о том, что вообще возможно будет сделать. Обратите внимание, что указанные переменные и функции могут также использоваться при обеспечении значений для действий. Следовательно, даже если Вы и не планируете использовать в дальнейшем код или писать сценарии, тем не менее, Вы получите полезные знания для дальнейшей работы.
Ниже используется следующие соглашения. Имена переменных помеченных значком * являются доступными только для чтения, то есть их значения не могут быть изменены. Имена переменной с [0..n] после имени - являются массивами. Диапазон возможных индексов приводится.
|