mgr inż. Wacław Libront * Bobowa 2017-2019
ZSO Bobowa, ul. Długoszowskich 1, 38-350 Bobowa, tel: 0183514009, fax: 0183530221, email: sekretariat@zsobobowa.eu, www: zsobobowa.eu
Na środku ekranu kręci się laserowa baza (trzy obrócone kwadraty), z której (klikając myszką) możemy wypuszczać laserowe strzały. Z brzegów ekranu „płyną” w kierunku bazy wrogie (kwadratowe) okręty przeciwnika. Jeśli laserowa strzała trafi w kwadratowy okręt, ten zmniejsza się, rozbija na mniejsze kawałki i znika, a my otrzymujemy punkty. Jeśli wrogi obiekt trafi w bazę punkty się zmniejszają.
Gra zostanie napisana metodą obiektową.
Podstawa
Wykorzystujemy typowy program do animacji: rekurencja i dwie systemowe funkcje
clearTimeout oraz setTimeout.
<canvas id="c1" width="500" height="500"> </canvas> <script> var c = c1.getContext('2d') var w = c.canvas.width; var h = c.canvas.height; var SKOK=50; var CZAS; function ANIMACJA(){ c.clearRect(0,0,w,h); c.strokeStyle="black"; c.strokeRect(0,0,w,h); clearTimeout(CZAS); CZAS=setTimeout(ANIMACJA,SKOK); } ANIMACJA(); </script> |
Obiekt KWA – kwadratowy wróg
Każdy obiekt-kwadrat KWA posiada typowe parametry: środek
(x,y), bok (bok), kąt (kat), szybkość przesuwania w pionie i poziomie (dx,dy) oraz szybkość obrotu (dk).
Metoda przerysuj oblicza nowe położenie i nowy kąt obrotu oraz sprawdza odbicia od brzegów. Na końcu metody wykonujemy od razu metodę
rysuj – nie trzeba jej wtedy wykonywać podczas przerysowywania w animacji. Metoda
rysuj, to kwadrat narysowany od środka.
// obiektowy kwadrat KWA wraz z metodami // tworzymy kwadratowy laser i obiekty wroga function KWA(x,y,bok,kat,kol,dx,dy,dk){ //własności metody KWA this.x=x; this.y=y; this.bok=bok; this.kat=kat; this.kol=kol; this.dx=dx; this.dy=dy; this.dk=dk; // obliczanie położenie, obrotu, odbić //i przerysowywanie kwadratu wroga i lasera this.przerysuj=function(){ var b2=this.bok/2; this.x=this.x+this.dx; this.y=this.y+this.dy; this.kat=this.kat+this.dk; if (this.x-b2 < 0 || this.x+b2 > h){ this.dx=-this.dx; this.dk=-this.dk; } if (this.y-b2 < 0 || this.y+b2 > w){ this.dy=-this.dy; this.dk=-this.dk; } if (this.kat>360){this.kat=0} //automatyczne przerysowanie this.rysuj(); } // rysowanie this.rysuj=function(){ var b2=this.bok/2; c.save(); c.translate(this.x,this.y); c.rotate(this.kat); c.strokeStyle=this.kol; c.strokeRect(-b2,-b2,this.bok,this.bok); c.restore(); } } |
Tworzenie – losowanie
Definiujemy pustą tablicę TKWA i do jej kolejnych elementów wstawiamy obiekty
KWA za pomocą instrukcji new i push.
Losujemy niezbędne parametry do zainicjowania kwadratowych obiektów. W pętli głównej animacji wystarczy wykonać przerysowanie wszystkich obiektów w tablicy,
aby zostały narysowane animowane kwadraty - 5 okrętów wroga.
for (var i=0;i<ileKWA;i++){ var x=Math.random()*(w-maxB)+maxB/2; var y=Math.random()*(h-maxB)+maxB/2; var kat=Math.random()*3; var kol="black"; var dx=Math.random()*maxV-1; var dy=Math.random()*maxV-1; var dk=(Math.random()*maxK-1)*0.1; // tworzenie nowego i przypisanie do tablicy TKWA.push( new KWA(x,y,maxB,kat,kol,dx,dy,dk)); } //w głównej pętli animacji - rysowanie wrogów for (var i=0;i<TKWA.length;i++){ TKWA[i].przerysuj(); } |
Inicjowanie podczas kreowania
Prawdziwy obiekt powinien w swoim wnętrzu posiadać metodę, która potrafi
utworzyć nowy losowy kwadrat. Przyjęło się, że takie inicjujące metody noszą
nazwę init. Co należy zmienić w obiekcie KWA?
Funkcja-obiekt KWA już nie potrzebuje parametrów
Nie potrzebujemy przepisywania parametrów na zmienne wewnętrzne this
Nowa metoda init będzie losować od razu do zmiennych wewnętrznych this
Gdy obiekt KWA zostanie utworzony za pomocą New to od razu wywołana zostanie metodę init
Przenosimy zmienne maxB, maxV i maxK na początek.
Zamiast losowania w pętli wystarczy inicjowanie obiektów KWA w pętli
<canvas id="c1" width="500" height="500"></canvas> <script> var c = c1.getContext('2d') var w = c.canvas.width; var h = c.canvas.height; var SKOK=50; var CZAS; // zmienne główne na początku var TKWA=[]; var maxB=30; var maxV=4; var maxK=4; var ileKWA=5; // NOWY obiektowy kwadrat bez parametrów function KWA(){ //NOWA metoda init - losowanie i tworzenie this.init=function(){ this.x=Math.random()*(w-maxB)+maxB/2; this.y=Math.random()*(h-maxB)+maxB/2; this.bok=maxB; this.kat=Math.random()*3; this.kol="black"; this.dx=Math.random()*maxV-1; this.dy=Math.random()*maxV-1; this.dk=(Math.random()*maxK-1)*0.1; } this.init(); // położenie this.przerysuj=function(){ var b2=this.bok/2; this.x=this.x+this.dx; this.y=this.y+this.dy; this.kat=this.kat+this.dk; if (this.x-b2 < 0 || this.x+b2 > h){ this.dx=-this.dx; this.dk=-this.dk; } if (this.y-b2 < 0 || this.y+b2 > w){ this.dy=-this.dy; this.dk=-this.dk; } if (this.kat>360){this.kat=0} //automatyczne przerysowanie this.rysuj(); } // rysowanie this.rysuj=function(){ var b2=this.bok/2; c.save(); c.translate(this.x,this.y); c.rotate(this.kat); c.strokeStyle=this.kol; c.strokeRect(-b2,-b2,this.bok,this.bok); c.restore(); } } //NOWE tworzenie nowych obiektowych wrogów for (var i=0; i<ileKWA; i++) { TKWA.push(new KWA()); } function ANIMACJA(){ c.clearRect(0,0,w,h); c.strokeStyle="black"; c.strokeRect(0,0,w,h); //przerysowania wrogów for (var i=0;i<TKWA.length;i++){ TKWA[i].przerysuj(); } clearTimeout(CZAS); CZAS=setTimeout(ANIMACJA,SKOK); } ANIMACJA(); </script> |
Obracająca się gwiazda
Na środku obszaru canvas kręci się gwiazda - trzy kwadraty, obrócone co 30
stopni. Metoda init w funkcji-obiekcie GWIAZDA inicjuje własności gwiazdy. Po zainicjowaniu od razu jest wywoływana
this.init().
Metoda przerysuj zmienia kąt i rysuje gwiazdę this.rysuj().
Metoda rysuj rysuje gwiazdę w punkcie (x,y), trzy obrócone kwadraty.
Po zdefiniowaniu obiektu GWIAZDA tworzymy zmienną obiektową GWI
za pomocą instrukcji new.
W części głównej ANIMACJA wystarczy wywołać metodę GWI.przerysuj().
//obiektowa laserowa gwiazda function GWIAZDA(){ //metoda init this.init=function(){ this.x=w/2; this.y=h/2; this.bok=30; this.kat=0; this.kol="blue"; this.dk=3; } // inicjowanie gwiazdy this.init(); // obliczanie i przerysowanie this.przerysuj=function(){ this.kat=this.kat+this.dk; this.rysuj(); } // rysowanie gwiazdy this.rysuj=function(){ var b2=this.bok / 2; var b=this.bok; c.save(); c.strokeStyle=this.kol; c.translate(this.x,this.y); c.rotate(Math.PI * this.kat / 180); c.strokeRect(-b2, -b2, b, b); c.rotate(Math.PI * 30 / 180); c.strokeRect(-b2, -b2, b, b); c.rotate(Math.PI * 30 / 180); c.strokeRect(-b2, -b2, b, b); c.restore(); } //tworzenie gwiazdy //zmienna obiektowa var GWI=new GWIAZDA(); //pętla ANIMACJA // przerysowanie gwiazdy GWI.przerysuj(); |
Laserowy strzał
Laserowy strzał, to czerwona linia biegnąca od środka obracającej się gwiazdy do kursora myszki. Rysowana linia jest nową metodą
strzel w obiekcie GWIAZDA. Rysujemy wtedy, gdy przycisk myszki jest wciśnięty
MyszPrzycisk=true, gdy puścimy przycisk myszy - linia nie jest rysowana.
Myszka – Zdarzenia
Położenie myszki zapisywane jest w zmiennej var MYSZKA={x:0, y:0}.
Zdarzenia związane z ruchem myszki, naciskaniem i zwalnianiem przycisku opisują zdarzenia przypisane do canvas:
onmousemove,
onmousedown,
onmouseup.
// zdarzenia myszki na CANVAS // położenie myszki var MYSZKA = {x: 0, y: 0}; // początkowo myszka nie wciśnięta var MyszPrzycisk = false; // ruch myszki c.canvas.onmousemove = function(e) { MYSZKA.x = e.clientX - 8; MYSZKA.y = e.clientY - 8; }; // gdy przycisk myszki wciśnięty c.canvas.onmousedown = function(e) { MyszPrzycisk = true; }; // gdy przycisk myszki zwolniony c.canvas.onmouseup = function(e) { MyszPrzycisk = false; }; // NOWA metoda w obiekcie GWIAZDA // laserowy strzał this.strzel=function(){ if (MyszPrzycisk){ c.beginPath(); c.moveTo(this.x, this.y); c.strokeStyle="red"; c.lineTo(MYSZKA.x, MYSZKA.y); c.stroke(); } } //POPRAWIONA metoda PRZERYSUJ w obiekcie GWIAZDA this.przerysuj=function(){ this.kat=this.kat+this.dk; this.rysuj(); this.strzel(); } |
Trafienie bazy – kwadrat uderzył w gwiazdę
Jak sprawdzić, czy gwiazda została trafiona przez jeden z kwadratów? Za każdym razem, gdy przerysowywany jest kwadrat trzeba będzie to sprawdzać
w pętli przerysowującej kwadraty w animacji głównej. Ale jak to sprawdzić?
Obliczymy odległość pomiędzy kwadratem a bazą. Jeśli jest mniejsza niż suma połowy kwadratu i połowy bazy, to znaczy, że baza została trafiona. Nową metodę CzyTrafiona umieścimy w obiekcie GWIAZDA. Parametrem metody będzie kwadrat TKWA[i], który będzie "wrzucany" do funkcji celem zbadania w pętli przerysowującej. Jeśli gwiazda trafiona to metoda przyjmuje wartość TRUE, w przeciwnym wypadku FALSE. W pętli głównej animacji umieszczamy rysowanie punktacji. Na początku programu należy również umieścić deklarację zmiennej PUNKTY i przypisać do niej początkową wartość zero.
//zliczanie punktów - na początku programu var PUNKTY=0; //NOWA metoda w obiekcie GWIAZDA //sprawdzanie trafienia kwadratu w gwiazdę this.CzyTrafiona=function(obiekt){ var sx=this.x-obiekt.x; var sy=this.y-obiekt.y; var dl=Math.sqrt(sx*sx+sy*sy); if (dl < this.bok/2 + obiekt.bok/2){ return true } return false; } //POPRAWIONE rysowywanie wrogów w pętli ANIMACJA for (var i=0;i<TKWA.length;i++){ TKWA[i].przerysuj(); //spradzanie trafiania if (GWI.CzyTrafiona(TKWA[i])) { PUNKTY=PUNKTY-10;; } } //punkty - pętla ANIMACJA c.fillText("punkty: " + PUNKTY, w * 0.8, 20); |
Zestrzelenie wrogiego kwadratu
Jak sprawdzić, czy koniec laserowego strzału (położenie myszki) trafiło do
wnętrza któregoś kwadratowego wroga? Za każdym razem, gdy strzelamy obliczamy odległość pomiędzy myszką, a środkiem kwadratu. Jeśli ta odległość jest mniejsza niż połowa boku, to znaczy, że strzał się udał. Zwiększamy
PUNKTY, zmniejszamy bok trafionego kwadratowego wroga, a gdy zniknie, to rodzi się nowy.
Całość w metodzie KWA.przerysuj.
Usuwanie wrogów z listy
Zmniejszenie wroga do zera, aby nie był widoczny, to jedno, ale dalej pozostają jego parametry w pamięci – trzeba je usunąć. Dodawanie na koniec listy realizowaliśmy za pomocą metody
push, a usuwanie wybranego elementu metodą splice – główna pętla animacyjna.
Wróg jest gotowy do usunięcia jeżeli jego bok zmniejszy się do zera.
Nowy wróg zawsze na brzegu
Nowy wróg powinien rodzić się losowo na brzegu. Jak to zrobić? Najpierw wylosujemy czy pojawi się na górnym-dolnym czy na
dolnym-prawym brzegu: Math.random()<0.5 (połowa przypadków). Jeśli góra-dół, to
współrzędną Y losujemy według starych zasad, a X losujemy do momentu, aż będzie na brzegu – pętla while. I podobnie w drugim przypadku. Polecenia umieszczamy w
KWA.init.
//sprawdzanie zestrzelenia //WSTAWIĆ do metody przerysuj obiektu KWA if (MyszPrzycisk) { var dx = MYSZKA.x - this.x; var dy = MYSZKA.y - this.y; var d = Math.sqrt(dx*dx+dy*dy); if (d < b2 || d < 10) { PUNKTY=PUNKTY+1; this.bok=this.bok-2; } } if (this.bok <= 0) { this.init(); // nowy } //usuwanie wrogów z tablicy i z pamięci //WSTAWIĆ do pętli ANIMACJA for (i=TKWA.length-1; i>=0; i=i-1) { if (TKWA[i].bok <= 0) { TKWA.splice(i, 1); } } //WSTAWIĆ DO obiektu KWA metoda init //nowy wróg zawsze losowany na brzegu //albo góra dół if (Math.random()<0.5) { //Y dowolny a X losowo na brzegu this.y=Math.random()*(h-maxB)+maxB/2; do { var xx=Math.random()*(w-maxB)+maxB/2; } while (xx> maxB && xx<w-maxB); this.x=xx; } // albo prawy lewy else { // X dowolny a Y losowo na brzegu this.x=Math.random()*(w-maxB)+maxB/2; do { var yy=Math.random()*(h-maxB)+maxB/2; } while (yy> maxB && yy<h-maxB) this.y=yy; } |
Dzieci
A gdyby tak po trafieniu wroga odrywały się od niego mniejsze wrogie kawałki?
Tworzenie nowych potomków-dzieci powinno odbywać się w programie w tym miejscu,
w którym sprawdzamy co się dzieje po wciskaniu przycisku myszki, czyli KWA.przerysuj.
Tworzymy nowy obiekt KWA dodajemy na listę za pomocą
push.
Ale okazuje się, że każdy z tych kwadracików dziedziczy po obiekcie-rodzicu tworzenie nowego dużego wroga na brzegu po zestrzeleniu potomka! Jak odróżnić zestrzelenie rodzica od zestrzelenia potomka? Każdemu dziecku nadamy dodatkowy parametr DZIECKO, a gdy się rodzi nowy, sprawdzamy, czy nie jest to dziecko.
//POPRAWIONY warunek strzelania //OBIEKT KWA metoda przerysuj if (MyszPrzycisk) { var dx = MYSZKA.x - this.x; var dy = MYSZKA.y - this.y; var d = Math.sqrt(dx*dx+dy*dy); if (d < b2 || d < 10) { PUNKTY=PUNKTY+1; this.bok=this.bok-2; //tutaj rodzą się DZIECI if (this.bok > 10) { var kw = new KWA(); kw.x = this.x + kw.dx * 5; kw.y = this.y + kw.dy * 5; kw.bok = 4; kw.DZIECKO = true; TKWA.push(kw); } } } // POPRAWIONE tworzenie nowego rodzica //dziecko nie może tworzyć nowego wroga //OBIEKT KWA metoda przerysuj if (this.bok <= 0 && !this.DZIECKO) { this.init(); |
LASER
I na koniec cały program ze wszystkimi zmianami
<canvas id="c1" width="500" height="500"> </canvas> <script> var c = c1.getContext('2d') var w = c.canvas.width; var h = c.canvas.height; var SKOK=50; var CZAS; var maxB=30; var maxV=4; var maxK=4; // ***************************************************** KWADRAT function KWA(){ this.init=function(){ // losowo na brzegach if (Math.random()<0.5) { this.y=Math.random()*(h-maxB)+maxB/2; do { var xx=Math.random()*(w-maxB)+maxB/2; } while (xx>maxB && xx<w-maxB); this.x=xx; } else { this.x=Math.random()*(w-maxB)+maxB/2; do { var yy=Math.random()*(h-maxB)+maxB/2; } while (yy>maxB && yy<h-maxB) this.y=yy; } this.bok=maxB; this.kat=Math.random()*3; this.kol="black"; this.dx=Math.random()*maxV-1; this.dy=Math.random()*maxV-1; this.dk=(Math.random()*maxK-1)*0.1; } this.init(); this.przerysuj=function(){ var b2=this.bok/2; this.x=this.x+this.dx; this.y=this.y+this.dy; this.kat=this.kat+this.dk; // odbijanie od brzegów if (this.x-b2 < 0 || this.x+b2 > h){ this.dx=-this.dx; this.dk=-this.dk; } if (this.y-b2 < 0 || this.y+b2 > w){ this.dy=-this.dy; this.dk=-this.dk; } if (this.kat>360){this.kat=0} // trafienie w kwadrat if (MyszPrzycisk) { var dx = MYSZKA.x - this.x; var dy = MYSZKA.y - this.y; var d = Math.sqrt(dx*dx+dy*dy); if (d < b2 || d < 10) { PUNKTY=PUNKTY+1; this.bok=this.bok-2; // kwadrat rozpada się na mniejsze if (this.bok > 10) { var kw = new KWA(); kw.x = this.x + kw.dx * 5; kw.y = this.y + kw.dy * 5; kw.bok = 4; kw.DZIECKO = true; TKWA.push(kw); } } } // nowy rodzi się gdy zmaleje do zera if (this.bok <= 0 && !this.DZIECKO) { this.init(); } this.rysuj(); } this.rysuj=function(){ var b2=this.bok/2; c.save(); c.translate(this.x,this.y); c.rotate(this.kat); c.strokeStyle=this.kol; c.strokeRect(-b2,-b2,this.bok,this.bok); c.restore(); } } var ileKWA=10; var TKWA=[]; for (var i=0; i<ileKWA; i++) { TKWA.push(new KWA()); } // ***************************************************** KWADRAT // ***************************************************** GWIAZDA function GWIAZDA(){ this.init=function(){ this.x=w/2; this.y=h/2; this.bok=30; this.kat=0; this.kol="blue"; this.dk=3; this.dx=0; this.dy=0; } //automatyczne inicjowanie własności gwiazdy this.init(); this.przerysuj=function(){ this.kat=this.kat+this.dk; this.rysuj(); this.strzel(); } this.rysuj=function(){ var b2=this.bok / 2; var b=this.bok; c.save(); c.strokeStyle=this.kol; c.translate(this.x,this.y); c.rotate(Math.PI * this.kat / 180); c.strokeRect(-b2, -b2, b, b); c.rotate(Math.PI * 30 / 180); c.strokeRect(-b2, -b2, b, b); c.rotate(Math.PI * 30 / 180); c.strokeRect(-b2, -b2, b, b); c.restore(); } this.strzel=function(){ if (MyszPrzycisk){ c.beginPath(); c.moveTo(this.x, this.y); c.strokeStyle="red"; c.lineTo(MYSZKA.x, MYSZKA.y); c.stroke(); } } // czy gwiazda trafiona przez kwadrat this.CzyTrafiona=function(obiekt){ var sx=this.x-obiekt.x; var sy=this.y-obiekt.y; var dl=Math.sqrt(sx*sx+sy*sy); if (dl < this.bok/2 + obiekt.bok/2){ return true } else { return false;} } } var GWI=new GWIAZDA(); // ***************************************************** GWIAZDA // ***************************************************** MYSZKA var MYSZKA = {x: 0, y: 0}; var MyszPrzycisk = false; c.canvas.onmousemove = function(e) { MYSZKA.x = e.clientX - 8; MYSZKA.y = e.clientY - 8; }; c.canvas.onmousedown = function(e) { MyszPrzycisk = true; }; c.canvas.onmouseup = function(e) { MyszPrzycisk = false; }; // ***************************************************** MYSZKA var PUNKTY=0; // ***************************************************** ANIMACJA function ANIMACJA(){ c.clearRect(0,0,w,h); c.strokeStyle="black"; c.strokeRect(0,0,w,h); //rysowanie kwadratów for (var i=0; i<TKWA.length; i++){ TKWA[i].przerysuj(); //sprawdzamy vzy kwadrat trafił w gwiazdę if (GWI.CzyTrafiona(TKWA[i])) { PUNKTY=PUNKTY-10;; } } c.fillText("punkty: " + PUNKTY, w * 0.8, 20); //rysowanie gwiazdy GWI.przerysuj(); //usuwanie z tabeli skasowane kwadraty for (i=TKWA.length-1; i>=0; i=i-1) { if (TKWA[i].bok <= 0) { TKWA.splice(i, 1); } } clearTimeout(CZAS); CZAS=setTimeout(ANIMACJA,SKOK); } ANIMACJA(); // ***************************************************** ANIMACJA </script> |
Szablon
<canvas id="c1" width="500" height="500"> </canvas> <script> var c = c1.getContext('2d') var w = c.canvas.width; var h = c.canvas.height; var SKOK=50; var CZAS; function ANIMACJA(){ c.clearRect(0,0,w,h); c.strokeStyle="black"; c.strokeRect(0,0,w,h); clearTimeout(CZAS); CZAS=setTimeout(ANIMACJA,SKOK); } ANIMACJA(); </script> |