Hoppa till innehåll

Callbacks

Det här avsnittet täcker insamling av data, byggande av effektfunktioner och användning av Meter-klassen för att utlösa dessa funktioner. Det läggs ingen tonvikt på att förklara animationer eller specifika detaljer om våra effekthanterare, så om du behöver fräscha upp minnet rekommenderar vi att du kollar in våra avsnitt Handledningar eller API-referens.

Processen för att effektivt utlösa en animation från start till slut är:

  1. Använd metoden Meter.setValue(value) inuti update-funktionen för att infoga råmätardata i en instans av din Meter-klass.
  2. Om Meter bedömer sig vara stabil aktiverar den återanropsfunktionen som skickades in som dess andra parameter.
  3. Den här återanropsfunktionen innehåller villkorslogik för att utvärdera Meter:ns tillstånd. Om tillståndet är sant bör din effekt läggas till i effektmatrisen (effects.push(new yourEffect())) eller tillståndshanteraren (steMgr.Push(new yourEffect())).
  4. Varje körning av update-funktionen utvärderar hela effektmatrisen och det senaste elementet i tillståndshanteraren. I det här steget ritas effekterna baserat på tillståndsvariabler.
  5. Efter att ha ritats bidrar varje effekt till att kontrollera sin livslängd. Effekter i effektmatrisen itererar helt enkelt en livslängdsvariabel satt till instansen av sin klass. När livslängden är 0 eller mindre tas effekten enkelt bort med en splice under utvärderingen av effektmatrisen. Effekter i tillståndshanteraren kan ta bort sig själva när det behövs eftersom tillståndshanteraren fungerar som en stack, och villkorslogiken för dessa effekter tenderar att vara mer komplicerad.

Den här mätaren letar efter en typisk hälsostapel och returnerar procentandelen matchande färg som ett tal mellan 0 och 1. HSL-intervallet är ganska brett i det här fallet för att ta hänsyn till en gradient i stapelns färgning. Det introducerar ett problem – när hälsostapeln krymper avslöjar den faktiskt en transparent bakgrund, som kan inkludera gröna element från spelmiljön som passerar färgkontrollen och förstör vår mätare.

<head>
<meta meter="health" tags="example" x= ".05" y=".9" width=".189" h="70-140" s="40-100" l="40-100" type="linear"/>
</head>

Här har jag skapat en instans av vår Meter-klass kallad healthM för att aktivera en “tog skada”-effekt.

<script>
// Meter-instans
var healthM = new Meter(25, healthHelper);
function update () => {
healthM.setValue(health);
window.requestAnimationFrame(update);
}
function healthHelper () => {
// "Tog skada"-effekt härnäst
}
// . . .
</script>

Jag har angett ett ganska högt stabilitetskrav (25) för att ta hänsyn till ovanstående instabilitetsproblem. Den bifogade återanropsfunktionen aktiveras bara om mätarvärdet är stabilt i 25 uppdateringar, vilket utesluter de flesta data som inte är den faktiska hälsostapeln, även om spelaren är stilla. Den enda nackdelen är den växande latensen för effekten, som alltid kommer att vara den begränsande faktorn i det här scenariot. För den här specifika hälsostapeln kan vi motverka det på åtminstone ett par sätt:

  • När spelaren träffas konverteras en del av den gröna stapeln omedelbart till röd innan den krymper ned till den nya nivån av grön hälsa. Om vi lägger till en mätare healthRed som letar efter rött på samma ställe som hälsomätaren kan vi kombinera deras värden för snabbare utlösning. Ett villkor if ( healthM.decreased && healthRed.increased ) bör ge god noggrannhet i det här fallet.
  • Vi kan också kombinera avläsningarna från flera linjära mätare med samma färginställningar. Jag har för närvarande en linjär mätare placerad i mitten av hälsostapeln. Om jag konfigurerar ytterligare två, en något ovanför och en nedanför originalet men fortfarande inuti hälsostapeln, kan närheten av de tre mätarvärdena användas som en ytterligare stabilitetskontroll. Alla tre mätarna skulle behöva läsa Meter.decreased och vara inom ett litet intervall av varandras värden för att utlösa effekten.

Den här återanropsfunktionen körs bara efter att mätaren har verifierat att alla element i dess värdesmatris är lika. Det säkerställer att mätarvärdena baseras på stabila data. Inuti återanropet använder jag en villkorssats för att avgöra om effekten ska spelas.

let healthPrev = 0;
function healthHelper () => {
if (healthM.decreased && healthM.value != healthPrev){
effects.push(new healthEffect());
healthPrev = healthM.value;
}
}

Villkorssatsen kräver två sanna villkor: en stabil minskning i mätaren och en skillnad mellan det aktuella mätarvärdet och ett tidigare registrerat värde. Jag lade till det andra villkoret för att undvika ett fel. Om vi bara kontrollerade en minskning skulle effekten spelas upprepade gånger. Eftersom Meter.decreased sätts när mätaren stabiliseras förblir dess värde detsamma så länge mätaren inte förändras. På det här sättet spelas effekten bara om det sker en minskning som skiljer sig från det senaste stabila värdet.

Jag skapar två effekter åt dig: en för effektmatrisen och en annan för tillståndshanteraren.

Det här första kodblocket definierar en effektfunktion avsedd för effektmatrisen. Den är designad för att vara snabb och lättviktig för att tillåta att flera mindre effekter körs samtidigt. För att minska systembelastningen har jag också gjort effekten enkel att anpassa via parametrar för återanvändning.

function healthEffect(color, direction, speed){
// Effektens livslängd bör itereras i draw-funktionen.
this.lifetime = 200;
this.speed = speed;
this.direction = direction;
this.color = color;
this.draw = () => {
// Ange fyllning från parameter
ctx.fillStyle = `hsl(${this.color}, 100%, 50%)`;
// Kontrollera riktning
if (this.direction == "up"){
ctx.fillRect(0, this.lifetime, width, 30);
} else {
ctx.fillRect(0, height - this.lifetime, width, 30);
}
// När livslängden minskar med 5 varje uppdatering rör sig rektangeln i enlighet med det.
this.lifetime -= speed;
}
}
function update() {
// Bakgrundsfärg
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 320, 200);
// Rita alla effekter i matrisen och ta bort dem om de är klara.
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){
// Se till att korrekt ange färg- och riktningsparametrar.
effects.push(new healthEffect(1, "down", 5));
healthPrev = healthM.value;
}
}

Här är hur det ska se ut i praktiken:

Tack vare funktionens redigerbara design kan vi enkelt skapa en läkningseffekt också! Lägg bara till ytterligare ett villkor i återanropsfunktionen.

function healthHelper () => {
if (healthM.decreased && healthM.value != healthPrev){
// Skadeeffekt
effects.push(new healthEffect(1, "down", 5));
healthPrev = healthM.value;
}
if (healthM.increased && healthM.value != healthPrev){
// Läkningseffekt
effects.push(new healthEffect(120, "up", 5));
healthPrev = healthM.value;
}
}

Enkelheten i den här effekten är dess största fördel för en LightScript-utvecklare. Istället för att skapa anpassade designereffekter som var och en kräver hundratals rader kod, bygg små, återanvändbara effekter som fungerar som byggstenar för specifika utlösare. De nästa tre effekterna är variationer baserade på den lilla effekten vi just skapade. Experimentera gärna och se vad du kan uppnå!

Jag reserverar personligen tillståndshanteraren för stora, komplexa effekter med mycket specifik tillståndslogik, särskilt när du vill att effekten ska ha prioritet framför alla andra. Kom ihåg att tillståndshanteraren fungerar som en stack och bara kör den översta effekten; varje tillståndseffekt ansvarar för att ta bort sig själv när den är klar. Det här exemplet visar en matchmaking-lobbyanmation som anpassar sig baserat på om du söker eller inte. Den innehåller också sin egen lilla effektmatris med en hjälpeffekt. Mätarna som används här är inLobby och yellowPlayButton, som bara aktiveras i matchmaking-lobbyn. yellowPlayButton ändrar färg beroende på om du söker.

function lobbyAnimation(){
// Instansvariabler
this.start = new Date().getTime();
this.elapsed = 0;
this.radius = 0;
this.searching = 1000;
// Instanseffektmatris
this.effects = [];
// Process är metoden som tillåter en tillståndseffekt att ta bort sig själv
this.Process = function () {
// Om lobbymeny och spelknapp försvinner, ta bort effekten
if (inLobby.decreased && yellowPlayButton.decreased) {
stateMgr.Pop();
// Globalt booleskt värde för att förhindra dubbletter av lobbyeffekter i tillståndshanteraren
lobbyAnim = false;
}
// Om spelknappen är synlig, redigera this.searching baserat på dess färg
yellowPlayButton.value == 1 ? this.searching = 1000 : this.searching = 100;
// Anropa Draw-funktionen kopplad till effekten
this.Draw();
};
this.Draw = function(){
// Iterera viktiga variabler
this.elapsed = new Date().getTime() - this.start;
this.radius = 50 + (Math.sin(this.elapsed/this.searching)*10);
// Fyll bakgrunden för att skriva över andra effekter
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 320, 200);
// Rita huvudbågen
ctx.beginPath();
ctx.strokeStyle = `hsl(120, 100%, 50%)`;
ctx.lineWidth = 20;
ctx.arc(160, 100, this.radius, 0, 2 * Math.PI);
ctx.stroke();
// Om huvudbågen har rätt storlek, skjut in hjälpeffekter i hjälpmatrisen
if(this.radius > 59 && this.elapsed % 2 == 0){
this.effects.push(new lobbyHelper(160, 100, 69));
}
// Spela och ta bort hjälpeffekter om nödvändigt
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(){
// Ange det globala transparensvärdet. 1 är ogenomskinlig, 0 är osynlig.
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();
// Återställ global transparens till 1 så att dina andra effekter är ogenomskinliga
ctx.globalAlpha = 1;
this.radius++;
}
}
// Deklarera tillståndshanterare (finns i Kodavsnitt)
var stateMgr = new StateHandler();
// Globalt booleskt värde för att förhindra dubbletter av lobbyanimationer
var lobbyAnim = false;
function update(){
// Kör Process-funktionen för att rita animationen
stateMgr.Process();
}
function lobbyAnimationPlay(){
if(inLobby.value == 1 && yellowPlayButton.value == 1 && !lobbyAnim){
stateMgr.Push(new lobbyAnimation());
lobbyAnim = true;
}
};

Söker inte:

Söker:

Och med det avslutar vi vår callbacks-handledning, samt den officiella SignalRGB dev-genomgången. Håll utkik efter nästa och sista tillägget till vår genomgång, Lightscript-underhåll!