Don't Touch The Spikes

Gra polega na sterowaniu postacią (Ptakiem), która automatycznie porusza się poziomo między dwiema ścianami. Gracz musi klikać/skakać, aby utrzymać postać w powietrzu i unikać kolców pojawiających się losowo na ścianach. Każde udane odbicie od ściany to punkt i wyzwalacz do pojawienia się nowej, trudniejszej przeszkody. Śmierć następuje po kontakcie z kolcem.

Written By Coder Matthew

Last updated 6 months ago


1) Grafiki użyte w tej grze

Tak to się prezentuje już w samym unity
  • Sky

    • Scena Sky w folderze Assets/Scenes ma jeden, bardzo specyficzny cel: służy jako wizualne tło dla głównej mechaniki gry.

    • Zamiast umieszczać nieruchome tło (np. gradient lub obraz nieba) bezpośrednio na głównej scenie gry (SampleScene), tworzy się je w oddzielnej scenie.

    • Sky jest ładowana równolegle (metodą Additive) z główną sceną (SampleScene) w trakcie uruchamiania gry.

    • Pozwala to na zachowanie czystej hierarchii w SampleScene (gdzie znajdują się tylko Ptak, Ściany i Kolce) oraz ułatwia ponowne użycie tego samego tła w innych projektach bez konieczności jego kopiowania.


2) Hierarchia w Unity

Wszystkie obiekty
  • Main Camera

    • Odpowiada za widok gry. Ustawia perspektywę i pole widzenia na główny obszar rozgrywki 2D.

    • Renderuje wszystkie elementy sceny (Gracza, Przeszkody, Tło i UI).

  • Sky

    • Obiekt wizualny, który renderuje tło.

    • Używany jako statyczny kontekst wizualny, załadowany w scenie.

  • Bird

    • Reprezentuje postać sterowaną przez gracza. Obiekt zawiera skrypt BirdController.cs.

    • Jest to główny Gracz. Wykrywa kolizje ze ścianami (Wall) dla punktacji i odbicia, oraz z kolcami (Spike) dla śmierci.

  • Square …

    • Fizyczne granice boczne. Obiekty te posiadają tag "Wall".

    • Ptak odbija się od nich, zmieniając kierunek. Kontakt z nimi jest wyzwalaczem dla naliczenia punktu i wywołania SpikeManager.

  • scoreText

    • Element tekstowy (TMP_Text) wyświetlający aktualny wynik gracza.

    • Jest na bieżąco aktualizowany przez skrypt BirdController po każdym udanym odbiciu od ściany.

  • tapToJumpText

    • Element tekstowy (TMP_Text) wyświetlający instrukcję startową ("TAP TO JUMP").

    • Jest aktywny na początku gry i wyłączany przez BirdController w momencie, gdy gracz rozpocznie grę pierwszym kliknięciem.

  • resultText

    • Element tekstowy (TMP_Text) wyświetlający końcowy wynik.

    • Jest nieaktywny na starcie i aktywowany przez funkcję Die() w skrypcie BirdController po śmierci Ptaka.

  • … Spikes

    • Kontenery nadrzędne dla obiektów kolców, zarządzane przez skrypt SpikeManager.cs.

    • Umożliwiają SpikeManager na łatwe grupowanie, ukrywanie i losowe aktywowanie kolców na danej krawędzi, zwiększając trudność gry.


3) Skrypty użyte w tej grze

Tak właśnie wyglądają gotowe skrypty w unity
  • BirdController

    • Główny kontroler Ptaka (Gracza), odpowiedzialny za ruch poziomy i skoki, logikę odbicia od ścian, punktację i zarządzanie śmiercią/restartem. Realizuje mechanikę zmiany kierunku po uderzeniu w ścianę.

    • Uruchamia grawitację i ruch po pierwszym wejściu. Kontroluje ciągły ruch poziomy (horizontalSpeed) oraz nadaje pionowy impuls (jumpForce) po kliknięciu myszą lub spacją.

    • Wykrywa uderzenie w "Wall". Zmienia kierunek Ptaka, odwraca jego grafikę (skalę), nalicza punkt i wywołuje akcję generowania kolców na przeciwnej ścianie (DelayedSwitchSpikes).

    • Wykrywa zderzenie z obiektami otagowanymi jako "Spike" (kolce), co prowadzi do sekwencji śmierci (Die()) i restartu sceny.

  • SpikeManager

    • Odpowiada za generowanie i losowe rozmieszczanie kolców na ścianach oraz dynamiczne zwiększanie poziomu trudności gry.

    • Wywołany przez BirdController po odbiciu od ściany. Najpierw usuwa wszystkie kolce, a następnie wybiera przeciwną ścianę do lotu Ptaka.

    • Zmienna currentSpikeCount (początkowo 2) jest zwiększana o 1 po każdej udanej rundzie (do maksymalnie 4), co powoduje, że Ptak ma coraz mniej miejsca do wylądowania.

    • Stosuje algorytm mieszania do losowania, które z dostępnych miejsc kolców na ścianie mają zostać aktywowane.


4) Całe skrypty

Tutaj znajdziesz całe skrypty bez owijania w bawełne 😊, każdy blok kodu jest starannie zakomentowany tak żebyś w miarę mógł ogarnąć o co chodzi nawet jak nie znasz unity i C#.

Example
using UnityEngine; using UnityEngine.SceneManagement; using TMPro; using UnityEngine.Networking; using System.Collections; public class BirdController : MonoBehaviour { // --- Parametry Ruchu, Fizyki i UI --- public float horizontalSpeed = 4.5f; public float jumpForce = 6f; public TMP_Text scoreText; public TMP_Text tapToJumpText; public TMP_Text resultText; // --- Prywatne Zmienne Stanu --- private Rigidbody2D rb; private int direction = 1; private bool isDead = false; private bool gameStarted = false; private int score = 0; // Stała określająca opóźnienie pojawienia się kolców po odbiciu (0.2 sekundy). private const float spikeDelay = 0.2f; void Start() { // [BLOK KOMENTARZY: Inicjalizacja Gry] // Ustawienie fizyki (zablokowanie rotacji, wyłączenie grawitacji na start). rb = GetComponent<Rigidbody2D>(); rb.freezeRotation = true; rb.gravityScale = 0f; UpdateScoreText(); // Aktywacja/Dezaktywacja elementów UI na start (widoczne "TAP TO JUMP"). if (tapToJumpText != null) tapToJumpText.gameObject.SetActive(true); if (resultText != null) resultText.gameObject.SetActive(false); // Upewnienie się, że kolce są ukryte na starcie. if (SpikeManager.Instance != null) SpikeManager.Instance.ResetSpikes(); } void Update() { if (isDead) return; // [BLOK KOMENTARZY: Obsługa Startu Gry] // Czeka na pierwsze kliknięcie/spacę, aby włączyć grawitację i rozpocząć grę. if (!gameStarted) { if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) { gameStarted = true; rb.gravityScale = 1f; // Włącz grawitację if (tapToJumpText != null) tapToJumpText.gameObject.SetActive(false); } return; } // [BLOK KOMENTARZY: Skok (Flap)] // W trakcie gry, każde kliknięcie/spacja nadaje Ptakowi pionową siłę. if (Input.GetMouseButtonDown(0) || Input.GetKeyDown(KeyCode.Space)) { rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce); } } void FixedUpdate() { // [BLOK KOMENTARZY: Ciągły Ruch Poziomy (Fizyka)] // Utrzymuje stałą prędkość poziomą w aktualnym kierunku (direction). if (isDead || !gameStarted) return; rb.linearVelocity = new Vector2(direction * horizontalSpeed, rb.linearVelocity.y); } void OnCollisionEnter2D(Collision2D collision) { if (isDead) return; // [BLOK KOMENTARZY: Uderzenie w Ścianę (Wall) - Odbicie i Punktacja] if (collision.gameObject.CompareTag("Wall")) { // 1. Odbicie i Grafika direction *= -1; // Odwrócenie kierunku Vector3 scale = transform.localScale; scale.x *= -1; // Odwrócenie grafiki Ptaka transform.localScale = scale; // 2. Punktacja score++; UpdateScoreText(); // 3. Generowanie Kolców (z opóźnieniem) if (SpikeManager.Instance != null) { // Anuluje poprzednie wywołanie, aby uniknąć błędów przy szybkich odbiciach. CancelInvoke(nameof(DelayedSwitchSpikes)); // Planuje włączenie kolców po krótkim opóźnieniu (aby Ptak zdążył się odbić). Invoke(nameof(DelayedSwitchSpikes), spikeDelay); } } } void DelayedSwitchSpikes() { // [BLOK KOMENTARZY: Opóźnione Generowanie Kolców] // Właściwe wywołanie logiki generowania kolców po minięciu czasu 'spikeDelay'. if (SpikeManager.Instance != null && !isDead) { SpikeManager.Instance.SwitchSpikes(direction); } } void OnTriggerEnter2D(Collider2D other) { if (isDead) return; // [BLOK KOMENTARZY: Kolizja z Kolcem (Spike) - Śmierć] // Wykrywa dotknięcie Triggera otagowanego jako "Spike" (kolce). if (other.CompareTag("Spike")) { Die(); // Koniec gry. } } void Die() { // [BLOK KOMENTARZY: Sekwencja Śmierci] isDead = true; // Zabezpieczenie: Anuluje wszystkie oczekujące akcje. CancelInvoke(nameof(DelayedSwitchSpikes)); // Animacja śmierci: odblokowanie rotacji, nadanie pędu i obrotu. rb.freezeRotation = false; rb.linearVelocity = new Vector2(0, 2f); rb.AddTorque(500f); // Wysyłka wyniku i wyświetlenie ekranu Game Over StartCoroutine( SendScore( 22, // id score // score ) ); if (resultText != null) { resultText.text = "WYNIK: " + score.ToString(); resultText.gameObject.SetActive(true); } Invoke("RestartLevel", 3f); // Restart sceny po 3 sekundach. } void RestartLevel() { // [BLOK KOMENTARZY: Restart] // Ponowne załadowanie aktywnej sceny. SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); } void UpdateScoreText() { // [BLOK KOMENTARZY: Aktualizacja UI] // Aktualizuje wyświetlany wynik. if (scoreText != null) scoreText.text = score.ToString(); } public IEnumerator SendScore(int gameId, int score) { // [BLOK KOMENTARZY: Procedura HTTP] // Standardowa korutyna do wysyłania wyniku na zewnętrzny serwer. string url = "https://bit.elita.fun/execs/api/set_score.php?game_id=" + gameId + "&score=" + score; WWWForm f = new WWWForm(); f.AddField("pass", System.DateTime.Now.ToString("mm") + "EcS9YcvMtWNnW0N8yi1mVK"); using (UnityWebRequest r = UnityWebRequest.Post(url, f)) { yield return r.SendWebRequest(); Debug.Log(r.result == UnityWebRequest.Result.Success ? r.downloadHandler.text : "ERR: " + r.error); } } }