Фрагменты кода
Этот раздел содержит три полезных фрагмента кода для LightScript: счётчики, обработчики состояний и обработчики эффектов.
Класс Meter
Заголовок раздела «Класс Meter»Как продолжение предыдущего раздела, класс Meter предназначен для отслеживания значимых изменений в нестабильных данных счётчиков. Думайте о нём как о педали тормоза для вашего кода — он обеспечивает стабильность даже в сложных зонах. Каждый счётчик хранит настраиваемый массив значений, собранных во время каждого запуска функции обновления. Когда все значения в массиве одинаковы (то есть данные «стабильны»), счётчик обновляет своё значение и запускает связанную функцию обратного вызова для активации эффекта.
| Метод | Описание |
|---|---|
| Meter(size, callback) | Конструктор принимает size и callback.\n\n\n\nsize — количество раз, которое одинаковое значение должно присутствовать, чтобы значение счётчика считалось достаточно «стабильным» для обработки. Большее значение даёт большую точность, меньшее — меньшую задержку.\n\n\ncallback — функция, вызываемая при стабилизации и изменении значения счётчика. |
| Meter.setValue(value) | Вызывайте setValue(value) каждый кадр или каждый раз, когда доступны новые необработанные данные для этого счётчика. |
Процесс сбора данных экрана должен в целом соответствовать следующей структуре:
- Значение engine.vision.meter обновляется на основе новых данных экрана.
- Внутри функции обновления вызывается Meter.setValue(engine.vision.meter) для записи нового значения.
- Meter проверяет свой внутренний массив — если все значения идентичны, он обновляет своё состояние и запускает назначенную функцию обратного вызова.
- Функция обратного вызова затем оценивает конкретные условия с использованием переменных Meter и выполняет код, если условия выполнены.
ВАЖНО:
Не пропускайте использование обратных вызовов. Поскольку функция обновления выполняется непрерывно, она должна оставаться максимально эффективной. Её основная роль — обновлять значения Meter. Избегайте цепочек условных операторов внутри update для проверки счётчиков и запуска эффектов — это приводит к ненужным проверкам при каждом цикле. Вместо этого определяйте эффекты в отдельных функциях и передавайте их в Meter как обратные вызовы.
Переменные Meter
Заголовок раздела «Переменные Meter»При стабилизации Meter предоставляет несколько значений своей функции обратного вызова:
| Переменные | Описание |
|---|---|
| Meter.value | Текущее значение счётчика. |
| Meter.increased | Булево значение, указывающее, увеличилось ли значение при этом обновлении |
| Meter.decreased | Булево значение, указывающее, уменьшилось ли значение при этом обновлении |
| Meter.diff | Абсолютное значение изменения, которое претерпел счётчик при этом обновлении. |
Пример Meter
Заголовок раздела «Пример Meter»Вот базовая настройка с одним счётчиком, включая код класса Meter:
<head> <title>Meter Example</title> <meta description="Step-by-step metering" /> <meta meter="health" tags="vlc,fortnite" x= ".05" y=".9" width=".189" h="70-140" s="40-100" l="40-100" type="linear" /></head>
<body style="margin: 0; padding: 0; background: #000;"> <canvas id="exCanvas" width="320" height="200"></canvas></body>
<script> var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d");
// Initialize meter var healthMeter = new Meter(5, healthEffect);
function update(){ // Update Meter values healthMeter.setValue(engine.vision.health);
window.requestAnimationFrame(update); }
function healthEffect(){ if(healthMeter.increased){ // Trigger effect } else if (healthMeter.value == 0){ // Trigger effect } else if (healthMeter.diff > .3){ // Trigger effect } }
function Meter(size, callback) { this.size = size; this.value = 0; this.diff = 0; this.increased = false; this.decreased = false; var values = [];
this.setValue = function (updatedValue) { // Add and shift. values.push(updatedValue); if (values.length > this.size) { values.shift(); }
// Exit early if we don't have a long-term match. for (var i = 0; i < values.length - 1; i++) { if (values[i] !== values[i + 1]) return; }
// We got here, so we've got a matching value collection. Set variables and execute callback. if (this.value !== values[0]) { this.diff = Math.abs(this.value - values[0]); this.increased = this.value < values[0]; this.decreased = this.value > values[0]; this.value = values[0]; callback(); } }; }
window.requestAnimationFrame(update);
</script><head> <title>Meter Example</title> <meta description="Step-by-step metering" /> <meta meter="health" tags="vlc,fortnite" x= ".05" y=".9" width=".189" h="70-140" s="40-100" l="40-100" type="linear" /></head><body style="margin: 0; padding: 0; background: #000;"> <canvas id="exCanvas" width="320" height="200"></canvas></body><script> var c = document.getElementById("exCanvas"); var ctx = c.getContext("2d");
// Initialize meter var healthMeter = new Meter(5, healthEffect);
function update(){ // Update Meter values healthMeter.setValue(engine.vision.health);
window.requestAnimationFrame(update); }
function healthEffect(){ if(healthMeter.increased){ // Trigger effect } else if (healthMeter.value == 0){ // Trigger effect } else if (healthMeter.diff > .3){ // Trigger effect } }
function Meter(size, callback) { this.size = size; this.value = 0; this.diff = 0; this.increased = false; this.decreased = false; var values = [];
this.setValue = function (updatedValue) { // Add and shift. values.push(updatedValue); if (values.length > this.size) { values.shift(); }
// Exit early if we don't have a long-term match. for (var i = 0; i < values.length - 1; i++) { if (values[i] !== values[i + 1]) return; }
// We got here, so we've got a matching value collection. Set variables and execute callback. if (this.value !== values[0]) { this.diff = Math.abs(this.value - values[0]); this.increased = this.value < values[0]; this.decreased = this.value > values[0]; this.value = values[0]; callback(); } }; }
window.requestAnimationFrame(update);
</script>function Meter(size, callback) { this.size = size; this.value = 0; this.diff = 0; this.increased = false; this.decreased = false; var values = [];
this.setValue = function (updatedValue) { // Add and shift. values.push(updatedValue); if (values.length > this.size) { values.shift(); }
// Exit early if we don't have a long-term match. for (var i = 0; i < values.length - 1; i++) { if (values[i] !== values[i + 1]) return; }
// We got here, so we've got a matching value collection. Set variables and execute callback. if (this.value !== values[0]) { this.diff = Math.abs(this.value - values[0]); this.increased = this.value < values[0]; this.decreased = this.value > values[0]; this.value = values[0]; callback(); } }; }Обработчик состояний
Заголовок раздела «Обработчик состояний»Обработчик состояний — это стек эффектов, специально предназначенный для обработки эффектов, имеющих приоритет над всеми остальными. Если вам нужно, чтобы одна большая доминирующая анимация выполнялась без прерывания до завершения, поместите её в обработчик состояний.
| Метод | Описание |
|---|---|
| StateHandler() | Конструктор не требует аргументов. |
| StateHandler.push(new State()) | Можно поместить новое состояние в стек с помощью push — обработка начнётся немедленно. |
| StateHandler.pop() | Извлекает текущее состояние из стека и начинает обработку предыдущего состояния. |
Любой объект, помещённый в обработчик состояний, обязан реализовывать Process(). Функция Process определяет жизненный срок эффекта в обработчике.
Пример обработчика состояний
Заголовок раздела «Пример обработчика состояний»Ниже показан базовый обработчик состояний вместе с функцией Process, которая должна выполняться внутри функций эффектов:
var stateHdlr = new StateHandler();
function StateHandler() { var stack = []; var state = null;
// Set current state to the top item in the stack var updateState = function () { if (stack.length > 0) { state = stack[stack.length - 1]; } else { state = null; } };
// Allows dev to add effect to state handler this.Push = function (newState) { stack.push(newState); updateState(); }; // Allows dev to remove effect from handler this.Pop = function () { stack.pop(); updateState(); }; // Call the Process function of the current state (effect) this.Process = function () { if (state != null) { state.Process(); } }; }
function update(){ stateHdlr.Process();}this.start = new Date.now()this.elapsed = 0;this.duration = 1000;
this.Process = function () { // Evaluate time since effect start this.elapsed = new Date.now() - this.start; // If the effect has reached its lifespan, remove it from the state handler if (this.elapsed > this.duration) { stateMgr.Pop(); } // If the effect is still running, call the effect draw function this.Draw();};Обработчик эффектов
Заголовок раздела «Обработчик эффектов»Обработчики эффектов отлично подходят для хранения и одновременного рендеринга небольших лёгких эффектов. Каждый эффект в обработчике оценивается при каждом вызове update и удаляется по достижении конца своего жизненного срока.
Обработчик эффектов — это просто массив для хранения эффектов. Это всё.
// Declare effects arraylet effects = [];
// (Not shown) Evaluate Meter, callback function pushes effect into effects arrayeffects.push(new SpecialEffect());
// Iterate through the array, animating a frame for each effectfor (let i = 0; i < effects.length; i++) { effects[i].draw(); // Remove effect if the lifetime has ended if (effects[i].lifetime <= 0) { effects.splice(i, 1); }}ПРЕДУПРЕЖДЕНИЕ: ЭФФЕКТ И СОСТОЯНИЕ
Заголовок раздела «ПРЕДУПРЕЖДЕНИЕ: ЭФФЕКТ И СОСТОЯНИЕ»Обработчик состояний предназначен специально для приоритетных эффектов, что означает необходимость давать ему приоритет при выполнении. В функции update обрабатывайте обработчик эффектов СНАЧАЛА, а затем обработчик состояний. Это отрисует эффект состояния поверх элементов обработчика эффектов.
Вот так:
function update(){ // Evaluate effects handler for (let i = 0; i < effects.length; i++) { effects[i].draw(); if (effects[i].lifetime <= 0) { effects.splice(i, 1); } }
// THEN evaluate state handler stateMgr.Process();}Руководство по захвату USB-данных
Заголовок раздела «Руководство по захвату USB-данных»Сохранение USB-данных — простой процесс, но драйверы, используемые этими программами, известны конфликтами с определёнными системами, иногда отключая USB-порты до удаления программного обеспечения. Распространённое решение — отключить «Безопасную загрузку» в настройках BIOS/UEFI. Перед этим настоятельно рекомендуется иметь точку восстановления системы на флэш-накопителе или возможность удалённого доступа к компьютеру без ввода данных при загрузке. Это важно в случае, если отключение безопасной загрузки не решит проблему и вы не сможете получить доступ к системе для удаления программы. В таком случае может потребоваться обновление Windows для удаления всех установленных программ при сохранении личных файлов. Вы делаете это на свой страх и риск. Если вы не чувствуете уверенности — запросите ваше устройство здесь. Не используйте ноутбук для этого процесса.
После установки процесс прост и перехватит все данные, проходящие через USB-устройства. Не вводите конфиденциальную информацию во время захвата, если планируете делиться данными, так как нажатия клавиш включаются в запись. Две сторонние программы: Usblyzer(бесплатная версия) и Wireshark (бесплатно). Обе работают, но при выборе Wireshark убедитесь, что выбран USBPcap.
Ознакомьтесь со следующей страницей — Обратные вызовы — для более подробной информации о создании обратных вызовов счётчиков и функций эффектов.