Читатель, мы вплотную подошли к системе SCV. Эта система, которая
позволяет сопоставлять ЛЮБОМУ игровому объекту - либо другой объект,
либо какое-то значение (или даже массив). Наподобие custom value, но
значительно более универсальная. Значение этой системы трудно
переоценить. Фактически, она позволяет упростить решение огромного
множества задач, избавиться от глобальных переменных и создавать так
называемые кешь-переменные прямо во время игры. С чего тут начать.
Пожалуй, с Кеша. Существует такая замечательная вещь, называемая кешь.
Программисты называет такие структуры - ассоциативный массив. Кешь в
war3 - это особый двумерный массив, в котором в качестве аргументов
используются строки. Т.е. вводишь аргументами 2 строки, им
сопоставляется значение. Можно сопоставить значение типа integer, типа
real, типа string и типа boolean. Как жаль, что в этот массив
нельзя записать ссылку на юниты, предметы, способности и т.п. Стоп, а
действительно ли нельзя? Или все таки можно? Ссылку может и
нельзя, но давай вспоминать, что мы узнали про RB. Каждому игровому
объекту соответствует уникальный номер, число типа integer. Это число
можно найти, и по этому числу можно найти объект. А ведь число типа
integer может быть записано в кешь! (*) Итак, если мы
используем кешь не для переброски данных, а для хранения информации, то
в качестве хранимой информации кешь способен записать указатели
(номера) объектов. Это первый важный вывод. А теперь подумаем,
если мы в кешь можем сохранять объекты, можем ли мы при помощи кешь
сопоставить какому-то игровому объекту какое-то значение? Игровой
объект имеет свой уникальный номер. Номер есть число, но специальные
функции позволяют перевести его в строку. (**)Договоримся,
если мы хотим сопоставить игровому объекту значение в кешь, то в
качестве первого аргумента записи будем использовать уникальный номер
этого объекта, переведенный в строку. (***)Что касается второго аргумента кешь, то мы можем использовать его, чтобы дать нашему сопоставлению уникальное имя. Сопоставь факты, отмеченные выше, и ты поймешь идею SCV. Рассмотрим функцию вида: function set_object_iparam takes handle h, string key, integer val returns nothing call StoreInteger(udg_cache, I2S(H2I(h)), key, val) endfunction
Эта функция предназначена, чтобы сопоставлять любому объекту параметр
типа integer. Аргументами выступает ссылка на объект handle h, строка
key - имя сопоставления и переменная val типа integer - это число,
которое мы сопоставляем объекту. udg_cache - это переменная типа кешь - специальный кешь-файл создается в самом начале игры. В функции единственное действие: call StoreInteger(udg_cache, I2S(H2I(h)), key, val) Это обычная команда занести значение в кешь. Для записи в кешь, нудно передать 2 строки-аргумента. Первая строка: I2S(H2I(h))
Разберемся подробнее. Здесь написана функция внутри функции. H2I(h) -
мы уже рассмотрели выше. Она вернет номер для объекта, переданного
через переменную h. Вторая функция I2S(...) - это обычная
варкрафтовская функция перевода числа в строку. Итак, вся конструкция в
целом приведет к тому, что первая строка - это переведенный в текст
уникальный номер объекта. Вторая строка key - это строка, которую заполняет сам пользователь, давая имя сопоставлению. Параметр для записи val. Итак, если у тебя есть юнит u и ему нужно сопоставить число 10, то можно использовать команду: call set_object_iparam(u, "int", 10) имя сопоставления "int".
Отлично! Как делать запись мы выяснили. А можно ли эту запись прочитать
обратно? Да! Во-первых, для удобства создадим вторую функцию: function get_object_iparam takes handle h, string key returns integer return GetStoredInteger(udg_cache, I2S(H2I(h)), key) endfunction
Она похожа по структуре на предыдущую, только аргументов на один
меньше. Это потому, что функция нужна не для записи значения в кешь, а
для чтения значения из кеша. return GetStoredInteger(udg_cache, I2S(H2I(h)), key)
Т.е. наша функция вернет значение выражения GetStoredInteger(udg_cache,
I2S(H2I(h)), key) . А что это за выражение? Стандартная функция для
чтения из кеша. В качестве первой строки указывается уникальный номер
объекта, переведенный в строку. Вторая строка - определена
пользователем. Итак, если мы хотим узнать, что записано в записи кеша "int" для юнита u, используем команду: set i = get_object_iparam(u, "int")
Т.е. можно и записывать значения и читать их. Читатель, не замечаешь
чего-то общего между нашими сопоставлениями и custom value? По сути,
custom value - это тоже сопоставление, но менее универсальное, т.к.
можно сопоставлять юнитам (и только юнитам) одно (и только одно)
значение типа integer. А при помощи SCV можно сопоставить что угодно и
чему угодно. Поэтому я называл эту систему Super Custom Value (SCV) , а
сопоставления-записи - для краткости cv. А как сопоставить юниту u - другой юнит u2? Очень просто. call set_object_iparam(u, "int", H2I(u2)) Мы записали в параметр "int" уникальный номер u2.
Этот номер мы можем прочесть обратно. Проблема лишь в том, как при
помощи этого номера получить ссылку обратно на u2. Для этого в SCV есть
специальные функции. function I2U takes integer i returns unit return i return null endfunction и function get_object_uparam takes handle h, string key returns unit return I2U(GetStoredInteger(udg_cache, I2S(H2I(h)), key)) endfunction
Первая функция по уникальному номеру возвращает сам юнит, вторая
сделана для простоты - она читает уникальный номер из записи в кеше и
при помощи первой функции возвращает ссылку на этот юнит. Так что, если нужно прочесть какой юнит записан в cv "int" для юнита u, используем команду set u2 = get_object_uparam(u, "int")
Вот и все. Остальное все по аналогии. Есть и другие функции для
сопоставления чисел real, строк, флагов boolean. Есть функции для
нахождения не только юнитов по их номеру, но и других объектов - точек,
регионов, спецэффектов и др. Есть правда еще одна функция function flush_object takes handle h returns nothing call FlushStoredMission(udg_cache, I2S(H2I(h))) endfunction - она позволяет быстро отчистить все записи кеша, относящиеся к какому-то объекту.
Скажем, собираешься ты удалить юнит u. Для того, чтобы cv этого объекта
не занимали место в памяти, когда объекта уже нет, пишешь команду: call flush_object(u)
Все эти функции в сумме вмещаются на 1-1.5 экрана. Переносить систему
из сценария в сценарий - элементарно. Просто копируем код, создаем
переменную cache и при событии Map Initizlization создаем кешь-файл.
Читатель, попробуй представить себе все возможные способы применения
SCV. Вспомни примеры, которые мы рассмотрели ранее. Может быть есть
способ что-то сделать проще, быстрее и надежнее ?
Когда ДимонТ выпустил систему, я разработал по ней небольшой обучающий
сценарий, который демонстрирует ее возможности, в том числе создание
переменных и массивов cv. Я хочу, чтобы ты подробно изучил этот
сценарий. Освоив SCV ты поднимешься на следующую ступень мастерства. 14. Да здравствует SCV!
Рассмотрим один из наших старых примеров – полет юнита снаряда. Можно
ли улучшить его при помощи SCV? Раньше нам приходилось использовать
массивы, чтобы сохранить информацию, что такой-то юнит-снаряд летит к
такой-то цели и имеет такой-то уровень заклинания. Теперь мы можем
сопоставить эти данные непосредственно юниту-снаряду при помощи SCV.
Т.е. записать все необходимые данные в cv. А как нам сделать
периодический цикл по всем юнитам снарядам, чтобы сдвигать их? О, тут у
нас появляются новые интересные возможности. Мы можем для каждого
юнита-снаряда создать отдельный триггер с периодом 0.05, отвечающий за
его передвижения к цели. Ну допустим, мы создали триггер с
событием Периодическое 0.05. А как прописать, что этот триггер должен
работать только для определенного юнита-снаряда? Очень просто, мы
сопоставим триггеру (триггер ведь тоже игровой объект!) нужный нам
юнит-снаряд. И при запуске триггера сможем определить, что нужно
двигать такой-то юнит-снаряд. В целом система организации
движения юнита-снаряда становится довольно простой. На основе этого
принципа я сделал несколько геройских заклинаний - предлагаю тебе
ознакомиться с ними. К примеру, герой Лорд Хаоса. Заклинания Звездный
конус, Групповой файербол и Сфера Хаоса сделаны таким способом. Это
открывает широчайшие возможности по созданию триггерных заклинаний
любой сложности. Рекомендую глянуть примеры подобных наработок здесь: - наработка Димона: герой Seal master - моя наработка: герой Еретик - моя наработка: герой Лорд Хаоса
Кстати, огромное достоинство системы SCV, что ее можно легко дополнить.
Допустим, нам нужно чтобы объектам можно было сопоставлять триггеры и
таймеры. К функциям SCV добавим новые: function I2Tm takes integer i returns timer return i return null endfunction function I2Tr takes integer i returns trigger return i return null endfunction и еще две function get_object_tmparam takes handle h, string key returns timer return I2Tm(GetStoredInteger(udg_cache, I2S(H2I(h)), key)) endfunction function get_object_trparam takes handle h, string key returns trigger return I2Tr(GetStoredInteger(udg_cache, I2S(H2I(h)), key)) endfunction Вот и все.
|