Callbacks
Ovaj odeljak pokriva preuzimanje podataka, kreiranje funkcija efekata i korišćenje klase Meter za aktiviranje tih funkcija. Neće se posebno objašnjavati animacije niti detalji naših handlera efekata. Ako želite da se osvežite, preporučujem naše odeljke Tutorijali ili API referenca.
Proces za efikasno aktiviranje animacije od početka do kraja je:
- Koristite metodu Meter.setValue(value) unutar funkcije update za ubacivanje sirovih podataka merača u instancu vaše klase Meter.
- Kada Meter proceni da je stabilan, aktivira callback funkciju prosleđenu kao drugi parametar.
- Ova callback funkcija sadrži uslovnu logiku za procenu stanja Metera. Ako je stanje istinito, efekat treba dodati u niz efekata (effects.push(new yourEffect())) ili u handler stanja (stateMgr.Push(new yourEffect())).
- Svaki prolaz funkcije update evaluira kompletni niz efekata i najnoviji element u handleru stanja. U ovoj fazi efekti se crtaju na osnovu promenljivih stanja.
- Nakon crtanja, svaki efekat doprinosi proveri svog životnog veka. Efekti u nizu efekata jednostavno iteriraju promenljivu životnog veka postavljenu na instancu njihove klase. Kada životni vek dostigne 0 ili manje, efekat se lagano uklanja sa splice-om tokom evaluacije niza efekata. Efekti u handleru stanja mogu se sami ukloniti po potrebi, budući da handler stanja funkcioniše kao stack i uslovna logika za te efekte teži da bude složenija.
Ovaj merač traži tipičnu traku zdravlja i vraća procenat odgovarajuće boje kao broj između 0 i 1. HSL opseg je u ovom slučaju prilično širok kako bi se uzeo u obzir gradijent u bojenju trake. Ovo dovodi do problema: kako traka zdravlja opada, otkriva transparentnu pozadinu koja može sadržavati zelene elemente iz okruženja igre, koji prolaze proveru boje i ometaju naš merač.
<head> <meta meter="health" tags="example" x= ".05" y=".9" width=".189" h="70-140" s="40-100" l="40-100" type="linear"/></head>Ovde sam kreirao instancu naše klase Meter nazvanu healthM za aktiviranje efekta “primljene štete”.
<script> // Meter instance var healthM = new Meter(25, healthHelper);
function update () => { healthM.setValue(health); window.requestAnimationFrame(update); }
function healthHelper () => { // "Took Damage" effect up next }
// . . .
</script>Izabrao sam prilično visok zahtev za stabilnost (25) kako bih uzeo u obzir gore pomenuti problem nestabilnosti. Priložena callback funkcija će se aktivirati samo kada je vrednost merača stabilna tokom 25 ažuriranja, što eliminiše većinu podataka koji nisu stvarna traka zdravlja, čak i kada igrač miruje. Jedini nedostatak je rastuća latencija efekta, koja će u ovom scenariju uvek biti ograničavajući faktor. Za ovu određenu traku zdravlja možemo to ublažiti na najmanje nekoliko načina:
- Kada pogodak sleti, deo zelene trake se odmah pretvara u crvenu pre nego što se smanji na novu zelenu vrednost. Ako dodamo merač healthRed koji traži crvenu na istom mestu kao merač zdravlja, možemo kombinovati njihove vrednosti za brže aktiviranje. Uslovni if ( healthM.decreased && healthRed.increased ) trebalo bi da pruži dobru preciznost u ovom slučaju.
- Možemo takođe kombinovati očitavanja više linearnih merača sa istim podešavanjima boje. Trenutno imam jedan linearni merač u sredini trake zdravlja. Ako podesim još dva, jedan malo iznad i ispod originala, ali još uvek unutar trake zdravlja, blizina tri vrednosti merača može se koristiti kao dodatna provera stabilnosti. Sva tri merača bi morala da prijave Meter.decreased i da budu unutar malog opsega vrednosti jedan od drugog da bi se efekat aktivirao.
Callback
Section titled “Callback”Ova callback funkcija se izvršava samo nakon što merač potvrdi da su svi elementi u svom nizu vrednosti jednaki. Ovo osigurava da su vrednosti merača zasnovane na stabilnim podacima. Unutar callbacka, koristim uslovnu izjavu da odredim da li treba reprodukovati efekat.
let healthPrev = 0;
function healthHelper () => { if (healthM.decreased && healthM.value != healthPrev){ effects.push(new healthEffect()); healthPrev = healthM.value; }}Uslovna izjava zahteva dva istinita uslova: stabilno smanjenje merača i razliku između trenutne vrednosti merača i prethodno zabeležene vrednosti. Dodao sam ovaj drugi uslov da bih izbegao grešku. Ako bismo proveravali samo smanjenje, efekat bi se reprodukovao ponovo i ponovo. Budući da se Meter.decreased postavlja kada merač postane stabilan, njegova vrednost ostaje ista sve dok se merač ne promeni. Na ovaj način, efekat se reprodukuje samo kada postoji smanjenje koje se razlikuje od poslednje stabilne vrednosti.
Efekat
Section titled “Efekat”Kreiram dva efekta: jedan za niz efekata i jedan za handler stanja.
Niz efekata
Section titled “Niz efekata”Ovaj prvi blok koda definiše funkciju efekta namenjenu nizu efekata. Dizajnirana je da bude brza i lagana, tako da više manjih efekata može da se izvršava istovremeno. Da bih smanjio opterećenje sistema, napravio sam efekat i malo prilagodljivim za ponovnu upotrebu putem parametara.
function healthEffect(color, direction, speed){ // The lifetime of the effect should be iterated in the draw function. this.lifetime = 200; this.speed = speed; this.direction = direction; this.color = color;
this.draw = () => { // Set fill from parameter ctx.fillStyle = `hsl(${this.color}, 100%, 50%)`; // Check direction if (this.direction == "up"){ ctx.fillRect(0, this.lifetime, width, 30); } else { ctx.fillRect(0, height - this.lifetime, width, 30); } // As the lifetime decreases by 5 each update, the rectangle moves accordingly. this.lifetime -= speed; } }function update() { // Background color ctx.fillStyle = "black"; ctx.fillRect(0, 0, 320, 200);
// Draw all effects in array and remove them if they are done. effects.forEach((ele, i) => { ele.draw(); if (ele.lifetime == 0){ effects.splice(i, 1); } })
window.requestAnimationFrame(update);}function healthHelper () => { if (healthM.decreased && healthM.value != healthPrev){ // Make sure to correctly set the color and direction paremeters. effects.push(new healthEffect(1, "down", 5)); healthPrev = healthM.value; }}Ovako bi trebalo da izgleda u akciji:

Zahvaljujući prilagodljivom dizajnu funkcije, možemo lako kreirati i efekat lečenja! Jednostavno dodajte dodatni uslov u callback funkciju.
function healthHelper () => { if (healthM.decreased && healthM.value != healthPrev){ // Damage effect effects.push(new healthEffect(1, "down", 5)); healthPrev = healthM.value; } if (healthM.increased && healthM.value != healthPrev){ // Healing effect effects.push(new healthEffect(120, "up", 5)); healthPrev = healthM.value; }}
Jednostavnost ovog efekta je njegova najveća prednost za programera LightScripta. Umesto kreiranja prilagođenih dizajnerskih efekata koji svaki zahtevaju stotine linija koda, gradite male, višekratno upotrebljive efekte koji služe kao gradivni blokovi za određene aktivatore. Sledeća tri efekta su varijacije zasnovane na malom efektu koji smo upravo kreirali. Eksperimentišite i vidite šta možete postići!



Handler stanja
Section titled “Handler stanja”Lično rezervišem handler stanja za velike, složene efekte sa vrlo specifičnom logikom stanja, posebno kada efekat treba da ima prioritet nad svima ostalima. Imajte na umu da handler stanja funkcioniše kao stack i izvršava samo vrh; svaki efekat stanja odgovoran je za sopstveno uklanjanje kada završi. Ovaj primer demonstrira animaciju lobby-ja za matchmaking koja se prilagođava u zavisnosti od toga da li tražite ili ne. Sadrži i sopstveni mali niz efekata sa pomocnim efektom. Merači koji se ovde koriste su inLobby i yellowPlayButton, koji se aktiviraju samo u lobbiju matchmakinga. yellowPlayButton menja boju u zavisnosti od toga da li tražite.
function lobbyAnimation(){ // Instance variables this.start = new Date().getTime(); this.elapsed = 0; this.radius = 0; this.searching = 1000; // Instance effects array this.effects = [];
// Process is the method that allows a state effect to remove itself this.Process = function () { // If the lobby menu and play button disappear, remove the effect if (inLobby.decreased && yellowPlayButton.decreased) { stateMgr.Pop(); // Global boolean to prevent duplicate lobby effects in the state handler lobbyAnim = false; } // If the play button is present, edit this.searching based on its color yellowPlayButton.value == 1 ? this.searching = 1000 : this.searching = 100; // Call the Draw function attached to the effect this.Draw(); };
this.Draw = function(){ // Iterate important variables this.elapsed = new Date().getTime() - this.start; this.radius = 50 + (Math.sin(this.elapsed/this.searching)*10);
// Fill the background to overwrite other effects ctx.fillStyle = 'black'; ctx.fillRect(0, 0, 320, 200);
// Draw the main arc ctx.beginPath(); ctx.strokeStyle = `hsl(120, 100%, 50%)`; ctx.lineWidth = 20; ctx.arc(160, 100, this.radius, 0, 2 * Math.PI); ctx.stroke(); // If the main arc is the right size, push helper effects into the helper array if(this.radius > 59 && this.elapsed % 2 == 0){ this.effects.push(new lobbyHelper(160, 100, 69)); } // Play and remove helper effects if necessary this.effects.forEach((e, i) => { ctx.lineWidth = 1; e.draw(); if (e.radius > 360) { this.effects.splice(i, 1); } }); } }
function lobbyHelper(x, y, radius){ this.x = x; this.y = y; this.radius = radius; this.draw = function(){ // Set the global transparency value. 1 is opaque, 0 is invisible. ctx.globalAlpha = 1 - this.radius/360; ctx.beginPath(); ctx.strokeStyle = `hsl(120, 100%, ${50 + (this.radius/7)}%)`; ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); ctx.stroke(); // Set the global transparency back to 1 so your other effects are opaque ctx.globalAlpha = 1; this.radius++; }}// Declare state handler (found in Snippets)var stateMgr = new StateHandler();// Global boolean to prevent duplicate lobby animationsvar lobbyAnim = false;
function update(){ // Run the Process function to draw the animation stateMgr.Process();}function lobbyAnimationPlay(){ if(inLobby.value == 1 && yellowPlayButton.value == 1 && !lobbyAnim){ stateMgr.Push(new lobbyAnimation()); lobbyAnim = true; }};Ne traži:

Traži:

Time završavamo naš tutorijal za Callbacks, kao i zvanični SignalRGB vodič za programere. Pratite sledeći i poslednji dodatak našem vodiču, Održavanje Lightscripta!