p5, Teil 3 / 2020-12-11 / Matthias Edler-Golla, CC BY-SA 4.0



Themen

  • Externe Funktionen
  • HSL-Farbraum
  • Bilder und Sounds einfügen
  • Elemente verschieben und drehen via push & pop
  • Sekundenanzeiger mit push & pop
  • diverse Anzeigemöglichkeiten der Zeit (hier hauptsächlich Sekunden)
  • Arc-Funktionen (Torte & Bogen)
  • Dan Shiffmann Videos zu den heutigen Themen

Externe Funktionen

Externe Funktionen erhöhen die Lesbarkeit Eures Codes und sorgen somit dafür, dass Ihr weniger Fehler macht!

Demo

Im Web Editor anzeigen

p5

function setup() {
  createCanvas(960, 200);
  background(240);
  frameRate(15);

  // ausgelagerte Funktionen
  // damit ist der Code besser lesbar…

  weisserKreis();
  rotesQuadrat();
  willkommen();
}

// ============= externe Funktionen =============

function weisserKreis() {
  strokeWeight(5);
  stroke("blue");
  fill("white");
  ellipse(50, 60, 80);
}

function rotesQuadrat() {
  noStroke();
  fill("red");
  rect(120, 20, 120);
}

function willkommen() {
  textFont("Georgia");
  textSize(24);
  noStroke();
  fill("black");
  text("Willkommen!", 280, 70);
}

Externe Funktionen mit Werte-Übergabe

Ihr könnt die externe Funktion auch so flexibel schreiben, dass Ihr mit der Übergabe von Werten den gleichen Code für immer wieder andere Varianten verwenden könnt.

Demo

Im Web Editor anzeigen

p5

function setup() { 
  createCanvas(960,200);
  background(220);

  // Reihenfolge: fuellFarbe, xPos, yPos, Durchmesser d
  kreisZeichnen(0, 50, 50, 30);
  kreisZeichnen(150, 130, 70, 20);

  // Farbnamen in Anführungsstrichen
  kreisZeichnen("red", 250, 66, 100);
  kreisZeichnen("orange", 450, 96, 140);
  kreisZeichnen("darkred", 750, 96, 180);
}

// externe Funktion mit 4 übergebenen Werten (fuellFarbe, xPos, yPos, Durchmesser d)
function kreisZeichnen(fuellFarbe, xPos, yPos, d) {
  strokeWeight(5);
  noStroke();
  fill(fuellFarbe);
  ellipse(xPos, yPos, d, d);
}

HSL Farbraum

Der HSL-Farbraum ist intuitiver als der RGB-Farbraum und orientiert sich an den Farbkreis von Itten.

Habt Ihr den Farbkreis im Kopf (oder als Bild auf dem Computer) könnt Ihr einfach einen Farbton auswählen…

HSL

Hue | Saturation | Lightness
Farbton | Sättigung | Helligkeit

Demo

Im Web Editor anzeigen

Umfangreicheres Beispiel

Im Web Editor anzeigen

p5.js

function setup() {
    createCanvas(960, 240);
    background(245);

    // 360° Farbkreis, andere Werte von 0% bis 100%
    colorMode(HSL, 360, 100);

    farbflaechen();
}

function farbflaechen() {
    /*
    schaut den Farbkreis an:
    0 Grad ist bei „12 Uhr“ und damit rot
    90 Grad ist bei „3 Uhr“ und somit grün
    180 Grad ist bei „6 Uhr“ und somit türkis
    270 Grad ist bei „6 Uhr“ und somit lila
    */

    // 0 Grad = rot
    fill(0, 100, 50);
    rect(10, 0, 300, 45);

    // 90 Grad = grün
    fill(90, 100, 50);
    rect(10, 50, 300, 45);

    // 180 Grad = türkis
    fill(180, 100, 50);
    rect(10, 100, 300, 45);

    // 270 Grad = lila
    fill(270, 100, 50);
    rect(10, 150, 300, 45);
}

Bilder einfügen

p5 kann auch Bilder verwenden, die Ihr hochgeladen habt. Bitte verkleinert die benötigten Bilder vor dem Hochladen z.B. in Photoshop auf die Größe, die Ihr in p5 benötigt!

Demo

Im Web Editor anzeigen

Bilder hochladen

Ihr könnt JPGs, GIFs, PNGs und SVGs hochladen!

Schritt 1

Den Pfeil neben „sketch.js“ anklicken

Schritt 2

dort auf den Pfeil nach unten klicken und „Upload File“ anklicken. Dann die gewünsche Datei direkt auf das aufspringende Fenster ziehen.

p5

let starPNG;
let h, b;

// das Bild muss "vorgeladen" werden, damit Ihr es verwenden könnt
function preload() {
  starPNG = loadImage("star.png");
}

function setup() {
  createCanvas(960, 400);
  frameRate(1);
}

function draw() {
  background(220);
  image(starPNG, 5, 5);

  let s = second();
  text(s + " sek", 10, height-15);

  // vertikale Position der Sternchen
  h = height / 2 - 50;

  // Breite & Höhe des Sternchen
  b = 52;

  // alle 15 Sternchen wird eine neue Zeile gestartet
  for (i = 0; i <= s; i++) {
    if (i < 15) {
      image(starPNG, 5 + i * (b + 1), h, b, b);
    } else if (i >= 15 && i < 30) {
      image(starPNG, 5 + (i - 15) * (b + 1), h + (b + 1), b, b);
    } else if (i >= 30 && i < 45) {
      image(starPNG, 5 + (i - 15 *2) * (b + 1), h + (b + 1) * 2, b, b);
    } else if (i >= 45 && i < 60) {
      image(starPNG, 5 + (i - 15 *3) * (b + 1), h + (b + 1) * 3, b, b);
    }
  }
}

Sound abspielen

Der Sound muss genauso wie andere Dateien erst hochgeladen werden – siehe vorherigen Slide!

Demo

Im Web Editor anzeigen

p5

let pingSound;

// Vorladen des Sounds notwendig!
function preload() {
  pingSound = loadSound("ping.mp3");
}

function setup() {
  createCanvas(960, 200);
  frameRate(1);

  textFont("Verdana");
  textStyle(BOLD);
  fill(255);
  textSize(280);
  textAlign(CENTER);
}

function draw() {
  background(220);

  let s = second();

  // Modulo-Operation sorgt dafür, dass alle 10sek 
  // ein Sound abgespielt wird
  // immer wenn sich der Sekundenwert ohne Rest durch 10 teilen lässt…
  if (s % 10 == 0) {
    console.log(s);
    background(255,0,0);
    pingSound.play();
  }

  text(s, width / 2, height / 2 + 100);

}

Elemente verschieben mit Push und Pop

mit push & pop kann man sich viel Arbeit sparen: Einfach die gewünschten Sachen am oberen Rand des Canvas anlegen und dann verschieben oder drehen…

Demo

Im Web Editor anzeigen

p5

function setup() {
  createCanvas(960, 150);
  background(240);
  strokeWeight(3);

  mitteMarkieren();

  // Figure, die am oberen Rand klebt
  figureZeichnen("red");

  // Identische Figure, die verschoben wird und andere Farben bekommt
  push();
  translate(20, 20);
  figureZeichnen("blue");
  pop();

  // Identische Figure, die verschoben wird und andere Farben bekommt
  push();

  translate(155, 50);
  figureZeichnen("green");
  pop();

  // Identische Figure, die verschoben wird und andere Farben bekommt
  push();
  translate(width / 2, height / 2);
  figureZeichnen("orange");
  pop();
}

function figureZeichnen(farbe) {
  fill(farbe);
  stroke(farbe);

  ellipse(0, 0, 20);
  line(0, 0, 0 + 200, 0);
  ellipse(200, 0, 20);
}

function mitteMarkieren() {
  strokeWeight(1);
  const xMitte = width / 2;
  const yMitte = height / 2;

  // Anzeige, wo Mitte ist
  stroke(200);
  line(0, yMitte, width, yMitte);
  line(xMitte, 0, xMitte, height);
}

Elemente drehen mit Push und Pop

Auch hier wieder der gleiche Prozess: Elemente am oberen Rand anlegen und dann mit push & pop verschieben und drehen…

Demo

Im Web Editor anzeigen

p5

// damit fängt der Zeiger oben an…
let winkel = 270;

function setup() {
  createCanvas(960, 450);
  background(240);
  frameRate(1);

  strokeWeight(3);
}

function draw(){
  background(240);
  mitteMarkieren();

  // Figure, die am oberen Rand klebt
  figureZeichnen("red");

  // Identische Figure, die verschoben und gedreht wird
  push();
  translate(width / 2, height / 2);
  rotate(radians(winkel)); // Gradangabe
  figureZeichnen("orange");
  pop();

  // 360° = ganze Drehung
  // 60sek = Zeit, in der der Sekundenzeiger 360° zurücklegen muss
  // 360° / 60sek = 6° pro Sekunde
  winkel = winkel + 6;
}

function figureZeichnen(farbe) {
  fill(farbe);
  stroke(farbe);

  // die Figure besteht aus 3 Elementen!
  ellipse(0, 0, 20);
  line(0, 0, 0 + 200, 0);
  ellipse(200, 0, 20);
}

// zeichnet die grauen Linien, die die Mitte markieren
function mitteMarkieren() {
  strokeWeight(1);
  const xMitte = width / 2;
  const yMitte = height / 2;

  // Anzeige, wo Mitte ist
  stroke(200);
  line(0, yMitte, width, yMitte);
  line(xMitte, 0, xMitte, height);
}

Sekundenzeiger mit Push und Pop

Das hier gezeigte Beispiel ist fast identisch mit dem vorherigem – nur wird jetzt vom Computer die aktuelle Sekunde abgefragt und beim Drehen des Zeigers verwendet.

Demo

Im Web Editor anzeigen

p5

// damit fängt der Zeiger oben (bei "Null") an…
let startPunkt = 270;

// die Sekunden-Variable
let s; 

function setup() {
  createCanvas(960, 450);
  background(240);
  frameRate(1);
}

function draw() {
  background(240);
  mitteMarkieren();

  s = second();

  // 360° = ganze Drehung
  // 60sek = Zeit, in der der Sekundenzeiger 360° zurücklegen muss
  // 360° / 60sek = 6° pro Sekunde
  let winkel = startPunkt + s * 6;

  // Identische Figure, die verschoben und gedreht wird
  push();
  // zum Mittelpunkt verschieben
  translate(width / 2, height / 2);
  rotate(radians(winkel)); // Gradangabe
  sekundenZeiger("blue");
  pop();

  // Ausgabe der Sekunden digital
  sekundenDigital();
}

function sekundenZeiger(farbe) {
  fill(farbe);
  stroke(farbe);
  strokeWeight(7);

  // die Figure besteht aus 3 Elementen!
  ellipse(0, 0, 60);
  line(0, 0, 0 + 200, 0);
  ellipse(200, 0, 20);
}

function sekundenDigital(){
  textSize(58);
  textAlign(CENTER);
  noStroke();
  fill(240)
  text(s, width/2, height/2 +18);
}

// zeichnet die grauen Linien, die die Mitte markieren
function mitteMarkieren() {
  strokeWeight(1);
  const xMitte = width / 2;
  const yMitte = height / 2;

  // Anzeige, wo Mitte ist
  stroke(200);
  line(0, yMitte, width, yMitte);
  line(xMitte, 0, xMitte, height);
}

Darstellung von Zeit (hier Sekunden)


Balken waagrecht

Die map-Funktion (Zeile 13) rechnet automatisch die aktuelle Sekunde auf die richtige Stelle in einem 360px breiten Streifen um – das spart uns viel Kopfrechnen!

Schaut Euch dazu die Erklärungen bei den p5 Referenzen an!

Demo

Im Web Editor anzeigen

p5

function setup() {
  createCanvas(960, 200);
  frameRate(1);
}

function draw() {
  background(150);

  let s = second();

  // automatische Umrechnung von Werten zwischen 0 und 60 (Sekunden)
  // auf Werte von 0 bis 360 (hier Pixel)
  let m = map(s, 0, 60, 0, width);

  // das jede Sekunde wachsende Rect
  noStroke();
  fill(255);
  rect(0, 0, m, height);

  // die digitale Sekundenanzeige, die mitläuft
  fill(150);
  textAlign(RIGHT);
  textSize(80);
  text(s, m - 5, height / 2 + 28);

  // waagrechte Linie oben und unten
  strokeWeight(6);
  stroke(0);
  line(0, 1, width, 1);
  line(0, height - 1, width, height - 1);

}

Balken senkrecht

Demo

Im Web Editor anzeigen

p5

// globale Variablen!
let s, m;

function setup() {
  createCanvas(960, 400);
  frameRate(1);
}

function draw() {
  background(200);

  s = second();

  // automatische Umrechnung von Werten zwischen 0 und 60 (Sekunden)
  // auf Werte von 0 bis 360 (hier Pixel)
  m = map(s, 0, 60, 0, 360);

  scalaZeichnen();
  beschriftungen();

  sekundenAnzeige();
}

function scalaZeichnen(){
  //Hintergrund Rect
  noStroke();
  fill(255);
  rect(270, height - 20, 100, -360);

  // die digitale Sekundenanzeige, die links mitläuft
  noStroke();
  fill(0);
  textAlign(RIGHT);
  textSize(40);
  text(s, 263, height - 5 - m);
}

function beschriftungen(){
  // alle 10s eine Linie und Beschriftung der Skale
  textAlign(LEFT);
  textSize(16);

  for (i = 0; i <= 10; i++) {

    // hier festlegen, wo Linien und Beschriftungen vertikal positioniert werden sollen
    let h = height - 20 - (60 * i);

    // die waagrechten Skala-Linien
    stroke(120);
    line(375, h, 395, h);

    // die Skala-Beschriftung
    noStroke();
    fill(120);
    text(0 + i * 10, 400, h + 5)
  }
}

function sekundenAnzeige(){
  // das jede Sekunde wachsende Rect
  noStroke();
  fill(255, 0, 0);
  rect(270, height - 20, 100, -m);

  // die waagrechte Linie an der exakten Sekundenposition
  stroke(0);
  line(265, height - 20 - m, 375, height - 20 - m)
}

Sekunden-Balken

Demo

Im Web Editor anzeigen

p5

function setup() {
  createCanvas(960, 400);
  frameRate(1);
}

function draw() {
  background(200);

  let s = second();

  // statische, weiße Linien im Hintergrund
  strokeWeight(13);
  strokeCap(SQUARE);

  stroke(255);
  for (i = 0; i <= 59; i++) {
    line(8 + 16 * i, 0, 8 + 16 * i, height)
  }

  // schwarze Linien zeigen aktuelle Sekunden an
  stroke(0);
  for (i = 0; i <= s; i++) {
    line(8 + 16 * i, 0, 8 + 16 * i, height)
  }

}

Arc-Function (Torte)

Das Tortenstück füllt sich jede Sekunde weiter auf bis eine Minute voll ist…

Demo

Im Web Editor anzeigen

p5

function setup() {
  createCanvas(960, 480);
  frameRate(1);
}

function draw() {
  background(245);

  let sek = second();

  noStroke();
  fill(255, 0, 0);

  let startPunkt = 270; // 12 Uhr oben, Gradangabe!
  let d = 460; // Durchmesser

  // arc(x, y, w, h, start, stop, mode)
  // Siehe dazu: https://p5js.org/reference/#/p5/arc
  arc(
    width / 2,
    height / 2,
    d,
    d,
    radians(startPunkt),
    radians(startPunkt + sek * 6),
    PIE  // wie soll es dargestellt werden?
  );
}

Arc-Function (Bogen)

Der Bogen füllt sich jede Sekunde weiter auf bis eine Minute voll ist…

Demo

Im Web Editor anzeigen

p5

function setup() {
  createCanvas(960, 480);
  frameRate(1);
}

function draw() {
  background(245);

  let startPunkt = 270; // 12 Uhr oben, Gradangabe!
  let sek = second();
  let x = width/2;
  let y = height/2;
  let d = 400;

  // HG-Kreis zur Orientierung
  strokeWeight(1);
  stroke(200);
  noFill();

  ellipse(x, y, d + 35); // etwas größer als die eigentliche Fläche
  ellipse(x, y, d - 35); // etwas größer als die eigentliche Fläche

  strokeWeight(28);
  stroke(255,0,0);
  strokeCap(ROUND);

  // https://p5js.org/reference/#/p5/arc
  arc(
    x,
    y,
    d,
    d,
    radians(startPunkt), //damit kann man hier Grad-Angaben eingeben!
    radians(startPunkt + sek * 6) // 60sek * 6 = 360°
  );
}

Analoge Uhr

Der Code des Beispiels schaut komplexer aus als es ist!

Die analoge Uhr ist eigentlich nur eine Erweiterung der Sekunden-Anzeige. Es werden hier neben der Sekunden aber auch die Minuten und Stunden abgefragt und zur Darstellung des jeweiligen Winkels der Zeiger verwendet.

Demo

Im Web Editor anzeigen

p5

let winkel;
let xMitte, yMitte;
const laenge = 130;

function setup() {
  // Breiten- und Höhen-Angabe nur relevant für Proportionen
createCanvas(960, 350);

  // Gradangabe, bei Sekunden ganz oben starten
  winkel = radians(270);


  // Angaben zur Schrift
  textFont("Verdana");

  xMitte = width / 2;
  yMitte = height / 2;

  background("#ddd");

  frameRate(1); // einmal pro Sekunde aktualisieren;
}

function draw() {
  background("#ddd");

  // zeichnen der Punkte…
  scalaZeichnen();

  // die digitale Uhrzeitangabe auf dem Zifferblatt
  digitalAusgabe();

  stundenZeiger();
  minutenZeiger();
  sekundenZeiger();
}

function digitalAusgabe() {
  textAlign(CENTER);
  textSize(18);
  fill(255);
  noStroke();

  let sek = second();
  let min = minute();
  let stunde = hour();

  // bei einstelligen Werten noch eine "0" einfügen
  // schaut sonst eigenartig aus
  if (sek < 10) {
    sek = "0" + sek;
  }

  if (min < 10) {
    min = "0" + min;
  }

  if (stunde < 10) {
    stunde = "0" + stunde;
  }

  text(stunde + ":" + min + ":" + sek, xMitte, yMitte - 17);
}

function scalaZeichnen() {
  // Anzeige der Mitte
  fill(60);
  noStroke();
  ellipse(xMitte, yMitte, 18);

  // fetter Dot bei den Viertelstunden
  for (var i = 0; i < 4; i++) {
    push();
    translate(width / 2, height / 2);
    rotate(winkel);
    noStroke();
    fill(60);
    ellipse(0 + laenge + 8, 0, 12);
    pop();
    winkel = winkel + radians(90);
  }

  // alle 5min ein Dot
  for (var i = 0; i < 12; i++) {
    push();
    translate(width / 2, height / 2);
    rotate(winkel);
    noStroke();
    fill(60);
    ellipse(0 + laenge + 8, 0, 8);
    pop();
    winkel = winkel + radians(30);
  }

  // alle 1min ein kleiner Dot
  for (var i = 0; i < 60; i++) {
    push();
    translate(width / 2, height / 2);
    rotate(winkel);
    noStroke();
    fill(255);
    ellipse(0 + laenge + 8, 0, 3);
    pop();
    winkel = winkel + radians(6);
  }
}

function stundenZeiger() {
  let stunde = hour();
  console.log(stunde); // Ausgabe in der Konsole

  let min = minute(); // wird hier auch gebraucht!

  // wir haben eine 12-Stunden-Anzeige
  // deswegen die Umrechnung…
  let stundenAnzeige;
  if (stunde > 11) {
    stundenAnzeige = stunde - 12;
  } else {
    stundenAnzeige = stunde;
  }

  // Aussehen des Stundenzeigers
  strokeWeight(12);
  stroke(120);

  push();
  translate(xMitte, yMitte);
  // "stundenAnzeige * 30", weil 12 Stunden, entspricht 30° * 12 = 360°
  // "270" damit bei "12Uhr" angefangen wird
  // ACHTUNG: Stunden bleiben nicht auf der Ziffer stehen,
  // sondern bewegen sich innerhalb der Stunde weiter!
  // 30° entspricht einem 5min-Abschnitt, diesen mit den Minuten multipliziert hinzufügen
  rotate(radians(270 + stundenAnzeige * 30 + radians(30) * min));

  line(0, 0, laenge - 30, 0);

  pop();
}

function minutenZeiger() {
  let min = minute();
  console.log(min); // Ausgabe in der Konsole

  // Aussehen des Minutenzeigers
  strokeWeight(8);
  stroke(60);

  push();
  translate(xMitte, yMitte);
  // "sek * 6", weil 60sek pro Minute, entspricht 6° * 60 = 360°
  // "270" damit bei "12Uhr" angefangen wird
  rotate(radians(270 + min * 6));
  // ellipse(0, 0, 5);

  line(0, 0, laenge - 4, 0);

  pop();
}

function sekundenZeiger() {
  let sek = second();

  // Aussehen des Sekundenzeiger
  strokeWeight(4);
  stroke(255, 0, 0);
  fill(255, 0, 0);

  push();
  translate(xMitte, yMitte);
  // "sek * 6", weil 60sek pro Minute, entspricht 6° * 60 = 360°
  // "270" damit bei "12Uhr" angefangen wird
  rotate(radians(270 + sek * 6));
  ellipse(0, 0, 5);

  line(0, 0, laenge, 0);

  pop();
}

Übung

Baut den oben gezeigten Sketch im p5-Webeditor nach und verwendet dabei das heute vorgestellten push & pop!

Ein Teil des dafür benötigten Codes findet Ihr hier:

p5

let winkel;

function setup() {
  createCanvas(960, 350);

  // Gradangabe, um wieviel gedreht wird
  winkel = radians(0);

  background(240);
  rectMode(CENTER);

  let laenge = 140; // Länge der Linie

  fill(255);
  stroke(255,0,0);

  // das "normale Elemente" am oberen Rand des Canvas
  line(0, 0, 0 + laenge, 0);
  rect(0 + laenge + 10, 0, 10);

  // mit for-Loop viele Drehungen erzeugen
  for (var i = 0; i < 36; i++) {
    push();

    translate(????);

    // der Drehpunkt liegt hier bei (width / 2, height / 2);
    rotate(winkel);

    // das "gedrehte Elemente" in der Mitte des Canvas
    line(0, 0, 0 + laenge, 0);
    rect(0 + laenge + 10, 0, 10);

    pop();

    // 36 * 10° = 360° (komplette Drehung)
    winkel = ???
  }
}

Königsaufgabe

Wer bekommt es hin, dass sich der „Stern“ animiert aufbaut – also bei jeder „draw“-Schleife ein weiteres Element angehängt wird?


Dan Shiffmann Videos zu den heutigen Themen


Bots are stupid!

Bots are stupid is a 2D Platformer/Puzzle Game where instead of using live inputs to control the character, the player has to program precise instructions for his Robots to follow to make it through the level.

… ein kostenloses Computer-Spiel, mit dem Ihr die Grundprinzipien des Programmieren lernen könnt …

https://lelegolla.itch.io/bots-are-stupid


Danke!

… und erholsame Feiertage!

Alle Scripte durchsuchen

Weitere Vorträge: