# Лучшие практики

Здесь собрана часть советов, которые были использованы нами при создании скриптов. Многие из них повышают производительность скриптов, улучшают его внешний вид и читабельность.

# Пользуйтесь таймерами

Иногда нам необходимо делать какое-либо действие раз в какой-то промежуток времени. В этом нам помогут функции setTimeout и setInterval.

let exampleScript = {};

exampleScript.OnScriptLoad = () => {
    setInterval(() => {
        console.log('Это сообщение будет выводиться раз в 1.5 секунды!');
    }, 1500);
};

exampleScript.OnEntityCreate = (entity) => {
    console.log(entity.GetClassName());
    
    // В данном случае таймаут нужен из-за того, что после создания Entity
    // у него не заполнено свойство GetUnitName, что накладывает
    // определенные ограничения на скрипты.
    
    // Поле заполняется ровно через 1 тик и мы уже можем получить
    // доступ к нему в теле таймаута.
    setTimeout(() => {
        console.log(entity.GetUnitName());
    }, 33);
};

RegisterScript(exampleScript);

# Меню и стрелочные функции

Чтобы код было легче поддерживать, а его читабельность была на высоком уровне рекомендуем использовать стрелочные функции при работе с элементами меню

ПЛОХО

let exampleScript = {};

let menuElement = Menu.AddToggle(['Example Script'], 
        'Example Toggle', false);
menuElement.SetNameLocale('ru', 'Пример элемента-переключателя');
let menuElementValue = menuElement.GetValue();
menuElement.OnChange(function(state) {
    menuElementValue = state.newValue;
});

RegisterScript(exampleScript);

ХОРОШО

let exampleScript = {};

let menuElementValue = Menu.AddToggle(['Example Script'], 
        'Example Toggle', false)
    .SetNameLocale('ru', 'Пример элемента-переключателя')
    .OnChange(state => (menuElementValue = state.newValue))
    .GetValue();

RegisterScript(exampleScript);

# Пользуйтесь Engine.OnceAt

Зачастую не имеет смысла вызывать, предположим, EntitySystem.GetHeroesList каждый тик. Если скрипт не требует частого обновления данных об героях, используйте технику "холодного" кэширования.

ПЛОХО

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

let exampleScript = {};

exampleScript.OnDraw = () => {
    for (let hero in EntitySystem.GetHeroesList()) {
        // ...
    }
};


RegisterScript(exampleScript);

ЛУЧШЕ

Мы сохраняем лист героев в OnUpdate, а затем используем его в OnDraw

let exampleScript = {};

let tickHeroesList = [];

exampleScript.OnDraw = () => {
    for (let hero in tickHeroesList) {
        // ...
    }
};

exampleScript.OnUpdate = () => {
    tickHeroesList = EntitySystem.GetHeroesList();
};

RegisterScript(exampleScript);

ЕЩЁ ЛУЧШЕ

Мы сохраняем лист героев в OnUpdate раз в 2 секунды, а затем используем его в OnDraw

let exampleScript = {};

let tickHeroesList = [];

exampleScript.OnDraw = () => {
    for (let hero in tickHeroesList) {
        // ...
    }
};

exampleScript.OnUpdate = () => {
    if(Engine.OnceAt(2)) {
        tickHeroesList = EntitySystem.GetHeroesList();
    }
};

RegisterScript(exampleScript);

# Используйте коллбеки вместо GameRules.IsActiveGame

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

ПЛОХО

Плохо вызывать функцию каждый раз, особенно в OnDraw

let exampleScript = {};

exampleScript.OnDraw = () => {
    if(GameRules.IsActiveGame()) {
        // ...
    }
};


RegisterScript(exampleScript);

ХОРОШО

Храним состояние игры локально, благодаря чему получаем высокую скорость доступа к данным

let exampleScript = {};

let isActiveGame = GameRules.IsActiveGame();

exampleScript.OnGameStart = () => {
    isActiveGame = true;
};

exampleScript.OnGameEnd = () => {
    isActiveGame = false;
};

exampleScript.OnDraw = () => {
    if(isActiveGame) {
        // ...
    }
};


RegisterScript(exampleScript);