Pacman
Gra polega na sterowaniu Pac-Manem, który porusza się po labiryncie i zjada wszystkie kropki (Pellets), unikając jednocześnie czterech Duchów. Zjedzenie Dużej Kropki (Power Pellet) odwraca sytuację, tymczasowo pozwalając Pac-Manowi na zjedzenie Duchów, co daje dodatkowe punkty. Utrata wszystkich żyć (lub zjedzenie przez Ducha w trybie normalnym) kończy grę.
Written By Coder Matthew
Last updated 6 months ago
1) Grafiki użyte w Packman

1.
Ghost(duchy)Warianty Kolorystyczne: Grafiki dla każdego z czterech Duchów w ich normalnym stanie, różniące się kolorem (np. czerwony, różowy).
Tryb Przestraszenia: Grafiki niebieskiego Ducha oraz migający wariant biało-niebieski, używane, gdy Pac-Man zje Dużą Kropkę (
PowerPellet). Logikę zmiany wyglądu kontroluje skryptGhostFrightened.Oczy: Grafiki samych oczu, które wskazują kierunek ruchu Ducha i są wyświetlane, gdy Duch został zjedzony i wraca do domu.
2.
Pac-Man(gracz)Animacja Ruchu: Kilka klatek, które po cyklicznym przełączaniu symulują otwieranie i zamykanie ust Pac-Mana.
Sekwencja Śmierci: Seria klatek, które odtwarzają animację znikania/eksplozji Pac-Mana po utracie życia.
3.
Wall(labirynt)Kafelki Ścian: Duża liczba (Wall_00 do Wall_37) małych grafik (Tile, zebranych razem w Tilemape), które są używane do budowania struktury labiryntu.
Warstwa Kolizji: Kafelki te stanowią wizualną warstwę przeszkód, z którą Duchy i Pac-Man kolidują.
4.
Pellet(obiekty do zbierania)Mała Kropka (
Pellet_...): Standardowa kropka do zbierania. Stanowi większość obiektów na planszy.Duża Kropka (
PowerPellet): Większy, często migający obiekt, który po zjedzeniu aktywuje trybFrightenedu Duchów
2) Hierarchia w Unity

1.
Game ManagerReprezentuje fizyczny obiekt kontrolujący globalny stan gry oraz jej dynamikę.\
Utrzymuje wzorzec Singleton (
GameManager.Instance), będąc centralnym punktem zarządzania grą.Jest to obiekt, do którego przypięty jest skrypt
GameManager. Kontroluje stan gry, naliczanie punktów, żyć oraz logikę wyzwalaniaGameOver().
2.
Main CameraOdpowiada za widok gry, czyli to, co gracz faktycznie widzi. Ustawia pole widzenia i projekcję, aby poprawnie wyświetlać labirynt 2D.
Jest statyczna i skupiona na obszarze labiryntu. Umożliwia wizualizację wszystkich akcji gry.
3.
Grid(siatka)Kontener nadrzędny dla wszystkich wizualnych i logicznych elementów labiryntu. Organizuje i grupuje wszystkie elementy statyczne na planszy.
Jest używany jako rodzic dla labiryntu, ułatwiając zarządzanie jego pozycją i skalą.
3.1
Walls(wewnątrz Grid)Reprezentuje fizyczne ściany labiryntu.
Zawiera wszystkie kafelki (
Tile) labiryntu, które są przeszkodami dla postaci.Obiekt ten posiada komponenty kolizji, z którymi skrypt
Movement(używany przez Pac-Mana i Duchy) koliduje, uniemożliwiając przejście.
3.2
Pellets(wewnątrz Grid)Kontener dla wszystkich kropek na planszy.
Jest to obiekt przechowujący wszystkie prefabrykaty
PelletiPowerPellet.Skrypt
GameManagerodwołuje się do tego obiektu (pellets) w celu resetowania rundy (aktywacja wszystkich kropek) i sprawdzania, czy pozostały jeszcze jakieś kropki do zjedzenia.
3.3
Nodes(wewnątrz Grid)Kontener dla wszystkich węzłów decyzyjnych (skrzyżowań) w labiryncie.
Obiekty te zawierają skrypt
Node, który definiuje dostępne kierunki ruchu.Duchy używają tych węzłów jako punktów nawigacyjnych, aby określić, w którym kierunku mają się poruszyć zgodnie ze swoim algorytmem (Chase, Scatter, Frightened).
3.3.1
Inside / Outside(wewnątrz Nodes)Specjalne węzły reprezentujące pozycje wejścia/wyjścia z Domu Duchów.
Używane jako punkty docelowe przez skrypt
GhostHomedo animacji wchodzenia i wychodzenia Duchów z centralnej klatki.
4.
Canvas(Kontener UI)Jest kontenerem nadrzędnym dla wszystkich elementów interfejsu użytkownika (UI). Zarządza skalowaniem i rozmieszczeniem elementów UI na ekranie.
Wymagany do hostowania elementów takich jak tekst i obrazy, które muszą pozostać na stałym miejscu na ekranie.
4.1
GameOverText / ScoreText(wewnątrz Canvas)Elementy tekstowe wyświetlające wynik oraz komunikat "Game Over".
Umożliwiają graczowi monitorowanie postępu i stanu gry.
Skrypt
GameManagerbezpośrednio odwołuje się do tych obiektów, aby aktualizować wyświetlane wartości
5.
PacmanReprezentuje postać sterowaną przez gracza.
Jest to główny obiekt gry, który ma przypięty skrypt
Pacmani moduł ruchuMovement.Kontroluje input gracza, rotację postaci, zderzenia (z duchami) oraz cykl życia (reset, sekwencja śmierci).
6.
Ghost(Blinky, Inky, Pinky, Clyde)Ghost_Blinky, Ghost_Inky, Ghost_Pinky, Ghost_Clyde (i ich komponenty: Body, Eyes, Blue, White)
Reprezentują czterech przeciwników o unikalnych osobowościach i algorytmach pościgu.
Każdy z Duchów ma przypięty skrypt
Ghostoraz wiele skryptów behawioralnych (Chase, Scatter, Frightened, Home).Body, Eyes, Blue, White: To są różne graficzne komponenty Ducha. Skrypty behawioralne (np.
GhostFrightened) włączają i wyłączają te komponenty, aby wizualnie zmienić stan Ducha (np. ukrycieBodyiEyes, a pokazanieBluew trybie przestraszenia).
3) Skrypty użyte w Packman

1.
GameManagerCentralna jednostka kontrolna całej gry, zarządzająca stanem, punktacją i cyklami rund. Utrzymuje wzorzec Singleton, życiami, wynikiem i logiką Game Over.
Kontroluje sekwencje
NewGame()iNewRound(), aktywując i resetując wszystkie obiekty.Zawiera logikę reakcji na kluczowe zdarzenia: zjedzenie Kropki, zjedzenie Dużej Kropki (Power Pellet) oraz zjedzenie Pac-Mana lub Ducha.
2.
AnimatedSpriteUniwersalny silnik animacji klatkowej dla wszystkich postaci i elementów. Odpowiada za cykliczne przełączanie grafik (
sprites) w stałym interwale (animationTime).Używany do ożywiania Pac-Mana (ruch ust), Duchów (ruch ciała), a także do animacji specjalnych (np. miganie trybu przestraszenia).
3.
GhostGłówny skrypt Duchów, będący bazą i kontenerem dla wszystkich ich zachowań
Zarządza ogólnym stanem Ducha (Reset, Aktywacja) oraz punktami, jakie daje za zjedzenie.
Przechowuje referencje do wszystkich skryptów behawioralnych (Home, Chase, Scatter, Frightened) i wykrywa kolizje z Pac-Manem, decydując, kto kogo zjada.
4.
GhostBehaviorAbstrakcyjna klasa bazowa (szablon) dla wszystkich specyficznych zachowań Duchów.
Zapewnia wspólną funkcjonalność: włączanie/wyłączanie zachowania oraz zarządzanie czasem jego trwania (
duration).Umożliwia płynne przełączanie między trybami Ducha (np. z
ChasenaScatter), dziedzicząc po niej cała logika AI.
5.
GhostChaseImplementuje logikę trybu "Pościg"
Odpowiada za algorytm, który kieruje Ducha w stronę najkrótszej drogi do Pac-Mana
W każdym Węźle (
Node) oblicza, który dostępny kierunek najbardziej przybliży go do celu, a po wyłączeniu automatycznie aktywuje trybScatter.
6.
GhostEyesKontroluje grafikę oczu Ducha, tak aby patrzyły w kierunku, w którym się porusza. Jest to wizualny wskaźnik, który zwiększa czytelność ruchu Duchów.
Na podstawie wektora ruchu (
Movement.direction) na bieżąco zmienia wyświetlany sprite oczu (góra, dół, lewo, prawo).
7.
GhostFrightenedImplementuje logikę trybu "Przestraszony" (Frightened), w którym Pac-Man może zjeść Ducha. Kontroluje zmianę wyglądu (na niebieski i migający) oraz logikę ucieczki od Pac-Mana.
Wprowadza mnożnik do prędkości (zwalnia Ducha) oraz w Węzłach wybiera kierunek, który prowadzi najdalej od Pac-Mana (logika ucieczki).
8.
GhostHomeZarządza logiką pobytu Ducha w "Domu" (centralnej klatce) oraz animacją jego wejścia i wyjścia. Odpowiada za płynną sekwencję
ExitTransition(), w której Duch opuszcza klatkę.Używa zdefiniowanych punktów (
inside,outside) jako celów animacji, a podczas pobytu w Domu powoduje "odbijanie się" Ducha od ścian klatki.
9.
GhostScatterImplementuje logikę trybu "Rozproszenie" (Scatter). Powoduje, że Duchy na zmianę z trybem Pościgu (Chase) uciekają do swojego bezpiecznego rogu mapy.
W Węzłach podejmuje decyzje o losowym kierunku, ale z preferencją unikania zawracania, a po wyłączeniu automatycznie aktywuje tryb
Chase.
10.
MovementPodstawowy moduł fizyki i ruchu dla Pac-Mana i Duchów.
Odpowiada za ciągłe przemieszczanie obiektów, zarządzanie prędkością (
speedMultiplier) i sprawdzanie kolizji ze ścianami.Używa metody
SetDirection()do zarządzania aktualnym kierunkiem i kierunkiem w kolejce (nextDirection), co zapewnia płynne i responsywne sterowanie.
11.
NodeDefiniuje punkty decyzyjne (skrzyżowania) w labiryncie. Określa, w których z czterech kierunków (góra/dół/lewo/prawo) nie ma ścian.
Skrypty Duchów (Chase, Scatter, Frightened) wchodzą w ten obiekt, aby uzyskać listę dostępnych dróg i na tej podstawie podjąć decyzję o ruchu.
12.
PacmanGłówny skrypt Gracza, zarządzający jego interakcjami i cyklem życia. Odpowiada za odczytywanie inputu (klawisze kierunkowe) i obracanie postaci.
Wywołuje moduł ruchu (
Movement.SetDirection()) na podstawie klawiszy, a także uruchamiaDeathSequence()w momencie zderzenia z nieprzestraszonym Duchem.
13.
PelletBaza dla wszystkich jadalnych kropek (obiektów do zbierania). Definiuje wartość punktową i logikę zjedzenia.
W momencie kolizji z Pac-Manem, wywołuje
GameManager.PelletEaten(), aby naliczyć punkty i usunąć kropkę ze sceny.
14.
PowerPelletDziedziczy po
Pelleti dodaje specjalną funkcjonalność Power-up'a. Przekazuje czas trwania efektu (duration).Nadpisuje standardową metodę
Eat()i wywołujeGameManager.PowerPelletEaten(), co uruchamia trybFrightenedu wszystkich Duchów.
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#.
Exampleusing UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using System.Collections;
[DefaultExecutionOrder(-100)]
public class GameManager : MonoBehaviour
{
// --- Singleton i Parametry Gry ---
public static GameManager Instance { get; private set; }
[SerializeField] private Ghost[] ghosts; // Referencje do wszystkich duchów.
[SerializeField] private Pacman pacman; // Referencja do gracza.
[SerializeField] private Transform pellets; // Kontener z wszystkimi kropkami.
[SerializeField] private Text gameOverText;
[SerializeField] private Text scoreText;
[SerializeField] private Text livesText;
public int score { get; private set; } = 0;
public int lives { get; private set; } = 1; // Zmodyfikowana liczba żyć.
private int ghostMultiplier = 1; // Mnożnik punktów za zjedzone duchy.
// --- Inicjalizacja Singletonu ---
private void Awake()
{
// [BLOK KOMENTARZY: Zarządzanie Instancją]
// Zapewnienie, że istnieje tylko jeden GameManager na scenie.
if (Instance != null) {
DestroyImmediate(gameObject);
} else {
Instance = this;
}
}
private void OnDestroy()
{
if (Instance == this) {
Instance = null;
}
}
private void Start()
{
// Rozpoczęcie nowej gry po starcie sceny.
NewGame();
}
private void Update()
{
// [BLOK KOMENTARZY: Logika Restartu Po Przegranej]
// Jeśli nie ma żyć i gracz naciśnie dowolny klawisz, rozpoczyna nową grę.
if (lives <= 0 && Input.anyKeyDown) {
NewGame();
}
}
private void NewGame()
{
// [BLOK KOMENTARZY: Inicjalizacja Nowej Gry]
// Ustawia wynik na 0, żyć na 1 i rozpoczyna nową rundę.
SetScore(0);
SetLives(1);
NewRound();
}
private void NewRound()
{
// [BLOK KOMENTARZY: Przygotowanie Nowej Rundy]
// Ukrywa tekst Game Over. Aktywuje wszystkie kropki, aby można było je zjeść od nowa.
gameOverText.enabled = false;
foreach (Transform pellet in pellets) {
pellet.gameObject.SetActive(true);
}
ResetState(); // Resetuje pozycje Pac-Mana i Duchów.
}
private void ResetState()
{
// [BLOK KOMENTARZY: Resetowanie Stanu Postaci]
// Resetuje stany (pozycje, kierunki, zachowania) Duchów i Pac-Mana.
for (int i = 0; i < ghosts.Length; i++) {
ghosts[i].ResetState();
}
pacman.ResetState();
}
private void GameOver()
{
// [BLOK KOMENTARZY: Koniec Gry]
// Wyświetla komunikat Game Over i deaktywuje wszystkie postacie.
gameOverText.enabled = true;
for (int i = 0; i < ghosts.Length; i++) {
ghosts[i].gameObject.SetActive(false);
}
pacman.gameObject.SetActive(false);
}
// --- Logika Zmiany Żyć i Wyniku ---
private void SetLives(int lives)
{
// [BLOK KOMENTARZY: Zarządzanie Życiami]
// Aktualizuje licznik żyć i wyświetla go w UI.
this.lives = lives;
livesText.text = "x" + lives.ToString();
}
private void SetScore(int score)
{
// [BLOK KOMENTARZY: Zarządzanie Wynikiem i Komunikacją Sieciową]
// Aktualizuje wynik, formatuje go (dwie cyfry) i wysyła na serwer.
this.score = score;
scoreText.text = score.ToString().PadLeft(2, '0');
StartCoroutine(
SendScore(
19, // id gry
score // wynik
)
);
}
// --- Obsługa Zdarzeń Gry ---
public void PacmanEaten()
{
// [BLOK KOMENTARZY: Pac-Man Zjedzony]
// Uruchamia sekwencję śmierci Pac-Mana, zmniejsza życia i albo resetuje stan, albo wywołuje Game Over.
pacman.DeathSequence();
SetLives(lives - 1);
if (lives > 0) {
Invoke(nameof(ResetState), 3f);
} else {
GameOver();
}
}
public void GhostEaten(Ghost ghost)
{
// [BLOK KOMENTARZY: Duch Zjedzony]
// Nalicza punkty (z użyciem mnożnika), zwiększa mnożnik i wysyła ducha do domu.
int points = ghost.points * ghostMultiplier;
SetScore(score + points);
ghostMultiplier++; // Mnożnik rośnie z każdym zjedzonym duchem.
}
public void PelletEaten(Pellet pellet)
{
// [BLOK KOMENTARZY: Kropka Zjedzona]
// Wyłącza zjedzoną kropkę, nalicza punkty i sprawdza warunek zakończenia rundy.
pellet.gameObject.SetActive(false);
SetScore(score + pellet.points);
if (!HasRemainingPellets())
{
// Jeśli wszystkie kropki zostały zjedzone, rozpoczyna nową rundę.
pacman.gameObject.SetActive(false);
Invoke(nameof(NewRound), 3f);
}
}
public void PowerPelletEaten(PowerPellet pellet)
{
// [BLOK KOMENTARZY: Duża Kropka Zjedzona (Power-up)]
// Uruchamia tryb 'Frightened' (przestraszony) dla wszystkich duchów.
for (int i = 0; i < ghosts.Length; i++) {
ghosts[i].frightened.Enable(pellet.duration);
}
PelletEaten(pellet); // Normalne naliczenie punktów za kropkę.
// Resetuje mnożnik punktów za duchy po upływie czasu trwania Power-up'a.
CancelInvoke(nameof(ResetGhostMultiplier));
Invoke(nameof(ResetGhostMultiplier), pellet.duration);
}
// --- Funkcje Pomocnicze ---
private bool HasRemainingPellets()
{
// [BLOK KOMENTARZY: Sprawdzanie Końca Rundy]
// Iteruje przez wszystkie kropki, sprawdzając, czy jakakolwiek jest wciąż aktywna.
foreach (Transform pellet in pellets) {
if (pellet.gameObject.activeSelf) {
return true;
}
}
return false;
}
private void ResetGhostMultiplier()
{
// [BLOK KOMENTARZY: Resetowanie Mnożnika]
// Ustawia mnożnik punktów za duchy z powrotem na 1.
ghostMultiplier = 1;
}
// --- Komunikacja Sieciowa (Wysyłanie Wyniku) ---
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);
}
}
}Exampleusing System.Collections;
using UnityEngine;
public class GhostHome : GhostBehavior
{
public Transform inside; // Pozycja wewnątrz Domu Ducha.
public Transform outside; // Pozycja wyjściowa z Domu Ducha.
private void OnEnable()
{
// [BLOK KOMENTARZY: Włączenie]
// Zatrzymuje wszystkie korutyny, aby nie kolidowały.
StopAllCoroutines();
}
private void OnDisable()
{
// [BLOK KOMENTARZY: Wyjście z Domu]
// Uruchamia sekwencję animacji wyjścia, gdy Duch ma opuścić Dom.
if (gameObject.activeInHierarchy) {
StartCoroutine(ExitTransition());
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
// [BLOK KOMENTARZY: Odbijanie się w Domu]
// Gdy Duch w Domu zderzy się ze ścianą, odwraca swój kierunek,
// tworząc efekt "odbijania się" w klatce.
if (enabled && collision.gameObject.layer == LayerMask.NameToLayer("Obstacle")) {
ghost.movement.SetDirection(-ghost.movement.direction);
}
}
private IEnumerator ExitTransition()
{
// [BLOK KOMENTARZY: Sekwencja Wyjścia z Domu]
// Ręczna kontrola pozycji, aby Duch płynnie przeszedł od środka do wyjścia.
// 1. Wyłączenie normalnego ruchu.
ghost.movement.SetDirection(Vector2.up, true);
ghost.movement.rb.isKinematic = true;
ghost.movement.enabled = false;
Vector3 position = transform.position;
float duration = 0.5f;
float elapsed = 0f;
// 2. Animacja do punktu startowego (wewnątrz klatki).
while (elapsed < duration)
{
ghost.SetPosition(Vector3.Lerp(position, inside.position, elapsed / duration));
elapsed += Time.deltaTime;
yield return null;
}
elapsed = 0f;
// 3. Animacja wyjścia z klatki (do outside.position).
while (elapsed < duration)
{
ghost.SetPosition(Vector3.Lerp(inside.position, outside.position, elapsed / duration));
elapsed += Time.deltaTime;
yield return null;
}
// 4. Przywrócenie kontroli ruchu (losowy kierunek lewo/prawo) i włączenie Rigidbody.
ghost.movement.SetDirection(new Vector2(Random.value < 0.5f ? -1f : 1f, 0f), true);
ghost.movement.rb.isKinematic = false;
ghost.movement.enabled = true;
}
}