Garry's Mod
Оценок: 159
Wiremod Expression 2. Изучаем основы
От j0ez
В этом руководстве я опишу базовые принципы Wiremod Expression 2, расскажу о типах данных, базовых функциях и конструкциях языка.
   
Наградить
В избранное
В избранном
Удалить
Что такое Expression 2
Expression 2 - это язык программирования, поставляемый с аддоном Wiremod, который позволяет манипулировать объектами, создавать голограммы, проводить математические вычисления и тому подобное. Как и каждый язык программирования, он имеет свои функции, конструкции и правила.
В компиляторе языка присутствует E2Helper, ваш верный помощник, позволяющий вам найти необходимую вам функцию и узнать, что эта функция делает, возвращает и какие аргументы принимает. Правда, он на английском языке, так что у некоторых могут возникнуть трудности.
Окно компилятора
Для начала рассмотрим наше окно компилятора. Чтобы открыть его, необходимо открыть Q-меню, справа сверху выбрать вкладку Wiremod (конечно же, надо скачать Wiremod, но раз вы читаете это руководство, я полагаю, что он у вас уже установлен), далее найти папку "Chips, Gates", нажать Expression 2 (далее Expression 2 я буду писать как E2) и потом слева в открывшемся окне нажать "New Expression".

Итак, перед нами появилось вот такое окно:


Слева мы видим древовидную структуру папок и файлов с E2 чипами. Тут вы можете выбрать файл, который хотите отредактировать. Сами эти файлы на компьютере хранятся в папке Steam\SteamApps\common\GarrysMod\garrysmod\data\expression2.

Небольшой совет: сортировать E2 файлы лучше именно через проводник Windows, потому что через встроенный проводник E2 это делается гораздо медленнее.

Если вы создали чип, но слева он не появился, нажмите внизу слева кнопку Update. Это обновит меню, и вы увидите ваш недостающий чип.

В самом верху этого меню есть строка поиска, с помощью которой можно найти чипы в вашей коллекции.

Сверху расположено 5 кнопок (по порядку): Скрыть/открыть левое меню с файлами, сохранить текущий файл, создать новый файл, удалить текущий файл, обновить файл. Думаю, что комментарии излишни.

Справа сверху также расположено 4 кнопки: Sound Browser - тут вы можете найти все звуки из игр Source, которые могут быть использованы при создании чипа, E2Helper - сборник всех функций E2 c их описанием на английском, Save as - сохранить файл под новым именем, Save & Exit - сохранить файл и выйти.

Чуть ниже расположено поле, где показываются все открытые на данный момент E2 чипы.

В самом низу есть строка, где у меня написано "Validation successful". При нажатии на эту строку компилятор проверяет ваш код на наличие ошибок и в зависимости от того, есть ли они или нет, меняет свой цвет на красный или зеленый, а также показывает место, где обнаружена ошибка.

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

Основная зона компилятора - место, где вы будете писать свой код. Именно тому, что туда нужно писать, и будет посвящена оставшаяся часть руководства.
Директивы
Создадим новый чип. Перед нами появляются 5 строк, начинающихся с @ (они называются директивами), и комментарии (заключены между #[...]#):

Комментарии можете смело удалять. Тут разработчики пишут последние обновления языка.

Рассмотрим теперь первые пять строк:

@name - имя нашего чипа. По умолчанию при сохранении чип будет брать имя, указанное в этой строке, иначе будет имя "generic".

@inputs - список переменных, которые будут подаваться на вход нашего чипа. Их нельзя менять непосредственно внутри чипа, а можно только считывать их текущие значения.

@outputs - противоположность @inputs. Объявляет переменные, которые будут подаваться на выход чипа. Используется в тех случаях, когда вы хотите передать значение из чипа на другой элемент из Wiremod'a, например, Screen или другой чип. Также сюда можно занести переменные, за значениями которых вы хотите следить через инструмент Debugger.

@persist - переменные, используемые внутри вашего чипа. Значения этих переменных нельзя вывести обычным путем, как, например, вы бы сделали с выходными переменными.

@trigger - сюда мы пишем события, при которых будет срабатывать наш чип, например, можно настроить срабатывание чипа на изменение определенных входных переменных или вообще всех входных переменных.

Также присутствует не указанная здесь директива @model. Она определяет модель нашего чипа. Вместо стандартной модели чипа можно использовать, например, бочку, череп, да что угодно!

В последнем обновлении, как видно на скриншоте, добавлена директива @autoupdate. Если она присутствует в чипе, то при создании или дублировании чипа он будет искать последнюю версию этого чипа и запускать ее.
Операторы
Операторы - это основные конструкции языка, которые вы будете постоянно использовать в программировании. Рекомендую их все запомнить, потому что без знания операторов практически никакой код не заработает.

Синтаксис
Пример
Описание
#
#This is a comment
Это однострочный комментарий. Все, что находится внутри него, не будет обрабатываться компилятором.
#[Comment]#
Это многострочный комментарий. Работает аналогично предыдущему за исключением того, что в него можно заключить как множество строк, так и часть одной строки.
if () {}
if(A){B=C}
Выполняет действия в фигурных скобках, если результат выполнения функции A - истина
else {}
else{A=B}
Этой конструкции должна предшествовать if(){} или elseif(){}. Выполняет действия в фигурных скобках, если не было выполнено условие предыдущей конструкции
elseif () {}
elseif (A) {B=C}
Смесь предыдущих двух конструкций. Этой конструкции также должна предшествовать if(){} или elseif(){}. Выполняет действия в фигурных скобках, если не было выполнено условие предыдущей конструкции и если было выполнено условие в круглых скобках
switch () {
case ... , ... break
default, ... break}
switch (A) {
case "lol",B=A break
case "more",B=C D=C
default, B="" break}
Оператор выбора. В зависимости от значения переменной в скобках проделывает те или иные действия. Если значение в круглых скобках соответствует одному из значений после case, то выполняется действие в соответствующей строке и дальше, пока не встретится ключевое слово break. Наличие break не обязательно. В этом случае будут выполняться операции из последующих условий. В примере у второго case не указан break. Это значит, что при A=="more" выполнятся действия B=C, D=C, а также(!) B="", и только после этого будет произведет выход из оператора выбора switch. Если же не найдено ни одного подходящего значения, но есть ключевое слово default, то в этом случае выполняется действие в строке с default и далее до ключевого слова break. Строка с default не обязательна. Если ее нет и не было найдено значение среди всех, стоящих после case, то ничего не происходит.
( ? : )
D = A ? B : C
Если A - истина, то D=B, иначе D=C
&
if (A & B) {C = 1}
Выдает значение истина, если были выполнены оба условия, связанных оператором &, иначе выдает значение ложь
|
if (A | B) {C = 1}
Выдает значение истина, если выполнено хотя бы одно из условий, связанных оператором |, иначе выдает значение ложь
==
if (A == B) {C = 1}
Оператор равенства (не путайте с оператором присваивания). Выдает значение истина, если значения по обе стороны от оператора равны
>=
if (A >= B) {C = 1}
Выдает значение истина, если значение слева больше или равно значению справа
<=
if (A <= B) {C = 1}
Выдает значение истина, если значение слева меньше или равно значению справа
>
if (A > B) {C = 1}
Выдает значение истина, если значение слева больше значения справа
<
if (A < B) {C = 1}
Выдает значение истина, если значение слева меньше значения справа
!=
if (A != B) {C = 1}
Выдает значение истина, если значение слева не равно значению справа
!
if (!A) {B = 1}
Необходимо наличие условия. Инвертирует результат выполнения условия
++
if (!A) {B++}
Инкремент. Прибавляет 1 к переменной. Эквивалентная конструкция B=B+1. Противоположная конструкция --. Вычитает 1 из переменной
~
if (~Button & Button) {DO stuff}
Выдает 1, если текущее срабатывание было вызвано изменением значения переменной
$
Opitch = Pitch + $Pitch * 3
Выдает разность значений переменной между текущим и предыдущим срабатыванием чипа.
->
->Input or ->Output
Если используется с входной переменной, то выдает 1, если этот вход соединен с чем-нибудь, если используется с выходной переменной, то показывает количество входов, к которым подключен данный выход
#ifdef
#ifdef entity:setAlpha(number)
Проверяет, доступна ли команда на сервере
#else
#else
Если функцию невозможно вызвать на сервере, то выполняется код после #else
#endif
#endif
Закрывает блок #ifdef
Циклы
Циклы - это такие конструкции языка, позволяющие выполняться определенным участкам кода несколько раз последовательно. Например, если вы хотите 100 раз вывести строку "Hello", не будете же вы писать 100 раз print("Hello"). Гораздо правильнее и логичнее будет создать цикл от 1 до 100, который будет выводить эту строку:

for(I=1,100){ print("Hello") }

Результат тот же самый, а времени ушло в разы меньше. Далее указаны все виды циклов с их описанием:

Синтаксис
Пример
Описание
while () {}
while (A) {B++}
Действия, описанные в фигурных скобках будут повторяться до тех пор, пока выполняется условие в круглых скобках
for () {}
for (I = A, B[, C]) {}
Действия в фигурных скобках будут выполняться до тех пор, пока I, которая изначально имеет значение А, не будет равно B, причем при каждом выполнении I будет увеличиваться или уменьшаться на значение C. Если C нет, то переменная I увеличивается на 1 при каждой итерации
foreach () {}
foreach(K,V:type=Table) {}
Проходит по всем элементам типа type в массиве/таблице Table. К - это индекс переменной в Table, а V - это непосредственно сам элемент. Аналогично for(){} будет выполнять действия внутри фигурных скобок, пока не пройдет по всем элементам Table. Если во время прохождения данного цикла были созданы новые переменные в Table, цикл их проигнорирует
continue
if (A) {continue}
Может быть использована только внутри цикла. При наличии такой конструкции, цикл мгновенно перейдет на следующую итерацию, игнорируя последующие функции
break
if (A) {break}
Может быть использована только внутри цикла. При наличии такой конструкции цикл мгновенно завершится, и начнется выполнение последующих операций
Таймеры и события
Cобытия заставляют чип запускаться многократно в зависимости от срабатывания какого-то условия, например, когда кто-то написал в чат определенное слово, или же постоянно после каждого прохода.

Функция
Возвращает
Описание
runOnTick(N)
Если N=1, то чип будет срабатывать каждый тик сервера.
runOnChat(N)
Если N=1, то чип будет срабатывать каждый раз, когда кто-то написал в чат. Это событие можно вызвать только один раз, а не при каждом срабатывании чипа.
runOnLast(N)
Если N!=0, то чип сработает в момент его удаления в помощью инструмента Remover.

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

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

Функция
Возвращает
Описание
interval(N)
Устанавливает временной интервал с именем "interval" в миллисекундах, через который будет срабатывать чип. Например, если указать interval(1000), то чип будет срабатывать каждую секунду (независимо от тиков сервера). Минимальная задержка для таймера - 10 мс.
timer(S,N)
Работает так же, как и interval(), только имя этого таймера определяется переменной S.
stoptimer(S)
Останавливает таймер с указанным именем. Может останавливать interval(), если указать в качестве S "interval".

В этом разделе есть еще функции, которые отображают текущее время, время с начала сервера, дату и прочее, однако это не входит в тематику данного обучающего руководства, так что узнать подробнее можно на сайте официальной вики Wiremod (ссылка указана в конце руководства).
Типы данных и переменные
Все переменные должны начинаться с большой буквы. Например:
@persist variable
работать не будет, а
@persist Variable
будет.

Констант как отдельный тип в языке не существует. Если хотите использовать константу, просто не изменяйте ее в ходе выполнения кода.

В языке присутствует 13 типов данных, некоторые из которых подразделяются еще на несколько подтипов:
  • Число (Number)
  • Вектор (Vector). Имеет двумерный, трехмерный и четырехмерный подтипы.
  • Угол (Angle)
  • Строка (String)
  • Сущность (Entity)
  • Массив (Array)
  • Таблица (Table)
  • Рэйнджер - измеритель расстояния (Ranger Data)
  • Кость (Bone)
  • Матрица (Matrix). Имеет подтипы 2х2, 3х3, 4х4.
  • Вайрлинк - сложная проводная связь (Wirelink)
  • Комплексное число (Complex Number)
  • Кватернион (Quaternion)



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

Если у вас несколько переменных одного типа, то можно их объединить оператором [...], например, так:
[Vector1 Vector2 Vector3]:vector
Это позволить вам сэкономить место и время.

Также есть особый тип данных: variable. Переменная этого типа может принимать значения любого типа, то есть, например, сейчас она число, а через две минуты она уже вектор.
Работа с переменными
Ввод переменных - это, конечно, хорошо. А что же насчет манипулирования ими?

Допустим, вы хотите к переменной Int, которая сейчас равна 5, прибавить 3. Для этого нужно написать:

@persist Int=5 . . . Int=Int+3

Или можно написать так:

Int+=3

Результат будет записан в переменную Int, так как она стоит перед оператором присваивания =.

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

Что если вы прибавите к строке "abc" строку "lol"?

@persist Str="abc", Str2="lol" . . . Str=Str+Str2

Результат будет "abclol". Вычитать, перемножать и делить строки нельзя, однако в языке присутствует множество других функций, которые полностью удовлетворят ваши потребности в работе с ними.

Как вы может быть заметили, я во втором примере к одной переменной прибавил значение другой переменной, а не число, как в первом примере. Так тоже можно делать, при этом компилятор просто возьмет значения обеих переменных, сложит их и занесет в переменную, стоящую перед знаком = (она не обязательно должна присутствовать после него).

Никто не ограничивает вас использовать множество операций в одной строке:
Int=3+Int/I*2

Так же можно вместо переменных использовать функции, возвращающие значения.

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

Int=e()*3 #e() возвращает экпоненту в первой степени I=sin(Int/10)+5 #sin(N) возвращает синус от числа в градусах
Заключение
Буду рад вашим комментариям. Если появились вопросы, смело задавайте их здесь или пишите в личку.

Пишите также, что хотели бы еще увидеть в этом руководстве (или в последующих). Указывайте на мои ошибки и недочеты, если они присутствуют.

Добавляйте руководство в избранное, чтобы иметь быстрый доступ к нему, если что-то забудете!

Удачного изучения Expression 2 :)
Комментариев: 89
FErOx 29 окт. 2023 г. в 21:27 
А как повтор кода сделать как while-true в Python?
kukusik 19 мар. 2023 г. в 4:26 
@persist Walkthrogh_rank
runOnTick(1)
for(I=1,10000){
Walkthrogh_rank+=10000
}

// Кто понял тот понял ;3 //
j0ez  [создатель] 31 июл. 2022 г. в 10:06 
Не сказал бы, что питон, но то что легок в освоении - это правда)
alexeythereal1 30 июл. 2022 г. в 13:19 
Чистейшей воды python, не думал что будет так легко его понять, спасибо)
Дак 17 июн. 2021 г. в 9:03 
Не совсем понял тему с таймером. На него можно поставить каллбэк в виде функции?
C-SSTV MANAGER 3 апр. 2021 г. в 1:32 
Оценка от Педи Моха
:HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star::HentaiGirlLinda_star:/10
Wiremod Expression 2-не простой язык программирования, хорошо что есть такие руководства, поясняющие что где и как на начальном уровне изучения.
j0ez  [создатель] 14 авг. 2019 г. в 11:20 
Лосяш Эйнштейнов, в принципе, это и есть краткий справочник, как и другие мои руководства по гмоду. Успехов в кодинге!)
iceworld 6 фев. 2019 г. в 23:17 
Чтобы понять это, надо отучиться 8 класс информатики
Ovsyanka 22 авг. 2018 г. в 2:17 
Для новичков подойдёт
Ovsyanka 22 авг. 2018 г. в 2:16 
Круто