Сейчас 21:58:02 Четверг, 16 сентября, 2021 год
Главная ⇒ Форум ⇐ RSS Файлы Cтатьи Картинки В о й т и   или   з а р е г и с т р и р о в а т ь с я

Меню сайта

Категории

Наш опрос
Ваша любимая раса?
Проголосовало: 177815

Сейчас на сайте
На сайте всего: 1
Гостей: 1
Пользователей: 0

Реклама

Главная » Статьи по WarCraft 3 » Создание карт » Jass
Как оптимизировать свой код
Правило 1: Удаляйте ненужные вызовы функций.

Вступление.
При написании кода вы должны сделать как можно меньше вызовов функций. Номер, который считает вызовы, это ОБЩЕЕ количество, это не количество вызванных функций в вашей функции. Если вызвать функцию, содержащую в себе вызовы 3-х функций, вы вызовете 4 функции. Если вызвать эти 3 функции напрямую, тогда вы избавитесь от вызова лишней функции.
Если вы вызовете GetTriggerUnit() в своей функции трижды, лучше использовать переменную, в которой будет храниться значение GetTriggerUnit(). В этом случае вы избавитесь от вызова 2-х лишних функций.
Конечно же, это баланс между созданием коротких кодов и небольшим вызовом функций. Однако если использованная вами функция вызывает 40 функций, вызов их напрямую сделал бы код намного длиннее, совсем не стоит этого делать ради того, чтобы вызвать на одну функцию меньше.

Используйте Native-функции.
Большинство BJ-функций (Blizzard.j) - это просто дополнительные функции, которые вызывают Native-функции (common.j). Если вы вызовете BJ-функцию такого типа, то 100% получите больше вызовов функций, чем если бы вы вызывали Native-функцию напрямую. Поэтому перед тем, как использовать BJ, подумайте, как можно заменить её на Native. Это основное правило JASS.

Изучайте функции.
Самый простой способ посмотреть начинку функции - найти программу, которая содержит списки функций. Для этих целей можно использовать JASS NewGen Pack или JassCraft. Если вы не хотите использовать программу, просто поищите нужную функцию на сайте wc3jass.com.
Самый простой способ сказать, что вызов функции не требуется и нужно вызвать функцию, которая находится внутри - посмотреть на её имя. Если оно заканчивается на BJ, вызов такой функции в большинстве случаев является лишним. Однако есть и исключения, некоторые BJ-функции действительно что-то делают и пригождаются. Некоторые функции из Blizzard.j могут не содержать BJ в конце своего имени.

Пример.
После того, как вы немного ознакомились с теорией, давайте разберём простой триггер:
Код
Unit Dies
  События Боевая единица - A unit Умирает
  Условия
  (Triggering unit) is Герой) равно Да
  Действия
  Игра - Display to (All players) for 30.00 seconds the text: ((Name of (Owner of (Triggering unit))) + ( was killed by + (Name of (Owner of (Killing unit)))))
  Игрок - Add ((Level of (Triggering unit)) x 50) to (Owner of (Killing unit)) Золото(текущ.)
  Wait ((Real((Level of (Triggering unit)))) x 10.00) seconds
  If (All Conditions are True) then do (Then Actions) else do (Else Actions)
  If - Conditions
  ((Triggering unit) belongs to an ally of Игрок 1(Красный)) равно Да
  Then - Actions
  Герой - Instantly revive (Triggering unit) at (Center of Team 1 Base <gen>), Показать revival graphics
  Камера - Pan camera for (Owner of (Triggering unit)) to (Center of Team 1 Base <gen>) over 2.00 seconds
  Else - Actions
  Герой - Instantly revive (Triggering unit) at (Center of Team 2 Base <gen>), Показать revival graphics
  Камера - Pan camera for (Owner of (Triggering unit)) to (Center of Team 2 Base <gen>) over 2.00 seconds

Это отличный триггер воскрешения героев. Будучи пользователем JASS, вы скажете, что здесь есть утечки. Сейчас мы сконвертируем этот триггер в код и устраним утечки. Получится что-то вроде этого:

Этот скрипт уже безутечен, однако ещё далеко не совершенен. Он прекрасно работает и не лагает. Давайте теперь его немного оптимизируем.
Начнём с конца триггера. InitTrig_Unit_Dies довольно неплох, однако в нём есть небольшая заминка - TriggerRegisterAnyUnitEventBJ. В некоторых случаях следует провести чёткую черту между понятиями "Длина функции" и "Использование Native". И я считаю, что раскрывать TriggerRegisterAnyUnitEventBJ (7 строк) не следует только из-за того, что мы хотим только Native-функции.
Но вы можете выбирать сами, что делать в подобных ситуациях. Автор решил раскрыть TriggerRegisterAnyUnitEventBJ. Ниже его код:

Примечание автора. Я сразу задал значение переменной index. Кто-то может вначале объявить её, а потом задать. Лучше этого не делать и задать значение сразу же после объявления.
Примечание переводчика. Видно, автор не пожелал избавиться от gg_trg посредством local trigger.

Итак, мы закончили с первой функцией. Теперь перейдём к функции Trig_Unit_Dies_Actions. Так как функция Trig_Unit_Dies_Func004C принадлежит к Trig_Unit_Dies_Actions, можно взять их вместе.

Здесь побольше работы. Прежде всего нужно заметить, что мы очень часто используем GetTriggerUnit() и GetKillingUnitBJ(). Это не очень хорошо. Объявите несколько локальных переменных в начале функции и используйте их.
Помните: Если это возможно, задавайте значение локальной переменной вместе с объявлением.
После того, как мы это сделаем, наша функция будет выглядеть так:

Примечание 1. Так как "if" - это другая функция, нельзя использовать там наши локальные переменные. Это плохо и вообще не имеет здесь смысла держать отдельную функцию для условия, поэтому впишем его в основную функцию.
Примечание 2. GetKillingUnitBJ() - это функция, возвращающая GetKillingUnit(), поэтому мы уберём BJ и напишем GetKillingUnit().

if более сложен, чем BJ, поэтому я объясню поподробнее.
Вы понимаете, что if, elseif, exitwhen и.т.п. - просто логические значения. Логические значения дают true или false.
Можно написать "true == true", как и просто "true". Или же можно написать "FunctionThatReturnsBoolean() == true", хотя лучше будет написать "FunctionThatReturnsBoolean()". Если FunctionThatReturnsBoolean() правдива, тогда выводимая логическая будет true. Написать "FunctionThatReturnsBoolean()" будет намного проще, чем "FunctionThatReturnsBoolean() == true".
Так как функция Trig_Unit_Dies_Func004C возвратится в случае, если Player(0) (Игрок 1 (Красный) в GUI) является союзником GetTriggerUnit(), можно заменить всё на "IsUnitAlly(victim, Player(0))".

Вместе с предыдущими изменениями мы получим это:

Примечание. Функция Trig_Unit_Dies_Func004C стала бесполезна, поэтому мы удалили её.
Код выглядит всё лучше и лучше.
Теперь мы видим ещё одну вещь - различие между if и else только лишь в значении переменной l. Остальное можно вынуть из этого блока, эта операция сделает код короче. После того, как мы немного почистили код, мы можем сделать ещё 4 вещи:
- Создать переменную типа "игрок" со значением GetOwningPlayer(victim) и использовать её.
- Создать переменную типа "игрок" со значением GetOwningPlayer(killer) и использовать её.
- Создать переменную типа "целочисленная" со значением GetUnitLevel(victim) и использовать её.
- Удалить ненужную конвертацию R2I. Целочисленные могут быть реальными без каких бы то ни было операций, но для обратной операции нужна конвертация R2I, иначе компилятор не скомпилирует код нормально.

После этого мы немного уменьшили итоговое кол-во вызванных функций. Это то, чего мы и хотели.
Вот код, который получился после всех операций, которые я указал:

Вам нужно запомнить 2 вещи:

- Я удалил некоторые скобки, используйте их только для случая, когда вы хотите посчитать одно перед другим. Например 3*(2+4), но не (3*2)+4, так как 3*2 по умолчанию считает перед тем, как прибавить 4. Но будьте внимательны: Не удалите случайно скобку, нужную для вызова функции.
- Эта функция длиннее предыдущей, но беспокоиться не стоит. Это не длина, которая важна, это число вызовов функций.

На этой стадии осталось сделать 2 вещи:

- DisplayTimedTextToForce не очень хороша. Мы же хотим вывести текст всем игрокам, поэтому нам не нужно проверять, находятся ли они в отряде.
Подсказка: Внимательно посмотрите начинку этой функции:

Эта функция проверяет, есть ли в заданном отряде GetLocalPlayer(), затем выводит текст для этого игрока. Так как мы хотим, чтобы текст отображался для всех игроков, мы можем использовать "call DisplayTimedTextToPlayer(GetLocalPlayer(), .0, .0, duration, message)" напрямую.
- AdjustPlayerStateBJ - это BJ-функция, которая имеет значимость. Она также обновляет кол-во "Всего добытого" ресурса. Но только лишь если изменение - позитивное число. Зная то, что значение здесь позитивное, можно здесь многое изменить.

После всего этого мы получим такой код:

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

Мы можем её упростить.
Если НЕ IsUnitType(GetDyingUnit(), UNIT_TYPE_HERO) == true, тогда вернётся false, в противном случае вернётся true. Подумайте, оно же просто вернёт значение IsUnitType(GetDyingUnit(), UNIT_TYPE_HERO) верно? Так почему бы не написать это вместо всего остального?
Итак, по поводу ошибки. Это условие, т.к. это функция, использованная в триггере и возвращающая логическое значение. Здесь не использована отдельная функция, какая наблюдалась в предыдущем условии. В этом условии мы используем функцию IsUnitType(), вот тут баг и начинается. IsUnitType() ДОЛЖЕН с чем-либо сравниваться, например IsUnitType() == true, иначе мы получим баги. Сейчас UNIT_TYPE_HERO и RANGED_ATTACKER - это единственные значение, которые не дают здесь багов (странно, не правда ли?). Однако для хорошей практики я всегда предотвращаю баг при использовании этой функции.

Перепишем эту функцию.
Итак, мы закончили первичную оптимизацию. Вот код, который у нас получился:

Когда вы будете более опытны, вы сможете перепрыгивать через всё и оптимизировать код напрямую. Когда вы будете на этой стадии, то вы сможете писать код без базы на триггерах. Использовать GUI вы будете только в целях генерации новых "триггеров", блоков, которые вы сможете использовать в своём коде.
Это ещё одна причина использовать программу для редакции JASS-кода. Тогда у вас будет лёгкий доступ ко всем функциям и их начинке.

Правило 2: Используйте х,y вместо Location.

Координаты Х и Y имеют одно большое преимущество перед локациями - они не нуждаются в обнулении. Единственные случаи, когда действительно необходимо использовать локации - это если вы используете GetSpellTargetLoc и GetLocationZ, для них нет эквивалентов Х и Y.
Примечание переводчика: В 1.24 появились новые функции GetSpellTargetX() и GetSpellTargetY(). GetSpellTargetLoc уже может быть заменена, однако GetLocationZ останется проблемой.

Если у вас уже есть локация, тогда преобразуйте её. Если у вас её нет, тогда сразу используйте координаты Х и Y.
Большинство функций используют реальные, но только после того, как вытащить их оттуда. К примеру, посмотрите на функцию GetItemLoc():

Лучше использовать GetItemX/Y напрямую, тогда мы вызовем меньше функций, об этом мы говорили ранее.

Вернёмся к старой функции Trig_Unit_Dies_Actions и заставим её использовать реальные.

Так как локация - это координаты X,Y (у неё есть ещё и значение Z, но нам оно не нужно), нам понадобятся 2 реальных.

Нам нужно поменять здесь всего 3 вещи:
- GetRectCenter(). Эта функция просто вызывает 2 другие - GetRectCenterX/Y. Вызовем их напрямую.
- ReviveHeroLoc(). Это Native, но лучше использовать ReviveHero, которая напрямую берёт координаты X и Y.
- PanCameraToTimedLocForPlayer(). Смотрите, что мы здесь нашли. Похоже, мы что-то забыли оптимизировать в Правиле 1. Это говорит нам о важном моменте: изучите все функции, которые вы используете, даже если в конце их имени нет BJ, они могут быть функциями из Blizzard.j. Поэтому мы напрямую обратимся к этой функции и используем значения Х и Y напрямую.

Вот то, что у нас получится:

Итоги.

Используйте как можно меньше вызовов функций.
Используйте Native-функции.
Не раскрывайте очень большие функции.
Задавайте значение переменной разу после её объявления.
Используйте координаты х и у везде, где это возможно.
Не утверждайте, что фунция - Native, если у неё после названия не стоит BJ.
Просмотров: 4133 Добавил: Duosora Добавлено: 12 Ноября 2009 в 13:05:18
Комментариев: 13 |

Всего комментариев: 131 2 »
09 Июня 2012
Все замечательно, но текст статьи не совсем соответствует скриншотам.

18 Февраля 2012
12. Букреев Николай (SirNikolas) [Материал]
По тексту статьи были восстановлены скриншоты.

P. S. Обновлен JASS NewGen Pack.

11 Июня 2011
эх молодость :D помню когда на гуи сидел через кастум код копировал отсюда))

26 Января 2011
Оо вот это вещь спасибо статья супер для начинающего JASSера

04 Января 2011
9. Константин (Lipokiller) [Материал]
Влад, нет, нельзя, пробывал.

26 Октября 2010
8. Алексей (DarkVader) [Материал]
Статья как источник начальных знаний о Jass оч хороша.

02 Марта 2010
7. Андрей (DiR94) [Материал]
*на ipicrure не залевай

02 Марта 2010
6. Андрей (DiR94) [Материал]
Перезалей скрины. и вообще на будущее - на не заливай, он вечно слетает

18 Ноября 2009
5. Владимир (Greshnyk) [Материал]
хорошо сделано

13 Ноября 2009
4. Алексей (War-Cry) [Материал]
нормально! Для новичков

1-10 11-13
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]

Форма входа
Логин:
Пароль:

Поиск

Случайная картинка

Случайный файл
[11 Декабря 2008]
[Карты · AoS]
Blades Of Vengeance v5.12 -
Особенности карты:
- Выбор между двумя разными воюющими сторонами.
- Максимальный уровень - 30.
- 50 разных героев, со своими уникальными заклинаниями.
- 37 сборных артефакта и 54 обычных артефакта.
- Множество «модов» и стилей игры.
- Использованы нестандартные модели, иконки, загрузочный экран и т.д.
- В общем, карта более увлекательна для игры по сети, но есть и AI (улучшенный)

Новые карты
[07 Февраля 2016]
Переезжаем на другой сайт, господа![Dota]
[18 Октября 2015]
Duel of Gods PreV[Другое]
[18 Октября 2015]
Hero of The Empire v1.18g[RPG]
[17 Октября 2015]
Servant War v1.05[Другое]
[17 Октября 2015]
Age of Vikings Edited v1.6[Другое]
[17 Октября 2015]
Strife of the Champions Beta v1.2[Arena]
[17 Октября 2015]
VirusBoll (rus)[Другое]
[17 Октября 2015]
Exterminators v1[AoS]
[17 Октября 2015]
The Lord Heroes v1.2[Другое]
[17 Октября 2015]
Versus heroe Arena 1.0 AI[Arena]

5 лучших по кол-ву добавленных статей
[ Duosora ] [ 58 ]
[ Messenger ] [ 52 ]
[ Bru ] [ 39 ]
[ Pand@ ] [ 35 ]
[ OrcRider ] [ 27 ]

Наша кнопка
Warcraft3FT.info - Всё для Warcraft 3 и DotA

Другие варианты

Статистика

Материалы:
Новости: 1010
Файлы: 8668
Статьи: 680
Картинки: 8256
Форум: 30520/954989
Комментарии: 58094
Copyright © 2006 - 2021 Warcraft3FT.info При копировании материалов c сайта ставьте, пожалуйста, активную обратную ссылку на нас • Design by gReeB04ki ©
Хостинг от uCoz