Napiszmy grę w JavaScript: Daj mi płótno, a namaluję świat
Cześć! To jest fragment książki JavaScript od podstaw, która ma pomóc w nauce programowania od zera.
W tym rozdziale tworzymy płótno (canvas) i rysujemy na nim plansze, piłeczki i liczniki punktów.
Nadszedł czas, aby wykorzystać zdobytą wiedzę w praktyce. JavaScript to bardzo uniwersalny język, więc może być wykorzystany do wielu rzeczy1. Chciałem jednak, by nasz przykład był prosty i wymagał możliwie jak najmniej wiedzy z dziedziny innych języków programowania, a dodatkowo był zabawny. Postanowiłem zatem, że napiszemy razem grę. Niemożliwe? Wręcz przeciwnie, stworzymy odpowiednik gry Pong z 1972 roku. Zasady są proste: dwóch graczy odbija piłeczkę, gdy któryś ją przepuści, piłeczka wraca na środek, a jego przeciwnik zdobywa punkt.
Co ciekawe, posiadasz już większość umiejętności niezbędnych by taką grę napisać. Brakuje nam tylko wiedzy jak wyświetlać elementy widoku oraz jak reagować na naciśnięcie przycisków przez graczy. Pierwszym tematem zajmiemy się w tym rozdziale, a drugim w kolejnym, zatytułowanym: Przejmujemy sterowanie. Reszta to właściwie praktyczne zastosowanie tego, czego nauczyliśmy się w poprzednich działach.
Płótno, czyli HTML canvas
Dla uproszczenia grafika w grach sprowadza się do tego, że na obszarze, na którym jest wyświetlana, rysowane są kolejne obrazki. Podobnie jak w filmach, gdzie sceny są szybko zmieniającymi się kolejnymi klatkami — takie "ruchome obrazki". By odnieść wrażenie postaci w ruchu wyświetlane są kolejno obrazki na następujących po sobie etapach tego ruchu. Do tego dodajemy przesunięcie — jeśli postać porusza się w prawo, to każdy kolejny obrazek powinien być narysowany nieco bardziej na prawo od poprzedniego. Należy też zetrzeć poprzedni obrazek, tak aby nie został po nim ślad. I tym samym odpowiednia ilość szybko wyświetlanych obrazków daje złudzenie płynnego poruszania się postaci.
W naszym przypadku nie zajmiemy się animowaniem postaci, ale zamiast tego wyświetlimy paletki oraz piłeczkę. Gdy nasze przedmioty będą w ruchu, wtedy narysujemy je w nieco innym miejscu.
Zacznijmy od miejsca, które umożliwi nam wyświetlanie poszczególnych elementów. W tym celu wykorzystamy Canvas (po angielsku płótno) z HTML2. Stwórz dokument z końcówką .html
o następującej treści:
<!DOCTYPE html>
<html>
<body>
<canvas id="gameCanvas" width="800" height="500"
style="border:1px solid" />
<script>
// Twój kod JavaScript tutaj
</script>
</body>
</html>
To, co dodaliśmy względem pierwotnego kodu przedstawionego w rozdziale Pierwszy program to element typu canvas
. Nadaliśmy mu id gameCanvas
, szerokość width
równą 800 pikseli, oraz wysokość height
równą 500 pikseli. Daliśmy mu też ramkę poprzez ustawienie style
na wartość border:1px solid
3. Na ten moment powyższy kod w zupełności nam wystarczy.
W miejscu // Twój kod JavaScript tutaj
będzie znajdował się nasz kod JavaScript. Tak więc następne prezentowane fragmenty kodu powinny zostać umieszczone dokładnie tam.
Pierwszy rysunek
Aby narysować coś na naszym płótnie, potrzebujemy się do niego odnieść. Aby rozpocząć rysunek, najpierw należy odnieść się do samego elementu canvas
. Możemy to zrobić po id, dzięki funkcji document.getElementById
. Przyjmuje ona id jako argument, a zwraca referencję (odniesienie) do elementu widoku. Używając jej, możemy dany element zmodyfikować albo sprawdzić jego właściwości.
const canvas = document.getElementById("gameCanvas");
Canvas
pozwala na różne tryby rysowania, takie jak 2D czy 3D. Określamy je przy pobieraniu obiektu zwanego kontekstem. Zawiera on charakterystyczne metody dla wybranego trybu. Na przykład dla 2D, użyjemy canvas.getContext("2d")
, po czym zapiszemy go do zmiennej o nazwie ctx
.
const ctx = canvas.getContext("2d");
Przy użyciu tej zmiennej jesteśmy już w stanie rysować potrzebne nam elementy, na przykład:
- prostokąt, przy użyciu funkcji
fillRect
; - tekst, przy użyciu funkcji
fillText
; - piłeczkę, przy użyciu
arc
orazfill
.
Wykorzystajmy je kolejno do narysowania paletki, punktacji oraz piłeczki.
Paletka
Najprostszym z potrzebnych nam elementów do namalowania jest paletka. Jest to nic innego jak cienki prostokąt, który możemy namalować przy użyciu metody fillRect
. Przyjmuje ona 4 argumenty:
- współrzędne
x
iy
lewego górnego rogu prostokąta w pikselach, - szerokość i wysokość prostokąta w pikselach.
Współrzędne na płótnie są liczone od lewego górnego rogu obrazka. x
rośnie, gdy przesuwamy się w prawo, a y
jak przesuwamy się w dół.
Zmieniając więc pierwsze dwie wartości, zmieniamy pozycję prostokąta, a przez pozostałe dwie jego wymiary.
ctx.fillRect(100, 100, 150, 100);
ctx.fillRect(400, 200, 200, 100);
Nasza paletka będzie miała standardowo: szerokość 20 i wysokość 100 pikseli. Poniżej przedstawiam cały kod wystarczający, by ją zobaczyć.
<!DOCTYPE html>
<html>
<body>
<canvas id="gameCanvas" width="800" height="500"
style="border:1px solid" />
<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
ctx.fillRect(10, 10, 20, 100);
</script>
</body>
</html>
Skoro będziemy potrzebowali wielokrotnie rysować takie paletki, wydzielmy funkcję, która się tym zajmie. Nazwijmy ją drawPaddle
. Paletki mogą się przemieszczać w górę i w dół, więc funkcja musi przyjmować jako argument wartość y
, informującą o aktualnym położeniu obiektu. Potrzebujemy dwóch paletek, a więc będą się znajdowały pod różnymi wartościami x
, tą zmienną również przekażemy jako argument. Wymiary paletki (wysokość i szerokość) pozostaną niezmienione.
function drawPaddle(x, y) {
ctx.fillRect(x, y, 20, 100);
}
drawPaddle(770, 100);
drawPaddle(10, 300);
Tekst
Następną rzeczą, której potrzebujemy, jest wyświetlenie tekstu. Przy jego pomocy pokażemy liczbę punktów każdego z graczy. Możemy go wyświetlić przy pomocy funkcji fillText
o 3 argumentach:
- tekście do wyświetlenia,
- współrzędnych x i y lewego górnego rogu obszaru, na którym wypisywany jest tekst.
Przykładowo moglibyśmy wyświetlić tekst następująco:
ctx.fillText("Hello World", 10, 50);
Tak wyświetlony tekst będzie domyślnie bardzo mały.
Aby ustawić wielkość tekstu oraz czcionkę, powinniśmy ustawić właściwość font
w obiekcie ctx
. Wystarczy zrobić to raz, na początku działania programu. Dopóki nie zmienimy tej właściwości, ustawienie to zapisze się jako domyślne. Ustawmy tą wartość na "30px Arial"
(Arial to nazwa czcionki, a 30px
to wielkość 30 pikseli).
ctx.font = "30px Arial";
Po wydzieleniu funkcja do wyświetlania tekstu powinna wyglądać następująco:
ctx.font = "30px Arial";
function drawText(text, x, y) {
ctx.fillText(text, x, y);
}
// Przykładowe użycie
drawText("3", 300, 50);
drawText("6", 500, 50);
W naszej grze, punkty wyświetlać będziemy przy pomocy następującej funkcji:
function drawPoints(text, x) {
ctx.fillText(text, x, BOARD_Y);
}
Piłeczka
Wyświetlenie koła jest największym wyzwaniem, ponieważ nie ma do tego dedykowanej funkcji. Aby nie wnikać w zawiłości tej technologii, uwierz mi na słowo, że nasza funkcja do wyświetlania koła będzie wyglądała następująco:
function drawCircle(x, y, r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
Gdzie:
x
iy
to współrzędne środka kołar
to promień koła, czyli odległość od środka do boku, determinująca wielkość naszej piłeczki.
Niestety w programowaniu nawet doświadczeni programiści często nie wiedzą jak coś zrobić i szukają gotowych rozwiązań w internecie. Za dobrą praktykę przyjmuje się zrozumienie znalezionego rozwiązania przed jego zastosowaniem. Poniżej przedstawiłem gotowe rozwiązanie, a Twoim zadaniem jest poszukanie w internecie, jak ono działa4.
Czyszczenie płótna
Brakuje nam już tylko jednej funkcji do rysowania na płótnie. Po narysowaniu elementów w jednym miejscu, musimy je zmazać zanim będziemy mogli wyświetlić je gdzie indziej. Bez tego nasze płótno wypełni się nieaktualnymi rysunkami. Do wyczyszczenia płótna użyjemy funkcji clearRect
, która podobnie do funkcji fillRect
przyjmuje:
- współrzędne lewego górnego punktu powierzchni, dla której chcemy wyczyścić płótno,
- wysokość i szerokość tej powierzchni.
Aby wyczyścić całe płótno, potrzebujemy zacząć od punktu (0, 0) i użyć szerokości i wysokości płótna. Te natomiast możemy pobrać z referencji tego elementu przy użyciu odpowiednio canvas.width
i canvas.height
.
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
Kod, z którym powinieneś skończyć po tym rozdziale, znajdziesz pod linkiem:
Po tej sekcji powinniśmy mieć już płótno, na którym możemy malować oraz funkcje do rysowania trzech koniecznych dla nas elementów. Brzmi jak dobry początek.
Patrz rozdział Co jeszcze można robić w JavaScript.
HTML to język opisujący jak ma wyglądać strona. Jest to jednak temat na osobną książkę, więc po prostu przeprowadzę Cię przez ten jeden krok, który w tym języku musimy zrobić.
Aby nauczyć się HTML, polecam szukać w Google pod hasłem "best free html course".
Wpisz w Google "js canvas arc".