27.06.2008, 02:40 | #1 | ||
Новичок
Регистрация: 05.03.2007
Сообщений: 38
Репутация: 10
|
Софт/руководство/советы для модостроителей
Бардачок картодела WC3 TFT
Редактирование Моделей и Графики:
GUI Manual - готовность 1ой версии llllllllllllllllllll 34% . Демо можно скачать здесь Просмотр реплеев 1.20 и 1.21 на 1.22 Скачать архив, распаковать, запустить war3x_repviewer_121x. В появившемся окне указать директорию вашего варкрафта и установить. После окончания установки для просмотра реплеев заходим в папку с ВарКрафтом и ищем 2 ярлычка 120x и 1.21x. Что бы просмотреть реплеии какой либо версии запускаем - 120x или 1.21x, смотря какой реплей версии. Вот как ярлыки проги выглядят, на скрине снизу: Скрытый текст: Скачать Последний раз редактировалось ZeroCoolDark; 07.12.2008 в 12:34. |
||
|
07.12.2008, 12:41 | #2 | ||
Юзер
Регистрация: 02.10.2006
Сообщений: 121
Репутация: 34
|
JASS – Наиболее полное руководство
Часть первая Вместо предисловия Чтобы сделать свою карту хорошей мало просто сделать красивый рельеф, продумать сценарий, расставить декорации, войска. Для хорошей карты нахватает триггеров. К сожалению, большинство стандартных триггерных действий сделаны неправильно. Неправильно с точки зрения JASS. Эта статья для тех, кто желает понять JASS. Возможно те, кто сейчас читает эту статью с целью изучить JASS, уже ни раз пытались читать другие статьи, но никак не могли понять их. Эта статья поможет понять JASS. Прежде всего, хочу сказать, что эта статья не является переделкой чужой статьи. Эта статья рекомендуется только для тех, кто уже хорошо знает триггеры. Так как статья получилась очень большой, я разделил её на 3 части. А после прочтения вы можете задать свой вопрос по статье или по JASS. Что есть JASSи для чего он нужен JASS - это интерпретируемый язык программирования движка WARCRAFT 3. А что же такое за слово? Интерпретируемый? Интерпретируемым называют тот язык программирования, который выполняется во время действия программы (в этом случае, во время игры). А точнее говоря, он интерпретируется перед игрой и затем выполняется. Само название JASS это сокращение от англ. Just Another Scripting System. В WARCRAFT 3 используется вторая версия этого языка (то есть JASS 2). Нужен JASS в основном для уменьшения нагрузки на движок варкрафта и на компьютер пользователя соответственно. Очень часто JASS используют для создания «триггерных» заклинаний (далее спеллов). Существует ошибочное мнение, что JASS нужен только дотам. Небольшой пример неправильного использования триггеров А теперь от теории к практике. Допустим, нам поставлена задача. Сделать так чтобы вокруг героя появилось 12 светлячков. Для этого мы скорее будем использовать полярную проекцию: Код:
События: Неважно, какие события – главное действия =) Условия: Тоже на свой вкус Действия: For each (Integer A) from 1 to 12, do (Actions) Цикл - Действия Боевая единица - Create 1 Светлячок for Игрок 1 (красный) at ((Position of (Triggering unit)) offset by 256.00 towards ((Real((Integer A))) x 30.00) degrees) facing Стандартная ориентация зданий degrees Введение в JASS Комментарии в JASS Можно сделать любую строку комментарием. Для этого достаточно поставить перед ней 2 косые черты. И любой код ставший комментарием не выполняется! Примеры: Код:
//Это комментарий Код:
//local type name – этот код не выполняется Код:
local type name //Это тоже комментарий Итак, вы поняли, для чего нужен JASS (я на это надеюсь). Теперь пришло время поговорить об одном из «кирпичей» программирования. А именно – переменных. Из редактора варкрафт вы знаете, что существуют переменные и для чего они нужны. К этим переменным вы можете обратиться из любого триггера. На самом деле в варкрафт существует 2 вида переменных. Глобальные и локальные. Глобальные – это те, с которыми вы уже знакомы, те, которые вы используете в своих триггерах постоянно. К ним вы можете обратиться из любого триггера. В JASS к глобальным переменным нужно обращаться с префиксом udg_ .Но есть и локальные. Локальные переменные доступны только в JASS. И самое главное – к локальным переменным можно обратиться только из того триггера, в котором они созданы. Но как же создать локальные переменные? Для начала создайте пустой триггер и конвертируйте его в текст (Редактор триггеров ->Правка ->Конвертировать в текст). Очистите весь код в конвертируемом триггере. В дальнейшем все «эксперименты» вы будете проводить именно в этом триггере… Создание локальной переменной В создании локальной переменной нет ничего сложного: Код:
local type name Код:
local integer myvariable array после типа: Код:
local integer array myvariable Присваивание значения локальной переменной Для присваивания значения локальной переменной используется оператор set: Код:
set myvariable = 10 Код:
set myvariable[0] = 10 В конце триггера все локальные переменные должны быть удалены (обнулены). Для удаления локальных переменных типа integer нужно присвоить им значение 0. Код:
set myvariable = 0 Код:
set myvariable = “” Код:
set myvariable = null Код:
set myvariable[0] = 0 Для начала, как я и говорил, переменные должны быть объявлены (созданы). И должны быть уничтожены (обнулены) в конце триггера (обычно). Не стоит забывать и о достаточно частой ошибке: Код:
//Где-то в коде создается боевая единица set myunit = NULL call RemoveUnit(myunit) //Попытка удалить юнита Типы в JASS Всего в JASS существует 6 основных типов переменных: boolean – Логическая переменная может иметь значения только ДА и НЕТ code – В этой переменной содержится функция real – В этой переменно содержатся числа с плавающей запятой (например, 2,67) string – Строки integer – Целые числа handle – Указатель на игровой объект (например, область, боевая единица). Манипулируя с переменной, мы изменяем не переменную, а сам объект. Является родительским ко всем «сложным» типам. Например тип handle является родительским типу unit Все остальные типы (unit, unit-type, item, effectи т.д. являются потомками handle) Работаем
__________________
Вот так вот... |
||
|
07.12.2008, 12:41 | #3 | ||
Юзер
Регистрация: 02.10.2006
Сообщений: 121
Репутация: 34
|
Часть вторая
Функции в JASS Теперь можно поговорить о еще одном «кирпиче» программирования – функциях. Функции – это набор команд который выполняется после вызова функции. Чтобы создать (объявить) функцию нужно написать следующий код: Код:
Function name takes – returns – //Код функции… endfunction Здесь name – это имя функции, takes – список переменных, которые функция «берет», returns – список переменных, которые функция возвращает. Для того чтобы указать, что функция ничего не берет/ничего не возвращает используют nothing. Например: Код:
Function name takes nothing returns nothing //Код функции… endfunction Если функция что-то возвращает, то в конце пишут return то, что функция возвращает, например: Код:
Function name takes nothing returns integer //Код функции… Return 9 //Функция возвращает число 9 endfunction Чтобы вызвать определенную функцию используют оператор call: Код:
call RemoveUnit(myunit) //Вызов функции удаления юнита Подробнее о том, что функция берет и возвращает Расскажу подробнее о структуре «возвращения» и «дачи» переменных функциям. Кода мы вызываем определенную функцию, нужно передать ей какие-то конкретные значения. А именно нам нужно удалить боевую единицу. Но для удаления мы должны вызвать функцию удаления юнита и передать только что вызванной функции переменную типа юнит. Ведь, как я и говорил, переменная ссылается на объект и без переменной юнит не удалится! Также чтобы вызвать функцию удаления предмета нужно передать этой функции (функции удаления предмета), которую мы вызываем, переменную типа предмет. Но как же передать переменной значения. Код:
call RemoveUnit(myunit) //Вызов функции удаления юнита В этом коде мы вызвали функцию удаления юнита (RemoveUnit). Но для чего после вызова функции идет (myunit). Очевидно, это локальная переменная типа юнит. И для того чтобы передать функции значения нужно сразу после её имя поставить (список аргументов, которые она берет). Если аргументов несколь, то они перечисляются через запятую. В нашем случае это (myunit). Функции можно передать несколько переменных или значений. И так постепенно я веду к главному – зачем функции «брать» и «отдавать»? Дело в том, что функции можно передать только то, что она «берет». Если функция ничего не берет (nothing), то при вызове функции после ее имя надо поставить пустые скобки – (). А теперь подробнее о самом устройстве. Как уже говорилось функции можно передать только те аргументы (переменные или просто значения), которые она берет. Это значит, что уже знакомой нам функции RemoveUnit можно передать только 1 аргумент (переменную или значение) типа юнит. И не как иначе! Теперь о возвращении. Когда функция вызывает другую функцию, она может получить определенное значение. Например, Код:
Function name takes nothing returns integer Local integer i = 72-2*2 //Так тоже можно делать в JASS Return i //Функция возвращает число 136 endfunction А кому же -товернется эта переменная? Она вернется той функции, которая её вызвала. Это значит, что если нашу функцию вызвала какая другая функция, то этой вызывающей функции вернется это значение. И с этим значением теперь можно будет работать в вызывающей функции. Функция может вернуть только 1 значение (об исключениях позже). И пусть вас не смущаяют имена переменных: Код:
Function name takes integer I, string S returns nothing //…Код функции… endfunction Что это за имена I, S? На самом деле это имена аргументов и их типы. Это ни значит что все передаваемые аргументы должны быть такого же имя! Имена нужны только для работы с ними (прибавление, присваивание значения и т.д.). В области return располагается тип возвращаемого аргумента. Правила обращения с функциями Самые основные правила обращения с функциями: Функция должна быть объявлена (создана) прежде, чем её «вызовут» При вызове функции аргументов, передаваемых функции должно быть столько же, сколько она и берет. Передаваемые аргументы должны быть таких типов, каких берет вызываемая функция. Циклы в JASS Скорее вы работали до этого с циклами в простом редакторе триггеров (в нашем первом примере была работа с циклом) и знаете, что циклы выполняются определенное количество раз – от опр. значения до опр. значения. В JASS циклы тоже есть, но они устроены немного по-другому. Код:
loop exitwhen int1 > int2 //Действия цикла Int1 = int1 + 1 endloop Здесь loop - это начало цикла endloop – конец цикла. exitwhen это условие после, которого выполнение цикла завершается. Можно делать любые условия. Код:
loop exitwhen bool = true //Действия цикла Int1 = int1 + 1 endloop Return в JASS Return в JASS используется не только для того чтобы вернуть какое-то значение, но и для того чтобы преждевременно выйти из триггера, не выполняя оставшихся действий. Это аналогично действию Skip Remaining Actions . Утечки памяти в WARCRAFT 3 Всегда удаляйте локальные переменные. Если их не удалить, то эти переменные остаются в памяти компьютера. Вплоть до закрытия вара. В 1 примере мы говорили об ошибках создания боевой единицы. В этом действии просто куча утечек. В одном только действии полярной проекции 3 утечки. Это значит, что если триггер выполняется каждую секунду, то каждую секунду в памяти остается по 3 точки, которые должны быть удалены. Утечки оставляют неудаленные переменные Функции правильные и неправильные Оказывается, существуют правильные и неправильные функции. В архиве вара среди множества файлов можно найти 2 файла – common.j и blizzard.j . Все файлы с расширением j представляют собой набор jass кода. Дак вот, в blizzard.j существует множество функций с утечками памяти. К сожалению именно bj (сокращение от blizzard.j) функции используются в обычных триггерных действиях. Помните, мы в первом примере говорили об ошибках 1 действия – CreateUnits. Вот именно подобными утечками обладает и эта функция. Но есть еще и common.j . В нем прописаны все функции и типы движка вара. Идентификаторы В редакторе триггеров вы манипулировали с юнитами посредством их имен. В JASS такое не пройдет. JASS работает не с именами, а с идентификаторами (ID). У каждой боевой единицы, заклинания, улучшения, декорации и др. есть свой уникальный идентификатор. Чтобы увидеть идентификаторы всех юнитов зайдите в редактор объектов и нажмите Ctrl + D. Идентификатор представляет собой 4-ух значное строковое значение. Устройство триггера в JASS Создадим триггер следующего содержания: Код:
События: Время - Elapsed game time is 5.00 seconds Условия: Да равно Да Действия: Do nothing Назовите его jasscode Конвертируем его в код (Правка ->Конвертировать в текст) Посмотрим на код… Код:
function Trig_jasscode_Conditions takes nothing returns boolean if ( not ( true == true ) ) then return false endif return true endfunction function Trig_jasscode_Actions takes nothing returns nothing call DoNothing( ) endfunction //=========================================================================== function InitTrig_jasscode takes nothing returns nothing set gg_trg_jasscode = CreateTrigger( ) call TriggerRegisterTimerEventSingle( gg_trg_jasscode, 5 ) call TriggerAddCondition( gg_trg_jasscode, Condition( function Trig_jasscode_Conditions ) ) call TriggerAddAction( gg_trg_jasscode, function Trig_jasscode_Actions ) endfunction В этом коде функция Trig_jasscode_Conditions является функцией условий триггера. Если условие выполнено, то функция возвращает true и выполняются действия. Функция Trig_jasscode_Actions является функцией действий триггера. Это значит, что в этой функции будет выполнен весь код, если событие и условия верны. Если вы заметили, то имена обоих функций выглядят примерно так: Trig_НазваниеТриггера_что(действия или условия). На самом деле, эти имена можно поменять на любые, только тогда придется менять аргументы у функций в функции InitTrig_jasscode. А сама InitTrig_jasscode представляет собой функцию создания и инициализации триггера, set gg_trg_jasscode = CreateTrigger( ) создает новый триггер (напомню, что триггеры это тоже виды переменных), TriggerAddCondition( gg_trg_jasscode, Condition( function Trig_jasscode_Conditions ) ) добавляет в только что созданный триггер условия. Ну а TriggerRegisterTimerEventSingle создает триггеру событие, в котором поясняется выполнение после некоторого игрового времени (в этом случае 5 сек). Замете, что к переменным типа триггер нужно обращаться с префиксов gg_trg_ . [
__________________
Вот так вот... Последний раз редактировалось ZeroCoolDark; 25.02.2009 в 13:19. |
||
|
25.02.2009, 13:18 | #4 | ||
Юзер
Регистрация: 02.10.2006
Сообщений: 121
Репутация: 34
|
B]Часть третья[/b]
Немного практики Теперь прочитанный материал неплохо бы закрепить практикой. Целью является создание триггерного заклинания (спелла), которое накладывает на цель сразу 2 заклинания. В нашем, случае это будет Омоложение и Духовное пламя. Для простоты понятия мы не будем создавать кучу действий и объектов. Дайте любому герою способность духовное пламя. Внимание! Предварительно уберите требования и стоимость манны у обоих заклинаний! Создайте новый триггер и назовите его TriggerSpell. Конвертируйте в код и замените полученный код на этот: Код:
function Trig_TriggerSpell_Conditions takes nothing returns boolean if ( not ( GetSpellAbilityId() == 'Ainf' ) ) then return false endif return true endfunction function Trig_TriggerSpell_Actions takes nothing returns nothing local unit op=GetTriggerUnit() local unit fu=CreateUnit(GetOwningPlayer(op),'u000',GetUnitX(op),GetUnitY(op),270) call UnitApplyTimedLife(fu, 'BTLF', 1.00) call UnitAddAbility(fu,'Arej') call IssueTargetOrderById(fu,OrderId("rejuvination"),GetSpellTargetUnit()) set op=null set fu=null endfunction //=========================================================================== function InitTrig_TriggerSpell takes nothing returns nothing set gg_trg_TriggerSpell = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_TriggerSpell, EVENT_PLAYER_UNIT_SPELL_CAST ) call TriggerAddCondition( gg_trg_TriggerSpell, Condition( function Trig_TriggerSpell_Conditions ) ) call TriggerAddAction( gg_trg_TriggerSpell, function Trig_TriggerSpell_Actions ) endfunction Этот код весьма сложен для новичков в JASS. А потому я опишу функции, которые используются в этом коде: local unit op=GetTriggerUnit() –Локальная переменная, которая хранит ссылку на триггерный юнит (наш герой со способностью Духовный огонь) local unit fu=CreateUnit(GetOwningPlayer(op),'u000',GetUnitX( op),GetUnitY(op),270 – 1-я часть:Локальная переменная которая хранит ссылку на юнита.2-я часть: создается юнит (1), который относится к игроку - владельцу тригерного юнита, типа ‘u000’ (ID нашего кастера) в точке GetUnitX(op),GetUnitY(op) развернутый на 270 градусов call UnitApplyTimedLife(fu, 'BTLF', 1.00) – Добавляет таймер кастеру (локальная переменная u), после которого он уничтожится. call UnitAddAbility(fu,'Arej') – добавляется способность Омоложение (‘ID – Arej’) call IssueTargetOrderById(fu,OrderId("rejuvination"),Ge tSpellTargetUnit()) – заставляет кастера использовать заклинание Омоложение (ID - Arej) на цель GetSpellTargetUnit() (это цель заклинания Духовное пламя) function Trig_TriggerSpell_Conditions takes nothing returns boolean if ( not ( GetSpellAbilityId() == 'Ainf' ) ) then return false endif Это условие, является ли способность, которую используют, духовным пламенем (ID – ‘Ainf’) call TriggerRegisterAnyUnitEventBJ( gg_trg_TriggerSpell, EVENT_PLAYER_UNIT_SPELL_CAST ) Это событие – юнит начинает применять способность Я повторяюсь, что для новичка в JASS это не не очень простое триггерное заклинание, но здесь используются только функции из common.j , которые не оставляют утечек в памяти. Также все локальные переменные обнуляются. Если вы не поняли этого триггера, скачайте карту-пример:http://dump.ru/files/f/f8901174473/ ReturnBug В принципе на этом можно было завершить введение в JASS, но есть ещё 2 интересных вещи… В JASS нельзя напрямую работать с памятью. Хотя это могло бы пригодиться. Для чего это – речь пойдет дальше. Однако есть такой баг, который позволяет получать ячейку памяти компьютера, в которой хранится объект типа handle (например, юнит) Код:
function FindHandle takes rect reg returns integer return reg return 0 endfunction Эта функция берет переменную reg типа rect и возвращает переменную типа integer, должна возвращать… Тут 2 return и получается какая-то ерунда. Игра должна вернуть регион reg, как целочисленное число, что невозможно, но тут вступает return bug. И JASS возвращает. Вообщем return bug вам конечно врятли понадобится, если вы не собираетесь создавать каких-то очень сложных систем. Под конец приведу функцию, которые позволяет находить и считывать номер ячейки памяти любого из игровых объектов (handle) Код:
function H2I takes handle h returns integer return h return 0 endfunction Чтобы присвоить integer переменной эту ячейку нужно сделать следующее: Код:
set i = H2I(Переменная любого игрового объекта) Кэш и JASS Тут я не буду писать ничего насчёт КЭШа, просто скажу зачем он нужен. Кэш позволяет сохранять переменные и боевые единицы и переносить эти данные между картами. В мультиплеере переносить кэш с карты на карту нельзя. Но в мультиплеерных картах можно пользоваться КЭШем (но не переносить). Кэш используют в качестве глобального массива. Однако ячейки КЭШа не могут хранить множество handle типов, а потому используют return bug. Больше не буду ничего говорить насчёт КЭШа, так как его используют в основном в картах типа ДОТЫ и для простых карт он необязателен. Могу лишь дать ссылки на эту тему. Прочитать про КЭШ и JASS можно здесь и здесь… Хитрости JASS Приведу 2 очень важные вещи, отличающие простые триггеры и JASS: Индексация игроков (также как и элементов массива) начинается с 0, это значит, что для того чтобы указать игрока 1, нужно обратиться: Player(0) Все пути к моделям нужно проставлять через \\ , например: "Abilities\\Spells\\Human\\ThunderClap\\ThunderCla pCaster.mdl" Вместо заключения Прочитав статью полностью и вникнув в нее, вы изучили основы JASS (я надеюсь). Но это лишь основы, для профессионального знания JASS нужно еще много тренироваться. Возможно вам поможет конвертирование простых триггеров в текст. В общем это все. В конечном варианте эта статья занимает около 10 страниц в Ворде и это без картинок! Писать больше не буду так как и так много. Работаем 2
__________________
Вот так вот... |
||
|
|