Главная » Статьи » Создание карт » JASS / AI скрипты учебники

Осваиваем Jass: Глава 4, Глава 5

04-12-2016» 4. Условия, циклы в jass
Рассмотрим такой пример: имеется фрагмент триггерного действия

Код:
For each (Integer i) from 1 to 10, do (Actions) Цикл If (All Conditions are True) then do (Then Actions) else do (Else Actions) Условие i равно 1 Действие Set s = (s + 2) Иначе Set s = (s + 1)
Цикл по i от 1 до 10 и условие внутри цикла. Во что превратится это действие, когда мы переведем его в jass? Создай в редакторе такой триггер и проверь.
Действие превратится в следующий фрагмент

Код:
set udg_i = 1 loop exitwhen udg_i > 10 if ( Trig_____________________________________001_Func001Func001C() ) then set udg_s = ( udg_s + 2 ) else set udg_s = ( udg_s + 1 ) endif set udg_i = udg_i + 1 endloop
Думаю, что пока не очень понятно, что здесь за что отвечает. Начнем с оператора if. Очевидно, он превратился в строки:

Код:
if ( Trig_____________________________________001_Func001Func001C() ) then set udg_s = ( udg_s + 2 ) else set udg_s = ( udg_s + 1 ) endif
Все что ниже первой строки - понятно, но почему вместо нормального условия в первой строке стоит "( Trig_____________________________________001_Func0 01Func001C() )"? Дело в том, что редактор триггеров довольно глупо переводит условия из триггеров или триггерных действий. После такого перевода часто приходится исправлять и оптимизировать код. В нашем случае, редактор создал специальную функцию с именем Trig_____________________________________001_Func0 01Func001C() для того, чтобы проверить нужное нам условие, что i=1. Эту функцию ты можешь увидеть вверху триггера:

Код:
function Trig_____________________________________001_Func001Func001C takes nothing returns boolean if ( not ( udg_i == 1 ) ) then return false endif return true endfunction
Пока не будем вдаваться в то, что это за функция и что она делает. Самое главное - эта функция возвращает значение true (истина) если i=1, или ложь, если i не равно 1. Возникает вопрос: что же, при каждом применении оператора if нам придется создавать какую-то функцию? Ничего подобного - можно обойтись и без нее! Стираем эту ненужную функцию, а в строчку вносим изменения:

Код:
if (udg_i == 1) then
И все. остальное оставляем неизменным. У нас получится фрагмент кода:

Код:
set udg_i = 1 loop exitwhen udg_i > 10 if (udg_i == 1) then set udg_s = ( udg_s + 2 ) else set udg_s = ( udg_s + 1 ) endif set udg_i = udg_i + 1 endloop
Теперь ты знаешь, что такое оптимизация :).
Но остается открытым вопрос: что же такое мы вставили в условие оператора if. Это просто проверка, равна ли переменная i единице. В jass есть специальные значки для проверки условий равенства или неравенства:
== (два знака равно) переводится как равно
!= переводится как не равно
< меньше
> больше
<= меньше или равно
>= больше или равно.

Т.е. если мы хотим записать условие **i[.b] не равно 10, то оно будет выглядеть
i!=10
Теперь ты можешь сам разобраться с условным оператором в jass:

Код:
if (udg_i == 1) then set udg_s = ( udg_s + 2 ) else set udg_s = ( udg_s + 1 ) endif
Переводится как "Если i=1 то делать то-то иначе делать то-то".

Хорошо, с условным оператором разобрались. А как насчет циклов?
К оператору цикла относятся следующие строки:

Код:
set udg_i = 1 loop exitwhen udg_i > 10 ... set udg_i = udg_i + 1 endloop
... - это могут быть любые действия, которые происходят внутри цикла
Итак, перед началом цикла переменной i присваивается значение 1 - это начальное значение для нашего цикла.
loop - ключевое слово, означающее начало цикла
endloop - конец цикла
Т.е. действия между loop и endloop будут повторяться. Но сколько раз они должны повторяться? Вообще говоря, 10. Но в jass все циклы устроены более универсально, чем в триггерах. Тут циклы повторяются не ОПРЕДЕЛЕННОЕ ЧИСЛО РАЗ, а ДО ТЕХ ПОР, ПОКА НЕ БУДЕТ ВЫПОЛНЕНО ТАКОЕ-ТО УСЛОВИЕ. За проверку этого условия отвечает строка:
exitwhen <УСЛОВИЕ> - переводится как выйти из цикла, когда выполнено УСЛОВИЕ.
exitwhen udg_i > 10 - переводится как выйти из цикла, когда переменная i станет больше 10.
Мы могли бы к примеру написать условие
exitwhen udg_i == 10- выйти из цикла, когда iстанет равно 10. Тогда в цикле будет выполнено на одно действие меньше.

Итак, вся наша структура

Код:
set udg_i = 1 loop exitwhen udg_i > 10 ... set udg_i = udg_i + 1 endloop
имеет следующий смысл. Переменная i приравнивается к 1. На каждом витке цикла проверяется, не стала ли переменная i больше 10. Если не стала, производится какое-то действие и затем переменная i увеличивается на 1. И так до тех пор, пока не будет выполнено условие окончания цикла.

Итоги:
Условный оператор при переводе триггера в jass не очень удобен, т.к. его приходится оптимизировать.
Оператор цикла в jass более универсальный, т.к. действие производится не фиксированное число раз, а до тех пор, пока не выполнится условие. Кроме того, переменную цикла в триггерах можно увеличивать только на 1, а в jass - ее можно изменять произвольным образом.
Читатель, про циклы можно сказать еще следующее. Если ты попробуешь перевести в текст действие

Код:
For each (Integer A) from 1 to 10, do (Actions)
... (т.е. воспользуешься одним из циклов с Integer A или Integer B, то на выходе получишь:

Код:
set bj_forLoopAIndex = 1 set bj_forLoopAIndexEnd = 10 loop exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd ... set bj_forLoopAIndex = bj_forLoopAIndex + 1 endloop
Что такое bj_forLoopAIndex и bj_forLoopAIndexEnd? Оказывается это специальные глобальные переменные, которые используются для проверки условий окончания такого вида цикла. Обычные переменные типа integer. Проверь сам - действие в цикле будет выполнено ровно 10 раз.
Отсюда вывод: хочешь ты того или нет, но редактор всегда вставляет в твой сценарий 4 специальных глобальных переменных:
set bj_forLoopAIndex
set bj_forLoopAIndexEnd
set bj_forLoopBIndex
set bj_forLoopBIndexEnd
В принципе они предназначены для циклов, а на самом деле при помощи jass в них можно записывать все что угодно. Кстати, подобных переменных на самом деле довольно много.

» 5. Функции в jass
Что такое функция? Функция, это фрагмент кода, в который можно передавать параметры, который может возвращать один параметр и производить определенные действия. Не очень понятно, но вспомни в предыдущем сообщении, как для определения выполняется ли условие i=1 создавалась специальная функция.

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

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

Код:
function <ИМЯ ФУНКЦИИ> takes <ПЕРЕЧЕНЬ ПАРАМЕТРОВ, которые функция БЕРЕТ> returns <тип параметра, который функция ВОЗВРАЩАЕТ> ... <ПЕРЕЧЕНЬ ДЕЙСТВИЙ ФУНКЦИИ> ... endfunction
Все это может быть выглядит страшно, но мы разберем на примерах. Самый простой вид функций - та которая ничего не берет и ничего не возвращает. К примеру, создадим функцию с именем property, которая при каждом ее запуске дает игроку1 1000 золотых.

Такая функция будет выглядеть следующим образом:

Код:
function property takes nothing returns nothing call AdjustPlayerStateBJ( 1000, Player(0), PLAYER_STATE_RESOURCE_GOLD ) endfunction
Несколько замечаний. Во-первых действие добавления денег взято путем перевода такого действия из триггеров в jass. Я сказал, что деньги даются игроку1, а в коде написано Player(0) - это потому что в jass игроки начинают нумероваться с нуля. Т.е. 0 - номер первого игрока, 1 - второго и т.д. PLAYER_STATE_RESOURCE_GOLD - кодовое слово, которое означает, что прибавляется именно золото, а не скажем лес.
Вместо перечня параметров, которые берутся и вместо типа параметра, который возвращается функцией стоит слово nothing - на английском означает ничего. Т.е. функция ничего не берет и ничего не возвращает. Она просто делает действие - добавляет деньги игроку1.

Для того, чтобы вызвать эту функцию на исполнение, достаточно написать команду

Код:
call property()
() - это скобки, в которых указывается список параметров для функции, но в нашем случае он пуст.
Ты можешь вставить эту команду в триггеры (в виде custom script) или в jass. Когда триггер запущен и очередь дойдет до этой команды, будет запущена функция и выполнены все ее действия. И при каждом запуске игрок1 будет получать 1000 золота.
Конечно, функция состоящая из одного действия не имеет смысла, но действий может быть и больше. Если в триггерах или коде имеются часто повторяющиеся фрагменты, то имеет смысл создать функцию и заменять фрагмент на вызов функции.

Пока что я не рассказал, а куда нужно вставлять текст функции. Это нельзя делать куда попало. Нельзя вставлять функцию внутрь другой функции. Функцию можно вставить в пустое пространство между другими функциями в триггере или в специально отведенное место (второй вариант предпочтительнее, позже расскажу почему).

Путь к этому специальному месту: открой редактор триггеров. Слева в окне найди дерево триггеров (список папок и самих триггеров). Самая высокая позиция этого дерева - иконка карты. Щелкни на нее. Справа откроется окно "Нестандартный код". Вот в него и нужно вставлять функции.

Вставь в это окно текст функции property. Затем сделай триггер с событием Map Initialization и действием: cs call property()
Запусти сценарий и проверь, что функция действительно работает.

Итак, первая и самая простая функция сделана. Но функции очень удобны тем, что они могут принимать определенные параметры, которые влияют на действие функции. К примеру, модернизируем функцию property, чтобы она давала 1000 золота не первому игроку, а игроку, которого мы укажем в параметре. Т.е. в функцию мы будем передавать параметр номер игрока (типа integer). В итоге, функция будет выглядеть так

Код:
function property takes integer n returns nothing call AdjustPlayerStateBJ( 1000, Player(n-1), PLAYER_STATE_RESOURCE_GOLD ) endfunction
Смотри, в первой строке вместо takes nothing теперь стоит takes integer n. Это означает, что функция имеет 1 параметр типа integer. Чтобы запустить функцию с параметром, нужно будет вставить строку

Код:
call property(<какое-то число>)
И это самое число будет передано в функцию при запуске и записано в локальную переменную n. Вот такой фокус. Мы можем вводить номер игрока, которому мы хотим дать 1000 золота и этот номер будет передан в функцию. А для того, чтобы дать 1000 золота игроку с этим номером, мы переделали вторю строку:

Код:
call AdjustPlayerStateBJ( 1000, Player(n-1), PLAYER_STATE_RESOURCE_GOLD )
Т.е. дать 1000 золоту игроку с индексом n-1. Я нарочно поставил не n, а n-1, т.к. мы привыкли нумеровать игроков с 1, а в jass нумерация идет с 0.
Итак, если у нас имеется указанная функция, то чтобы дать игроку1 1000 золота, мы можем набрать команду

Код:
call property(1)
Еще несколько слов о параметрах. Во-первых, параметров может быть любое число и они могут быть любого типа. Если параметров более одного, то они идут перечислением через запятую. Например, вот модернизированная функция, в которую мы в качестве параметров передаем не только номер игрока, но и количество золота.

Код:
function property takes integer n, integer gold returns nothing call AdjustPlayerStateBJ( gold, Player(n-1), PLAYER_STATE_RESOURCE_GOLD ) endfunction
Но сколько параметров у функции, столько должно передаваться и при ее вызове. Т.е. для вызова нужно использовать строку

Код:
call property(1,1000)
Во-вторых, параметры, как я и говорил, передаются в локальные переменные. Но в любой функции могут быть и другие локальные переменные. Просто нужно объявить их в начале функции

Код:
function property takes integer n, integer gold returns nothing **local real r** call AdjustPlayerStateBJ( gold, Player(n-1), PLAYER_STATE_RESOURCE_GOLD ) endfunction
Ну и еще одно замечание. В большинстве языков программирования имеется разделение понятий процедура и функция. Процедура - фактически тоже самое что и функция, но она ничего не возвращает в качестве параметра. Все примеры, рассмотренные нами выше - брали или не брали параметры, но все равно ничего не возвращали. Т.е. грамотнее было бы назвать их процедурами.

И переходим к последнему - самому общему варианту, когда функция что-то возвращает. Раньше мы везде писали returns nothing, но если мы хотим, чтобы функция что-то вернула, нужно указать какой-нибудь тип. Скажем returns integer (возвратить параметр типа integer). Например, если мы хотим создать функцию, которая будет возвращать нам сумму чисел от 1 до n, где n - параметр, передаваемый в функцию. Функция выглядит так:

Код:
function summa takes integer n returns integer local integer i local integer s set i = 1 set s = 0 loop exitwhen i > n set s = s + i set i = i + 1 endloop return s endfunction
Попытайся разобраться с действием этой функции. Внутри есть цикл, который нужен для нахождения суммы 1+2+...+n. Далее есть ключевое слово return - это одновременно команда прекратить выполнение функции, и способ заставить функцию вернуть значение.
return s означает, что функция вернет значение из переменной s, т.е. искомую сумму.

Как же обратиться к такой функции для ее вызова? Функции, возвращающие определенное значение, вызываются по-особому. Их можно использовать в каких-то выражениях или равенствах. К примеру, если у тебя есть глобальная переменная i, ты можешь вызвать функцию summa следующим образом:

Код:
cs set udg_i = summa(10)
И тогда РЕЗУЛЬТАТ ФУНКЦИИ, то что она возвращает - сумма, будет помещен в переменную i. Или можно сделать так:

Код:
cs set udg_i = summa(9+1)+2
Тогда в переменную i будет помещена сумма чисел от 1 до 10 плюс еще 2 единицы.

В этом и состоит смысл функций, с возвращаемым значением.
Примечания:
Тип данных, возвращаемых функцией должен совпадать с переменной, куда мы пишем это значение. integer-integer или real-real.
Вообще говоря, даже если функция возвращает значение, ее можно запустить методом
Код:
call <Функция> (<параметры>)
Но понятное дело, значение функции, которое оно возвращает, не будет никуда записано.
Команда return представляет определенный интерес сама по себе. Если ты проверишь, во что превратится команда skip remaining actions в jass - она превратится в return. Т.е. это команда, которая прерывает исполнение функции.
Допускается запуск одной функции из другой. К примеру, в функцию summa можно вставить строчку
Код:
call property(1,1000)
Но может возникнуть ошибка. Обращаться можно только к функции, которая записана выше данной (т.е. создана раньше). Т.е. если функция property будет ниже чем summa - то обращаться к property из summa нельзя.

Кстати, код в специальном месте для триггерных функций расположен ВЫШЕ чем код всех игровых триггеров. Поэтому к функциям записанным здесь можно обращаться из любого триггера.
5. Если внимательно приглядеться, то кроме функций, определенных пользователем (т.е. тобой) существуют еще и встроенные функции. К примеру, глянь команду

Код:
call AdjustPlayerStateBJ( gold, Player(n-1), PLAYER_STATE_RESOURCE_GOLD )
Слово call тебе ни о чем не говорит? :). AdjustPlayerStateBJ - это встроенная функция с тремя параметрами. Список всех таких встроенных функций имеется в MPQ архивах. Так что получается у нас, что все триггеры устроены так, что одни функции ссылаются на другие, те на третьи и т.д. :).

На этом о функциях пока все.

В качестве примера по тому, что мы пока прошли, предлагаю изучить сценарий AR, в котором реализован достаточно простой огнемет на jass с использованием массивов, циклов и функций.

К статье прикреплен файл: AR.w3m

Категория: JASS / AI скрипты учебники | Добавил: imDarkCount (04-12-2016 в 19:17:07)
Просмотров: 1379 | Рейтинг: 5.0/1
Всего комментариев: 0
avatar
Рейтинг@Mail.ru
Яндекс.Метрика

Copyright © 2010-2017
Вакансии :: Контакты
Мобильная версия сайта