DM Guide 5
Это 5 глава перевода оригинального руководства по Dream Maker от разработчиков.
Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.
Глава 5: Переменные
Часы вашей жизни - это крылья, пронзающие пространство с каждым взмахом. -- Халиль Джебран, Пророк.
Программы, созданные в DM, напоминают черную коробку, которая принимает и отдает информацию. Внутри этой черной коробки находится множество маленьких отделений, каждое из которых хранит важные для обработки информации данные. Эти ячейки называются переменными, и каждая из них имеет свое название, выгравированное творцом.
Глобальные переменные
До сих пор вы видели только объектные переменные и переменные аргумента. Есть еще два места, где переменные могут задаваться: внутри процедуры, или внутри ничего, так называемые, глобальные переменные.
Глобальные переменные используется в основном для хранения информации о свойствах всего мира. Например, вы могли бы использовать такую переменную, чтобы хранить информацию о погоде.
var/weather = "Looks like another beautiful day!" mob/verb/look_up() usr << weather mob/DM/verb/set_weather(txt as text) weather = txt
Этот пример состоит из трех частей: инициализация глобальной переменной погоды, действие для игроков, которое позволяет проверить погоду, и действие для DM, чтобы погоду изменять. Стоит обратить внимание на процесс создания переменной. Это происходит под тегом var, который находится в корне программы (если мы задаем глобальную переменную). В этом примере для переменной погоды мы задали начальное значение, но это делать не обязательно.
Объектные переменные
Позиция, в которой задают переменную, определяет ее область действия. Переменная, которую задали в корне (верхнем уровне кода), применима в любом месте программы. В то же время, если переменную задали внутри некоторого объекта, то она может применяться только внутри него или внутри его потомков.
Заметим, что когда мы говорим верхний уровень кода, то подразумеваем корень дерева программы. Это не первые строчки вашего файла.
Инициализация объектной переменной
Вы уже использовали объектные переменные, вроде name и icon, но они уже были заданы. Вы можете добавить свои собственные переменные, отличные от встроенных, для достижения разных целей. Например, вы можете ввести переменную для хранения информации о стоимости предмета.
obj var/value stone value = 1 ruby value = 50 diamond value = 100
Когда переменная определяется впервые, она записывается под тегом var. Потом можно изменить значение переменной, не определяя ее заново. Процесс изменение и определения переменных схож с действиями. Синтаксис специально построен так, чтобы предотвратить случайные ошибки.
Получение доступа к объектной переменной
В описании процедуры можно получить доступ к объектной переменной. Мы уже видели это в случаях, когда действие изменяет свойства своего источника. Например:
obj/verb/set_value(v as num) set src in view() value = v
Однако, что если мы хотим установить новое значение, только при помощи DM? Это можно сделать с помощью нового действия с измененными свойствами. Вместо того чтобы прикреплять действие к объекту (obj), мы можем присоединить его к DM. Однако в таком случае нам нужно получить доступ к значению переменной объекта из действия DM. Для этого нам нужно указать путь до переменной. Вот, как это можно сделать:
mob/DM/verb/set_value(obj/O,v as num) O.value = v
Первая переменная объявляется как obj/O, это говорит нам о том, что О является объектом, поэтому мы можем работать с переменными этого объекта. Чтобы получить доступ к конкретной переменной, мы используем оператор “точка”. Объект О пишется слева от точки, а справа имя нужной переменной, к которой мы хотим получить доступ. Оператор “точка” позволяет нам получить доступ к переменной, принадлежащей объекту O.
Посмотрите еще раз на синтаксис первой процедуры. Мы можем написать obj/O as obj in view(), но так как мы указали путь, все остальное предусматривается по умолчанию. Впрочем, длинная версия лучше воспринимается при чтении кода.
Указание пути до переменной
В DM переменной, которую вы объявляете, может быть присвоен тип её значений, это может быть, например, строка или число. Если программисту нужно получить доступ к переменной объекта, то тогда необходимо сообщить компилятору путь, до объекта, к которому относится переменная. Это можно сделать, определив путь перед именем переменной. Это может быть obj/O или что-то более длинное, вроде obj/scroll/O. В общем, нужно указать полный путь до переменной. Например, obj/O даст нам доступ к O.name, O.icon и другим переменным этого объекта. Переменная, которая определена только для свитка, скажем O.duration, требует более точного пути: obj/scroll/O.
Переменные usr и src
Две переменные, которые вы уже видели, могут быть использованы сразу с точкой, потому что путь до их объекта определен автоматически. Переменная usr объявляется как var/mob/usr, поэтому она всегда относится к нужному существу. Переменная src, очевидно, работает также, только с объектами.
Так как получение доступа к переменной источника довольно-таки частая задача, вы можете делать это напрямую. Например, вы можете использовать value, вместо src.value. Это одно и то же.
Переменная usr довольно полезная, когда src и usr обозначают разные объекты. Например, мы можем сделать предмет маскировки, который изменяет внешний вид носителя.
obj/disguise verb/wear() usr.icon = icon //zing!
Чтобы пользователь мог выключить маскировку, нам нужно хранить настоящий спрайт в переменной. Вот как это можно сделать:
obj/disguise var/old_icon verb wear() old_icon = usr.icon usr.icon = icon remove() usr.icon = old_icon
Кстати, интересная мысль. Что, если кто-то отключит маскировку другому игроку!? Вот такие причудливые вещи могут устроить игроки. Никогда не доверяйте им! В следующей главе мы рассмотрим некоторые инструменты, которые помогут это предотвратить.
Переменные процедуры
Переменные аргумента это специальный уровень переменных процедур. Встроенные переменные usr и src также находятся на этом уровне. Вы можете объявить свою собственную переменную внутри процедуры, используя такой же синтаксис, как и при объявлении любой другой переменной.
Предположим, что вы хотите поменять два объекта местами – несколько другой способ маскировки. В этом примере, свиток дает владельцу возможность стать свитком, в то время как свиток будет выглядеть, как его владелец (только не пытайтесь его использовать в туалете, иначе кто-то совершит ужасную ошибку).
obj/mirror_scroll verb/cast() var/usr_icon = usr.icon usr.icon = icon icon = usr_icon
Промежуточная переменная usr_icon помогает двум объектам обменяться своими изображениями. Мы могли бы объявить ее отдельно, но ее инициализация в действии лишает нас некоторых проблем.
Жизнь переменных
Переменные можно задавать в верхнем уровне программы, в объекте или внутри процедуры. Это три разных сферы, определяющие диапазон действия и продолжительность существования переменной.
Глобальные переменные появляются в самом начале и существуют до конца выполнения программы. Объектные переменные появляются вместе с объектом и существуют до тех пор, пока есть объект. У двух одинаковых объектов есть по своей копии одной и той же переменной, в отличие от глобальной переменной, которая существует одна для всех. Самый низкий уровень: переменные процедуры, они появляются, когда процедура начинает свою работу, и исчезают в конце процедуры. Их часто называют локальными переменными.
Уровень переменной определяет ее порядок среди других переменных. Предположим такую ситуацию: пусть у переменной процедуры будет такое же имя, как и у объектной.
mob/verb/call_me(name as text) name = name //очевидно, не то, что мы хотим src.name = name //а это уже то
Мы объявили переменную процедуры (или аргумент), которая называется name, но есть переменная существа, которая называется также. Переменные уровня процедуры имеют приоритет над объектными переменными. Чтобы получить доступ к объектной переменной, нам нужно написать src.name, а не просто name.
Аналогично, глобальные переменные имеют более низкий приоритет, чем объектные или локальные переменные. В этом случае конфликт может быть решен путем использования global.name. Ошибки с конфликтами имен в некоторых случаях сложно заметить.
В ваших интересах избежать этой проблемы. Иногда полезно иметь переменную, которая ведет себя как глобальная, но определяется на более низком уровне. Например, переменная может представлять интерес только в части кода, но вы хотите иметь ее постоянную копию. Вы этого достигните, если зададите ее как глобальную, но на требуемом уровне (многие языки используют термин “статичная” вместо “глобальная”, когда говорят о переменной, которую задают в нужном месте, но с постоянным существованием - это одно и то же).
Например, мы можем сделать магическую бумагу, при написании на которой, надпись проявится на всей бумаге мира.
obj/magic_paper var/global/msg verb write(txt as text) msg = txt read() usr << "[src] says, '[msg]'"
То, что мы определили переменную сообщения внутри объекта magic_paper, помогает нам избежать захламления глобального пространства имен ненужным мусором, который потребуется только в конкретном месте. Просто нужно вставить тег global сразу после var в пути переменной. Это поможет избежать содержания в каждой отдельной волшебной бумаге отдельной переменной для одного и того же текста. Это также позволяет освободить имя msg, чтобы использовать его в любом другом месте кода.
Константы
Другой тег, который может применяться при определении переменной это const. Это метка для переменных, которые после инициализации никогда не будут изменяться. Мы называем их постоянными переменными (здесь намеренно есть оксюморон).
Константы помогают вам сохранить ваш код от загромождения разными значениями, которые постоянно используются, но не изменяются. Вы уже видели другой путь избавления от глобальных переменных, однако преимущество использования постоянных переменных заключается в том, что они не обязательно должны носить глобальный характер.
Например, вы можете сделать двойника некоторого объекта:
obj/doppelganger var/const/init_icon = 'doppel.dmi' icon = init_icon verb clone() set src in view() icon = usr.icon revert() set src in view() icon = init_icon
Конечно, вы можете использовать везде "doppel.dmi", вместо init_icon, но если вы захотите использовать другую картинку, то вам придется вручную изменить ее по всему коду (это верный путь запутаться и сделать кучу ошибок).
Память и Переменные
Теперь поговорим о памяти. Наверное, многие из вас уже задумывались о том, как программы DM хранят информацию. Например, когда мы присваиваем спрайт одной переменной другой, то сам файл дублируется? К счастью, ответ “нет”, иначе многие операции работали бы менее эффективно.
В DM переменные могут хранить только два типа данных: цифры и ссылки. Цифры это просто. Когда вы присваиваете переменной число, то это число копируется и “кладется” в переменную. Если вы изменяете эту переменную, то ничего не меняется, кроме числа, которое переменная содержит.
Все переменные, отличные от цифровых, хранят ссылку, которая указывает, где хранится информация (программисты на C, знают ссылки как указатели). Таким образом, эти переменные хранят только ссылку к требуемой информации, будь то спрайт, существо, строка или что-то еще. Когда содержимое переменной копируется другой, то изменяется только ссылка. Сами данные не зависят от этих операций.
Программисты, которые знакомы с управлением и распределением памяти, отметят тот факт, что DM заботится о “мусоре”. Это значит, что когда на какую-то информацию (например, сообщение внутри действия say) больше не ссылается ни одна переменная, она автоматически удаляется из памяти, чтобы освободить место для новой информации. Это делает работу с множеством различной информации: текстом, картинками, звуками и другим – значительно легче. При этом вам даже не требуется беспокоиться об этом.