Dino Game

Gra polega na sterowaniu postacią (dinozaurem) w nieskończonym, bocznym biegu. Postać automatycznie biegnie w prawo (elementy sceny przesuwają się w lewo), a gracz musi naciskać przycisk (np. Spację), aby skakać i unikać losowo generowanych przeszkód (kaktusów i pterodaktyli). Głównym celem jest osiągnięcie jak najwyższego wyniku.

Written By Coder Matthew

Last updated 6 months ago


1) Grafiki użyte w Dino Game

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

    • Jest to pierwsza klatka z dwuelementowej sekwencji, która symuluje ruch nóg.

    • Wykorzystywana przez skrypt AnimatedSprite na obiekcie Player. Cykliczne przełączanie tej grafiki z Dino_Run02 tworzy iluzję ruchu.

  • 2. Dino_Run02

    • Klatka ta uzupełnia sekwencję, tworząc pełny cykl ruchu.

    • Wykorzystywana przez skrypt AnimatedSprite do płynnego przełączania w ramach pętli animacyjnej. Szybkość przełączania jest skalowana przez GameManager.gameSpeed, dynamicznie przyspieszając animację wraz z grą.

  • 3. Cactus_Large

    • Grafika przedstawiająca duży, pojedynczy kaktus. Służy jako wizualny wzorzec dla jednej z podstawowych przeszkód naziemnych.

    • Używany jako prefab przez skrypt Spawner do generowania przeszkód o większej wysokości. Obiekt z tą grafiką posiada Collider, a zderzenie z nim wywołuje GameOver().

  • 4. Cactus_Large_Group

    • Grafika przedstawiająca grupę dużych kaktusów. Tworzy przeszkodę o większej szerokości kolizji, podnosząc poziom trudności.

    • Generowany losowo przez Spawner jako trudniejszy wariant, wymagający dłuższego skoku do uniknięcia. Obiekt ten symbolizuje złożoną przeszkodę, często będąc rzadziej wybieranym przez system losujący.

  • 5. Cactus_Small

    • Grafika przedstawiająca mały, pojedynczy kaktus. Jest to najprostszy wariant przeszkody.

    • Używany jako prefab, jest generowany przez Spawner z określonym prawdopodobieństwem. Często pojawia się w szybszych sekwencjach lub jest kombinowany z innymi przeszkodami.

  • 6. Cactus_Small_Group

    • Grafika przedstawiająca grupę małych kaktusów. Wariant złożonej przeszkody naziemnej, składającej się z kilku mniejszych elementów.

    • Służy do zwiększania różnorodności przeszkód i testowania precyzji skoku gracza.

  • 7. Bird_01

    • Pierwsza klatka animacji lecącego Pterodaktyla (przeszkoda powietrzna). Symuluje moment z wzniesionymi skrzydłami.

    • Skrypt AnimatedSprite przełącza tę klatkę z Bird_02.

  • 8. Bird_02

    • Druga klatka animacji lecącego Pterodaktyla. Symuluje moment z opuszczonymi skrzydłami.

    • Używana do stworzenia iluzji trzepotania skrzydłami podczas ruchu w lewo.

  • 9. Ground

    • Jest to wizualna baza, po której porusza się dinozaur.

    • Używana przez skrypt Ground, który przewija (offsetuje) jej teksturę w czasie, dając iluzję nieskończonego biegu i ruchu w świecie gry.

  • 10. Retry

    • Grafika przycisku "Spróbuj ponownie". Element interfejsu użytkownika (UI) wywołujący akcję restartu. Działa podobnie jak znany nam onclick().

    • Jest domyślnie ukryta, a skrypt GameManager aktywuje ją po przegranej.

      Kliknięcie tego elementu UI wywołuje metodę GameManager.NewGame().


2) Hierarchia w Unity

Wszystkie obiekty
  • 1. Game Manager

    • Reprezentuje fizyczny obiekt kontrolujący globalny stan gry oraz jej dynamikę.

    • Do tego obiektu przypięty jest skrypt GameManager. Jest kluczowy, ponieważ utrzymuje Singleton (GameManager.Instance), kontroluje globalną prędkość gry (gameSpeed), zarządza punktacją, inicjuje nową grę (NewGame()) oraz obsługuje jej zakończenie (GameOver()).

  • 2. Main Camera

    • Odpowiada za widok gry, czyli to, co gracz faktycznie widzi.

    • Ustawia perspektywę i granice świata gry. W Dino Game jest statyczna i skierowana na obszar biegu dinozaura.

  • 3. Player

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

    • Obiekt, do którego przypięty jest skrypt Player. Zawiera również skrypt AnimatedSprite do animacji biegu. Odpowiada za całą logikę fizyki (grawitacja, skoki za pomocą CharacterController) i wykrywanie kolizji z przeszkodami. Jest aktywowany i dezaktywowany przez GameManager (w NewGame() i GameOver()).

  • 4. Ground

    • Reprezentuje wizualne podłoże, po którym biegnie dinozaur.

    • Przypięty jest do niego skrypt Ground. Ten obiekt nie porusza się, lecz to jego tekstura jest ciągle przesuwana w lewo (metoda mainTextureOffset), tworząc iluzję nieskończonego biegu i ruchu.

  • 5. Spawner

    • Reprezentuje punkt i logikę generowania wszystkich przeszkód.

    • Do tego obiektu przypięty jest skrypt Spawner. Znajduje się on zazwyczaj poza prawą krawędzią ekranu. Cyklicznie generuje prefabrykaty przeszkód (np. kaktusy, pterodaktyle), uwzględniając ich losowość i prawdopodobieństwo pojawienia się (struktura SpawnableObject).

  • 6. Canvas

    • Jest kontenerem nadrzędnym dla wszystkich elementów interfejsu użytkownika (UI).

    • Zapewnia, że wszystkie elementy UI (tekst, przyciski) są wyświetlane i skalowane poprawnie na różnych rozdzielczościach ekranu.

  • 7. Game Over (wewnątrz Canvas)

    • Element graficzny/tekstowy (TextMeshPro), który informuje o przegranej.

    • Jest ukryty podczas gry, a aktywowany (SetActive(true)) przez GameManager w momencie kolizji gracza.

  • 8. Retry (wewnątrz Canvas)

    • Przycisk lub ikona, która pozwala rozpocząć nową grę.

    • Jest ukryty podczas gry. Po jego wyświetleniu, kliknięcie wywołuje metodę GameManager.NewGame().

  • 9. Score (wewnątrz Canvas)

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

    • Skrypt GameManager stale aktualizuje jego wartość w metodzie Update, formatując ją do pięciu cyfr ("D5").


3) Skrypty użyte w Dino Game

Tak właśnie wyglądają gotowe skrypty w unity
  • 1. GameManager

    • Główna jednostka kontrolująca globalny stan gry oraz jej dynamiczne parametry. Utrzymuje wzorzec Singleton, umożliwiając innym skryptom łatwy dostęp do kluczowych funkcji.

    • Kontroluje prędkość gry (gameSpeed), która z czasem przyspiesza, zwiększając trudność, a także obsługuje punktację.

    • Wywołuje funkcje NewGame() i GameOver(), zarządzając widocznością interfejsu (Retry/GameOver) oraz aktywnością Gracza i Spawnera.

  • 2. Player

    • Skrypt odpowiedzialny za sterowanie i fizykę głównej postaci (dinozaura). Obsługuje kluczową mechanikę gry: skok oraz stosowanie grawitacji na postać.

    • Wykorzystuje komponent CharacterController do bezpiecznego przemieszczania obiektu w pionie i wykrywania, kiedy jest na ziemi (isGrounded).

    • Wykrywa kolizje z obiektami oznaczonymi tagiem Obstacle i natychmiast wywołuje GameManager.GameOver().

  • 3. Spawner

    • Zarządza losowym i cyklicznym generowaniem wszystkich przeszkód. Odpowiada za logikę losowania, które prefabrykaty (kaktusy, pterodaktyle) mają się pojawić, bazując na ich prawdopodobieństwie (spawnChance).

    • Cyklicznie wywołuje funkcję Spawn() po upływie losowego czasu (pomiędzy minSpawnRate a maxSpawnRate).

    • Generuje nowe instancje przeszkód i umieszcza je na scenie, poza widokiem kamery.

  • 4. Obstacle

    • Steruje ruchem i cyklem życia pojedynczej przeszkody po jej wygenerowaniu. Zapewnia, że przeszkoda porusza się w lewo z prędkością równą globalnej GameManager.gameSpeed

    • W metodzie Update przesunięcie przeszkody jest bezpośrednio skalowane przez globalną prędkość, co powoduje, że przeszkody przyspieszają wraz z grą.

    • Niszczy (Destroy) obiekt, gdy ten przekroczy lewą krawędź ekranu, oszczędzając zasoby gry.

  • 5. Ground

    • Tworzy efekt nieskończonego biegu dla podłoża. Odpowiada za wizualny aspekt ruchu terenu, bez fizycznego przesuwania całego obiektu.

    • Modyfikuje parametr mainTextureOffset komponentu MeshRenderer, przesuwając teksturę podłoża w czasie.

    • Prędkość przewijania jest również skalowana przez GameManager.gameSpeed, synchronizując ruch tła z resztą gry.

  • 6. AnimatedSprite

    • Uniwersalny moduł do odtwarzania prostej animacji klatkowej. Zarządza sekwencją grafik (tablica sprites), przełączając je cyklicznie.

    • Jest używany głównie na obiekcie Player (do animacji biegu) oraz na obiekcie pterodaktyla.

    • Kontroluje częstotliwość przełączania klatek na podstawie globalnej prędkości gry, co sprawia, że animacja jest szybsza, gdy rośnie poziom trudności.


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 TMPro; using UnityEngine; using UnityEngine.UI; using UnityEngine.Networking; using System.Collections; // Gwarantuje, że ten skrypt uruchomi się pierwszy. [DefaultExecutionOrder(-1)] public class GameManager : MonoBehaviour { // --- Ustawienie Singletonu i Kontrola Prędkości --- // [BLOK KOMENTARZY: Globalne Zmienne Stanu Gry] // Singleton (dostępny zewsząd), początkowa prędkość, // o ile prędkość ma się zwiększać co sekundę, oraz aktualna prędkość gry. public static GameManager Instance { get; private set; } public float initialGameSpeed = 5f; public float gameSpeedIncrease = 0.1f; public float gameSpeed { get; private set; } // --- Odniesienia do UI i Obiektów Sceny --- // Zmienne do ustawienia w edytorze: pola tekstowe (wynik, Game Over) i przycisk Ponów. [SerializeField] private TextMeshProUGUI scoreText; [SerializeField] private TextMeshProUGUI gameOverText; [SerializeField] private Button retryButton; // Prywatne referencje do kluczowych skryptów (Gracza i Generatora). private Player player; private Spawner spawner; // Zmienna przechowująca wynik. private float score; public float Score => score; // --- Inicjalizacja i Czyszczenie (Singleton) --- // [BLOK KOMENTARZY: Zarządzanie Instancją] // Metody zapewniające, że na scenie istnieje tylko JEDEN GameManager (Singleton). private void Awake() { if (Instance != null) { DestroyImmediate(gameObject); } else { Instance = this; } } private void OnDestroy() { if (Instance == this) { Instance = null; } } private void Start() { // Znajdowanie i przechowanie referencji do Gracza i Spawnera. player = FindObjectOfType<Player>(); spawner = FindObjectOfType<Spawner>(); // Rozpoczęcie nowej gry. NewGame(); } // --- Kontrola Stanu Gry: Nowa Gra --- public void NewGame() { // [BLOK KOMENTARZY: Resetowanie Sceny] // 1. Wyszukuje wszystkie istniejące przeszkody i niszczy je, aby przygotować scenę. Obstacle[] obstacles = FindObjectsOfType<Obstacle>(); foreach (var obstacle in obstacles) { Destroy(obstacle.gameObject); } // 2. Resetuje wynik i prędkość gry do wartości początkowej. score = 0f; gameSpeed = initialGameSpeed; enabled = true; // Włącza metodę Update tego skryptu. // 3. Aktywuje obiekty Gry (Gracz, Spawner) i ukrywa elementy interfejsu "Koniec Gry". player.gameObject.SetActive(true); spawner.gameObject.SetActive(true); gameOverText.gameObject.SetActive(false); retryButton.gameObject.SetActive(false); } // --- Kontrola Stanu Gry: Koniec Gry --- public void GameOver() { // [BLOK KOMENTARZY: Zakończenie Gry i Zapis Wyniku] // 1. Zatrzymuje ruch (gameSpeed = 0) i wyłącza skrypt (żeby nie naliczał punktów). gameSpeed = 0f; enabled = false; // 2. Deaktywuje obiekty Gry (Gracza i Spawner), zatrzymując dalsze generowanie/ruch. player.gameObject.SetActive(false); spawner.gameObject.SetActive(false); // 3. Pokazuje elementy interfejsu "Koniec Gry" i przycisk "Ponów". gameOverText.gameObject.SetActive(true); retryButton.gameObject.SetActive(true); // 4. Rozpoczyna procedurę wysłania wyniku na zewnętrzny serwer. StartCoroutine ( SendScore( 15, // id gry Mathf.FloorToInt(score) // wynik (zaokrąglony w dół do liczby całkowitej) ) ); } // --- Logika Punktacji i Przyspieszenia --- private void Update() { // [BLOK KOMENTARZY: Ciągły Ruch i Punktacja] // W każdej klatce: // 1. Nieustannie zwiększa prędkość gry, dążąc do wyższego poziomu trudności. gameSpeed += gameSpeedIncrease * Time.deltaTime; // 2. Zwiększa wynik proporcjonalnie do aktualnej prędkości gry. score += gameSpeed * Time.deltaTime; // 3. Aktualizuje tekst wyniku (wynik jest zaokrąglany w dół i formatowany na 5 cyfr). scoreText.text = Mathf.FloorToInt(score).ToString("D5"); } // --- Komunikacja Sieciowa (Wysyłanie Wyniku) --- // [BLOK KOMENTARZY: Procedura HTTP] // Korutyna wysyłająca wynik do serwisu internetowego, używając stałego ID gry // i prostego tokenu zabezpieczającego opartego na bieżącej minucie. 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); } } }