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

Курс JASS + vJASS

Содержание:

Локальные переменные

В противовес глобальным переменным, которые доступны с любого места в коде, существуют локальные переменные. Локальные переменные доступны только внутри функции в которой они были объявлены. В отличие от глобальных переменных, которые существуют всегда в одном экземпляре, локальные переменные создаются каждый раз при вызове функции и удаляются при завершении её работы. Даже если так совпало, что одна функция была запущена несколько раз, в каждом экземпляре функции будет свой экземпляр локальной переменной со своим значением. Сейчас всё это может быть непонятным, но увидев некоторые примеры вы поймете, что я имею в виду.
Для того чтобы объявить локальную переменную, нужно в начале функции написать local тип имя. Пример:
function MyFun takes nothing returns nothing
    local integer Number
endfunction
В одной функции можно объявить сколько угодно переменных. Пример:
function MyFun takes nothing returns nothing
    local integer NumberOne
    local integer NumberTwo
    local real RealNumber
    local unit Worker
endfunction
Все объявления локальных переменных должны находится до остального кода, иначе будет ошибка компиляции. Следующий код вызовет ошибку компиляции:
function MyFun takes nothing returns nothing
    local integer NumberOne
    call BJDebugMsg("Привет!")
    local integer NumberTwo
endfunction
Так же как и глобальные переменные, локальные переменные можно инициализировать сразу, потом или никогда. Пример:
function MyFun takes nothing returns nothing
    local integer NumberOne = 1
    local integer NumberTwo
    local integer NumberThree
    set NumberTwo = 2
endfunction
Тут есть только одно отличие, если попытаться использовать локальную переменную до её инициализации, то будет ошибка компиляции, а не прекращение работы функции и триггера во время игры. Следующий код вызовет ошибку компиляции:
function MyFun takes nothing returns nothing
    local string Str
    call BJDebugMsg(Str)
endfunction
В остальном они ведут себя так же, как и остальные переменные. Их можно менять и использовать сколько угодно раз. Они большей частью предназначены для того, чтобы хранить какие-то промежуточные вычисления или объекты с которыми работает функция.

Примеры

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

Первый пример

Роль локальной переменной в этом примере не велика, она нужна лишь для того, чтобы передать одного юнита в несколько функций.
Я хочу сделать функцию, которая будет создавать призрака на основе любого типа юнита:
function CreateGhostAtLoc takes player p, integer unitTypeId, location loc, real angle returns unit
  local unit ghost = CreateUnitAtLoc(p, unitTypeId, loc, angle)
  call SetUnitVertexColor(ghost, 150, 255, 150, 128)
  call SetUnitPathing(ghost, false)
  call UnitAddType(ghost, UNIT_TYPE_UNDEAD)
  
  return ghost
endfunction
В первой строчке мы создаём юнита с помощью функции CreateUnitAtLoc и помещаем ссылку на него в локальную переменную ghost. Затем с помощью функции SetUnitVertexColor, мы красим юнита из переменной ghost в зеленоватый цвет и делаем его полупрозрачным. С помощью функции SetUnitPathing мы делаем так, чтобы юнит мог проходить сквозь другие объекты. Далее с помощью функции UnitAddType мы делаем юнита нежитью. А в конце возвращаем ссылку на нашего юнита.
Давайте протестируем нашу функцию в деле. Создайте новую карту и скопируйте функцию к себе. Если вы забыли как это делать, напоминаю: в редакторе триггеров нажмите на название карты, справа откроется пространство в котором можно объявлять свои функции, скопируйте её туда.
Я хочу сделать триггер который после смерти юнита, создает призрака на месте его смерти. Для работы нашей функции ей нужно передать значения: для которого игрока создавать юнита, какой тип юнита, место где его нужно создать и угол поворота юнита. Все эти данные можно достать через некоторые функции. Лепить всё в одну строчку можно, но не красиво и трудночитаемо:
call CreateGhostAtLoc(GetOwningPlayer(GetTriggerUnit()), GetUnitTypeId(GetTriggerUnit()), GetUnitLoc(GetTriggerUnit()), GetUnitFacing(GetTriggerUnit()))
Кроме нашей функции CreateGhostAtLoc, здесь были использованы следующие функции:
  • GetTriggerUnit - возвращает триггерного юнита, все следующие функции принимают этого юнита.
  • GetOwningPlayer - возвращает игрока владельца юнита (тип player).
  • GetUnitTypeId - возвращает ИД типа юнита (тип integer).
  • GetUnitLoc - возвращает позицию юнита (тип location).
  • GetUnitFacing - возвращает угол поворота юнита (тип real).
Думаю, стоит создать еще одну функцию, которая вытащит все необходимые данные об юните и передаст их нашей прошлой функции. Она должна сделать код более читаемым:
function CreateGhostForUnit takes unit u returns unit
  return CreateGhostAtLoc(GetOwningPlayer(u), GetUnitTypeId(u), GetUnitLoc(u), GetUnitFacing(u))
endfunction
Мало того что код стал понятнее, так теперь его можно еще и переиспользовать в других триггерах\функциях.
Теперь вернемся к триггеру. Создайте триггер с событием когда юнит умирает и условием, что умерший юнит не нежить. Впишите туда вызов нашей функции:
call CreateGhostForUnit(GetTriggerUnit())
Скриншот:
Теперь выставьте кучу юнитов на поле боя и устройте побоище. Из юнитов после смерти будут появлятся призраки, которые могут проходить сквозь объекты. Используйте эту особенность, чтобы зайти в тыл врага и убить юнитов поддержки.

Второй пример

Следующий пример не очень хороший. В нем используется Wait, который не стоит использовать так, как это сделано тут. Он предназначен для других целей, вместо него стоило использовать таймеры, которые мы еще не изучали. Без Wait или таймеров достаточно сложно наткнуться на ситуацию, когда два или более экземпляра триггеров\функций одновременно используют одну и ту же глобальную переменную, из-за чего могут получиться баги. Мне нужен такой случай, чтобы продемонстрировать то, как локальная переменная позволяет избежать таких проблем. Также в этом примере я покажу, как еще можно использовать джасс внутри гуи. К примеру такой триггер:
Он должен создавать юнита каждые 2 секунды, а затем через 5 секунд убирать его. Для демонстрации я даже поместил его в глобальную переменную Unit, но это было лишним. Если вы не знали, при создании юнита через гуи, он и так помещаться в глобальную переменную bj_lastCreatedUnit. Но на практике получается так, что сначала создаются два юнита как положено, а затем появляется следующий и исчезает через секунду. Прошлые юниты при этом остаются навсегда, а новый появляется каждые 2 секунды и исчезает через секунду. Немного разобравшись, мы понимаем, что всё работает так, как написано. Но написали мы не то, что хотели на самом деле. Всё начинается с того, что триггер срабатывает в первый раз, создает юнита и идет спать на 5 секунд. Через 2 секунды срабатывает второй экземпляр триггера, он также создает юнита и идет спать на 5 секунд. Итого прошло 4 секунды, через секунду должен проснутся первый экземпляр триггера, но функция Wait работает не точно и он запаздывает. На 6 секунде создается третий юнит и уже через секунду исчезает. Что пошло не так? Правильно, в глобальной переменной лежит только последний юнит, которым и был третий юнит. Каждый экземпляр триггера создавал юнита и клал его в глобальную переменную, при этом выбрасывая предыдущее значение. Проснувшись, первый триггер выполнил нашу просьбу и убрал юнита по ссылке из глобально переменной. Затем был создан четвертый юнит, но через секунду проснулся второй триггер и убрал его. Такая цепочка будет продолжаться пока срабатывает триггер.
Итак, модернизируем триггер для решения нашей проблемы:
В первой строчке кода мы объявляем локальную переменную с именем u и типом unit (боевая единица). Как видим, объявлять переменные можно не только внутри функций, но и внутри действий триггеров. Код:
local unit u
В третьей строке мы копируем ссылку на юнита из глобальной переменной bj_lastCreatedUnit (последний созданный юнит) в локальную переменную u. Код:
set u = bj_lastCreatedUnit
После 5 секундного ожидания, мы копируем ссылку на юнита из локальной пременной u в глобальную переменную Unit. Делается это из-за того, что гуи может работать только с глобальными переменными. Разумеется вы могли использовать чистый джасс и избежать лишних глобальных переменных. Код:
set udg_Unit = u
Теперь всё работает как нужно. У каждого экземпляра триггера есть своя локальная переменная и они не могут вмешаться в работу друг друга.
Все триггеры и функции выполняются непрерывно от начала до конца, только в нескольких случаях может происходить прерывание, как например с Wait. Эти случаи не случайны и предсказуемы, но их мы разберём в следующих уроках. А всё, что я хотел вам показать это то, что у каждого экземпляра функции\триггера свои экземпляры локальных переменных. Так сказать, у каждого сотрудника свой рабочий стол.

`
ОЖИДАНИЕ РЕКЛАМЫ...
0
17
3 года назад
0
Хорошая статья

Этот код вызывает небольшую утечку, так как переменная ghost не обнуляется
function CreateGhostAtLoc takes player p, integer unitTypeId, location loc, real angle returns unit
  local unit ghost = CreateUnitAtLoc(p, unitTypeId, loc, angle)
  call SetUnitVertexColor(ghost, 150, 255, 150, 128)
  call SetUnitPathing(ghost, false)
  call UnitAddType(ghost, UNIT_TYPE_UNDEAD)
  
  return ghost
endfunction
0
32
3 года назад
0
Главное локейшин не забыть удалить.
0
11
3 года назад
0
Весь мой код в ближайшее время будет вызывать утечки, сейчас просто нет смысла с ними бороться. На мой взгляд, их можно изучить потом, а сейчас они лишь раздуют код и увеличат смысловую нагрузку. По моему плану, мы сначала изучим конструкции языка и основные API, а уже потом утечки. Их можно вместить в один урок, после которого все примеры уже будут без утечек. Если есть какие-то мысли, замечания или предложения по этому поводу, я готов их выслушать.
0
32
3 года назад
0
jass_dev, ну могу кинуть карту, то ты спрашивал про реализацию спеллоов на 1 триггере
0
11
3 года назад
0
quq_CCCP:
jass_dev, ну могу кинуть карту, то ты спрашивал про реализацию спеллоов на 1 триггере
Кидай, буду благодарен.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.