Przejdź do głównej zawartości

Snippets

Ta sekcja zawiera trzy przydatne fragmenty kodu dla twoich LightScripts: mierniki, procedury obsługi stanu i procedury obsługi efektów.

Kontynuując poprzednią sekcję, klasa Meter jest zaprojektowana do śledzenia znaczących zmian w niestabilnych danych miernika. Traktuj ją jak pedał hamulca dla twojego kodu — zapewnia stabilność nawet w złożonych strefach. Każdy miernik zawiera konfigurowalną tablicę wartości zbieranych podczas każdego wykonania funkcji aktualizacji. Gdy wszystkie wartości w tej tablicy są takie same (tj. dane są “stabilne”), miernik aktualizuje swoją wartość i aktywuje dołączoną funkcję callback, aby uruchomić efekt.

MetodaOpis
Meter(size, callback)Konstruktor akceptuje zarówno size jak i callback.\n\n\n\nsize — liczba razy, kiedy identyczna wartość musi być obecna, aby wartość miernika była uznana za wystarczająco “stabilną” do przetworzenia. Wyższe wartości dają większą dokładność, niższe wartości dają mniejsze opóźnienie.\n\n\ncallback — funkcja wywoływana, gdy wartość miernika jest stabilna i zmieniła się.
Meter.setValue(value)Musisz wywoływać setValue(value) w każdej klatce, lub za każdym razem, gdy są dostępne nowe surowe dane dla tego miernika.

Proces zbierania danych ekranu powinien generalnie mieć tę strukturę:

  1. Wartość engine.vision.meter jest aktualizowana na podstawie nowych danych z ekranu.
  2. Wewnątrz funkcji aktualizacji wywoływane jest Meter.setValue(engine.vision.meter), aby przechwycić nową wartość.
  3. Meter sprawdza swoją wewnętrzną tablicę, aby zobaczyć, czy wszystkie wartości są identyczne. Jeśli tak, aktualizuje swój własny stan i wykonuje przypisaną funkcję callback.
  4. Funkcja callback następnie ocenia konkretne warunki przy użyciu zmiennych Meter i wykonuje swój kod, jeśli te warunki są spełnione.

WAŻNA UWAGA:

Nie pomijaj używania callbacków. Ponieważ funkcja aktualizacji działa ciągle, musi pozostawać jak najbardziej wydajna. Jej główną rolą powinno być aktualizowanie wartości Meter. Unikaj łączenia warunków wewnątrz aktualizacji, aby sprawdzać mierniki i aktywować efekty, ponieważ prowadzi to do niepotrzebnych sprawdzeń przy każdym cyklu. Zamiast tego definiuj swoje efekty w oddzielnych funkcjach i przekazuj je do swojego Meter jako callbacki.

Gdy jest stabilna, Meter udostępnia kilka wartości swojej funkcji callback:

ZmienneOpis
Meter.valueBieżąca wartość miernika.
Meter.increasedWartość boolean wskazująca, czy nasza wartość wzrosła w tej aktualizacji
Meter.decreasedWartość boolean wskazująca, czy nasza wartość spadła w tej aktualizacji
Meter.diffWartość bezwzględna zmiany, którą miernik przeszedł w tej aktualizacji.

Oto prosta konfiguracja z jednym miernikiem, zawierająca kod klasy 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();
}
};
}

Procedura obsługi stanu to stos efektów przeznaczony specjalnie do obsługi efektów, które mają priorytet nad wszystkimi innymi. Jeśli chcesz, aby jedna duża dominująca animacja działała do końca bez przerwania, umieść ją w procedurze obsługi stanu.

MetodaOpis
StateHandler()Konstruktor nie wymaga argumentów.
StateHandler.push(new State())Możesz umieścić nowy stan na stosie stanu za pomocą push, a przetwarzanie rozpocznie się natychmiast.
StateHandler.pop()Usuwa bieżący stan ze stosu stanu i zaczyna przetwarzać poprzedni stan na stosie.

Każdy obiekt umieszczony w procedurze obsługi stanu musi implementować Process(). Funkcja Process określa czas życia efektu w procedurze obsługi.

Poniżej przedstawiono prostą procedurę obsługi stanu oraz funkcję Process, która musi być wykonywana wewnątrz twoich funkcji efektów:

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();
};

Procedury obsługi efektów są doskonałe do przechowywania i renderowania mniejszych, lżejszych efektów razem. Każdy efekt w procedurze obsługi jest oceniany przy każdym wywołaniu aktualizacji i jest usuwany po osiągnięciu końca swojego czasu życia.

Procedura obsługi efektów to po prostu tablica do przechowywania efektów. To wszystko.

// Declare effects array
let effects = [];
// (Not shown) Evaluate Meter, callback function pushes effect into effects array
effects.push(new SpecialEffect());
// Iterate through the array, animating a frame for each effect
for (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);
}
}

Menedżer stanu jest przeznaczony dla efektów priorytetowych, co oznacza, że musimy mu nadać priorytet podczas wykonywania. W funkcji aktualizacji przetwarzaj procedurę obsługi efektów A NASTĘPNIE procedurę obsługi stanu. Rysuje to efekt stanu na wierzchu elementów procedury obsługi efektów.

Tak:

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();
}

Przechowywanie danych USB to prosty proces, ale sterowniki używane przez te programy są znane z powodowania konfliktów z niektórymi systemami, czasami wyłączając porty USB do momentu odinstalowania oprogramowania. Typowym rozwiązaniem jest wyłączenie “Bezpiecznego rozruchu” w ustawieniach BIOS/UEFI. Przed kontynuowaniem zdecydowanie zaleca się posiadanie przywracania systemu dostępnego na pendrive lub sposobu zdalnego dostępu do komputera, który nie polega na danych wejściowych użytkownika podczas uruchamiania. Jest to ważne w przypadku, gdy wyłączenie bezpiecznego rozruchu nie rozwiąże problemu i nie możesz uzyskać dostępu do systemu w celu odinstalowania programu. W takich przypadkach może być konieczne odświeżenie systemu Windows, aby usunąć wszystkie zainstalowane programy przy zachowaniu plików osobistych. Robisz to na własne ryzyko. Jeśli nie czujesz się z tym komfortowo, poproś o swoje urządzenie tutaj. Nie używaj laptopa do tego procesu.

Po zainstalowaniu proces jest prosty i przechwytuje wszystkie dane przechodzące przez twoje urządzenia USB. Nie wpisuj poufnych informacji podczas przechwytywania, jeśli planujesz udostępniać dane, ponieważ naciśnięcia klawiszy są rejestrowane w nagraniu. Dwa programy firm trzecich, które można użyć, to Usblyzer (bezpłatna wersja próbna) i Wireshark (bezpłatny). Oba działają, ale jeśli wybierzesz Wireshark, upewnij się, że USBPcap jest zaznaczony.

Sprawdź naszą następną stronę, Callbacki, aby uzyskać więcej szczegółów na temat składania callbacków mierników i funkcji efektów.