Callbacks
Deze sectie behandelt het verzamelen van gegevens, het bouwen van effectfuncties en het gebruik van de Meter-klasse om die functies te activeren. Er wordt geen nadruk gelegd op het verklaren van animaties of de details van onze effecthandlers; als u hierover wilt opfrissen, raden we onze secties Tutorials of API-referentie aan.
Het proces voor het effectief activeren van een animatie van begin tot eind is:
- Gebruik de methode Meter.setValue(value) in de updatefunctie om ruwe meterdata in een instantie van uw Meter-klasse in te voegen.
- Als de Meter zichzelf als stabiel beschouwt, activeert hij de callbackfunctie die als tweede parameter is meegegeven.
- Deze callbackfunctie bevat conditionele logica om de status van de Meter te evalueren. Als de status truthy is, moet uw effect worden toegevoegd aan de effecten-array (effects.push(new uwEffect())) of de toestandshandler (stateMgr.Push(new uwEffect())).
- Elke uitvoering van de updatefunctie evalueert de volledige effecten-array en het laatste element in de toestandshandler. In deze fase worden de effecten getekend op basis van toestandsvariabelen.
- Na het tekenen draagt elk effect bij aan het controleren van zijn levensduur. Effecten in de effecten-array itereren simpelweg een levensduurvariabele die is ingesteld op de instantie van hun klasse. Wanneer de levensduur 0 of minder is, kan het effect eenvoudig worden verwijderd met een splice tijdens de evaluatie van de effecten-array. Effecten in de toestandshandler kunnen zichzelf verwijderen wanneer nodig, omdat de toestandshandler als een stapel werkt en de conditionele logica voor deze effecten vaak complexer is.
De meter
Section titled “De meter”Deze meter zoekt naar een typische gezondheidsbalk en retourneert het percentage overeenkomende kleur als een getal tussen 0 en 1. Het HSL-bereik is in dit geval vrij ruim om een kleurverloop in de balk op te vangen. Dit introduceert een probleem: naarmate de gezondheidsbalk krimpt, onthult het een transparante achtergrond, die groene elementen uit de spelomgeving kan bevatten die de kleurcontrole doorstaan en onze meter verstoren.
<head> <meta meter="health" tags="example" x= ".05" y=".9" width=".189" h="70-140" s="40-100" l="40-100" type="linear"/></head>De Meter
Section titled “De Meter”Hier heb ik een instantie van onze Meter-klasse aangemaakt, genaamd healthM, om een “schade ontvangen”-effect te activeren.
<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>Ik heb een vrij hoge stabiliteitseis (25) ingesteld om de bovengenoemde instabiliteit op te vangen. De bijgevoegde callbackfunctie wordt alleen geactiveerd als de meterwaarde gedurende 25 updates stabiel is, waardoor de meeste gegevens die niet de daadwerkelijke gezondheidsbalk zijn worden uitgesloten, zelfs als de speler stilstaat. Het enige nadeel hiervan is de toenemende vertraging van het effect, wat altijd de beperkende factor in dit scenario zal zijn. Voor deze specifieke gezondheidsbalk kunnen we dit op ten minste een paar manieren tegengaan:
- Wanneer geraakt wordt een deel van de groene balk direct omgezet naar rood voordat het krimpt naar het nieuwe niveau van groene gezondheid. Als we een meter healthRed toevoegen die zoekt naar rood op dezelfde plek als de gezondheidsmeter, kunnen we hun waarden combineren voor snellere activering. Een conditionele if ( healthM.decreased && healthRed.increased ) zou in dit geval een goede nauwkeurigheid bieden.
- We kunnen ook de metingen van meerdere lineaire meters combineren met dezelfde kleurinstellingen. Momenteel heb ik één lineaire meter ingesteld in het midden van de gezondheidsbalk. Als ik er nog twee instelde, één iets boven en één iets onder de originele maar nog steeds binnen de gezondheidsbalk, kan de nabijheid van de drie meterwaarden worden gebruikt als extra stabiliteitcontrole. Alle drie meters zouden Meter.decreased moeten lezen en binnen een klein bereik van elkaars waarden moeten liggen om het effect te activeren.
De callback
Section titled “De callback”Deze callbackfunctie wordt alleen uitgevoerd nadat de meter heeft geverifieerd dat alle elementen in zijn waardenarray gelijk zijn. Dit zorgt ervoor dat de meterwaarden gebaseerd zijn op stabiele gegevens. Binnen de callback gebruik ik een conditionele instructie om te bepalen of het effect moet worden afgespeeld.
let healthPrev = 0;
function healthHelper () => { if (healthM.decreased && healthM.value != healthPrev){ effects.push(new healthEffect()); healthPrev = healthM.value; }}De conditionele instructie vereist twee truthy-condities: een stabiele afname in de meter en een verschil tussen de huidige meterwaarde en een eerder vastgelegde waarde. Ik heb deze tweede conditie toegevoegd om een bug te voorkomen. Als we alleen zouden controleren op een afname, zou het effect herhaaldelijk worden afgespeeld. Omdat Meter.decreased wordt ingesteld zodra de meter stabiliseert, blijft de waarde hetzelfde zolang de meter niet verandert. Op deze manier wordt het effect alleen afgespeeld als er een afname is die verschilt van de laatste stabiele waarde.
Het effect
Section titled “Het effect”Ik zal twee effecten voor u aanmaken: één voor de effecten-array en één voor de toestandshandler.
Effecten-array
Section titled “Effecten-array”Dit eerste codeblok definieert een effectfunctie bedoeld voor de effecten-array. Het is ontworpen om snel en lichtgewicht te zijn, zodat meerdere kleine effecten tegelijkertijd kunnen worden uitgevoerd. Om de systeembelasting te verminderen, heb ik het effect ook gemakkelijk aanpasbaar gemaakt via parameters voor hergebruik.
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; }}Zo ziet het er in actie uit:

Dankzij het bewerkbare ontwerp van de functie kunnen we ook eenvoudig een genezingseffect maken! Voeg gewoon een extra conditie toe aan de callbackfunctie.
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; }}
De eenvoud van dit effect is het grootste voordeel voor een LightScript-ontwikkelaar. In plaats van aangepaste designereffecten te maken, die elk honderden regels code vereisen, bouwt u kleine, herbruikbare effecten die als bouwstenen dienen voor specifieke triggers. De volgende drie effecten zijn variaties op basis van het kleine effect dat we zojuist hebben aangemaakt. Experimenteer gerust en ontdek wat u kunt bereiken!



Toestandshandler
Section titled “Toestandshandler”Ik reserveer de toestandshandler persoonlijk voor grote, complexe effecten met zeer specifieke toestandslogica, met name wanneer u wilt dat het effect prioriteit heeft boven alle andere. Houd er rekening mee dat de toestandshandler werkt als een stapel en alleen het bovenste effect uitvoert; elk toestandseffect is verantwoordelijk voor het verwijderen van zichzelf wanneer het klaar is. Dit voorbeeld demonstreert een matchmaking-lobby-animatie die zich aanpast op basis van of u zoekt of niet. Het bevat ook een eigen kleine effecten-array met een hulpeffect. De hier gebruikte meters zijn inLobby en yellowPlayButton, die alleen activeren in de matchmaking-lobby. De yellowPlayButton verandert van kleur afhankelijk van of u aan het zoeken bent.
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; }};Niet aan het zoeken:

Aan het zoeken:

Daarmee ronden we onze callbacks-tutorial af, evenals de officiële SignalRGB-ontwikkelaarswalkthrough. Houd de volgende en laatste toevoeging aan onze walkthrough in de gaten: Lightscript-onderhoud!