Unity Eğitim
Dersler
Forum Sınav Merkezi Premium 💎
C# Dersleri

Unity ile Pac-Man Klonu: Hayalet Yapay Zekası ve Yol Bulma

Paylaşan: Unity Eğitim 28 November 2025 12 dakika okuma 263 görüntülenme

Giriş: Pac-Man Hayaletlerinin Gizemi

Pac-Man, oyun dünyasının en ikonik karakterlerinden biri olmasının yanı sıra, basit ama derin yapay zekasıyla da dikkat çeker. Özellikle oyunun baş kahramanları olan hayaletler (Blinky, Pinky, Inky ve Clyde), oyuncuyu sürekli tetikte tutan, öngörülebilir ama aynı zamanda şaşırtıcı davranışlarıyla bilinirler. Bu makalede, Unity oyun motorunu kullanarak kendi Pac-Man benzeri oyununuz için bu hayaletlerin yapay zekasını (AI) ve yol bulma (pathfinding) mekanizmalarını nasıl oluşturacağınızı detaylı bir şekilde inceleyeceğiz. Oyun geliştirme serüveninizde karşılaşacağınız temel taşlardan biri olan Unity Hayalet AI sistemlerini adım adım ele alacağız.

Hayaletlerin sadece rastgele hareket etmekle kalmayıp, Pac-Man'i kovalama, tuzak kurma veya kaçma gibi farklı stratejiler uygulaması, oyunun derinliğini artırır. Bu davranışları Unity'de C# ile nasıl modelleyeceğimizi, özellikle A* (A-Star) gibi popüler yol bulma algoritmalarını nasıl entegre edeceğimizi ve performans ipuçlarını paylaşacağız. Hazırsanız, Pac-Man'in labirentinde hayalet avına (veya avlanmaya) başlayalım!

Hayalet Yapay Zekasına Giriş: Temeller

Pac-Man hayaletlerinin davranışları üç ana moda ayrılır: Kovalama (Chase), Dağılma (Scatter) ve Korkmuş (Frightened). Her modda hayaletlerin hedef belirleme stratejileri değişir ve bu da oyuna dinamizm katar. Bu modlar arasında geçiş yapmak, oyuncuyu meşgul eden bir Unity Hayalet AI oluşturmanın ilk adımıdır.

  • Kovalama (Chase) Modu: Hayaletler aktif olarak Pac-Man'i hedefler. Ancak her hayaletin hedef belirleme yöntemi farklıdır.
  • Dağılma (Scatter) Modu: Belirli aralıklarla hayaletler, haritanın kendi köşelerindeki önceden belirlenmiş güvenli bölgelere doğru hareket ederler. Bu, oyuncuya kısa bir nefes alma fırsatı tanır.
  • Korkmuş (Frightened) Modu: Pac-Man bir güç hapı yediğinde hayaletler maviye döner ve rastgele yönlerde kaçmaya başlar. Bu durumda Pac-Man onları yiyebilir.

Tüm bu modlarda, hayaletlerin temel hareketi bir ızgara (grid) tabanlıdır. Yani sadece yatay veya dikey olarak hareket edebilirler ve bir kavşağa geldiklerinde yön değiştirebilirler. Bu ızgara yapısı, yol bulma algoritmaları için mükemmel bir zemin sağlar.

Yol Bulma Algoritmaları: A* ve BFS

Hayaletlerin Pac-Man'i veya belirli bir hedefi takip edebilmesi için bir yol bulma algoritmasına ihtiyacımız var. Izgara tabanlı oyunlarda en sık kullanılan ve en verimli algoritmalar arasında Genişlik Öncelikli Arama (BFS - Breadth-First Search) ve A* (A-Star) algoritmaları bulunur. Bu algoritmalar, Unity Hayalet AI için temel bir yol bulma mekanizması sunar.

BFS (Breadth-First Search)

BFS, hedef noktaya en kısa yolu bulmak için komşu düğümleri katman katman keşfeden basit ama etkili bir algoritmadır. Her adımda, mevcut düğümden ulaşılabilen tüm komşu düğümleri inceler ve bunları bir kuyruğa (queue) ekler. Ağırlıksız bir grafikte (yani her adımın maliyeti aynı olduğunda) en kısa yolu garanti eder. Pac-Man gibi basit ızgara tabanlı oyunlar için başlangıçta iyi bir seçenek olabilir, ancak daha karmaşık senaryolarda A*'ın performansına ulaşamaz.

A* (A-Star) Algoritması

A*, BFS'ten daha gelişmiş ve yaygın olarak kullanılan bir yol bulma algoritmasıdır. Hedefe giden en kısa yolu bulmak için bir maliyet (gCost) ve bir sezgisel (hCost) fonksiyonunu birleştirir. Bu iki değerin toplamı olan F maliyeti (fCost = gCost + hCost) ile en düşük maliyetli yolu arar. A* algoritması, daha verimli olduğu ve genellikle daha az düğümü ziyaret ettiği için, çoğu oyun senaryosunda tercih edilen yöntemdir.

  • gCost: Başlangıç düğümünden mevcut düğüme kadar olan gerçek maliyet.
  • hCost (Heuristic): Mevcut düğümden hedef düğüme olan tahmini maliyet (örn. Manhattan mesafesi veya Öklid mesafesi).
  • fCost: gCost + hCost. Algoritma her zaman en düşük fCost'a sahip düğümü seçerek ilerler.

A* algoritması genellikle şu adımları izler:

  1. Bir 'açık liste' (open list) ve bir 'kapalı liste' (closed list) oluşturulur.
  2. Başlangıç düğümü açık listeye eklenir.
  3. Açık listede düğümler olduğu sürece:
    • En düşük fCost'a sahip düğüm açık listeden çıkarılır ve kapalı listeye eklenir.
    • Eğer bu düğüm hedef düğüm ise, yol yeniden yapılandırılarak bulunur ve algoritma sona erer.
    • Aksi takdirde, seçilen düğümün tüm komşuları için:
      • Eğer komşu bir engel ise veya kapalı listede ise, göz ardı edilir.
      • Yeni bir gCost hesaplanır. Eğer bu gCost, komşunun mevcut gCost'undan daha düşükse veya komşu açık listede değilse, komşunun ebeveyni (parent) güncellenir, gCost ve hCost'u hesaplanır ve açık listeye eklenir (veya güncellenir).

Unity'de Hayalet AI Uygulaması

Unity Hayalet AI sistemini uygulamak için öncelikle bir ızgara yapısına ihtiyacımız var. Bu ızgara, oyun alanını temsil edecek ve hangi noktaların geçilebilir, hangilerinin engel olduğunu belirleyecektir. Her bir ızgara hücresi (düğüm) için bir `Node` sınıfı oluşturmak iyi bir başlangıçtır.

Grid Yapısının Oluşturulması


public class Node
{
    public Vector2Int gridPosition; // Izgaradaki konumu
    public bool isWalkable; // Geçilebilir mi?
    public int gCost; // Başlangıçtan bu düğüme maliyet
    public int hCost; // Bu düğümden hedefe tahmini maliyet
    public Node parent; // Yolu yeniden oluşturmak için ebeveyn düğüm

    public int fCost { get { return gCost + hCost; } }

    public Node(Vector2Int _gridPos, bool _isWalkable)
    {
        gridPosition = _gridPos;
        isWalkable = _isWalkable;
    }
}

Bu `Node` sınıfını kullanarak, oyun alanınızı temsil eden bir `Node[,]` dizisi (iki boyutlu dizi) oluşturabilirsiniz. Engelleri (duvarlar) `isWalkable = false` olarak işaretleyeceksiniz.

Hayalet Davranışları ve Hedef Belirleme

Her hayaletin kendine özgü bir hedef belirleme mantığı vardır, bu da Pac-Man'i ilginç kılar:

  • Blinky (Kırmızı): En agresif hayalettir. Kovalama modunda doğrudan Pac-Man'in mevcut konumunu hedefler.
  • Pinky (Pembe): Pac-Man'in 4 birim önündeki bir noktayı hedefler. Bu, Pac-Man'i köşeye sıkıştırma ve önünü kesme stratejisiyle hareket ettiği anlamına gelir.
  • Inky (Mavi): En karmaşık hedefe sahiptir. Pac-Man'in 2 birim önündeki nokta ile Blinky'nin konumu arasındaki vektörün iki katını hedefler. Bu, Blinky ile koordineli çalışarak oyuncuya çaprazdan yaklaşmasını sağlar.
  • Clyde (Turuncu): Pac-Man'e 8 birimden daha yakınsa, kendi dağılma köşesine kaçar (korkmuş gibi). Eğer 8 birimden daha uzaksa, Pac-Man'i hedefler. Bu, tahmin edilemez bir davranış sergilemesine neden olur.

Bu hedef noktalarını belirledikten sonra, hayaletler A* algoritmasını kullanarak bu hedeflere ulaşmak için en uygun yolu bulurlar.

Basit Bir Yol Bulma Sistemi (Pathfinding)

Bir `PathfindingManager` veya `GhostAI` script'i içinde A* algoritmasını uygulayabilirsiniz. Bu script, başlangıç ve bitiş noktalarını alarak bir `List<Vector2Int>` şeklinde yol döndürecektir.


public class Pathfinding
{
    private Node[,] grid; // Oyun ızgarası
    private List<Node> openList; // Açık liste
    private HashSet<Node> closedList; // Kapalı liste (HashSet performanslı arama için)

    public Pathfinding(Node[,] gameGrid)
    {
        grid = gameGrid;
    }

    public List<Vector2Int> FindPath(Vector2Int startPos, Vector2Int targetPos)
    {
        Node startNode = grid[startPos.x, startPos.y];
        Node targetNode = grid[targetPos.x, targetPos.y];

        if (!targetNode.isWalkable) return null; // Hedef geçilemezse yol yok

        openList = new List<Node>();
        closedList = new HashSet<Node>();
        openList.Add(startNode);

        // Tüm düğümleri sıfırla
        foreach (Node n in grid)
        {
            n.gCost = int.MaxValue;
            n.hCost = 0;
            n.parent = null;
        }

        startNode.gCost = 0;
        startNode.hCost = GetDistance(startNode, targetNode);

        while (openList.Count > 0)
        {
            Node currentNode = openList[0];
            for (int i = 1; i < openList.Count; i++)
            {
                if (openList[i].fCost < currentNode.fCost || (openList[i].fCost == currentNode.fCost && openList[i].hCost < currentNode.hCost))
                {
                    currentNode = openList[i];
                }
            }

            openList.Remove(currentNode);
            closedList.Add(currentNode);

            if (currentNode == targetNode)
            {
                return ReconstructPath(startNode, targetNode);
            }

            foreach (Node neighbor in GetNeighbors(currentNode))
            {
                if (!neighbor.isWalkable || closedList.Contains(neighbor))
                {
                    continue;
                }

                int newMovementCostToNeighbor = currentNode.gCost + GetDistance(currentNode, neighbor);
                if (newMovementCostToNeighbor < neighbor.gCost || !openList.Contains(neighbor))
                {
                    neighbor.gCost = newMovementCostToNeighbor;
                    neighbor.hCost = GetDistance(neighbor, targetNode);
                    neighbor.parent = currentNode;

                    if (!openList.Contains(neighbor))
                    {
                        openList.Add(neighbor);
                    }
                }
            }
        }
        return null; // Yol bulunamadı
    }

    private List<Node> GetNeighbors(Node node)
    {
        List<Node> neighbors = new List<Node>();
        int x = node.gridPosition.x;
        int y = node.gridPosition.y;

        // Dört ana yön
        if (x > 0) neighbors.Add(grid[x - 1, y]);
        if (x < grid.GetLength(0) - 1) neighbors.Add(grid[x + 1, y]);
        if (y > 0) neighbors.Add(grid[x, y - 1]);
        if (y < grid.GetLength(1) - 1) neighbors.Add(grid[x, y + 1]);

        return neighbors;
    }

    private int GetDistance(Node nodeA, Node nodeB)
    {
        int dstX = Mathf.Abs(nodeA.gridPosition.x - nodeB.gridPosition.x);
        int dstY = Mathf.Abs(nodeA.gridPosition.y - nodeB.gridPosition.y);
        return dstX + dstY; // Manhattan mesafesi
    }

    private List<Vector2Int> ReconstructPath(Node startNode, Node endNode)
    {
        List<Vector2Int> path = new List<Vector2Int>();
        Node currentNode = endNode;

        while (currentNode != startNode)
        {
            path.Add(currentNode.gridPosition);
            currentNode = currentNode.parent;
        }
        path.Add(startNode.gridPosition); // Başlangıç noktasını da ekle
        path.Reverse(); // Yolu doğru sıraya getir
        return path;
    }
}

Pratik İpuçları ve En İyi Uygulamalar

Kendi Unity Hayalet AI sisteminizi geliştirirken aşağıdaki ipuçları işinize yarayacaktır:

1. Grid Görselleştirme ve Hata Ayıklama

Yol bulma algoritmaları karmaşık olabileceğinden, `OnDrawGizmos()` metodunu kullanarak Unity editöründe ızgarayı, geçilebilir alanları, engelleri, açık/kapalı listeleri ve bulunan yolları görselleştirmek, hata ayıklama sürecini inanılmaz derecede hızlandırır. Bu sayede algoritmaların beklediğiniz gibi çalışıp çalışmadığını anında görebilirsiniz.

2. Hedef Güncelleme Sıklığını Ayarlama

Hayaletlerin her karede (frame) yol bulma algoritmasını çalıştırması, özellikle büyük haritalarda veya çok sayıda hayalet olduğunda ciddi performans sorunlarına yol açabilir. Bunun yerine, yol bulma işlemini belirli aralıklarla (örn. 0.2-0.5 saniyede bir) veya Pac-Man önemli bir kavşaktan geçtiğinde yeniden hesaplamak, performansı önemli ölçüde artıracaktır. Hayaletlerin hedefi sürekli değişmediği sürece, aynı yolu tekrar tekrar hesaplamaya gerek yoktur.

3. Köşe Kontrolü ve Hareket Kısıtlamaları

Orijinal Pac-Man oyununda hayaletler sadece kavşaklarda yön değiştirebilirler. Bu kuralı kendi oyununuza entegre etmek, hayaletlerin hareketini daha öngörülebilir ve "Pac-Man benzeri" hale getirir. Hayaletler bir kavşağa ulaşana kadar mevcut yönlerinde ilerlemeli ve ancak o zaman yeni bir yol (veya yön) hesaplamalıdır. Bu, aynı zamanda yol bulma sıklığını da düşürebilir.

4. Basit Hayalet Davranışları için Alternatifler

Her hayalet için karmaşık A* algoritması kullanmak yerine, Clyde gibi daha basit davranışlara sahip hayaletler için daha temel bir "rastgele hareket" veya "en yakın kaçış yolu" mantığı uygulamak, kod karmaşıklığını ve hesaplama yükünü azaltabilir. Örneğin, Clyde'ın Pac-Man'den kaçtığı durumlarda, sadece Pac-Man'den uzaklaşan rastgele bir yön seçmesi yeterli olabilir.

Yaygın Hatalar ve Çözümleri

Unity Hayalet AI geliştirirken karşılaşılan bazı yaygın hatalar ve çözümleri şunlardır:

1. Sonsuz Döngüler

Yol bulma algoritmalarında en sık karşılaşılan sorunlardan biridir. Genellikle bir düğümün açık listeden çıkarılmaması veya kapalı listeye eklenmemesi durumunda ortaya çıkar. Her düğümün yalnızca bir kez işlendiğinden ve açık listeden çıkarıldıktan sonra kapalı listeye eklendiğinden emin olun.

2. Yol Bulunamaması veya Yanlış Yollar

Hedef noktasının geçilemez bir alanda olması, ızgara verilerinin yanlış tanımlanması (duvarlar veya boşluklar) veya `GetDistance` gibi sezgisel fonksiyonların hatalı olması bu soruna yol açabilir. `isWalkable` değerlerini ve komşu düğüm kontrolünü dikkatlice gözden geçirin. Sezgisel fonksiyonun her zaman hedefe olan gerçek maliyetten daha düşük veya eşit bir değer döndürdüğünden emin olun (tutarlı sezgisel).

3. Hayaletlerin Köşelerde Takılması

Hayaletlerin hareket yönü ve ızgara hizalaması arasındaki uyumsuzluktan kaynaklanabilir. Hayaletlerin pozisyonlarını her zaman ızgaraya hizalı tutun (örn. `Mathf.RoundToInt` kullanarak) ve sadece tam bir ızgara hücresinde iken yön değiştirmelerine izin verin.

Performans ve Optimizasyon Notları

Büyük haritalarda veya çok sayıda AI ajanı ile çalışırken, yol bulma işlemleri önemli bir performans darboğazı oluşturabilir. Unity Hayalet AI sisteminizi optimize etmek için şunları göz önünde bulundurun:

  • Yolu Önbelleğe Alma (Path Caching): Hayaletler aynı hedefe tekrar tekrar gidiyorsa, ilk bulunan yolu önbelleğe alın ve yeniden kullanın. Yalnızca hedef veya engeller değiştiğinde yolu yeniden hesaplayın.
  • Sınırlı Yol Bulma Sıklığı: Yukarıda belirtildiği gibi, yol bulma sıklığını azaltın. Her karede değil, belirli aralıklarla veya önemli olaylarda (örn. Pac-Man'in konumu değiştiğinde) yolu güncelleyin.
  • Basit Heuristikler: Izgara tabanlı oyunlar için Manhattan mesafesi gibi basit ve hızlı sezgisel fonksiyonları kullanın.
  • Unity Job System veya Coroutine: Karmaşık yol bulma algoritmalarını ana iş parçacığından ayırmak için Unity'nin Job System'ini veya Coroutine'leri kullanabilirsiniz. Bu, oyunun donmasını engeller ve akıcılığı artırır.
  • NavMesh (Daha Karmaşık Senaryolar İçin): Eğer oyununuz 2D grid tabanlı olmaktan çıkıp daha serbest hareketli 3D bir yapıya sahipse, Unity'nin yerleşik NavMesh sistemini kullanmayı düşünebilirsiniz. Ancak Pac-Man tarzı bir oyun için A* genellikle daha uygun ve kontrol edilebilir bir çözümdür.

Sonuç

Pac-Man benzeri bir oyunda hayaletlerin yapay zekası ve yol bulma mekanizmaları, oyunun ruhunu ve zorluğunu belirleyen en kritik unsurlardandır. Bu makalede, ızgara tabanlı hareketten A* algoritmasının derinliklerine, hayaletlerin özel davranışlarından performans optimizasyonlarına kadar Unity Hayalet AI sisteminin temel bileşenlerini ele aldık.

Umarız bu detaylı rehber, kendi Pac-Man klonunuzu geliştirirken size ilham vermiş ve yol göstermiştir. Unutmayın, iyi tasarlanmış bir yapay zeka sistemi, oyuncular için unutulmaz ve eğlenceli bir deneyim sunmanın anahtarıdır. Kendi hayaletlerinizi programlarken bol şans!

🧠 Ders Sonu Değerlendirme Testi

Dersi tamamladıktan sonra bilgilerinizi test edin ve ekstra puanlar kazanın.

🔥 +50 XP Ödül
🔒

Sınava Katılmak İçin Giriş Yapın

Bu ders sonu testini çözebilmek, bilginizi test edip **+50 XP** kazanmak ve **Sınav Şampiyonu** rozetinin kilidini açmak için üye girişi yapmalısınız.

Yorumlar (0)

Yorum yazabilmek ve derslere katkıda bulunabilmek için giriş yapmalısınız.

İlk yorumu siz yapın!