Flappy Bird

Gra polega na sterowaniu małym bohaterem (zazwyczaj ptakiem) i prowadzeniu go przez serię przeszkód (rur/słupów), które pojawiają się na ekranie. Gracz musi klikać (lub używać spacji), aby ptak podskoczył i utrzymał się w powietrzu, unikając zarówno górnej i dolnej części rur, jak i podłogi/sufitu. Celem jest osiągnięcie jak najwyższego wyniku poprzez przelecenie przez jak największą liczbę przeszkód.

Written By Coder Matthew

Last updated 6 months ago


1) Grafiki użyte w Flappy Bird

Tak to się prezentuje już w samym unity
  • 1. Background

    • Główna grafika tła gry.

    • Stanowi wizualną scenerię, za którą przesuwa się gracz i przeszkody. Jest używana przez skrypt BackgroundEffect do stworzenia wrażenia ruchu i głębi.

  • 2. Bird_01, Bird_02, Bird_03

    • To są klatki animacji (ramki) postaci gracza, czyli ptaka.

    • Skrypt Player wykorzystuje te trzy grafiki, cyklicznie je zmieniając, aby symulować ruch skrzydeł ptaka podczas lotu. Ta zmiana klatek tworzy iluzję animacji.

  • 3. GameOver

    • Tekst lub grafika wyświetlana po przegranej.

    • Jest to element interfejsu użytkownika (UI). Skrypt GameManager aktywuje ten obiekt (ustawia go jako widoczny) w momencie wywołania funkcji GameOver(), informując gracza o końcu rozgrywki.

  • 4. GetReady

    • Grafika mówiąca użytkownikowi aby się przygotował do rozgrywki "Get Ready".

    • Grafika ta jest wyświetlana na początku gry, kiedy gracz czeka na naciśnięcie przycisku "Play" lub wykonanie pierwszego skoku. Sugeruje, że gra jest w stanie Pauzy (Time.timeScale = 0f).

  • 5. Ground

    • Grafika przedstawiająca podłoże/ziemię.

    • Służy jako dolna granica mapy. Kontakt ptaka z tą grafiką wywołuje funkcję GameOver(). Ta grafika jest również animowana podobnie jak tło, aby symulować ruch.

  • 6. Pipe

    • Grafika przedstawiająca pojedynczy element przeszkody (rury).

    • Ta grafika jest używana jako Prefab (wzorzec obiektu) przez skrypt Pipes. W grze są używane dwie instancje tej rury (jedna obrócona) do stworzenia górnej i dolnej części przeszkody, z prześwitem pośrodku.

  • 7. PlayButton

    • Grafika przycisku.

    • Jest to klikalny element interfejsu użytkownika (UI). Jest widoczny na początku gry i po przegranej. Po kliknięciu wywołuje funkcję GameManager.Play(), rozpoczynając nową rundę.


2) Hierarchia w Unity

Wszystkie obiekty
  • 1. Game Manager

    • Reprezentuje fizycznie na scenie jednostkę zarządzającą grą.

    • Jest to obiekt, do którego przypięty jest skrypt GameManager. Dzięki temu skryptowi kontroluje on stan gry (Play, Pause, GameOver), punkty oraz komunikację sieciową. Jest to niezbędny element dla działania Singletonu.

  • 2. Main Camera

    • Odpowiada za perspektywę gracza. To przez nią widzimy świat gry.

    • Ustawia pole widzenia, proporcje oraz sposób renderowania grafiki. W tej grze jest to statyczna kamera, skupiona na obszarze, w którym porusza się gracz.

  • 4. Canvas

    • Jest to kontener nadrzędny dla wszystkich elementów interfejsu użytkownika (UI).

    • System UI w Unity wymaga obiektu Canvas. Upewnia się on, że elementy takie jak tekst i przyciski są wyświetlane prawidłowo na ekranie, niezależnie od rozdzielczości.

  • 5. Score (wewnątrz Canvas)

    • Element tekstowy, który wyświetla aktualny wynik gracza.

    • Skrypt GameManager odwołuje się do tego obiektu (scoreText) i aktualizuje jego zawartość za każdym razem, gdy gracz pomyślnie minie przeszkodę (w metodzie IncreaseScore()).

  • 6. GameOver (wewnątrz Canvas)

    • Reprezentuje graficzny element tła sceny.

    • Do tego obiektu przypięty jest skrypt BackgroundEffect (lub Parallax). Skrypt ten stale modyfikuje teksturę tego obiektu, tworząc iluzję ruchu tła i efekt paralaksy.

  • 7. Ground

    • Reprezentuje podłoże/ziemię w grze.

    • Jest to obiekt, który służy jako dolna granica dla gracza. Posiada on komponent Collider, a kontakt z nim jest traktowany tak samo jak kolizja z przeszkodą, co prowadzi do wywołania GameOver(). Jest on również animowany.

  • 8. Player

    • Reprezentuje postać sterowaną przez gracza (ptaka).

    • Jest to obiekt, do którego przypięty jest skrypt Player. Obiekt ten odpowiada za ruch, animację skrzydeł, stosowanie siły grawitacji oraz skoki, a także fizycznie wykrywa zderzenia z rurami i strefami punktowymi.

  • 9. Spawner

    • Reprezentuje punkt na scenie, z którego generowane są przeszkody.

    • Do tego obiektu przypięty jest skrypt Spawner. Jego pozycja określa, gdzie dokładnie pojawią się nowe rury. Jest on aktywny tylko w trakcie gry i wywołuje cyklicznie tworzenie nowych rur (Pipes).


3) Skrypty użyte w Flappy Bird

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

  • 1. GameManager (Menedżer Gry):

    • To centralna jednostka kontrolna całej aplikacji. Jego najważniejszą rolą jest zarządzanie stanem gry (Pauza, Gra, Koniec Gry) i synchronizacja wszystkich kluczowych elementów.

    • Implementuje wzorzec Singleton, co oznacza, że zapewnia jedną, łatwo dostępną instancję siebie w każdym miejscu kodu (poprzez GameManager.Instance), umożliwiając innym skryptom, takim jak Player czy Pipes, komunikację i wywoływanie kluczowych metod (np. GameOver(), IncreaseScore()).

    • Kontroluje mechanikę punktacji, inicjując wynik na zero przy starcie i aktualizując go na interfejsie użytkownika (scoreText) po pomyślnym przejściu przez przeszkodę.

    • Odpowiada za logikę restartu, czyszcząc scenę z istniejących przeszkód (Pipes) i resetując stan gracza, gdy wywołana jest funkcja Play().

    • Obejmuje komunikację sieciową (SendScore), która jest uruchamiana po zakończeniu gry, wysyłając wynik gracza na zewnętrzny serwer.

  • 2. Player (Gracz / Ptak):

    • To skrypt odpowiedzialny za wszelkie interakcje i ruch sterowanej przez gracza postaci.

    • Zarządza fizyką obiektu: stosuje stałą grawitację (gravity) w dół i reaguje na wejście użytkownika (spacja/kliknięcie myszą), nadając natychmiastowy impuls w górę (strength), co jest podstawową mechaniką "Flap".

    • Utrzymuje wizualną dynamikę postaci, aktualizując jej rotację (tilt) w zależności od kierunku i prędkości pionowej, co daje efekt naturalnego opadania lub wznoszenia.

    • Kontroluje animację postaci, cyklicznie przełączając się między kolejnymi sprite'ami zdefiniowanymi w tablicy, aby symulować ruch skrzydeł.

    • Realizuje kluczową logikę zderzeń (OnTriggerEnter2D), wykrywając, czy ptak uderzył w przeszkodę (wywołując GameManager.GameOver()) czy przeleciał przez strefę punktową (wywołując GameManager.IncreaseScore()).

  • 3. Spawner (Generator Rur):

    • Jego wyłącznym zadaniem jest generowanie przeszkód (rur) w regularnych odstępach czasu.

    • Używa funkcji InvokeRepeating w metodzie OnEnable, aby cyklicznie wywoływać metodę Spawn() zgodnie z zadaną częstotliwością (spawnRate).

    • Zarządza losowością wysokości rur, ustawiając nowo utworzony obiekt w losowej pozycji pionowej w ramach zdefiniowanego zakresu (minHeight do maxHeight).

    • Przekazuje kluczowy parametr (verticalGap) do tworzonego skryptu Pipes, zapewniając, że wszystkie nowo powstałe przeszkody mają spójną szerokość prześwitu.

  • 4. Pipes (Rury / Przeszkody):

    • Ten skrypt steruje pojedynczą parą przeszkód po jej wygenerowaniu przez Spawner.

    • W metodzie Start oblicza i stosuje pozycjonowanie górnej i dolnej części rury na podstawie przekazanego prześwitu (gap).

    • W metodzie Update odpowiada za ciągły, liniowy ruch rury w lewo z ustaloną prędkością.

    • Zawiera logikę oczyszczania pamięci, automatycznie niszcząc obiekt rury, gdy ta przekroczy lewą krawędź ekranu (leftEdge), co zapobiega gromadzeniu się niepotrzebnych obiektów w pamięci gry.

  • 5. BackgroundEffect (Efekt Tła / Paralaksa):

    • Jest to skrypt odpowiedzialny za efekty wizualne tła, zwiększając wrażenie prędkości i głębi.

    • Osiąga to poprzez ciągłe przesuwanie koordynat tekstury (mainTextureOffset) w poziomie, zamiast przesuwania samego obiektu.

    • Dzięki temu tło wydaje się przesuwać wolniej niż przeszkody (jeśli animationSpeed jest mniejsza niż prędkość rur), tworząc klasyczny efekt paralaksy bez konieczności tworzenia nowych obiektów tła.


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.UI; using UnityEngine.Networking; using System.Collections; [DefaultExecutionOrder(-1)] public class GameManager : MonoBehaviour { // --- Ustawienie Singletonu i Zmienne Gry --- // [BLOK KOMENTARZY: Kontrola Instancji i Parametry Gry] // Ten kod zapewnia, że w grze jest tylko JEDEN Menedżer Gry (Singleton), // dostępny łatwo z każdego innego skryptu (GameManager.Instance). // Poniższe zmienne 'SerializeField' to odniesienia do kluczowych obiektów // na scenie (Gracz, Spawner, Tekst Wyniku, Przyciski UI), // które ustawia się w edytorze Unity. public static GameManager Instance { get; private set; } [SerializeField] private Player player; [SerializeField] private Spawner spawner; [SerializeField] private Text scoreText; [SerializeField] private GameObject playButton; [SerializeField] private GameObject gameOver; public int score { get; private set; } = 0; // --- Inicjalizacja i Czyszczenie --- // [BLOK KOMENTARZY: Zarządzanie Życiem Obiektu] // Funkcje Awake i OnDestroy dbają o to, żeby istniała tylko jedna // instancja Menedżera przez cały czas trwania gry. // Funkcja Start natychmiast pauzuje grę po jej załadowaniu, // czekając na akcję gracza (naciśnięcie "Play"). private void Awake() { if (Instance != null) { DestroyImmediate(gameObject); } else { Instance = this; } } private void OnDestroy() { if (Instance == this) { Instance = null; } } private void Start() { Pause(); } // --- Kontrola Stanu Gry --- // [BLOK KOMENTARZY: Sterowanie Czasem i Ruchem] // Metoda Pause: Zatrzymuje czas (Time.timeScale = 0), zatrzymując ruch i fizykę. // Wyłącza skrypt sterowania graczem, uniemożliwiając mu ruch. public void Pause() { Time.timeScale = 0f; player.enabled = false; } public void Play() { // Metoda Play: // 1. Resetuje wynik do zera i ukrywa elementy interfejsu "Start/Game Over". score = 0; scoreText.text = score.ToString(); playButton.SetActive(false); gameOver.SetActive(false); // 2. Wznawia czas (Time.timeScale = 1) i włącza sterowanie graczem. Time.timeScale = 1f; player.enabled = true; // 3. Wyszukuje wszystkie istniejące rury (Pipes) na scenie i je niszczy, // zapewniając czysty start nowej rundy. Pipes[] pipes = FindObjectsOfType<Pipes>(); for (int i = 0; i < pipes.Length; i++) { Destroy(pipes[i].gameObject); } } public void GameOver() { // [BLOK KOMENTARZY: Koniec Gry i Zapis Wyniku] // Po zderzeniu, pokazuje elementy "Koniec Gry" i uruchamia // procedurę wysłania wyniku do zewnętrznego systemu. Na koniec pauzuje grę. playButton.SetActive(true); gameOver.SetActive(true); StartCoroutine( SendScore ( 14, // id gry score // wynik gracza ) ); Pause(); } public void IncreaseScore() { // [BLOK KOMENTARZY: Punktacja] // Prosta metoda wywoływana, gdy gracz przeleci przez punktowaną strefę. // Zwiększa wynik i aktualizuje tekst na ekranie. score++; scoreText.text = score.ToString(); } // --- Komunikacja Sieciowa (Wysyłanie Wyniku) --- // [BLOK KOMENTARZY: Procedura HTTP] // Ta funkcja (korutyna) buduje URL i formularz, aby wysłać aktualny wynik // gracza na zewnętrzny serwer (https://bit.elita.fun). Zawiera prosty // mechanizm zabezpieczający (hasło generowane na podstawie bieżącej minuty). public IEnumerator SendScore(int gameId, int score) { 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") + "E-----K"); using (UnityWebRequest r = UnityWebRequest.Post(url, f)) { yield return r.SendWebRequest(); Debug.Log(r.result == UnityWebRequest.Result.Success ? r.downloadHandler.text : "ERR: " + r.error); } } }