Как писать хорошие скрипты
На этой странице собраны практические рекомендации по написанию скриптов Melonity, которые проще поддерживать, отлаживать и безопасно расширять.
Это не абстрактные правила TypeScript. Это рекомендации, основанные на реальном процессе разработки скриптов для Melonity.
Храните логику сценария внутри пространства имен
Не распыляйте логику скрипта по глобальной области видимости. Храните переменные, вспомогательные функции и callback-обработчики внутри отдельного пространства имён.
Если другому скрипту действительно нужен доступ к части вашей логики, экспортируйте только то, что должно быть общедоступным.
let MyScript: ScriptDescription = {};
namespace myScript {
let enabled = false;
export function IsEnabled(): boolean {
return enabled;
}
MyScript.OnScriptLoad = () => {
enabled = true;
};
RegisterScript(MyScript, 'Example');
}let MyScript: ScriptDescription = {};
let enabled = false;
function isEnabled(): boolean {
return enabled;
}
MyScript.OnScriptLoad = () => {
enabled = true;
};
RegisterScript(MyScript, 'Example');Сохраняйте состояние меню простым
Избегайте разделения одной части состояния на несколько переменных, когда достаточно одного четкого значения.
let enabled = Menu.AddToggle(PATH, 'Enable', false)
.OnChange(state => {
enabled = state.newValue;
})
.GetValue();let enabled = Menu.AddToggle(PATH, 'Enable', false)
.OnChange(state => {
enabledValue = state.newValue;
});
let enabledValue = enabled.GetValue();В первой версии состояние сценария легко читается. Вторая версия работает, но усложняет понимание логики.
Совет
Если вам нужны и объект пункта меню, и его текущее значение, используйте понятные имена, например enabledOption и enabled.
Масштабировать значения рендеринга до текущего разрешения
Жестко закодированные значения рендеринга обычно выглядят корректно только на том мониторе, на котором они были созданы.
Размеры шрифта, позиции, отступы и размеры элементов должны масштабироваться относительно текущего разрешения экрана.
const ratio = screenHeight / 1080;
const fontSize = Math.floor(14 * ratio);
const offsetX = 20 * ratio;
const offsetY = 12 * ratio;Это делает макет более последовательным при разных разрешениях.
Пересчитать значения, зависящие от разрешения, в OnScreenSizeChange
Если ваш скрипт зависит от размера экрана, не думайте, что значения останутся правильными навсегда.
При изменении разрешения пересчитывайте шрифты, смещения и значения кэшированного макета внутри OnScreenSizeChange.
let font: Font;
let ratio = 1;
let MyScript: ScriptDescription = {};
namespace myScript {
MyScript.OnScreenSizeChange = (_width, height) => {
ratio = height / 1080;
font = Renderer.LoadFont('Tahoma', Math.floor(14 * ratio), Enum.FontWeight.BOLD);
};
RegisterScript(MyScript, 'Example');
}Важно
Если вы кэшируете значения, зависящие от разрешения, и никогда их не обновляете, результат может выглядеть корректным только до тех пор, пока сценарии не будут перезагружены с помощью F7.
Кэшируйте стабильные ссылки вместо того, чтобы запрашивать их каждый такт.
Не вызывайте EntitySystem.GetLocalHero() каждый OnUpdate, если можете закэшировать результат при запуске игры.
let MyScript: ScriptDescription = {};
namespace myScript {
let myHero: Hero | null = null;
let enabled = true;
MyScript.OnGameStart = MyScript.OnScriptLoad = () => {
myHero = EntitySystem.GetLocalHero();
};
MyScript.OnGameEnd = () => {
myHero = null;
};
MyScript.OnUpdate = () => {
if (!enabled || !myHero) {
return;
}
// Script logic
};
RegisterScript(MyScript, 'Example');
}Это делает сценарий более понятным и позволяет избежать ненужных повторных поисков.
Держать OnDraw сосредоточился на рендеринге
OnDraw вызывается каждый кадр. При более высоком FPS он срабатывает чаще.
Из-за этого:
- держите код рендеринга внутри
OnDraw - переносите тяжёлую логику в
OnUpdate - кэшировать значения, когда это возможно
Это снижает вероятность падения FPS и упрощает отладку пути рендеринга.
Отдавайте предпочтение оговоркам о ранней защите
Защитные условия делают callback-обработчики скрипта более читаемыми и упрощают их дальнейшее расширение.
MyScript.OnUpdate = () => {
if (!enabled || !myHero) {
return;
}
// Main logic
};Обычно это проще, чем оборачивать всё тело callback-обработчика в несколько вложенных блоков if.
Сначала проверьте консоль, если пользовательский интерфейс или рендеринг прерываются.
Если меню выглядит сломанным, рендеринг работает неправильно или перестало работать что-то визуальное, сначала откройте консоль.
Использовать:
F10Во многих случаях ошибку легче выявить с консоли, чем гадать по видимым симптомам.
Краткое содержание
Хорошие сценарии Melonity обычно следуют нескольким простым правилам:
- сохранять логику организованной внутри пространства имен
- сохраняйте состояние простым и читаемым
- масштабирование пользовательского интерфейса и рендеринг в соответствии с разрешением
- обновлять кэшированные значения, зависящие от экрана, при изменении размера экрана
- кэшировать стабильные игровые объекты вместо того, чтобы запрашивать их каждый тик
- использовать
OnDrawтолько для рендеринга - проверяйте консоль перед слепой отладкой