2017-11-04 3 views
6

Ich versuche, eine Anwendung zu erstellen, bei der Kreise auf die Zeichenfläche durch Lesen von Informationen aus einer Firebase-Datenbank gezeichnet werden, die die x- und y-Koordinaten speichert die Kreise. Das Ausführen des folgenden Codes erzeugt jedoch einfach nichts, ohne irgendein Zeichen der Kreise, da die Funktion drawCricles asynchron ausgeführt wird und der Befehl background(40) alles löscht, bevor die Kreise gezeichnet werden können.Firebase Javascript + P5.js: Asynchrone Funktion beim Neuzeichnen der Zeichenfläche

Hier ist mein Code:

function setup() { 
    createCanvas(windowWidth, windowHeight); 
    background(40); 
    stroke(80); 
    smooth(); 
    frameRate(60); 
} 

function drawCircles() { 
    firebase.database().ref("circles").once("value", function(snapshot) { 
     var snapshotVal = snapshot.val(); 
     var circleCount = snapshotVal.numCircles; 

     for (var j = 0; j < circleCount; j++) { 
      firebase.database().ref("circles" + j).once("value", function(snapshot) { 
       var snapshotValue = snapshot.val(); 
       fill(143, 2, 2); 
       ellipse(snapshotValue.xPos, 50, 50); 
      }); 
     } 
    }); 
} 

function draw() { 
    stroke(80); 
    background(40); 

    stroke(0); 
    drawCircles(); 
} 
+0

Bitte versuchen Sie [zerlegen Sie Ihr Problem in kleinere Stücke] (http://happycoding.io/tutorials/how-to/program) und arbeiten Sie immer nur an einem Stück. Können Sie beispielsweise ein einfaches Programm schreiben, das Daten von Firebase abruft? Lass das perfekt funktionieren, bevor du weitermachst. Können Sie separat davon ein Programm erstellen, das einige fest codierte Punkte anzeigt? Lass das perfekt funktionieren, bevor du sie zu einem Programm kombinierst. Wenn Sie in einem bestimmten Schritt stecken bleiben, dann veröffentlichen Sie einen [mcve] von genau diesem Schritt. Viel Glück. –

+1

@KevinWorkman Ich habe das Problem auf die Tatsache eingegrenzt, dass Hintergrund (40) alle auf dem Bildschirm gezeichneten Kreise löscht, weil drawCircles() asynchron läuft. – Vaporeon

Antwort

0

Es scheint Ihr Problem pro Sekunde einfach 60 Frames ist, die einen Fuß-Race-Bedingung verursacht. Firebase .once führt Async aus, wenn das Abrufen abgeschlossen ist, und P5 wartet nicht auf das Abrufen, da es mit dem Framrate-Timing bleibt.

In diesem speziellen Fall habe ich mehrere Empfehlungen, die Sie hoffentlich sehr nah an das gewünschte Ergebnis bringen.

1 - Umstrukturieren Code

Es gibt zwei Probleme mit der aktuellen Struktur des Codes.

  • Fall 1: Ihr aktueller Code würde mich führen in der Datenbank zu denken, aktualisiert Ihre Kreise in Echtzeit, und Sie müssen auf dem neuesten Stand bleiben, so halten Sie die neueste Position zu holen. Wenn dies der Fall ist, sollten Sie .on("value") anstelle von .once("value") verwenden und Firebase Ihnen die Updates senden, wenn sich die Kreise ändern, anstatt sie 60 Mal pro Sekunde zu fragen, um die Anfrage nach Roundtrip zu speichern. Wenn dies der Fall ist: Siehe meine Lösung 1 unten.

  • Fall 2: Wenn Ihre Kreise nicht in Echtzeit in der Datenbank aktualisiert werden, und Sie nur die ganze Liste der Kreise möchten, dann holen Sie die Liste 60 Mal pro Sekunde ohne Grund. Sie sollten stattdessen die Liste mit .once beim Setup abrufen und später in dieser Liste in draw() iterieren. Siehe Lösung 2 unten.

2 - Umstrukturieren Ihrer Datenbank

In jedem Fall Ihr aktuelles Datenbankmodell erfordert, dass Sie in einer Schleife zu halten holen. Das bedeutet, dass Sie so viele Anfragen stellen wie Ihre circleCount. Das ist schlecht für Ihre Verwendung, einfach weil jede Anfrage zusätzliche Reisezeit benötigt und wir versuchen, die benötigte Zeit zu reduzieren, so dass sie näher an Echtzeit ist. (oder die Framerate entsprechen)

Derzeit sind Ihre Kreise scheinbar gespeichert als circles1circles2 etc alle auf Wurzel, weil Sie .ref("circles" + j) verwenden sie zurückzuholen. Machen Sie es so, dass Sie Ihre Kreise so speichern: .ref("circles/" + j) das würde bedeuten, dass jeder circle jetzt incircles gespeichert wird. wie circles/circle1circles/circle2 usw.

Der Vorteil davon ist, dass Sie jetzt nicht die zusätzlichen Anforderungen an Firebase alle Kreise benötigen. Firebase hat unglaublich praktische Dinge wie forEach, um alle Kinder mit einer einzigen Anfrage zu durchlaufen.

3 - Klarer Hintergrund in Ihrer Feuerbasis Rückruf

Derzeit löschen Sie den Hintergrund in einer Frame-Rate-spezifische Art und Weise. Dies bedeutet, dass Sie, wenn jeder Ihrer Firebase-Aufrufe länger als 1/60 Sekunde dauert (16 Millisekunden), den Hintergrund gelöscht haben und weitergegangen sind. Die Wahrscheinlichkeit, dass Sie diese Geschwindigkeit erreichen, ist sehr gering, selbst nachdem wir unsere Datenbank strukturiert haben. Stattdessen würde ich empfehlen, zuerst 30 fps zu verwenden, was die Anzahl der Anrufe, die Sie an firebase tätigen, auf 30 Anrufe pro Sekunde reduziert.

Lösung 1

Wenn Ihr Kreise in der Datenbank aktualisiert werden (sagen wir zum Beispiel von einem anderen Spiel-Spieler oder jemand anderes, und Sie wollen Ihren Code immer die neuesten xPos anzuzeigen)

var latestCirclePositionsSnapshot; 

function setup() { 
    createCanvas(windowWidth, windowHeight); 
    background(40); 
    stroke(80); 
    smooth(); 
    frameRate(60); 

    firebase.database().ref("circles").on("value", function(snapshot) { 
    // got a new value from database, so let's save this in a global variable. 
    latestCirclePositionsSnapshot = snapshot; 
    // we will keep drawing this update until we get a new one from the database. 
    }); 
} 

function draw() { 
    drawCircles(); 
} 

function clearBackground() { 
    stroke(80); 
    background(40); 
} 

function drawCircles() { 
    clearBackground(); 
    stroke(0); 
    latestCirclePositionsSnapshot.forEach(function(circleSnapshot) { 
    // circleData will be the actual contents of each circle 
    var circleData = circleSnapshot.val(); 
    fill(143, 2, 2); 
    ellipse(circleData.xPos, 50, 50); 
    }); 
} 

Im Grunde wird dies die letzten Kreispositionen, die wir vom Firebase erhalten haben, solange zeichnen, bis wir einen neuen bekommen. (So ​​wird P5 halten bei 60fps erfrischend, aber Ihre Feuerbasis Updates werden als Echtzeit als Feuerbasis sein kann von Feuerbasis usw. laufen und holen)

Lösung 2

Wenn Sie keine Neuigkeiten in Echtzeit haben in Ihrer Datenbank, und alles, was Sie möchten einmal, indem sie Daten aus Feuerbasis Kreise zeichnen (zum Beispiel sagen, ein paar Punkte auf einige Daten basierend plotten)

var circlePositions; 
var gotPositions = false; 

function setup() { 
    createCanvas(windowWidth, windowHeight); 
    background(40); 
    stroke(80); 
    smooth(); 
    frameRate(60); 

    firebase.database().ref("circles").once("value", function(snapshot) { 
    // got the circle values from the database 
    // let's store them and we'll keep drawing them forever. 
    circlePositions = snapshot; 
    gotPositions = true; 
    }); 
} 

function draw() { 
    drawCircles(); 
} 

function clearBackground() { 
    stroke(80); 
    background(40); 
} 

function drawCircles() { 
    clearBackground(); 
    stroke(0); 

    if (gotPositions) { 
    circlePositions.forEach(function(circleSnapshot) { 
     // circleData will be the actual contents of each circle 
     var circleData = circleSnapshot.val(); 
     fill(143, 2, 2); 
     ellipse(circleData.xPos, 50, 50); 
    }); 
    } else { 
    // Display some text here like "LOADING DATA FROM SERVERS..." 
    } 
} 

Hoffe, dass diese Hilfe :) Es ist gut, einen anderen zu sehen Kollegen Fan der Verarbeitung & Firebase.

0

ich in die docs sah und hier ist ein example, wie sie vorschlagen, mit einem abgerufenen Daten zu beschäftigen. In Ihrem Fall versuchen zu trennen Abrufen und Zeichnung, zwischenzuspeichern Ihre Daten eine globale Variable:

var circles = []; 
 

 
function fetchData() { 
 
    firebase.database().ref("circles").once("value", 
 
    function(snapshot) { 
 
     var snapshotVal = snapshot.val(); 
 
     var circleCount = snapshotVal.numCircles; 
 
     
 
     circles = []; 
 

 
     for (var j = 0; j < circleCount; j++) { 
 
      firebase.database().ref("circles" + j).once("value",     function(snapshot) { 
 
       circles.push(snapshot.val()); 
 
      }); 
 
     } 
 
    }); 
 
} 
 

 
function setup() { 
 
    createCanvas(windowWidth, windowHeight); 
 
    background(40); 
 
    stroke(80); 
 
    smooth(); 
 
    frameRate(60); 
 
    fetchData(); 
 
} 
 

 
function drawCircles() { 
 
    circles.forEach(function (snapshotValue) { 
 
     var snapshotValue = snapshot.val(); 
 
     fill(143, 2, 2); 
 
     ellipse(snapshotValue.xPos, 50, 50); 
 
    }); 
 
} 
 

 
function draw() { 
 
    stroke(80); 
 
    background(40); 
 

 
    stroke(0); 
 
    drawCircles(); 
 
}

Wenn Sie immer relevante Daten angezeigt werden müssen, versuchen fetchData Funktion setInterval mit nennen, wie :

function setup() { 
 
     createCanvas(windowWidth, windowHeight); 
 
     background(40); 
 
     stroke(80); 
 
     smooth(); 
 
     frameRate(60); 
 
     setInterval(fetchData, 5000); //will call fetchData every 5000 ms 
 
    }

+0

Ich habe versucht, Ihren Code, aber leider sind die Kreise immer noch nicht sichtbar. – Vaporeon

0

Das Problem ist nicht so drawCircles() asynchron ist - das Problem ist, dass bei draw()frameRate() genannt wird, und background() löscht den Bildschirm mit einer Volltonfarbe jeden Bruchteil einer Sekunde, als Ziehschleife: draw reference und background sehen. Wenn Sie die Zeile background(40) von draw() entfernen, wird der Bildschirm nicht für jeden Frame gelöscht, und gezeichnete Kreise werden wie gewünscht akkumuliert. Dies ist einfacher als das erneute Zeichnen aller Firebase-Daten in jedem Frame.

Die folgende Skizze, die das Konzept demonstriert: background() wird nur während setup(), nicht während draw() aufgerufen, so dass der Bildschirmbereich einmal gefärbt ist, dann schrittweise mit Kreisen, die sich ansammeln.

function setup() { 
 
    createCanvas(400, 200); 
 
    frameRate(5) 
 
    background(40); 
 
} 
 
function drawCircles() { 
 
    fill(143, 2, 2); 
 
    ellipse(random(width), 50, 50); 
 
} 
 
function draw() { 
 
    // background(40); 
 
    drawCircles(); 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js"></script> 
 
<html> 
 
    <head> 
 
    </head> 
 
    <body> 
 
    </body> 
 
</html>

Wenn Sie einige Dinge akkumulieren wollen und wegzuwischen andere jeden Frame, dann müssen Sie Ihre Kreise auf einem createGraphics Puffer akkumulieren. Jeder Rahmen zeichnet den Puffer aus Kreisen auf die Leinwand zurück und zeichnet dann ephemere Elemente (wie einen Mausanzeiger usw.) obenauf.

Hier ist ein Beispiel: Für jeden Rahmen wird die Leinwand mit background() gelöscht, dann wird der pg Puffer auf die Zeichenfläche gezeichnet, dann wird ein weißer Kreis an der Maus gezeichnet. Da Hintergrund den Bildschirm löscht, hinterlässt der weiße Kreis keine Spuren von Bild zu Bild - aber die roten Kreise werden in einen nicht gelöschten Grafikpuffer gezeichnet, so dass sie bestehen bleiben.

var pg; 
 
function setup() { 
 
    createCanvas(400, 200); 
 
    pg = createGraphics(400, 200); 
 
    background(40); 
 
} 
 
function drawCircles() { 
 
    pg.fill(143, 2, 2); 
 
    pg.ellipse(random(pg.width), 50, 50); 
 
} 
 
function draw() { 
 
    background(40); 
 
    drawCircles(); 
 
    image(pg,0,0); 
 
    fill(255); 
 
    ellipse(mouseX,mouseY,50,50); 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js"></script> 
 
<html> 
 
    <head> 
 
    </head> 
 
    <body> 
 
    </body> 
 
</html>