ГлавнаяБлогКарты Warcraft 3Гайды для первой ДотыГайды для Доты 2 [ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Cвои функции на jass +" Утечки или Blizzard - Говнокодеры"
Дата: Вторник, 18.10.2011, 00:15 | Сообщение # 1
Генералиссимус
Ньюсмейкер
Сообщений: 2438
Награды: 8
Репутация: 85
Утечки или Blizzard - Говнокодеры

Что нам необходимо знать, для понимания этой статьи?
1) Знание ЛЮБОГО языка программирования на уровне ПТУ
2) Уметь пользоваться WinMPQ (Скачать)
3) Знать что такое локальные переменные.

Конечно же те, кто прочитали заголовок статьи заинтересовались словосочетанием: «Blizzard – говнокодеры». Наверняка подумали, что это понт какой-то, однако речь пойдет именно о косяках сделанных самими разработчиками WarCraft III.

Кому эта статья полезна?

Эта статья полезна тем, кто начал изучать jass, тем, кто хочет научится писать собственные функции и не менее интересна тем, кто хочет делать карту (пусть и на ГУИ + не хочет писать своих функций) без утечек.

Зачем писать свои функции для карт WarCraft III ?

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

Синтаксис функции и пример её использования

И так… разберемся на примере:

Необходимо создать магический спел, который отнимает ровно половину от жизней цели.
Т.е. У героя 1000 хитов, делаем спел – у героя 500, делаем ещё раз – 250 и тд.
Конечно, такое можно сделать триггером и даже без переменных, но мы рассмотрим, как это сделать с помощью своей функции.

Никаких сложных действий тут не будет, вся функция будет представлять из себя всего 1ну строку, но в конце статьи эта строка превратится в несколько.

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

Вот, как выглядит функция:
function TestFun takes unit U1 returns nothing
call SetUnitLifeBJ( U1, ( GetUnitStateSwap(UNIT_STATE_LIFE, U1) / 2.00 ) )
endfunction

function – оператор, который сообщает компьютеру, что мы начинаем писать функцию.
Endfunction – оператор, который сообщает компьютеру, что мы закончили функцию.
TestFun – Имя функции, оно может быть каким угодно, но обязательно без пробелов и начинаться с буквы, нельзя использовать русские буквы.
Takes – Оператор, который сообщает компьютеру, что далее идут те данные которые необходимы для выполнения функции.
В нашем случае это Юнит, чьи хитпоинты делятся пополам.
Unit – Тип переменной
U1 – Название переменной, оно может быть каким угодно, но обязательно без пробелов и начинаться с буквы, нельзя использовать русские буквы.
Returns - Оператор, который сообщает компьютеру, что далее идут те данные которые можно получить после выполнения функции. В нашем случае не будет никаких выходных данных.
Вот если бы задачей нашей функции было бы решить уравнение, то выходной информацией был бы корень уравнения.
Nothing – дословный перевод: «ничего». Т.е. оператор, который говорит, что ничего делать не надо.
Далее идет тело функции, в котором изменяется кол-во жизни для выбранного юнита.

Как узнать какие функции есть и как их писать?

Самый простой, на мой взгляд, способ – написать триггер на ГУИ, а затем при помощи меню «правка» -> конвертировать в текст.
Таким образом, можно создавать триггер с одним только действием и глянуть, как он пишется буквами.

Как использовать написанную функцию?

Наш триггер будет выглядеть следующим образом:

Событие:
Юнит – приводит способность в действие
Условие:
Ability Comparison – (Ability being cast) = наша способность
Действие:
Кустом скрипт: call TestFun(GetSpellTargetUnit())

сall – это оператор, который сообщает, что далее идет какое либо действие (не присвоение), помните, что jass регистрозависимый и операторы call и set пишутся с маленькой буквы ВСЕГДА.

TestFun – имя нашей функции
GetSpellTargetUnit() – юнит чьи хиты мы делим.
В данном случае, это юнит, который стал целью нашего заклинания.

Всё?

Нет, теперь поговорим об утечках. Казалось бы чему тут и куда утекать?
Может вы мне не поверите, но тут 2 утечки) Одну нашли? Давайте её исправим.

Те кто с утечками борются, но не делают своих функций сразу же догадались, что утечка происходит из-за переменной U1. Обратите внимание:

function TestFun takes unit U1 returns nothing
call SetUnitLifeBJ( U1, ( GetUnitStateSwap(UNIT_STATE_LIFE, U1) / 2.00 ) )
endfunction

У нас есть переменная типа unit, она локально создается для каждого выполнения функции. Т.е. сколько раз мы используем спел, столько переменных типа юнит будет висеть у компьютера в памяти. Для того чтобы это исправить, переменную необходимо обнулять, для этого добавим:
Set U1 = null
И получится:
function TestFun takes unit U1 returns nothing
call SetUnitLifeBJ( U1, ( GetUnitStateSwap(UNIT_STATE_LIFE, U1) / 2.00 ) )
set U1=null
endfunction
Вот, одну утечку мы устранили. Кто сможет найти вторую, тот смело может взять пирожок с полки, да самый вкусный.

Blizzard – Говнокодеры

По заголовку можно догадаться, что как раз таки во второй утечке виноваты создатели варика.
Дело в том, что те операторы, на конце которых приписано «BJ» это тоже функции! Только их создавали не мы, а программисты из Blizzard.
В нашей функции есть такая, это: SetUnitLifeBJ
Но как же заглянуть во внутрь этих функций?
Для этого вам и понадобится WinMPQ (Скачать).
1) Запускаем WinMPQ
2) Открываем файл из папки варика War3.mpq
3) Находим там файл Scripts\Blizzard.j

4) Открываем с помощью блокнота.

И так, перед вашими глазами все встроенные функции «нагишом».
Теперь давайте узнаем, из чего состоит SetUnitLifeBJ. Для этого откройте поиск по файлу (ctrl+F), введите в окошко поиска: SetUnitLifeBJ.

Вы найдете это:
function SetUnitLifeBJ takes unit whichUnit, real newValue returns nothing
call SetUnitState(whichUnit, UNIT_STATE_LIFE, newValue)
endfunction

Ну как?) Нашли вторую утечку?
Как и в нашей функции, здесь создается переменная типа unit и с названием whichUnit
Она не обнуляется.
В итоге, когда вы изменяете какому либо юниту хитпоинты стандартным триггером, выполняется именно этот код, который засерает память ПК.

Как решить эту проблему?

Вообще есть 3 способа:
1)забить на все эти утечки и идти с миром.
2)Исправить код прямо в этом файле и переархивировать его. Вариант хорош, но ведь это исправит ситуацию лишь у вас на компе, но не у всех.
3)Написать свою такую же функцию без утечки. Этим мы и займемся.
Теперь надо подумать как же так переделать функцию, что бы она у нас работала без утечек и выполняла нужную нам задачу.
В данном примере всё элементарно, мы просто заменяем функцию SetUnitLifeBJ на SetUnitState.
Как мы видим, SetUnitState использует всего 3 параметра:
whichUnit – Юнит, для которого изменяется что-то
UNIT_STATE_LIFE – параметр юнита, который изменяется, в данном случае обозначает текущее кол-во хитов. (его можно заменить на UNIT_STATE_MANA (если необходимо работать с маной юнита))
newValue – число, к которому будет приравниваться кол-во хитпоинтов.

В результате, если мы заменим SetUnitLifeBJ на SetUnitState, то получим:
function TestFun takes unit U1 returns nothing
call SetUnitState( U1,UNIT_STATE_LIFE, ( GetUnitStateSwap(UNIT_STATE_LIFE, U1) / 2.00 ) )
set U1=null
endfunction

Вот так вот :)
Устранили обе утечки и теперь у нас есть собственная функция, которая делит жизни юнита пополам и при этом не создает утечек памяти.

Вся проблема BJ функций в том, что парой используются через чур запутанные функции, и переменные в них никогда не обнуляются.

Для примера «запутанной функции» посмотрим на простейшую функцию (создать юнита):
CreateNUnitsAtLoc
Заметили, что нету BJ? Да-да Близы не все свои функции заклеймили, смотрим, что там внутри:
function CreateNUnitsAtLoc takes integer count, integer unitId, player whichPlayer, location loc, real face returns group
set bj_lastCreatedGroup = CreateGroup()
loop
set count = count - 1
exitwhen count < 0
call CreateUnitAtLocSaveLast(whichPlayer, unitId, loc, face)
call GroupAddUnit(bj_lastCreatedGroup, bj_lastCreatedUnit)
endloop
return bj_lastCreatedGroup
endfunction

Тут целый винегрет )
Игрок, точка.
Код в принципе ясен и он будет полезен при создании нескольких юнитов, а если нам нужен всего один? Тогда тут лишний цикл, лишние операторы для работы с группой.
Думаете всё? А хрен там:
CreateUnitAtLocSaveLast это тоже функция BJ:

function CreateUnitAtLocSaveLast takes player id, integer unitid, location loc, real face returns unit
if (unitid == 'ugol') then
set bj_lastCreatedUnit = CreateBlightedGoldmine(id, GetLocationX(loc), GetLocationY(loc), face)
else
set bj_lastCreatedUnit = CreateUnitAtLoc(id, unitid, loc, face)
endif

return bj_lastCreatedUnit
endfunction

Зрелище не для слабонервных, не правда ли? ))
А знаете, как всё это исправить?
Создать свою функцию! ))
Даже если она будет выглядеть так:

function MyFun takes player id, integer unitid, location loc, real face returns unit
set bj_lastCreatedUnit = CreateUnitAtLoc(id, unitid, loc, face)
endfunction

То работать будет быстрее и наделает чуть ли не в двое меньше утечек. (но работать будет для создания одного юнита. Для адаптации к производству нескольких юнитов, можно убрать «set bj_lastCreatedUnit =»
И запихнуть в цикл:
call CreateUnitAtLoc(id, unitid, loc, face)
Вообще
«set bj_lastCreatedUnit =» необходимо писать, только если вы хотите в триггере распознать этого юнита, как последнего созданного (LastCreatedUnit()). Если вам это не надо, то функция может выглядеть так:

function MyFun takes player id, integer unitid, location loc, real face returns nothing
call CreateUnitAtLoc(id, unitid, loc, face)
endfunction

Если устранять утечки, то так:

function MyFun takes player id, integer unitid, location loc, real face returns nothing
call CreateUnitAtLoc(id, unitid, loc, face)
call RemoveLocation(loc)
set loc = null
set id = null
endfunction

Вот и всё. Буду рад если кто-то доработает функцию в создание группы юнитов так же без утечек. Поставлю + в репу т.к. на wc3 не знаю, кто кроме меня эту статью поймет (

Как правильно устранять утечки памяти?

Для этого нужно понимать, что существуют в памяти компьютера объекты варика это юниты, регионы, декорации, спецэффекты. Для работы с этими объектами мы создаем или же код автоматически генерирует переменные, которые являются ссылками или ярлыками к тем или иным объектам.
Т.е. мы создали переменную типа unit и назвали её U1. Теперь те действия, что мы адресуем к U1 будут произведены с юнитом, на который ссылается U1.
Однако, если мы обнулим U1, (set U1 = null), То мы удалим сам ярлычок, но никак не объект.
В большинстве случаев это и не надо, но если в переменной был юнит, которого убили или надо убить, или он больше нам не нужен, то сперва необходимо удалить юнита через команду:
call RemoveUnit(U1)
и только потом
set U1 = null
Точно так же с точками, декорациями и спецэффектами. Особенно со спецэффектами и точками, т.к. в большинстве случаев они используются лишь на 1но действие триггера.

Такие типы переменных, как целочисленная, реальная, текстовая – не обнуляются, оно в принципе и не нужно, но если число или текст огромны, то лучше к тексту присвоить пустую строку, а к числу 0.
set text = ‘’
set int = 0
set real = 0.00
Эти переменные не так опасны, потому и обнулять их нет необходимости.
Прикрепления: 2307905.jpg (4.9 Kb) · 6665751.jpg (83.5 Kb)
 
Дата: Вторник, 14.02.2012, 00:33 | Сообщение # 2
Генералиссимус
Проверенные
Сообщений: 2584
Награды: 6
Репутация: 58
Плюсую, но, думаю, мало кому из посетителей сайта это интересно :(
 
Дата: Вторник, 14.02.2012, 01:06 | Сообщение # 3
Генералиссимус
Модераторы
Сообщений: 12997
Награды: 22
Репутация: 141
Да лааадно? Мне интересно =)

это Ксопик!
 
Дата: Вторник, 14.02.2012, 13:59 | Сообщение # 4
Генералиссимус
Проверенные
Сообщений: 2259
Награды: 9
Репутация: 70
Мне понравилось))) Статья хорошая!!!!!!!!

Тот кто владеет информацией, тот владеет миром.
 
Дата: Среда, 15.02.2012, 09:44 | Сообщение # 5
Генералиссимус
Проверенные
Сообщений: 5123
Награды: 13
Репутация: 72
Quote (DonLaonda)
Пожалуй, именно косяки самих разработчиков и являются мотивом к написанию своих собственных функций. Некоторые скажут, что делают свои функции, чтоб упростить себе кодинг и расширить возможности стандартного редактора, это тоже верно.

Ага, хотите сказать можно сделать свою систему диалогов, как, например в Готике? Ну, знаете, чтобы не париться с переменными, нажал на диалоговую кнопочку, нпс сказал текст, а она - исчезла? :Q


Телеграм @Tshkn
Мой YouTube-канал Se Squared
 
Дата: Пятница, 02.03.2012, 12:18 | Сообщение # 6
Лейтенант
Проверенные
Сообщений: 45
Награды: 0
Репутация: 0
Спасиб за статью, буду учится)
 
Дата: Четверг, 08.03.2012, 21:21 | Сообщение # 7
Admin
Администраторы
Сообщений: 15097
Награды: 43
Репутация: 188
У нас еще есть много полезных статей по картостроению, их список в первой теме данного подраздела форума

Warcraft 3 - это уже легенда
WC3 - это мини-легенда
Дота - это альтернативный путь развития варкрафта
 
Дата: Среда, 28.03.2012, 00:54 | Сообщение # 8
Генералиссимус
Проверенные
Сообщений: 2259
Награды: 9
Репутация: 70
Quote (DewidWill)
свою систему диалогов, как, например в Готике?

Свою систему диалогов и вообще что ни будь свое (интерфейс) это DGUI
Штука оч сложная, но с ней все можно сделать! хоть выбор оружий как в GTA ^_^ :)


Тот кто владеет информацией, тот владеет миром.
 
Дата: Среда, 28.03.2012, 13:20 | Сообщение # 9
Генералиссимус
Проверенные
Сообщений: 5123
Награды: 13
Репутация: 72
Проще свой движок создать.

Телеграм @Tshkn
Мой YouTube-канал Se Squared
 
Дата: Вторник, 22.11.2016, 12:19 | Сообщение # 10
Рядовой
Пользователи
Сообщений: 14
Награды: 0
Репутация: 0
мне нужны 3 ответа что бы создать тему по этому напишу 3 суда осталось 1
 
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск: