Ognie sztuczne

Z Zasoby CoderDojo
Skocz do: nawigacji, wyszukiwania

Zadanie polega na napisaniu symulatora sztucznych ogni

Poziom 3

Co losowy czas z dołu ekranu powinny startować losowe ilości sztucznych ogni, lecieć do góry zgodnie z zasadami grawitacji, a następnie eksplodować kolorowo na mniejsze kawałki i we wszystkich kierunkach od punktu eksplozji w sposób naturalny (lecąc trochę w kierunku resztkowej prędkości eksplodującego “rodzica”). W miarę upływu czasu lecące rakiety powinny zanikać (stawać się coraz bardziej przezroczyste). Fireworki całkiem przezroczyste lub te, które już eksplodowały trzeba usuwać aby zapewnić płynność działania programu.

Poziom 2

Piszemy w p5 (np. editors.p5js.org lub jsbin.com - w tym drugim przypadku nie zapominamy o podpięciu biblioteki p5 w sekcji html).

Piszemy obiektowo. Potrzebujemy jednej definicji obiektu (funkcji konstruktora): function Firework(), funkcji setup() i głównej pętli animacyjnej - funkcji draw() oraz paru zmiennych globalnych (tablicy obiektów i wektora grawitacji).

Ponieważ nasze ognie sztuczne mają wylatywać za każdym razem z innego miejsca (z linii na dole ekranu lub, po wybuchu, z miejsca wybuchu) i jeszcze dodatkowo dziedziczyć trochę prędkości “rodzica”, musimy wyposażyć nasz obiekt w kilka cech.

1. Właściwości (co obiekt wie o sobie)

- pozycja - dół ekranu lub pozycja “rodzica”
- prędkość - jeśli z dołu ekranu to w górę, jeśli w wyniku eksplozji, to odśrodkowa + trochę prędkości “rodzica”
- rozmiar - coraz mniejszy z kolejną eksplozją
- kolor - będzie taki sam jak kolor “rodzica” (na początku - losowy)
- zapalnik - jeśli ustawiony na > 0 - obiekt eksploduje; jeśli na -1 - obiekt zgaśnie nie wybuchając
- czas gaśnięcia - będzie stały dla wszystkich ogni sztucznych

2. Umiejętności (co obiekt umie zrobić)

- funkcja update()
Aktualizacja położenia, prędkości (dodanie grawitacji), przezroczystości (fade), obsługa lontu (fuse), decyzja o wybuchu, decyzja o zgaśnięciu
- funkcja draw()
Rysowanie kształtu ognia sztucznego o odpowiedniej przezroczystości
- funkcja boom()
Eksplozja i wygenerowanie następnych fireworków w miejscu wybuchu

3. Parametry początkowe (konieczne przy wywołaniu funkcji konstruktora - powołaniu obiektu do istnienia): gdzie obiekt ma powstać, z jaką prędkością wyruszyć, czy ma eksplodować po pewnym czasie, jaki ma mieć kolor i rozmiar.

Dla wygody obiekty będziemy przechowywać w tablicy objects = []. W głównej pętli animacyjnej (czyli w funkcji draw()) umieścimy generator fireworków, a następnie dla każdego elementu tablicy wywołamy jego funkcję update() oraz draw(). Ostatnim krokiem będzie przejrzenie całej tablicy i usunięcie elmentów “martwych” aby program nie musiał ich animować.

Zmienne globalne:

let objects = [];
let gravity;

Funkcja konstruktora obiektu Firework:

function Firework(pos, vel, size, fuse, col) {
}

Właściwości obiektu Firework:

 this.color = col;
 this.size = size;
 this.fade = 150;
 this.fuse = fuse;
 this.pos = pos;
 this.vel = vel;
 this.isDead = false;

Umiejętności obiektu Firework:

 // funkcja eksplozji
 this.boom = function() {
   // pętla for, która wykona się 20 * this.size
     // kopiujemy wektor pozycji rodzica (this.pos.copy())
     // losujemy wektor prędkości (p5.Vector.random2D())
     // ustawiamy mu wartość na losową z przedziału np. (1,6)
     // dodajemy prędkość rodzica (this.vel)
     // tworzymy nowy obiekt Firework(), któremu przekazujemy skopiowaną pozycję rodzica, wyliczoną prędkość, 1/3 rozmiaru rodzica, -1 jako lont/zapalnik, kolor rodzica (this.color)
     // wpychamy nowy obiekt na listę obiektów
 };
 // funkcja aktualizacji stanu
 this.update = function() {
   // do prędkości dodaj grawitację
   // do pozycji dodaj prędkość
   // zmniejsz przezroczystość o 1
   // zmniejsz lont o 1
   // jeśli przezroczystość < 0 - obiekt przestaje istnieć (this.isDead = true;)
   // jeśli lont === 0 - obiekt wybucha (this.boom()) i przestaje istnieć
 };
 // funkcja rysująca
 this.draw = function() {
   // ustawiamy kolor na fill(this.color[0],this.color[1],this.color[2], przezroczystość=2*this.fade)
   // rysujemy kółko w this.pos.x i this.pos.y o rozmiarze this.size
 };

Funkcja setup():

 function setup() {
   // tworzymy canvas np. (800,800) - do tego rozmiaru trzeba będzie dostosować wielkość zapalnika (fuse)
   // tworzymy wektor grawitacji (wartość y = 0.05)
 }

Funkcja draw(): - główna pętla animacyjna

 function draw() {
   // czyścimy ekran (background)
   // dla każdego obiektu z tablicy obiektów wywołujemy funckję update() i draw()
   // w drugiej pętli przechodzimy przez tablicę od końca i sprawdzamy, które obiekty przestały istnieć (isDead == true) i je usuwamy (objects.splice(i,1))
   // następnie generujemy nowe ognie sztuczne na dole ekranu co pewien losowy czas:
   if (frameCount % floor(random(40, 120)) === 0) { //co pewien losowy czas wygenerujemy nowe ognie sztuczne
       // losujemy ilość nowych (np. random(1,10)) a następnie w pętli for dodajemy je do tablicy, wykonując za każdym razem poniższe operacje
           // dla każdego nowego obiektu losujemy random(width) i tworzymy wektor jego pozycji początkowej (na dole ekranu)
           // losujemy prędkość początkową (random(-1,1) random(-10,-6)) i zapisujemy ją jako wektor
           // losujemy kolor (i zapisujemy jako tablicę z trzema liczbami od 0 do 255)
           // powołujemy do życia nowy Firework() i przekazujemy mu parametry (pozycję, prędkość, rozmiar=8, fuse=100, kolor)
           // i wpychamy obiekt na listę obiektów

Zadanie dodatkowe (tylko w środowisku editor.p5js.org lub lokalnie na dysku) - spróbuj dodać pliki z dźwiękiem i do każdej eksplozji i lotu rakiety dodać odpowiedni efekt dźwiękowy).

Poziom 1

Co losowy czas z dołu ekranu powinny startować losowe ilości sztucznych ogni, lecieć do góry zgodnie z zasadami grawitacji, a następnie eksplodować kolorowo na mniejsze kawałki i we wszystkich kierunkach od punktu eksplozji w sposób naturalny (lecąc trochę w kierunku resztkowej prędkości eksplodującego “rodzica”). W miarę upływu czasu lecące rakiety powinny zanikać (stawać się coraz bardziej przezroczyste). Fireworki całkiem przezroczyste lub te, które już eksplodowały trzeba usuwać aby zapewnić płynność działania programu.

Piszemy w p5 (editors.p5js.org). Sztuczne ognie będą obiektami, które będziemy przechowywali w tablicy objects.

Zdefiniujmy zmienne globalne (tablicę obiektów i grawitację):

let objects = [];
let gravity;

Napiszmy funkcję konstruktora naszego obiektu:

function Firework(pos, vel, size, fuse, col) {
 // to co obiekt o sobie wie
 this.color = col;
 this.size = size;
 this.fade = 150;
 this.fuse = fuse;
 this.pos = pos;
 this.vel = vel;
 this.isDead = false;
 // to co obiekt umie robić
 this.boom = function() { // wybuch na 20 * this.size elementów
   for (let i = 0; i < 20 * this.size; i++) {
     let p = this.pos.copy();
     let v = p5.Vector.random2D().setMag(random(1, 6));
     v.add(this.vel); // dodajemy prędkość rodzica
     let f = new Firework(p, v, this.size / 3, -1, this.color); // nowy
     objects.push(f);
   }
 };
 this.update = function() { // zwykła aktualizacja położenia i prędkości
   this.vel.add(gravity);
   this.pos.add(this.vel);
   this.fuse--; // lont się skraca
   this.fade--; // przezroczystość coraz większa
   if (this.fuse === 0) { // jak skończy się lont - wybuchamy
     this.boom();
     this.isDead = true;
   }
   if (this.fade < 0) { // obiekt przezroczysty przestaje istnieć
     this.isDead = true;
   }
 };
 this.draw = function() { // po prostu kółko; Uwaga: this.color jest tablicą
   fill(this.color[0], this.color[1], this.color[2], 2 * this.fade);
   ellipse(this.pos.x, this.pos.y, this.size);
 };
}
function newFirework() { // nowy firework - użyjemy go poniżej
 let p = createVector(random(width), height); // z dołu ekranu
 let v = createVector(random(-1, 1), random(-10, -6)); // do góry
 let col = [random(255), random(255), random(255)];
 let f = new Firework(p, v, 8, 100, col); // size=8, fuse=100
 objects.push(f);
}
function setup() {
 createCanvas(windowWidth, windowHeight);
 gravity = createVector(0, 0.05);
 noStroke();
}
function draw() {
 background(0);
 if (frameCount % floor(random(40, 120)) === 0) { // taka sztuczka, dzięki której co randomową ilość klatek z podanego przedziału odpalę rakietę
   for (let i = 0; i < floor(random(1, 10)); i++) { // losujemy ilość nowych
     newFirework();
   }
 }
 objects.forEach(x => x.update()); // dla każdego elementu tablicy
 objects = objects.filter(x => !x.isDead); // zostają tylko te co “żyją”
 objects.forEach(x => x.draw()); // dla każdego elementu tablicy
}

Zadanie dodatkowe 1 - nadaj ogniom sztucznym inny kształt niż kółko. Spróbuj np. użyć dwóch trójkątów - triangle() lub prostokątów - rect() i funkcji push() translate() i pop() dla ułatwienia rysowania, żeby uzyskać efekt gwiazdki. Dla zaawansowanych - użyj funkcji rotate() w połączeniu z powyższymi, żeby nadać gwiazdkom rotację w trakcie lotu. Możesz także zmieniać im wielkość (oprócz przezroczystości)

Zadanie dodatkowe 2 - spróbuj napisać ostatnie 3 komendy funkcji draw() w sposób klasyczny - posługując się pętlami for... Pamiętaj, że przy usuwaniu elementów tablicy funkcją splice(i,1) trzeba przechodzić pętlę od końca tablicy :)