J
expert
offline
Опыт:
48,747Активность: |
Оптимизация
Рассчитывается на то, что читатель нормально - хорошо владеет триггерами. Данная статья расскажет, как довести свой код до совершенства, отполировать и начистить до блеска.
Начнем с переменных: ПеременныеТипы переменныхПеременные это, если иначе сказать, - ссылки. Типов переменных очень много, даже больше чем стандартных которые находятся в “редакторе переменных” в “редакторе триггеров”. Но основных типов только 6:
Есть типы переменных (не integer'ы), которые тоже могут принимать целое значение, например “unittype”, хотя это переменная не является integer'ом, она ее заменяется для типов юнитов (для удобства) (она равна например ‘A000’, ‘B05O’ и т.д. (в 256 значной системе исчисления)) integer-ы в варе могут быть в 4-ех системах исчисления:
Когда вы пользовались триггерами, вам было необходимо при математических действиях, чтобы внести в целую переменную реальное число – вам надо было перевести его сначала в целое с помощью определенной триггерной функции в типе “Преобразование”, это все правильно. Т.к. чтобы внести в целое число реальное, надо сначала реальное представить в виде целого. НО: При внесении в реальную переменную целого числа, его надо было переводить в реальное число, в джазе этого делать необязательно, т.к. по сути это разные типы переменных.. но вар автоматически использует приведение целово к реальному числу, а тратить время, чтобы перевести его в реальное с помощью функции I2R(I) необязательно. А вот при возвращении функцией, какое либо число real/integer приведения не происходит, потому нужно точно указывать тип переменой. ОбнулениеУ переменых типа integer, real, boolean есть некая “область видимости”, по выходу из этой области – переменная удаляется. У всех остальных переменных этой “области” нет, а значит, их нужно обнулять вручную Код:
Если ее не обнулить, то они останется в памяти компьютера навсегда по протяжению игрового процесса, и тем самым в большом количестве будет вызывать тормоза! функцииИх виды:
Одна из многих проблем этих функций в том, что в них создаются локальные переменные типа “handle” и не обнуляются в конце, и вызывают лишнее загрязнение памяти (иначе - "утечки"), и именно поэтому большинство джаззеров (включая меня) настроены против BJ. Я всегда пользуюсь native функциями (в 80% случаев), а юзаю BJ - только в том случае, если они не вызывают утечек, и содержат много других используемых функций (потому что иначе пришлось бы один и тот же массив функций писать в большинстве кодов, и это приводило бы к “очень большому коду” и “трудно читаемости”). Пример таких функций, которые вызывают утечки это практически все BJ функции по изменению multiboard-а - они не только громоздкие (из-за того, что сделаны двойными циклами т.к. рассчитаны на указание в их параметры нуля), но еще вызывают утечку из-за не обнуленной переменой - потому их можно сократить… К примеру, вот так:Обычная BJ функция по изменению текста в ячейке multiboard-а: Код:
Это то как это можно сократить - жертвуя относительно немногим: Код:
И есть еще много BJ функций, которые сами по себе независимо вызывают утечки. Также все функции требуют время на вызов, чем больше это время тем сильнее будет подтормаживать игра во время их вызова, но правда это время измеряется долями микросекунды, так что в не динамических триггерах это имеет малое значение, ну а вот в динамических...: Это означает что плохо вызывать функции, которая вызывает другую нужную нам функцию. Например: Код:
По сути это функция делает то же что и в ней содержащаяся, так какой смысл юзать функцию DestroyEffectBJ? Если можно напрямую воспользоваться DestroyEffect. И это весьма слабый пример, некоторые функции вызывают функции нужные нам только через 2-4 функции: Например: Нам нужно создать одного юнита - мы это делаем на триггерах с помощью специальной функции - CreateNUnitsAtLoc: Код:
Обратите внимание, что там все происходит через цикл, в котором используется сама функция создающая юнита, а именно CreateUnitAtLocSaveLast: Код:
В ней уже используются if-ы, нормальное создание юнитов уже выполняется с помощью нормальной native функции - CreateUnitAtLoc: Код:
И что получается? В триггерах мы пользовались функцией для создания юнита, которая через 2 функции, через цикл, и через if и создавала юнита, а все можно было сделать сразу с помощью одной native функции! Огромное количество подобных недочетов в периодических триггерах может тормозить игру. Просмотр переменой занимает намного меньше времени чем вызов функции, как этим можно воспользоваться, а очень просто, если у вас в коде во многих местах вызывается одна и та же функция, то намного лучше было бы с самого начала занести эту функцию в переменную, а потом использовать именно переменную. Самый простой пример: Код:
Эта функция делает GetTriggerUnit() мертвым, если жизней меньше 500, или отнимает 500, если у него жизней больше 500, но меньше 1000 Обратите внимание, как много в ней функций, практически половину из них можно убрать, используя переменные. Вот, что должно получиться: Код:
Обратите внимание, что мы сократили функции GetTriggerUnit и GetUnitState, и, в общем, количество функций уменьшилось на 8. Да и текст принял вполне читаемый вид (Это тоже имеет довольно важное значение). Игровые объектыМы рассмотрели переменные, и как их обнулять, переменная - это ссылка на определенный игровой объект, но обнуление переменой не значит что и удалится объект по ссылке из переменой, объект нужно удалять отдельно (ДО(!) обнуления переменой) с помощью специальной функции. т.к. иначе после обнуление переменой ссылка на объект потеряется, и его нельзя будет удалить. К примеру эффекты удаляются с помощью DestroyEffect Юниты: RemoveUnit Декорации: RemoveDestructable Точки: RemoveLocation Триггеры: DestroyTrigger Таймеры: DestroyTimer и т.д. Правда некоторые объекты не удаляются... т.е. DestroyTrigger не полностью удаляет триггер... перед этим еще нужно удалить все его действия выключить подождать секунду и потом удалить.. и обнулить Но даже это может не спасти от утечек.. потому между выбором - таймер или периодический триггер - выбирайте таймер. Например - спел... в нём создается много эффектов, и не удаляются, а это значит, что информация по положению эффекта, по его типу остается в памяти на всегда. Обращаться с локальными переменными между функциями очень трудно, т.к. обязательно нужно следить, чтобы ссылка на этот спецэффект не была потеряна, и чтобы он рано или поздно был удален из игры, тоже касается юнитов (в основном имеется в виду "дами-юниты"). Самая распространенная утечка - это точки, они очень слабо загрязняют память компа, но их так много! что это одна из серьезных утечек. Например, давайте разберем простое периодическое перемещение юнита: на триггерах эта выглядит просто... событие в период 0.04, и в нем движение юнита в какую не было сторону, НО большинство триггерщиков используют в подобных перемещениях полярные координаты - что очень ошибочно, т.к. они вызывают куча утечек, вот к примеру разберем периодический триггер с действием: Боевая единица - Move unit instantly to ((Position of unit) offset by len towards ang degrees), facing ang degrees где: unit - переменная перемещаемого юнита len - расстояние, на которое будет перемещен юнит ang - Под каким углом Этот триггер переведенный в джазз: Код:
давайте обращу внимание на недочеты в этой функции:
Исправим все эти недочеты: Код:
Обратите внимание на Cos(udg_ang*0.0174) и тот же Sin, почему так? а просто BJ функция Код:
использует обычные градусы, и умножает на bj_DEGTORAD, чтобы перевести их в радианы, а сама функция Cos уже и находит косинус радиан этого угла, но т.к. bj_DEGTORAD - константа, то можно сразу вместо нее записать численное значение, это ~ 0.0174. И смотрите: нет ни одной точки, если есть выбор между юзанием точек или ее координат, я выбираю координаты. Для все native функций работающих на точках, есть аналоги работающие на их координатах, например: SetUnitPositionLoc и SetUnitPosition, CreateUnitAtLoc и CreateUnit, и т.д. И что получилось? код начал работать в два раза быстрее и без утечек. Любой объект надо обязательно создавать в переменной, если мы будет создавать ее как оператор функции, например, так: Код:
То ссылка на точку GetUnitLoc(udg_unit) затеряется, и мы не сможем указать какой объект надо удалить, потому надо делать все через переменные: Код:
ПроверкаКак же проверить, много утечек или нет? Очень просто, каждый игровой объект имеет уникальный номер, его можно получить использую модуль RB, с помощью функции: Код:
Уникальные номера всех создаваемых объектов типов handle выстраиваются по порядку, если какой-то из объектов удаляется, его уникальный номер освобождается, и его место займет следующий созданный объект. Потому если периодически будет создаваться какой-нибудь объект и на экран будет выводиться его номер, и после объект будет удаляться, то выведенное сообщение на экран было бы равно количеству объектов на карте. Например, создаем периодический триггер в 0.1 сек., создаем в нем все действия, что было сказано выше: Создайте триггер под названием "check", удалите все содержимое, и вставьте это: Код:
На экран будут выводиться цифры... начиная с "миллиона с копейками", значение имеет не величина этого номера, а его то насколько он изменяется в процессе игры... Однако не стоит так на этом засижеватся... избавится от всех утечек всеравно не возможно... такчто не принимайте это слижком всерьез Отредактировано Jon, 10.10.2006 в 13:58. |
06.10.2006, 14:51 | #1
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
FoK_KruGer
<3 Vocal Trance
offline
Опыт:
19,540Активность: |
Цитата:
О_о знакомая вещь ;) Jon - добавь как можно сократить "Событие" ( когда триггер в код переводишь ) НУ а так - моладэц и т.п. О_о прочитал 2-й раз --- стало еще понятнее.... щас 3-й... Отредактировано FoK_KruGer, 06.10.2006 в 16:23. |
|
06.10.2006, 16:10 | #2
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Sergey
Старейший
offline
Опыт:
43,563Активность: |
Jon, отличная статья. Поздравляю :).
|
06.10.2006, 16:18 | #3
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Reptai1
offline
Опыт:
2,293Активность: |
Да статья супер... Вот и нашлись ответы на мои вопросы! |
06.10.2006, 17:05 | #4
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Кет
offline
Опыт:
111,234Активность: |
я не джассер, но Джону всё равно кланяюсь за сие творение... |
06.10.2006, 21:06 | #5
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
exploder
iOS zealot
offline
Опыт:
19,394Активность: |
В следущий раз поменьше катов... они раздражают... Статья хорошая... |
06.10.2006, 21:16 | #6
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
bugmaker
invulnerable
offline
Опыт:
2,282Активность: |
статья отличная. Джон - маладец - так подробно расписал основные недочеты и часто случающиеся ошибки, по вине неаккуратной оптимизации =) и вообще будет полезна многим людям. Ты незря потратил время написав статью ;) |
07.10.2006, 00:21 | #7
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
remal
нечто
offline
Опыт:
2,087Активность: |
гм.... читал по диагонали, но тем не менее не уидел самого главного:
надо трезво оценивать с какими утечками надо бороться, а с какими нет! если у нас пусть даже 50 утечек в одном месте, но это место вызывается 2 раза за игру, то тут даже и заморачиваться не стоит. а вот если у нас переодический триггер с периодом 0.1, то он должен быть идеальным. ЗЫ: ну и вопросик к статье: где результаты тестов? |
07.10.2006, 07:00 | #8
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
WarCrafter
Покидаю вместе с форумом
offline
Опыт:
28,980Активность: |
Спасибо мне за то. что правил ошибки, опечатки, доводил до более приемлимого вида тэговое оформление статьи :bis: |
07.10.2006, 07:06 | #9
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,747Активность: |
Sergey пасиба, я сторалсо!:) никто не помогал... даже WarCrafter-ер
Цитата:
просто я нелюблю когда больше количество статьи занимют коды.. потому и сунул их в каты Jon добавил: WarCrafter лжот:p Отредактировано WarCrafter, 07.10.2006 в 07:14. |
|
07.10.2006, 07:08 | #10
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
WarCrafter
Покидаю вместе с форумом
offline
Опыт:
28,980Активность: |
ну я чуть-чуть помог )
WarCrafter добавил: Цитата:
WarCrafter жжот ;) |
|
07.10.2006, 07:15 | #11
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,747Активность: |
remal, я написал "как?", что касается "где?" - это уже дело читателя, вот я например даже единствено используемый тригер в игре вмегда довожу до совершенства.
Отредактировано Jon, 07.10.2006 в 12:09. |
07.10.2006, 07:39 | #12
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
DioD
offline
Опыт:
45,184Активность: |
CODE тип константа, не может быть массивом создан или удалён в игре.
Еще его можно вернуть как интегер но вот интегер как код вернуть не получиться.
про строку бред
local string S = ""
здесь набора символов нет, но это строка, конечно цеплятся к словам это глупо, но тут это явная ошибка
про реалы и интегеры
.0 != 0
для доказательства используем следующую конструкцию.
constant function ZZ takes integer X returns real
return X return .0 endfunction constant function ZZ takes integer X returns real
return X return 0 endfunction Так что то что написано в описании статьи будет долго и счастливо вызывать глюки особенно при ретурн баге и кеше.
У всех типов переменных кроме handle есть некая “область видимости”
Сюда относятся строки и код, так как переменные делятся на общие и локальнологические.
локальнологическими могут быть только локальные переменные базового типа INTEGER
так как строка и реал будут сохранены в кеш движка после первого использования навсегда а вернее до появления некоторого количества других строк и реалок они не могут быть локальнологическими.
В качестве доказательства этих строк можете проанализировать метод обработки строк ридсетапом ну или вот такой функцией.
constant function I2SRB takes integer VALUE returns string
return VALUE return "" endfunction 0-21 создаются в БЖ 21-до 66584 строки создающиеся уже скриптом вашей карты
скажу прямо это еще не все недочёты по статье, достаточно взять команду
DestroyTrigger(X) 1 Не останавливает триггер
2 Не удаляет действие 3 При вызове функций через вэйты крашит вар 4 При вызове функций после вэйтов всем объектам выдаёт один хэндл Таким образом перед удаление тригера необходимо удалить все его действия выключить подождать секунду удалить включить и потом обнулить. |
07.10.2006, 09:14 | #13
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
exploder
iOS zealot
offline
Опыт:
19,394Активность: |
DioD, в большинстве случаев происходит приведение integer к real, за исключением случая когда эти типы возвращаются функцией, который ты собственно и привел.
--- Еще я бы посоветовал добавить в статью, пару слов о важности очистки уже не используемых данных в кэш. |
07.10.2006, 10:54 | #14
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
DioD
offline
Опыт:
45,184Активность: |
интересно зачем тогда я привёл доказательства именно эту функцию |
07.10.2006, 10:58 | #15
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
exploder
iOS zealot
offline
Опыт:
19,394Активность: |
Просто вся постановка твоей фразы и этот контр пример, приводила к мысли, что приведение этих типов вообще не происходит... |
07.10.2006, 11:05 | #16
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
dk
offline
Опыт:
61,843Активность: |
Разместил на сайте. +1000ехр
Посмотреть можно тут. |
10.10.2006, 11:23 | #17
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
DioD
offline
Опыт:
45,184Активность: |
поздравляют с отправкой статьи содержащей в себе грубые ошибки что искать придёться долго и счастливо. даже очень счастливо. |
10.10.2006, 11:50 | #18
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
NETRAT
offline
Опыт:
83,762Активность: |
DioD набор может быть пустым =) вообще, трактовка строки достаточно распространенная...
Если уж быть точным, то строка - это тот же обьект, который хранится в таблице строк, из которой он не может быть удален, то есть чем меньше обьектов типа строка в карте, тем меньше лаги Статья отправлена в премодерацию, предлагаю все это обмозговать еще раз ... |
10.10.2006, 12:27 | #19
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,747Активность: |
хоть бы сказали че нетак....:(
все что было сказано выше уже поправлено, если нед то напимните еще раз...:) |
10.10.2006, 12:40 | #20
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|