Основные операторы Кроме функций бывают еще
операторы. Для не программистов лучше всего пояснять это понятие на примерах.
Все математические действия и сравнения (сложение, вычитание, умножение, равенство, больше / меньше и т. д.) - операторы. Здесь особо нечего рассказывать.
Оператор присваивания - с его помощью переменной присваивается значение. Тут все довольно просто: выбираем переменную, выбираем значение.
Однако возможные функции и переменные (из которых Вы выбираете) будут разными в зависимости от типа выбранной переменной. То есть, целочисленным переменным можно присвоить одни значения, переменным юнитам - другие. Случайно присвоить числу юнита в таком случае почти невозможно.
Условный оператор - с его помощью можно "разветвлять" алгоритм, чтобы в одном случае он сработал одним образом, а в другом - другим. Для этого есть условный оператор, у которого есть "условие" (как условие в триггере) - если оно истинно, то алгоритм "пойдет" по одному ветвлению, если оно ложно - по другому. Ветвления называются
then (в случае истинны) и
else (в ложном).
Окошки выбора условий и действий стандартные.
Приведем пример. (Иногда я буду делить пример на несколько строк, хотя реально в триггерах так не делается. Это чисто ради того, чтобы Вам было удобнее читать.)
If ((Race of Player 1 (Red)) Equal to (==) Human) then
do (Player - Add 200 to Player 1 (Red) Current gold) else
do (Player - Add 500 to Player 1 (Red) Current gold)
Если раса первого игрока - альянс, то ему добавится 200 золото, в ином случае добавится 500.
Примечание: в Варкрафте есть два вида таких операторов: с одним действием на каждое разветвление и с несколькими (любым кол-вом). Я рекомендую пользоваться вторым. Вообще не понятно, зачем было делать такой оператор с одним действием на ветвление?.. Хотя иногда это выглядит красивее в коде.
Сами операторы действуют абсолютно одинаково, даже с точки зрения оптимизированности кода.
Цикл мы будем рассматривать подробнее далее в этой статье. Но их есть несколько видов, хотя суть не меняется.
Примечание: в триггерах есть один-единственный вид цикла -
for. Правда, есть возможность сделать и
while, и
repeat, но с помощью внедрение маленького кусочка кода JASS, который совсем не трудно сделать (минимальных знаний по JASS хватит). Не забывайте, что есть "оператор" custom text, который можно вставлять прямо в код, в том числе и в тело цикла... туда-то мы и можем написать условие выхода цикла.
Skip Remaining Actions - как видно из названия - пропускает оставшиеся действия в триггере. Не запутайтесь - завершает работу именно триггер, а не какой-нибудь отдельный цикл / разветвление.
Описанное выше уже дает Вам некоторую базу, чтобы начать изучать триггеры самостоятельно (никто не собирался полностью описывать все триггеры). Но триггеры - своеобразное программирование, в котором Вы программируете саму игру - её правила, параметры и т. д.
А программирование требует не только знаний, но и определенного мышления. Даже если Вы знаете назначение большей части триггеров, Вам надо найти им правильное применение. Одну и ту же задачу можно осуществить разными путями, и нужно искать самый лучший (рациональный). Задача второй части статьи - помочь Вам в этом.
Объекты Термин объект используется программистами. По сути объект – это какой-то предмет, обладающий определенным набором свойств и может выполнять определенную группу действий (и над ним может выполняться определенная группа действий). Вам необходимо научиться находить объекты в игре.
Примеры:
- Конкретный юнит в игре – объект. Свойств у него достаточно много: количество жизней, количество маны, игрок, которому он принадлежит, цвет, размер, положение на карте, направление взгляда, приказание, данное юниту, анимация, исполняемая юнитом, спецэффекты и заклятия наложенные на юнит, тип атаки, тип брони, доступные заклятия, потребляемая еда и т. д. С объектом юнит могут быть произведены определенные действия: изменить количество жизни и маны у юнита, изменить его положение на карте, изменить цвет, размер, направление взгляда и т. д.
- Является ли курсор мыши объектом? Да, является. Его свойства – положение на экране, форма курсора. Возможные действия – изменить положение курсора, сменить его форму. К Варкрафту это, правда, не относится.
- Сама игра несомненно является объектом. Некоторые ее свойства: продолжительность игры, количество игроков, игровая карта, вместе со всеми юнитами и доодадами и т. д. Действия с игрой, как объектом: игру можно начать и закончить, приостановить, продолжить.
Как видно из последнего примера – существуют сложные объекты, включающие в себя другие более простые. Так, внутри объекта
игра имеются объекты
юниты.
Ясно, что почти каждый "предмет" можно рассматривать как объект. Однако при программировании нам понадобится работать с конкретными объектами. Например, триггерная система не позволяет нам определять и тем более менять положение курсора мыши на экране. Поэтому этот объект мы сразу исключаем из своего рассмотрения. Но кроме лишних объектов, есть так же и лишние свойства и действия. Так например, свойство объекта
юнит – внешний вид (используемая модель) невозможно поменять с помощью триггеров. Мы можем поменять его с помощью редактора объектов, но не триггерами. Значит это свойство юнита – модель и действие – смена модели можно исключить из рассмотрения при создании триггеров.
Проведя анализ игры и средств WE можно прийти к выводу, что в игре используется не так уж много объектов, каждый из которых имеет ограниченное число свойств и действий, которые можно с ним производить с помощью триггеров. Объектов достаточно много, можете посмотреть в типах переменных (по-пробуйте создать переменную). Даже числа и триггеры — объекты...
В WE для простоты триггерные действия разделили на категории, каждая из которых соответствует действиям над определенным типом объектов. Так легче искать нужное действие.
Что касается свойств объектов, то тут ситуация немного сложнее. Их готового списка не существует, но кое-что можно узнать при создании условий триггера либо когда Вы пытаетесь присвоить переменной определенное значение (либо просто рассуждением).
Вы помните, что условия триггеров тоже разделены на категории:
boolean,
integer,
real,
unit,
unit type и т. д. Но в отличие от триггерных действий здесь деление по другому принципу. Эти категории не имеют прямого отношения к объектам. Просмотрев список условий из разных категорий Вы сможете выделить свойства объектов.
Например, в категории
boolean есть условие "юнит жив". Это свойство объекта
юнит – жив он или мертв. Еще пример из той же категории "юнит в регионе". Данное свойство принадлежит не одному, а двум объектам: с одной стороны это свойство юнита – находиться в определенном регионе, с другой – это свойство региона содержать определенный юнит.
Пример Проведу анализ объекта регион.
Ход рассуждений. В WE регион обладает следующими свойствами: название, положение, цвет, размер и погодный эффект, наложенный на регион. Действия с регионами – создать, удалить, переместить. Однако с помощью триггеров можно определять не все свойства и производить не все действия – лишь часть. Приведем их.
Свойства:
- Название
- Содержать определенный юнит
- Содержать определенную точку карты
- X и Y координаты центра региона
- X и Y координаты центра
- Размер — длинна и ширина
Действия:
Проведя такой анализ для каждого игрового объекта, Вы будете знать все потенциальные возможности триггеров в Варкрафте, узнаете параметры объектов и способы воздействия на них. Конечно, изучить все объекты с их свойствами и действиями довольно сложно, но можно изучать не сразу, а по необходимости.
Задание По образцу из примера провести анализ 2-3 объектов (кроме региона). Ход рассуждений приводить не надо, но свойства и действия должны быть. Самый сложный объект в игре — объект
юнит. Если проведете его анализ — можно больше ничего не анализировать
Хотя можно проанализировать объект
триггер.
Постановка задач Первый шаг – увидеть в игре не набор команд, но набор взаимодействующих объектов. Следующий – научиться ставить перед собой задачи и выполнять их.
Программирование на любом языке требует четкой постановки задач. Прежде всего надо уточнить, что нам дано (объекты, переменные и т. п.) и что нужно сделать.
Приведу пример. Один мой знакомый картостроитель попросил помочь ему разработать триггеры для огненной ловушки. Суть ее в том, что когда юнит входит в регион, в определенном месте загорается огонь, а когда юнит наступает на этот огонь, то повреждается. На первый взгляд все понятно, но фактически многих данных не хватает. Например, не указано, какой юнит должен войти в регион, чтобы сработала ловушка: любой или контролируемый человеческим игроком или вообще имеется ввиду конкретный юнит. А может быть летающие юниты не должны получать повреждения? В каждом случае эта задача решается по-разному. Затем, когда юнит входит в регион – загорается огонь. А когда выходит – огонь гаснет? Наверное да, но в условии об этом не говорится.
Итак, формулируем условие задачи. Дано: два региона — внешний и внутренний (один внутри другого) и спецэффект
огонь, который находится во внутреннем регионе.
Требуется:
- При входе во внешний регион юнита определенного игрока, активируется спецэффект огонь.
- При входе юнита во внутренний регион – юнит начинает получать повреждения через определенные промежутки времени
- При выходе юнита из внешнего региона – спецэффект огонь дезактивируется.
Согласитесь, что когда задача сформулирована так, становится понятнее, что же нам нужно делать. Однако нужно предусмотреть все возможные исходы:
- Что делать, если в регион войдет не один юнит, а несколько? Активировать огонь нужно лишь после входа первого вошедшего юнита. Повреждения во внутреннем регионе должен получать не один юнит, а все находящиеся в нем юниты.
- Что делать, если юнит умирает внутри региона? (Вопрос важный, т.к. мертвый юнит тоже считается юнитом. Если не учитывать это, то даже когда все юниты покинут внешний регион, огонь будет продолжать гореть, пока не разложится труп.) Значит при подсчете количества юнитов в регионе нужно не учитывать трупы.
- Что делать, если юнит умер, а других юнитов в регионе нет? Очевидно, дезактивировать огонь.
Необходимо сделать дополнения к требованиям.
Дополнения:
- При входе во внешний регион юнита определенного игрока, во внутреннем регионе активируется спецэффект огонь, при условии что в регионе уже не находятся другие юниты игрока.
- Если спецэффект огонь активирован, каждый юнит во внутреннем регионе получает повреждения через определенные промежутки времени.
- Если все живые юниты покинули внешний регион – дезактивировать огонь.
- Если юнит из внешнего региона умирает и других живых юнитов определенного игрока во внешнем регионе не присутствует – дезактивировать огонь.
Вот теперь задача поставлена корректно. Более того, теперь ясно, сколько и каких триггеров надо создавать. Можно переходить к следующему шагу – разбиение сложных задач на более простые до тех пор, пока каждую из них можно будет реализовать с помощью триггеров. Нужно анализировать не только сами задания, но и разные пути их решения.
К сожалению, на данном примере этот этап не продемонстрируешь. Каждую из поставленных задач можно реализовать в одном триггере (причем относительно не сложном). Правда придется добавить еще один триггер: спецэффект
огонь при загрузке сценария будет активирован. Значит понадобится триггер, чтобы дезактивировать его в начале игры. И еще один момент: как определить, активирован ли спецэффект
огонь (это нужно для второй задачи)? Могут быть разные способы. Например, создать переменную логического типа (булин) и записывать в нее значение
true, если юнит вошел в регион и
false, если живые юниты покинули регион либо все юниты в регионе мертвы. Но тут есть способ сделать проще, о котором я говорил. При входе юнитов — включать триггер, при выходе всех живых — выключать.
Предлагаю решать задачи по схеме:
- Что дано (объекты, переменные и др.), что нужно сделать.
- Рассмотреть различные варианты развития событий, сделать дополнение к условию.
- Разбить каждую из поставленных задач на более мелкие так, чтобы каждую из них можно было реализовать с помощью триггеров.
- Создать нужные триггеры.
- Проверить правильность работы триггеров.
На третьем этапе Вы можете столкнуться с нерешаемыми задачами. Может быть, Вы не знаете, как решать эту задачу, а может быть её вообще нельзя решить. Многие из задач, которые на первый взгляд не имеют решений, на самом деле могут быть решены. Например, раньше я считал, что от читов в игре нет защиты, т. к. триггерами они не фиксируются. Но немного подумав нашел способ обезвредить большинство из них. Если Вы столкнулись с нерешаемой задачей, попробуйте ее сначала проанализировать с конца. Нужно получить такой-то результат, для этого необходимо произвести такие-то действия, а для них в свою очередь нужно сделать еще что-то... Если не получается – посмотрите внимательно, нет ли другого способа решить эту задачу. Обычно такие способы есть.
Не стоит зацикливаться на нерешаемых задачах. Не решается задача – оставьте ее в покое и постарайтесь обойтись без нее.
Пример Ставлю такую задачу: определить, использует ли герой паладин в определенный момент свою способность неуязвимости (это может понадобиться, например, для ловушки с огнем: в огне повреждаются все юниты без разбора, а мы сделаем так, чтобы паладин, использующий неуязвимость не повреждался).
Примечание: описанный пример был создан еще в старых версиях buhs, когда невозможно было отследить, наличие баффов на юнитах. Сейчас эта задача решается намного проще.
Хорошая задачка, правда? Никаких обычных способов определить, является ли юнит неуязвимым с помощью триггеров не существует. Можно отлавливать событие – когда паладин использует способность неуязвимости. Но это не выход, т. к. способность неуязвимости может быть разного уровня и длится разное время. К тому же неуязвимость может быть отменена игроком. Так как же нам определять неуязвим паладин или нет? Давайте рассуждать: о том, что паладин стал неуязвим мы судим не только по тому, что он использовал соответствующую способность, но главным образом по такому критерию: паладина невозможно атаковать и накладывать заклятия.
Невозможность наложения заклятий – необходимое, но не достаточное условие. Например, у паладина может быть неуязвимость к магии (зелье или баньши). А вот условие, что на паладина невозможно напасть – является свидетельством, что паладин действительно неуязвим. Значит, если мы заставим какой-то юнит атаковать паладина и он не сможет этого сделать – паладин неуязвим, а если сможет – значит паладин уязвим.
Где-нибудь рядом с паладином создаем юнит-стрелок с очень слабой, неощутимой (0) атакой и тут же прячем его (действие —
unit - hide unit), чтобы игрок ничего не заметил. Параллельно даю приказ атаковать паладина. Если паладин неуязвим — то юнит начнет двигаться к нему (все юниты, которым дан приказ атаковать неуязвимую мишень ведут себя так). Если нет — то будет стрелять, но это никак не отразится на здоровье паладина.
Далее мы запоминаем положение созданного юнита. Если через долю секунды оно осталось прежним —паладин уязвим, если нет — неуязвим.
Проверил — работает. Вот Вам и нерешаемая задача.
Задание: распишите схему решения задач так, как это сделано для задачи с огненной ловушкой (если Вы еще и триггеры сделаете – будет совсем замечательно).
Выберите одну из следующих задач:
- Если герой ночью заходит на кладбище, из могил вылезают скелеты. Если герой из расы нежить – они присоединяются к нему, если нет — нападают.
- Заставить юнит патрулировать определенную местность: он должен ходить из одного места в другое, останавливаться ненадолго, затем продолжать движение. В случае если он замечает врага – должен побежать к себе на базу.
- За героем должен следовать определенный юнит (вроде эскорта). Если герой атакован, то эскорт вступает в сражение. Если эскорт теряет героя из вида – то больше не идет за ним, но если юнит вновь замечает героя – то снова идет за ним.