Добавлен , опубликован

Курс JASS + vJASS

Содержание:

Базовые знания JASS

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

Код

Как я уже писал в прошлых уроках, у нас есть три варианта куда писать код.
Первый вариант. Втавить джасс код в гуи-триггере с помощью действия "Custom Script" (как?). Каждая строчка кода должна находится в отдельном действии.
Например, следующий код:
call BJDebugMsg("Раз")
call BJDebugMsg("Два")
call BJDebugMsg("Три")
Должен выглядеть в гуи-триггере так:
Второй вариант. Преобразовать гуи-триггер в джасс код (как?) и писать туда, но данный способ мы не будем использовать в ближайшее время.
Третий вариант. В редакторе карт нажать на имя карты, справа откроется область для нестандартного кода. Туда можно писать свои собственные функции с которыми мы познакомимся ближе к концу данного урока.
До тех пор, пока я не скажу что мы переходим к варианту 3, мы будем писать код в гуи-триггеры, как в первом варианте. Каждый пример кода нужно писать в отдельный триггер с событием "Map Initialization" (инициализация карты). Я не буду каждый раз писать типа: "создаем новый триггер, добавляем ему срабатывание во время инициализации карты" и т.д. Но так как вы уже знаете триггеры, то можете и сами выбирать при каком событии выполнять наш код.

Переменные, типы данных, литералы

Переменные - это такие хранилища для данных. Они бывают разные, например уже знакомые вам глобальные переменные, а еще есть локальные переменные. Параметры в функциях это также переменные. Как понятно из названия, переменные могут изменяться. Мы можем что-то положить в переменную, а затем еще сколько угодно раз изменить её.
Напоминаю как создавать глобальные переменные:
У каждой переменной есть тип хранимого значения. Если переменная настроена на хранение целых чисел, то туда нельзя положить строку, юнита или дробное число.
В варкрафте существует огромное количество типов данных (число, текст, юнит...), но основных не так уж и много. К основным типам данных мы будем относить 4 типа: integer - целочисленное число, real - рациональные числа (целые и дробные), boolean - логический тип, string - строка (любой текст).
Из основных типов состоят все более сложные структуры. К примеру, location (точка) состоит из трех real (x, y, z), а у юнита еще больше разных составляющих: имя - string, здоровье - real, мана - real, позиция - location...
В джассе, для того, чтобы положить значение в переменную, нужно написать с новой строки слово set затем ИмяПеременной = Значение. Данная операция называется присвоением.
Для примера создадим глобальную переменную с именем Number и типом integer (целочисленная), а также создадим триггер как на скриншоте:
код:
set udg_Number = 1
данный триггер равносилен следующему триггеру:
Сначала мы положили в переменную Number единицу, а потом вывели её содержимое на экран.
Отдельно стоит обсудить приставку "udg_" перед "Number". Эта приставка ставится только перед глобальными переменными созданными в редакторе триггеров. На самом деле, она является частью названия переменной. Забегая на перед, глобальные переменные созданные через код джасс не обязаны её содержать, разве что вы её сами напишите. Если далее в моем коде вы увидите приставку "udg_" у названия переменной, то знайте, это глобальная переменная, которую нужно создать в редакторе триггеров.
Также, в переменную можно копировать значение другой переменной.
set udg_A = udg_B
Данный код скопирует текущее значение глобальной переменной B в глобальную переменную A.
У каждого из основных типов есть свои литералы, это такие самостоятельные значения, которые не находятся в переменной. Например, в предыдущем коде 1 и 2 это литералы. Далее рассмотрим литералы для всех основных типов.
Для integer (целочисленная) всё просто, любое целое число будет литералом этого типа. Например: -50, -21, -1, 0, 1, 72, 105...
Также, числа можно писать не только в десятичном формате, но поговорим об этом в следующих уроках.
Для real (реальная) литералом могут быть как целые, так и дробные числа. Например: -2, -0.5, 0, 0.6, 2.543242, 432.13...

Для boolean (логическая) есть всего два литерала, true (правда) и false (ложь).
Для string (строка) это любой текст внутри двойных кавычек "текст" (именно в двойных, не путать с одинарными). Например: "привет", "good-bye", "несколько слов", "а", "1", "234", "-234.423", "урон: 34"...
Важно! Число и строка не являются одним и тем же, даже если они похожи, 1 и "1" не одно и тоже! При попытке запихнуть число туда, где нужена строка или наоборот - произойдет ошибка. Для преобразования строки в число и наоборот, существуют специальные функции, которые мы рассмотрим позже.

Операции и выражения

Как и в математике, в программировании мы можем проводить операции над числами и не только. Всего для чисел нам доступны четыре операции: суммирование (+), вычитание (-), умножение (*) и деление (/). Все остальные математические операции делаются через функции, которые мы рассмотрим в следующих уроках.
Следующий код присвоит целочисленной переменной Number значение 5:
set udg_Number = 2 + 3
Операции можно проводить не только с литералами, но и с переменными численных типов (integer, real). Следующий код присвоит переменной A значение 5, а переменной B значение 7, затем увеличит B на единицу:
set udg_A = 5
set udg_B = udg_A + 2
set udg_B = udg_B + 1
Как и в математике, в программировании любая последовательность связанных между собой операций называется выражением. Оно может быть сколь угодно сложным и в нем будут действовать все правила математики.
Следующий код присвоит целочисленной переменной Number значение 6
set udg_Number = 2 + 2 * 2
Здесь "2 + 2 * 2" это выражение.
Еще можно использовать скобки. Следующий код присвоит целочисленной переменной Number значение 8:
set udg_Number = (2 + 2) * 2
У знака "+" есть и другое предназначение, он может соединять строки. Такая операция называется конкатенацией.
Следующий код присвоит строковой переменной Str значение "Приветствую вас, Андрей":
set udg_Name = "Андрей"
set udg_Str = "Приветствую " + "вас, " + udg_Name
Как видите, в джессе легче писать выражения. Вместо того чтобы пробираться сквозь бесконечные окна сложных арифметических выражений или конкатенаций в гуи, мы просто ввели одну строчку кода.
В джассе есть и другие операции, но их мы рассмотрим в следующих уроках.

Функции

Функции это фрагменты кода, которые можно использовать в другом коде многократно. Некоторые из функций что-то вычисляют, другие проверяют, третьи создают, меняют, перемещают и т.д.
Среди всех функций есть так называемые native функции, их еще зовут нативками. Все базовые возможности, такие как создание юнитов, спецэффектов и т.д. доступны только им. Если какой-то другой функции нужно создать юнита, то она должна обратиться к одной из нативок, которая умеет это делать. Также нативки работают быстрее любых других функций джасса, потому как написаны не на джассе, а скорее всего на С++. По этой же причине мы не можем посмотреть как нативки работают внутри.
Каждой функции нужны или не нужны входные данные, на основе которых она сделает свою роботу, все зависит от конкретной реализации. Входные данные зовутся аргументами.
Также функции могут возвращать или не возвращать результат своей работы. К примеру функция создающая юнита также даст вам ссылку на него, чтобы вы могли что-то с ним сделать. Функция выводящая текст на экран просто выведет сообщение, но ничего не вернет.
Для того чтобы выполнить функцию нужно написать call ИмяФункции(). Внутри скобок пишутся необходимые для работы функции данные.
Например, функция BJDebugMsg принимает всего один аргумент, строку с текстом который нужно вывести на экран:
call BJDebugMsg("Привет мир!")
Данный код выведет на экран фразу "Привет мир!".
Не стоит пытаться "всунуть" неподходящий тип данных в функцию, иначе будет ошибка.
Например, функция BJDebugMsg умеет выводить только текст. Поетому следующий код вызовет ошибку:
call BJDebugMsg(1)
Но иногда выводить числа всё же нужно. Для таких целей можно превратить число в текст с помощью функции I2S, эта функция превращает только целые числа в текст, например 1 в "1". Скомбинируем BJDebugMsg и I2S, чтобы вывести число на экран:
call BJDebugMsg( I2S(56) )
Сначала число 56 будет превращено в строку "56", а затем строка "56" будет отдана функции BJDebugMsg, которая выведет её на экран.
Выражения и функции можно вкладывать друг в друга как матрешки, но не стоит таким сильно увлекаться, так как код быстро станет нечитабельным.
Переменные, функции с возвращаемым значением и выражения можно комбинировать в сколь угодно сложные конструкции. Например:
set udg_Name = "Дима"
set udg_Age = 2021 - 1997
call BJDebugMsg( "Это " + udg_Name + ", ему " + I2S( udg_Age ) + " года" )
В данном коде, через редактор триггеров были созданы две переменные, udg_Name - глобальная переменная с именем Name и типом string (строка), и udg_Age - глобальная переменная с именем Age и типом integer (целочисленная). Сначала мы присваиваем переменной Name значение "Дима", затем присваиваем переменной Age значение 24. В конце, соединяем все в одну строку и выводим на экран "Это Дима, ему 24 года".
Создание собственных функций
Для того чтобы создать функцию, нужно её задекларировать. Декларация выглядит примерно так:
function ИмяФункции takes тип1 имя1, тип2 имя2, тип3 имя3 ... return возвращаемыйТип
...
код
...
endfunction
Декларация функции может находиться где угодно в коде, только не внутри другой функции или блока с глобальными переменными. При этом, для каждой функции доступны только те другие функции, которые были задекларированы выше по коду. Также декларация иногда зовется объявлением.
Помните, в начале урока я говорил про три возможных варианта куда можно писать код? Так вот, задекларировать функции возможно только во втором и третьем варианте. Третий вариант наиболее подходит для общих функций, там много места, а еще весь код от туда будет доступен для всех триггеров.
В редакторе триггеров нажмите на название вашей карты. Справа откроется пустое пространство в которое мы будем писать декларации функций.
Впишите туда следующий код:
function A takes nothing returns nothing
    call BJDebugMsg("Aaaaaaa!")
endfunction
Чтобы использовать созданную функцию в триггере, добавьте в него следующую строчку:
call A()
Данный код выведет на экран " Aaaaaaa!".
Для лучшего понимания происходящего давайте разберем каждое слово в коде.
function (функция) сообщает редактору что дальше в коде будет декларация функции.
А - имя функции, оно может быть любой английской буквой или словом. Об именовании функций, переменных и всего остального поговорим позже, там есть свои особенности.
takes (берет) - слово после которого должны бить написаны типы принимаемых значений и их названия. Но наша функция ничего не принимает, поэтому там написано nothing (ничего)
returns (возвращает) - слово после которого должен быть написан тип возвращаемого значения. Наша функция ничего не возвращает, поэтому там написано nothing (ничего)
endfunction (конец функции) - тут нечего комментировать, весь код от function до этого слова будет принадлежать функции.
А теперь сделаем функцию которая ничего не возвращает, но принимает на вход два целых числа, суммирует их и выводит на экран результат:
function Sum takes integer a, integer b returns nothing
    call BJdebugMsg( I2S( a + b ) )
endfunction
Вызовем эту функцию в триггере:
call Sum(5, 3)
В этом коде "a" и "b" зовутся параметрами, они ведут себя так же как и переменные, но при вызове функции в них ложатся входные значения. В примере из триггера, a = 5, b = 3. Важно обращать внимание на то, какой тип стоит у параметров (в нашем случае и у "a" и у "b" тип integer), если попытаться передать в функцию не тот тип данных, произойдет ошибка. По итогу на экран будет выведено 8.
Теперь сделаем функцию, которая возвращает значение. Для этого после returns пишем тип возвращаемого значения, например integer. Затем в коде нужно написать c новой строки слово return и возвращаемое значение, выражение, переменную или другую функцию с подходящим возвращаемым значением. Пример:
function R takes nothing returns integer
    return 9
endfunction
Эта функция всегда возвращает число 9
Более сложный пример:
function Double takes integer number returns integer
    call BJDebugMsg("Входное значение: " + I2S(number))
    return number * 2
endfunction
Вызовем её в триггере:
call BJDebugMsg("Выходное значение: " + I2S( Double(4) ) )
Данный код, сначала передаст число 4 в функцию Double. Затем в функции сработает первая строчка call BJDebugMsg("Входное значение: " + I2S(number)), которая выведет на экран "Входное значение: 4". Затем из функции будет возращена 4 умноженная на 2, то есть 8. В конце будет выведено на экран "Выходное значение: 8".
Нужно помнить, функции можно переиспользовать в разных частях кода, это очень полезно и экономит время. Например, я могу создать, функцию, которая телепортирует юнита в указанную точку. Но делает она это хитро, к примеру она сначала создает эффект телепортации на месте юнита и скрывает его, как будто он пропал в другое измерение. Затем, спустя некоторое время, в указанной точке появляется эффект телепортации и наш юнит. Написание такого функционала займет немало времени и строк кода у неопытного кодера. А теперь представьте, что я хочу использовать эту возможность в нескольких триггерах. У меня будет раса даларанцев, юнитов которых телепортирует в точку сбора после обучения. У меня будет герой со способностью массовой телепортации, а также юниты-маги с телепортацией самих себя. Используя только гуи-триггеры, я должен был бы скопировать весь код, во все три триггера. А в случае нахождения бага в телепортации, мне пришлось бы менять код во всех местах. Используя джасс, я могу написать одну функцию, а в остальных местах просто её вызвать написав по одной строчке кода в каждом триггере. В случаи нахождения бага в функции, я просто исправлю код функции, а в триггерах ничего менять не прийдется.

Комментарии к коду

Я думаю вы поняли, что написание любого вольного текста в коде приводит к ошибке.
function Fun takes nothing returns nothing
    например, я хотел написать тут "этот код выведет текст"
    call BJDebugMsg("Текст")
endfunction
Этот код вызовет ошибку! Редактор будет думать что, это какие-то команды, но они ему не знакомы.
Иногда возникает потребность оставить какое-то пояснение к коду, к примеру объяснить что делает следующий код, суть которого вот так сразу неясна. Или может как-то разделить части кода и подписать их. Например, тут у нас находятся функции для работы с юнитами, а тут с точками и т.д. Для этого были придуманы комментарии.
Для того чтобы написать коментарий, нужно написать две косые черты "//", а после них ваш текст.
Писать их можно либо в конце строки:
function SomeFun takes integer a returns nothing //тут
   call BJDebugMsg("FUN!") //тут
endfunction //или тут
Либо с новой строки:
function SomeFun takes integer a returns nothing
   //тут
   call BJDebugMsg("FUN!")
   //тут 
   call BJDebugMsg("FUN!")
   //тут
   //или тут
endfunction
Весь текст после "//" будет проигнорирован и не будет считаться кодом. Поэтому, можно закомментировать какой-то код, чтобы его отключить, а затем когда-то раскомментировать, чтобы включить его обратно:
   //call BJDebugMsg("Ты лузер!")
   call BJDebugMsg("Ты красава!")
В данном коде сработает только вторая строчка, которая выводит на экран "Ты красава!". Если убрать косые черты перед первой строчкой кода, то она снова заработает.
В vJASS есть более универсальный вид комментариев, которые могут быть размещены где угодно и занимать больше одной строки или неполную строку. Для этого нужно поместить ваш комментарий внутрь такой конструкции:
/*ваш комментарий*/
Пример:
function /*тут*/ PrintInt takes integer a returns  /*тут*/ nothing  /*тут*/ 
    /*тут*/
    /*тут*/ call BJDebugMsg( /*тут*/ I2S(a) ) /*тут*/ 
    /*
          много строк
          раз 
          два
          ...
    */
endfu/*даже внутри слова, но не нада так, онож не читаемое*/nction
С их помощью можно закомментировать что-то более мелкое, чем с помощью предыдущих:
set udg_Number = 2 + /* 2 + */ 2
В глобальную переменную будет записано значение 4.

Полезная информация

В предыдущем уроке по установке редактора карт JNGP, я писал, что с помощью модификации TESH можно просматривать декларации стандартных функций. Для этого в редакторе триггеров, сначала нажмите TESH на верхней панели, а затем Functions list.
В открывшемся окне, есть поисковая строка, список функций и место, где отображается декларация выбранной функции.
Здесь вам встреться и уже знакомые на вид декларации, в которых даже можно посмотреть на внутренний код функции:
function AddSpecialEffectLocBJ takes location where, string modelName returns effect
    set bj_lastCreatedEffect = AddSpecialEffectLoc(modelName, where)
    return bj_lastCreatedEffect
endfunction
Также здесь будут и декларации нативных функций, внутренний код которых нам не покажут:
native CreateUnit takes player id, integer unitid, real x, real y, real face returns unit 
Нативные функции ведут себя так же как и обычные, только работают быстрее.

`
ОЖИДАНИЕ РЕКЛАМЫ...
2
27
3 года назад
2
Всё норм, но хотелось бы видеть больше примеров для чего это всё нужно и какие преимущества перед гуи открываются
2
17
3 года назад
2
Для того чтобы код подсвечивался одинаково, надо его обрамлять вот так:
спойлер
Загруженные файлы
0
29
2 недели назад
Отредактирован nazarpunk
0
function /*тут*/ PrintInt takes integer a returns  /*тут*/ nothing  /*тут*/ 
    /*тут*/
    /*тут*/ call BJDebugMsg( /*тут*/ I2S(a) ) /*тут*/ 
    /*
          много строк
          раз 
          два
          ...
    */
endfu/*даже внутри слова, но не нада так, онож не читаемое*/nction
А разве комментарий может разорвать токен?
На строки это распространяется?
globals
	string a = "/*"
	string b = "*/"
	integer c = '/***'
	integer d = '***/'
endglobals

Проверили. И правда вжас вырезает комментарии склеивая токены обратно. Строки не отваливаются.
0
37
1 неделю назад
Отредактирован ScorpioT1000
0
Не надо костылить и колхозить в txt2, пишите просто блоки кода, остальное починим.
Ответы (4)
0
29
1 неделю назад
0
ScorpioT1000, что-то я не понял, что костылить?
0
37
1 неделю назад
0
nazarpunk, писать ((код jass не нужно
0
29
1 неделю назад
0
ScorpioT1000, но он же тогда воспринимается как луа.
0
37
1 неделю назад
0
nazarpunk, это баг
0
37
1 неделю назад
0
Для того чтобы создать функцию, нужно её задекларировать. Декларация выглядит примерно так:
У вас там ошибка, надо писать returns, а не return
Чтобы оставить комментарий, пожалуйста, войдите на сайт.