Scratch: zegar odliczający czas zadania bez zatrzymywania całej gry

Zegar w Scratch można zbudować przez odejmowanie jednej sekundy po każdym czekaniu, ale takie rozwiązanie łatwo miesza pomiar z wykonywaniem innych czynności. Do odliczania rundy wygodniej wykorzystać stoper projektu i obliczać, ile czasu pozostało. Osobny skrypt kontroluje termin zakończenia, a pozostałe nadal mogą przesuwać postacie i reagować na użytkownika. Nie trzeba zatrzymywać całego projektu, aby wyłączyć naliczanie punktów po upływie limitu.

Poniższy projekt ma 20-sekundową rundę i prostą animację. Nie jest narzędziem do precyzyjnych pomiarów ani gwarantowanym alarmem działającym po uśpieniu komputera. Przeglądarka i stan urządzenia wpływają na wykonywanie programu. Celem jest poznanie rozdzielenia czasu, stanu gry i prezentacji. Najpierw budujemy przewidywalny koniec, dopiero później dodajemy punkty i kolejne typy zadań.

Stoper mierzy czas, a zmienna pokazuje wynik obliczenia

Scratch udostępnia wartość stopera oraz polecenie jego zerowania w kategorii czujników. Wartość stopera opisuje czas od ostatniego wyzerowania w projekcie. Nie tworzymy własnej zmiennej o identycznej nazwie, aby nie pomylić dwóch różnych rzeczy. Zmienne w tym ćwiczeniu nazywamy Limit, Pozostało i Gra. Limit określa długość rundy, Pozostało służy do prezentacji, a Gra oznacza jej aktywność.

Na początku ustawiamy Limit na 20. Zasada obliczenia brzmi: Pozostało = Limit minus aktualny stoper. Jeśli minęło około 6,5 sekundy, pozostaje około 13,5. Nie trzeba co sekundę zmniejszać poprzedniej wartości o 1. Program ponownie oblicza wynik z pomiaru. Dzięki temu czas wykonywania innych czynności w pętli nie jest automatycznie dodawany jako kolejne opóźnienie do własnego licznika odejmowań.

Stoper jest wspólnym zasobem projektu. Jeśli inny skrypt wyzeruje go w środku rundy, odliczanie zacznie zachowywać się inaczej. Ustalamy więc jedno miejsce odpowiedzialne za reset. Nie zerujemy stopera przy każdym punkcie, kliknięciu ani odbiciu postaci. Jeśli kilka zadań potrzebuje różnych terminów, można zapisywać ich własne czasy początku zamiast wielokrotnie resetować wspólny pomiar. To rozwinięcie warto dodać dopiero po zrozumieniu jednej rundy.

Przygotowanie kończymy przed rozpoczęciem pomiaru

Po zielonej fladze ustawiamy Gra na 0 i przywracamy wygląd sceny. Nadajemy komunikat przygotowania z czekaniem, aby duszki zdążyły wrócić do swoich pozycji i wyczyścić wynik. Następnie ustawiamy Limit, zerujemy stoper i dopiero wtedy zmieniamy Gra na 1. Dzięki temu czas poświęcony na wprowadzenie lub przygotowanie nie skraca właściwej rundy. To ważne zwłaszcza wtedy, gdy przed startem pokazujemy krótką instrukcję.

W podstawowej wersji nie dodajemy osobnego odliczania „trzy, dwa, jeden”. Jeśli je wprowadzimy, musi nastąpić przed zerowaniem stopera rundy. Inaczej część limitu zostanie zużyta jeszcze przed możliwością działania. Warto zapisać kolejność na kartce: reset stanu, przygotowanie, ewentualna zapowiedź, reset czasu, start. Program powinien odzwierciedlać ten plan, a nie uruchamiać kilka niezależnych skryptów zgadujących, kiedy rozpoczyna się gra.

Po zmianie Gra na 1 nadajemy komunikat startu do duszków. Skrypt zarządzający czasem od razu przechodzi do obserwowania stopera. Odbiorcy startu nie powinni ponownie ustawiać Limitu ani resetować czasu. Ich zadaniem jest ruch, animacja lub obsługa kliknięć zgodnie z aktualnym stanem. Jedno centrum odpowiedzialności za pomiar ułatwia późniejsze sprawdzenie, dlaczego runda kończy się zbyt wcześnie albo nigdy nie dochodzi do finału.

Pozostałe sekundy wyświetlamy bez wartości ujemnych

W pętli aktywnej rundy obliczamy różnicę Limit minus stoper. Do prostego wyświetlania pełnych sekund można zastosować zaokrąglenie w górę, dostępne w operatorze funkcji matematycznych, lub pozostawić wartość dziesiętną podczas budowania. Wybór musi być świadomy. Zaokrąglenie zwykłe może pokazać 0 jeszcze przed rzeczywistym końcem. Nie chcemy, żeby gracz widział zero, podczas gdy logika nadal pozwala zdobywać punkty przez zauważalną część sekundy.

Gdy stoper osiągnie lub przekroczy Limit, ustawiamy Pozostało na 0. Nie pokazujemy kolejnych ujemnych liczb. Warunek końca opieramy na rzeczywistym porównaniu stopera z Limitem, a nie na samym zaokrąglonym napisie. To oddziela pomiar od prezentacji. Wartość widoczna na scenie ma być czytelna, ale nie musi przechowywać całej dokładności potrzebnej do decyzji o zakończeniu rundy.

Na czas budowania można pozostawić widoczne monitory stopera, Limitu i stanu. Pomagają zauważyć przypadkowy reset lub niewłaściwą wartość. W wersji dla gracza chowamy techniczne pola, jeśli nie pomagają w obsłudze. Wystarczy jasny napis z pozostałym czasem i informacja, co należy robić. Nie zmuszamy użytkownika do rozumienia nazw wewnętrznych zmiennych, aby mógł uczestniczyć w prostym zadaniu.

Zakończenie nadajemy tylko raz

Skrypt czasu może działać w pętli do chwili, gdy stoper osiągnie Limit. Po wyjściu ustawia Gra na 0, Pozostało na 0 i nadaje komunikat Koniec. Taka skończona sekwencja uruchamia finał jeden raz. Jeśli użyjemy pętli zawsze z samym warunkiem „czas przekroczony”, komunikat może być nadawany wielokrotnie. Dymki i dźwięki końcowe zaczęłyby wtedy restartować się bez przerwy.

Można również zastosować w pętli sprawdzenie aktywnego stanu: zakończenie wykonuje się tylko wtedy, gdy Gra nadal wynosi 1. Po zmianie na 0 ten warunek przestaje być prawdziwy. Najważniejsze jest jednoznaczne przejście między stanami. Nie potrzebujemy kilku niezależnych skryptów kończących rundę na podstawie różnych wersji czasu. Jedno miejsce odpowiada za termin, pozostałe reagują na informację, że gra już nie trwa.

Jeśli zadanie może skończyć się wcześniej, na przykład po zebraniu kompletu przedmiotów, trzeba uwzględnić to w warunku pętli czasu. Może kończyć obserwowanie również po Gra=0, a finał uruchamiać tylko wtedy, gdy to upływ czasu zamknął aktywną rundę. Inaczej drugi komunikat końca mógłby pojawić się po wcześniejszej wygranej. To przykład zależności, którą warto zaplanować przed połączeniem zegara z większą grą.

Ruch i punktowanie sprawdzają aktywny stan

Duszek poruszający się po scenie wykonuje swoją pętlę tylko podczas Gra=1. Może przesuwać się i odbijać od brzegów, podczas gdy skrypt stopera działa niezależnie. Nie wkładamy całej animacji do środka jednego długiego bloku oczekiwania na koniec. Oddzielenie zadań pozwala zachować ruch w czasie pomiaru. Odbiorca widzi odliczanie i może wykonywać czynności, a nie czeka na zakończenie zamrożonego programu.

Jeżeli kliknięcie daje punkt, skrypt kliknięcia najpierw sprawdza Gra. Po finale nie powinien naliczać wyniku, nawet jeśli duszek nadal jest widoczny. Samo zatrzymanie animacji nie wyłącza automatycznie wszystkich zdarzeń. To częsta różnica między wyglądem a logiką. Warto nacisnąć obiekt po końcu rundy i sprawdzić wynik. Jeśli nadal rośnie, brakuje warunku aktywności właśnie w skrypcie punktowania.

Komunikat Koniec może uruchomić dymek, napis lub spokojny dźwięk. Nie używamy od razu zatrzymania wszystkiego, jeśli chcemy, aby te elementy wykonały swój przebieg. Zmiana Gra wyłącza właściwe działania, pozostawiając możliwość prezentacji finału. Każdy duszek powinien mieć jasno określoną rolę: jedne przestają reagować, inne pokazują podsumowanie. Zatrzymanie całego projektu jest odrębną decyzją i nie zastępuje takiego podziału.

Krótka runda ułatwia sprawdzanie

Do prób ustawiamy Limit na 5, żeby nie czekać za każdym razem pełnych 20 sekund. Oczekujemy startu po przygotowaniu, czytelnego odliczania, jednego finału i braku punktów po końcu. Następnie przywracamy docelowy limit i powtarzamy pełną rundę. Nie zostawiamy testowej wartości w wersji dla odbiorców. Warto przed zapisaniem sprawdzić pole Limitu, nie tylko tekst instrukcji informujący o długości zadania.

Osobna próba polega na zmianie czasu przygotowania. Wydłużamy wprowadzenie i sprawdzamy, czy właściwa runda nadal ma ten sam limit. Jeśli się skróciła, stoper został uruchomiony za wcześnie. Sprawdzamy też ponowne uruchomienie w środku działania. Wynik, stan, położenia i pomiar mają wracać do początku. Prawidłowy reset nie może zależeć od tego, czy poprzednia próba zakończyła się naturalnie, czy została przerwana.

Przy dużej liczbie efektów lub nieaktywnej karcie wykonanie skryptów może być mniej płynne. Ten projekt nie służy do odmierzania krytycznych terminów ani zastępowania systemowego alarmu. Jeśli zadanie wymaga wyłącznie wygodnego minutnika, lepsze może być narzędzie do tego przeznaczone. Tutaj czas jest elementem nauki programowania i mechaniką gry. Uczciwe określenie tego zakresu pomaga uniknąć oczekiwań, których prosty projekt w przeglądarce nie powinien obiecywać.

Rozwijamy mechanizm bez mieszania zegarów

Po zapisaniu podstawowej wersji można dodać kilka długości rund. Użytkownik wybiera Limit przed startem, a reszta mechanizmu pozostaje taka sama. Nie trzeba tworzyć osobnego skryptu dla 10, 20 i 30 sekund. Warto jednak sprawdzić, czy wybrana wartość jest dodatnia i czy nie została zmieniona podczas trwania gry. Zasady wyboru powinny być widoczne w interfejsie, a nie ukryte w przypadkowym monitorze zmiennej.

Pauza wymaga dodatkowego modelu czasu. Samo ustawienie Gra na 0 zatrzyma reakcje, lecz stoper nadal może mierzyć upływ. Jeśli chcemy wznowić rundę z zachowanym pozostałym czasem, trzeba zapisywać przerwy lub zmienić sposób wyznaczania terminu. Nie nazywamy prostego wyłączenia ruchu pełną pauzą zegara. To osobne rozszerzenie, które warto zaprojektować i sprawdzić na małym przykładzie, zamiast dopisywać jedną zmienną bez analizy skutków.

Najważniejsze jest rozdzielenie odpowiedzialności. Stoper dostarcza pomiar, obliczenie wyznacza pozostały czas, stan określa możliwość działania, a komunikat uruchamia finał. Gdy te części są czytelne, można wykorzystać mechanizm w quizie, grze zręcznościowej albo własnym zadaniu. Najpierw jednak potwierdzamy reset, jednorazowy koniec i brak reakcji po zakończeniu. To one decydują o poprawnym działaniu bardziej niż ozdobny wygląd odliczających cyfr.