Kliknij na niebieski prostokąt, by upuścić górny klocek na dolny!
Wieża
Przejdź do trybu pełnoekranowego
Celem gry jest ustawienie na sobie możliwie największej liczby klocków.
Kiedy klikniesz na ekran, klocek odbijający się od krawędzi zacznie spadać. Jeżeli nie trafisz dokładnie w moment, w którym dwa klocki są ustawione dokładnie jeden pod drugim, wystająca część klocka spadnie ("debris" = gruz), a kolejny klocek będzie miał mniej miejsca na lądowanie.
Aby jeszcze bardziej utrudnić sprawę, każdy kolejny klocek odbija się szybciej od poprzedniego.
Pełny kod źródłowy z krótkimi wyjaśnieniami
[Linie 1-7] Inicjalizacja HTML5 Canvas.
[8-20] Deklaracja zmiennych i obiektów.
[22-28] Funkcja tworząca nowy klocek po opadnięciu poprzedniego.
[30-33] Funkcja 'Koniec gry'.
[35-89] Główna pętla gry. Spójrzmy na szczegóły, bo to najważniejsza część programu.
[36] Jeżeli gra jest aktywna:
[37-38] Wyczyść poprzednią ramkę i wyświetl wynik.
[39-43] Narysuj wszystkie klocki.
[41] Wybierz kolor klocka.
[44-45] Narysuj gruz.
[46] Jeżeli gra jest w trybie "odbijania" (aktualny klocek odbija się poziomo od ścian):
[47-52] Przesuń aktualny klocek w osi X. Odwróć kierunek, jeśli uderza w ścianę.
[53] Jeżeli gra jest w trybie "spadania" (aktualny klocek został zwolniony):
[54] Przesuń aktualny klocek w osi Y.
[55] Jeżeli wylądował na poprzednim klocku:
[56] Zmień tryb na "odbijanie".
[57] Oblicz długość wystającej części.
[58-60] Jeżeli różnica jest większa niż klocek, to znaczy, że gracz zupełnie nie trafił: koniec gry!
[61-64] Uaktualnienie obiektu "gruz".
[65-72] Czy gruz powinien być po lewej czy prawej stronie klocka?
[73-76] Zwiększ prędkość odbijania.
[77-79] Tworzenie nowego klocka.
[82] Animowanie gruzu - spada, dopóki nie zostanie uaktualniony ponownie w [61-64].
[83-85] Zmienna scrollCounter (licznik przewijania) określa, o ile ramek powinna zostać podniesiona kamera. Jeżeli ma dodatnią wartość, pozycja kamery zostaje uaktualniona.
[88] Oczekiwanie na następną ramkę animacji.
[91] Funkcja przypisująca pierwotne wartości zmiennych na początku gry.
[102-109] Obsługa kliknięcia (myszą lub ekranu dotykowego):
[103-4] W przypadku trybu "gameOver" po prostu restartujemy grę.
[106-7] W przypadku trybu "odbijanie" zmieniamy na tryb "spadek" (zwalniamy klocek).
[111] Inicjalizacja pierwszej gry.
[112] Uruchomienie animacji.
<html> |
<body> |
<canvas id="myCanvas" width="800" height="600"></canvas> |
<script> |
let canvas = document.getElementById("myCanvas"); |
let context = canvas.getContext("2d"); |
context.font = 'bold 30px sans-serif'; |
let scrollCounter, cameraY, current, mode, xSpeed; |
let ySpeed = 5; |
let height = 50; |
let boxes = []; |
boxes[0] = { |
x: 300, |
y: 300, |
width: 200 |
}; |
let debris = { |
x: 0, |
width: 0 |
}; |
|
function newBox() { |
boxes[current] = { |
x: 0, |
y: (current + 10) * height, |
width: boxes[current - 1].width |
}; |
} |
|
function gameOver() { |
mode = 'gameOver'; |
context.fillText('Koniec gry. Kliknij, by zagrać ponownie!', 50, 50); |
} |
|
function animate() { |
if (mode != 'gameOver') { |
context.clearRect(0, 0, canvas.width, canvas.height); |
context.fillText('Wynik: ' + (current - 1).toString(), 100, 200); |
for (let n = 0; n < boxes.length; n++) { |
let box = boxes[n]; |
context.fillStyle = 'rgb(' + n * 16 + ',' + n * 16 + ',' + n * 16 + ')'; |
context.fillRect(box.x, 600 - box.y + cameraY, box.width, height); |
} |
context.fillStyle = 'red'; |
context.fillRect(debris.x, 600 - debris.y + cameraY, debris.width, height); |
if (mode == 'bounce') { |
boxes[current].x = boxes[current].x + xSpeed; |
if (xSpeed > 0 && boxes[current].x + boxes[current].width > canvas.width) |
xSpeed = -xSpeed; |
if (xSpeed < 0 && boxes[current].x < 0) |
xSpeed = -xSpeed; |
} |
if (mode == 'fall') { |
boxes[current].y = boxes[current].y - ySpeed; |
if (boxes[current].y == boxes[current - 1].y + height) { |
mode = 'bounce'; |
let difference = boxes[current].x - boxes[current - 1].x; |
if (Math.abs(difference) >= boxes[current].width) { |
gameOver(); |
} |
debris = { |
y: boxes[current].y, |
width: difference |
}; |
if (boxes[current].x > boxes[current - 1].x) { |
boxes[current].width = boxes[current].width - difference; |
debris.x = boxes[current].x + boxes[current].width; |
} else { |
debris.x = boxes[current].x - difference; |
boxes[current].width = boxes[current].width + difference; |
boxes[current].x = boxes[current - 1].x; |
} |
if (xSpeed > 0) |
xSpeed++; |
else |
xSpeed--; |
current++; |
scrollCounter = height; |
newBox(); |
} |
} |
debris.y = debris.y - ySpeed; |
if (scrollCounter) { |
cameraY++; |
scrollCounter--; |
} |
} |
window.requestAnimationFrame(animate); |
} |
|
function restart() { |
boxes.splice(1, boxes.length - 1); |
mode = 'bounce'; |
cameraY = 0; |
scrollCounter = 0; |
xSpeed = 2; |
current = 1; |
newBox(); |
debris.y = 0; |
} |
|
canvas.onpointerdown = function() { |
if (mode == 'gameOver') |
restart(); |
else { |
if (mode == 'bounce') |
mode = 'fall'; |
} |
}; |
|
restart(); |
animate(); |
</script> |
</body> |
</html> |