В очередной раз меня спросили о том, почему при срабатывании подбора предмета начинает тормозить игра?
И вот я решил немножко пройтись по.. событиям. В большинстве своём события это весьма классная вещь, но есть у них один «изъян» — при срабатывании события происходит запуск нового экземпляра присоединённой функции. И как следствие их может быть запущено огромное количество, если событие происходит непрерывно.
Яркий пример события которое используется очень часто — касание. Возьмём для примера разместим парт на пустом плейсе и добавим в него скрипт реагирования на касание:
local function onPartTouch(otherPart) print("Color...") end script.Parent.Touched:Connect(onPartTouch)
Ну я сделаю три парта: полупрозрачный через который можно пройти, платформу по которой можно пройтись и обычный блок для касания.
Запускаем и пройдусь слева на право, через синий, по красном и упрусь в зелёный. И что же мы видим в логах?
А ведь мы рассчитывали что оно сработает только один раз, не так ли? И какой из этого можно сделать вывод? А очень простой, если в функции будет что-то тяжёлое или этого будет много или будет куча игроков, то и вся игра начнёт тормозить. Почему? Потому что сейчас тут очень простая функция, которая моментально выполняется, а представим, что она выполняется.. хотя бы секунду?
Чуть чуть изменим скрипт:
local function onPartTouch(otherPart) print("Blue..Start") wait(1) print("Blue...End") end script.Parent.Touched:Connect(onPartTouch)
И что же мы видим? Что теперь окончание скрипта происходит после того, как уже несколько раз начался новый скрипт. Например, в самом начале выполнился 20 раз запуск синего и даже 4 запуска красного, прежде чем первый синий закончил работу. Т.е. было запущено аж 24 экземпляра данных скриптов!!! до того, как завершил работу первый запущенный скрипт.
Как же этого избежать? Есть два варианта
- отсоединить функцию после срабатывания события
- использовать какой-то флаг того, что событие в процессе обработки
Первый вариант не рассматриваю, потому как это полная задница будет в коде.
Второй вариант прост в реализации.
- создаём переменную с областью видимости вне функции
- в функции проверяем её значение
- если значение разрешает, запускаем код и меняем значение переменной на запрет
- по окончании функции возвращаем значение функции на разрешение
Итак, модернизируем скрипты:
local debounce = false -- эта переменная будет видна в любой объявленной далее функции, -- если там не объявить свою с таким же именем local function onPartTouch(otherPart) if debounce then return end -- проверяем наш флаг debounce = true -- устанавливаем запрет print("Blue..Start") wait(1) print("Blue...End") debounce = false -- возвращаем разрешение end script.Parent.Touched:Connect(onPartTouch)
и смотрим на логи:
Бинго! Можно сказать что мы сделали всё хорошо, хоть и не идеально! Но главное, тело функции отработало касание по одному разу, а не десятки.
Что нужно для идеального варианта?
- выяснить и запоминать какой игрок коснулся
- если этот игрок касался — запрет на исполнение
- если коснулся новый игрок — выставить запрет и запустить функцию
Делать это чутка сложней. Тут уже нужна будет не булева переменная — флаг, а таблица. И хранится в ней нужно имена игроков.
Оставлю ка я это к вашему размышлению. Желающий может разместить свой вариант решения в комментариях.