İçeriğe geç

Menü

Berk Akademi
Yorumlar Google yorumları Birebir ders başvurusu Ücretsiz ön görüşme Ana Sayfa

Unity 2D Oyunda Skor Sistemi Nasıl Yapılır? Yıldız Toplama Örneği

unity-2d-oyunda-skor-sistemi-nasil-yapilir
Bu yazıda neler var?
  1. Skor sistemi üç sorunun cevabıdır: Olay, yönetici ve arayüz
  2. Yıldız nesnesini hazırlama: Is Trigger, Rigidbody2D ve etiket
  3. Skor yöneticisi ve OnTriggerEnter2D ile yıldız toplama kodu
  4. Skoru ekranda göstermek: Text mi TextMeshPro mu?
  5. Sahne değiştiğinde skor nasıl korunur?
  6. Hata ayıklama kontrol listesi: skor artmıyor, yazı güncellenmiyor, çift puan veriyor
  7. Sık Sorulan Sorular

Unity 2D oyunda skor sistemi kurarken skorun başlangıç değerini, puanı artıran olayı ve değerin ekrana yazılmasını ayrı ayrı planlaman gerekir. Skor sistemi üç soruya cevap vermelidir: Puan ne zaman artıyor, nerede tutuluyor, kullanıcıya ne zaman gösteriliyor? Yıldız toplama örneğinde cevaplar şöyledir: puan yıldıza dokunulduğunda artar, değer tek bir yönetici sınıfta tutulur, arayüz de değer değiştiğinde güncellenir. Üçünü tek script'e doldurmak başta hızlı görünür, ama oyun büyüdükçe sorun çıkarır.

Skor sistemi üç sorunun cevabıdır: Olay, yönetici ve arayüz

Skor sistemi üç ayrı sorumluluktan oluşur ve her birinin tek bir sahibi olmalıdır:

  • Olay: Puan kazanma anıdır. Örnekte yıldızın toplanması. Olay yalnızca "bir şey oldu" der, skorun nasıl tutulduğunu bilmez.
  • Yönetici: Skor değerinin tutulduğu ve değiştirildiği tek yerdir. Başlangıç değeri de burada belirlenir.
  • Arayüz: Değerin gösterildiği yerdir. Skoru hesaplamaz, yalnızca yöneticiden gelen değeri yazar.

Akış her zaman aynı sırayla ilerler:

  1. Oyuncu yıldıza temas eder ve yıldız toplanır.
  2. Yıldız, skor yöneticisine puan eklenmesini bildirir.
  3. Yönetici skoru artırır.
  4. Arayüz yeni değeri ekrana yazar.

Yıldızı, skor verisini ve ekran yazısını aynı sınıfa bağlarsan sorunlar somutlaşır. Düşman öldürünce ya da madalya alınca da puan vermek istediğinde bu sınıfı kopyalaman veya içine yeni koşullar eklemen gerekir. Skor yazısının yerini ya da biçimini değiştirmek, puan hesabını bozma riski taşıyan bir düzenleme olur. Her yıldız kendi skorunu tutarsa da sahnede birden fazla yıldız birbirinden bağımsız sayılara sahip olur. Yönetici ayrı olduğunda yeni bir puan kaynağı yalnızca "puan ekle" çağrısı yapar, arayüz hiç değişmez.

Bu ayrımı koda geçmeden önce kâğıt üzerinde çizmek, hataları editör açılmadan yakalamanı sağlar. Akışı dilden bağımsız kurabildiğini ölçmek istersen ücretsiz Algoritmik Düşünme Testini deneyebilirsin.

Yıldız nesnesini hazırlama: Is Trigger, Rigidbody2D ve etiket

Yıldız nesnesini hazırlama: Is Trigger, Rigidbody2D ve etiket

Yıldızın oyuncuya dokunduğunu fark edebilmesi için üç editör ayarı birlikte doğru olmalıdır. Hiç kod yazmadan önce bunları kur:

  1. Yıldız sprite'ına bir 2D collider ekle ve Inspector'da Is Trigger seçeneğini işaretle.
  2. Oyuncuda bir Rigidbody2D bileşeni bulunduğundan emin ol.
  3. Oyuncu nesnesine Inspector'daki Tag alanından "Player" etiketini ver.

Her ayarın gerekçesi farklıdır:

  • Is Trigger: Yıldızdan oyuncunun içinden geçmesini ve çarpışma fiziği uygulanmamasını istiyorsun; yalnızca "temas oldu" bildirimini istiyorsun. Is Trigger kapalı kalırsa yıldız katı bir engel gibi davranır ve oyuncu ona çarpar.
  • Rigidbody2D: Unity'nin 2D trigger olaylarını üretebilmesi için temas eden nesnelerden en az birinde Rigidbody2D bulunmalıdır. Oyuncuda olması bu yüzden yeterlidir; her yıldıza ayrıca eklemen gerekmez.
  • Player etiketi: Sahnedeki her temas yıldızı toplamamalıdır. Koddaki etiket kontrolü, yalnızca oyuncunun temasında puan verilmesini sağlar. Böylece zemin, düşman ya da başka bir yıldız dokunsa bile skor artmaz.
  • Bu ayarlardan biri eksik ya da yanlışsa tipik belirti, yıldıza değdiğinde olayın hiç çağrılmamasıdır: ne hata alırsın ne de skor değişir. Böyle bir durumda neyi, hangi sırayla kontrol edeceğini yazının hata ayıklama bölümünde adım adım göreceksin.

    Skor yöneticisi ve OnTriggerEnter2D ile yıldız toplama kodu

    İki script yeterli: biri skoru tutar ve değişince haber verir, diğeri yıldızın çarpışma anını yönetir. Yönetici arayüzü hiç tanımaz.

    using System;
    using UnityEngine;
    
    public class ScoreManager : MonoBehaviour
    {
        public static ScoreManager Instance { get; private set; }
    
        public event Action<int> ScoreChanged;
    
        private int score = 0;
        public int Score => score;
    
        private void Awake()
        {
            Instance = this;
        }
    
        public void AddScore(int amount)
        {
            score += amount;
            Debug.Log("Skor: " + score);
            ScoreChanged?.Invoke(score);
        }
    }

    Skor private olduğu için dışarıdan yalnızca AddScore ile değişir. ScoreChanged olayı her artışta yeni değeri yayınlar; dinleyen yoksa ?.Invoke hata vermez. Instance sahnedeki tek ScoreManager nesnesine giden basit bir erişim yoludur. Bu yol, sahnede tek örnek olduğu varsayımına dayanır; iki yönetici olursa son atanan kazanır.

    using UnityEngine;
    
    public class Star : MonoBehaviour
    {
        [SerializeField] private int points = 10;
    
        private void OnTriggerEnter2D(Collider2D other)
        {
            if (!other.CompareTag("Player")) return;
    
            ScoreManager.Instance.AddScore(points);
            Destroy(gameObject);
        }
    }

    Yıldız skoru tutmaz, yalnızca "ben toplandım" der. CompareTag kontrolü başka nesnelerin değerli yıldızı yanlışlıkla toplamasını engeller. Puan Destroy öncesinde eklenir; nesne aynı karede yok edilir ve ikinci tetiklemeye açık kalmaz.

    Oyuncu üç yıldız topladığında Console çıktısı şöyle olur:

    Skor: 10
    Skor: 20
    Skor: 30

    Skoru ekranda göstermek: Text mi TextMeshPro mu?

    Skoru ekranda göstermek: Text mi TextMeshPro mu?

    Eski UI Text bileşeni basit metinler için yeterlidir, ancak ölçeklendiğinde bulanıklaşabilir ve biçimlendirme seçenekleri sınırlıdır. TextMeshPro ise daha keskin metin çizer, zengin biçimlendirme olanakları sunar. Yeni projelerde genellikle TextMeshPro tercih edilir. Sahneye eklemek için UI menüsünden TextMeshPro metin nesnesi eklemen yeterli.

    Üçüncü sorumluluk olan arayüz, yöneticinin olayına abone olur:

    using TMPro;
    using UnityEngine;
    
    public class ScoreUI : MonoBehaviour
    {
        [SerializeField] private TMP_Text scoreText;
    
        private void OnEnable()
        {
            ScoreManager.Instance.ScoreChanged += UpdateText;
            UpdateText(ScoreManager.Instance.Score);
        }
    
        private void OnDisable()
        {
            if (ScoreManager.Instance != null)
                ScoreManager.Instance.ScoreChanged -= UpdateText;
        }
    
        private void UpdateText(int value)
        {
            scoreText.text = "Skor: " + value;
        }
    }

    Inspector'daki scoreText alanına metin nesnesini sürükle. İkinci yıldızdan sonra ekranda Skor: 20 görünür.

    İki dikkat noktası var:

    • OnEnable içindeki UpdateText çağrısı, oyun başında metnin boş kalmasını önler ve "Skor: 0" gösterir.
    • Bu çağrı ScoreManager.Awake çalışmadan önce yapılırsa Instance boş kalır. Böyle bir durumda yöneticinin önce çalışmasını sağla, örneğin Unity'nin script çalışma sırası ayarıyla.

    Eski Text kullanıyorsan yalnızca üç satır değişir: using TMPro; yerine using UnityEngine.UI; yazılır, alan türü TMP_Text yerine Text olur ve metin atama satırı aynı kalır.

    Sahne değiştiğinde skor nasıl korunur?

    Skoru tutan yönetici nesnesi sahnenin içindeyse, yeni sahne yüklendiğinde sahnenin diğer nesneleriyle birlikte yok olur. Yeni sahnede yönetici yeniden oluşur ve skor başlangıç değerine döner. Skorun sahneler arasında yaşaması için iki yaygın yol var.

    Seçenek 1: Yöneticiyi DontDestroyOnLoad ile yaşatmak

    DontDestroyOnLoad, verdiğin nesnenin yeni sahne yüklenirken yok edilmemesini sağlar. Nesnenin hiyerarşide başka bir nesnenin çocuğu değil, kök nesne olması gerekir. Tek dikkat noktası şudur: ilk sahneye geri dönersen sahnede duran yönetici ile yaşatılan yönetici aynı anda var olur. Bu yüzden Awake içinde kısa bir kopya kontrolü yapılır.

    public class ScoreManager : MonoBehaviour
    {
        public static ScoreManager Instance { get; private set; }
    
        private void Awake()
        {
            if (Instance != null && Instance != this)
            {
                Destroy(gameObject); // ikinci kopya kendini yok eder
                return;
            }
    
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
    }

    İlk yönetici kalır, sonradan gelen kopya kendini siler. Skor değeri ilk yöneticide durduğu için sahneler arasında korunur.

    Seçenek 2: Skoru ayrı bir veri sınıfında tutmak

    İkinci yaklaşımda skor, sahneden bağımsız bir sınıfta durur. Yönetici sahneyle birlikte yok olsa da değer kaybolmaz:

    public static class GameSession
    {
        public static int Score;
    }

    Bu veri oyun açık kaldığı sürece bellekte durur. Yönetici yalnızca GameSession.Score değerini okur ve değiştirir. Yaklaşım basittir, ancak olay tetikleme ve arayüz bildirimi gibi işleri yine yöneticiye bırakman gerekir.

    Küçük projelerde ilk yol, yönetici zaten tek bir noktada toplanmışsa pratiktir. Skor dışında da oturum verisi tutacaksan ikinci yol düzeni daha temiz tutar.

    Yeni sahnedeki ScoreUI'ı yeniden bağlamak

    Hangi yolu seçersen seç, yeni sahnedeki ScoreUI yeni bir nesnedir. Olaya yeniden abone olmalı ve açıldığında mevcut skoru okuyup yazıya yansıtmalıdır. Yoksa skor doğru olduğu hâlde ekranda eski ya da boş metin görünür.

    private void OnEnable()
    {
        ScoreManager.Instance.OnScoreChanged += UpdateText;
        UpdateText(ScoreManager.Instance.Score); // mevcut değeri hemen oku
    }
    
    private void OnDisable()
    {
        ScoreManager.Instance.OnScoreChanged -= UpdateText;
    }

    Oyunu kapatıp açınca da kalacak bir en yüksek skor ayrı bir ihtiyaçtır; bunun için PlayerPrefs gibi kalıcı bir depolama gerekir.

    Hata ayıklama kontrol listesi: skor artmıyor, yazı güncellenmiyor, çift puan veriyor

    Skor sorunlarının çoğu üç yerden çıkar: temas algılanmıyor, olay arayüze ulaşmıyor ya da olay birden fazla kez tetikleniyor. Aşağıdaki listeyi yukarıdan aşağıya takip edebilirsin.

    Skor artmıyor

    • Yıldızın collider bileşeninde Is Trigger işaretli mi?
    • Çarpışan iki nesneden en az birinde Rigidbody2D var mı?
    • Oyuncunun etiketi kodda yazdığınla birebir aynı mı? Büyük küçük harf ve boşluk farkı koşulu bozar.
    • 2D yerine 3D collider mı eklendi? OnTriggerEnter2D 3D collider ile çalışmaz; iki nesnede de 2D collider kullan.
    • Script nesneye eklenmiş ve etkin mi?
    • Console'da kırmızı bir hata var mı? Bir hata, kodun geri kalanını çalıştırmadan keser.

    Yazı güncellenmiyor

    • ScoreUI olaya abone mi? OnEnable içindeki satırın çalıştığından emin ol.
    • Inspector'da metin referansı atanmış mı?
    • Yönetici referansı null mı? Sahnede yönetici yoksa ya da Instance henüz kurulmadıysa abonelik hata verir.
    • Olay, puan eklendiğinde yönetici tarafından gerçekten çağrılıyor mu?

    Çift puan veriyor

    • Yıldızda ya da oyuncuda birden fazla collider olabilir; her biri ayrı temas olayı üretir.
    • Star script'i aynı nesnede iki kez bulunabilir.
    • Destroy nesneyi hemen değil kare sonunda yok eder; bu arada olay yeniden tetiklenebilir.

    Son durum için yıldıza bir collected bayrağı eklemek yeterince basit bir önlemdir:

    public class Star : MonoBehaviour
    {
        private bool collected;
    
        private void OnTriggerEnter2D(Collider2D other)
        {
            if (collected) return;
            if (!other.CompareTag("Player")) return;
    
            collected = true;
            Debug.Log("Yıldız toplandı");
            ScoreManager.Instance.AddScore(10);
            Destroy(gameObject);
        }
    }

    Her yıldız için Console'da tek bir "Yıldız toplandı" satırı görmelisin. Aynı yıldız için iki satır çıkıyorsa temas olayı gerçekten tekrar ediyor demektir. Hiç satır çıkmıyorsa sorun trigger ayarlarındadır.

    Teşhiste kodu tahminle değiştirmek yerine Debug.Log ile zinciri adım adım doğrula: temas, AddScore çağrısı, olay, yazı güncellemesi. Log'un kesildiği yer sorunun bulunduğu yerdir.

    Sık Sorulan Sorular

    Unity'de skoru static değişkende mi yoksa ayrı bir yöneticide mi tutmalıyım?

    Çok küçük bir denemede static değişken işini görür. Ancak puan değişince arayüzü bilgilendirmek, kuralları tek yerde toplamak ve ileride başka sistemleri (can, süre) eklemek için ayrı bir yönetici daha düzenli bir temel sunar. İstersen ikisini birleştirip değeri static veri sınıfında, mantığı yöneticide tutabilirsin.

    Oyunu kapatıp açınca en yüksek skoru nasıl saklarım?

    Basit sayısal değerler için PlayerPrefs kullanabilirsin. Oyun bittiğinde mevcut skoru kayıtlı en yüksek skorla karşılaştırır, yalnızca büyükse PlayerPrefs.SetInt ile yazarsın. Oyun açılırken PlayerPrefs.GetInt ile okursun. Daha karmaşık veriler için dosyaya kaydetme gibi başka yöntemler gerekebilir.

    Yıldız toplanınca puan yerine can ya da süre eklemek için sistemde ne değişir?

    Temas algılama kısmı aynı kalır. Değişen yer, yıldızın çağırdığı yöneticidir: AddScore yerine AddLife ya da AddTime gibi bir metot çağrılır. Bu işler için ayrı yönetici ve ayrı arayüz bileşeni kurmak, her değerin kendi olayını yayınlamasını sağlar ve sistemleri birbirinden bağımsız tutar.

    Skor sistemi küçük görünse de olay, yönetici ve arayüz ayrımını doğru kurduğunda hem hata ayıklamak hem genişletmek kolaylaşır. Yıldız toplama örneğindeki bu ayrımı can, süre ya da başka bir toplanabilir nesne için de aynı şekilde kullanabilirsin.

Bu içerik aradığın cevabı verdi mi?
Yanıtın, hangi yazıları geliştirmemiz gerektiğini anlamamıza yardımcı olur.
Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

Bu konudan sonra ne okuyabilirsin?

Tüm yazılar

İlgili Eğitimler

Berk Keskin, yazılım geliştirici ve eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; İzmir Ekonomi Üniversitesi'ni bölüm birincisi ve yüksek şeref öğrencisi olarak tamamladı. Bugün yalnızca eğitim vermekle kalmıyor, sektörde aktif olarak yazılım projeleri geliştiriyor ve gerçek dünya deneyimini birebir derslerine taşıyor. Ezberden uzak, mühendislik zihniyetini merkeze alan sürdürülebilir öğrenme sistemleri tasarlayarak sorgulayan, üreten ve problem çözebilen yeni nesil yazılımcılar yetiştiriyor.

Sektörel Deneyim & Projeler

  • Ticarify Entegrasyon Yazılım logosu CEO Ticarify Entegrasyon YazılımPazaryerleri ve e-ticaret sitelerine otomatik e-fatura kesimi, sipariş ve kargo takibi hizmetleri sunan e-Dönüşüm platformunun API mimarisini ve yazılım ekibini yönetmektedir.
  • Benim Düğünüm logosu CEO Benim DüğünümDijital etkinlik ve anı paylaşım platformu.
  • Siberdizayn logosu Yazılım Ekibi Lideri SiberdizaynYüksek anlık oyuncu trafiğine sahip oyun kontrol panelleri ve sunucu altyapıları geliştiren yazılım ekibine liderlik etmektedir.
  • MEDYOGRAFYA 360° Dijital Çözümler logosu Dijital Strateji Lideri MEDYOGRAFYA 360° Dijital ÇözümlerŞirketlerin dijital çözümlerde uzun vadede nasıl ilerlemesi gerektiği ve dijital dönüşüm süreçlerinin yönetilmesine destek olmaktadır.
  • İzmir Ekonomi Üniversitesi logosu Danışma Kurulu Üyesi İzmir Ekonomi ÜniversitesiMezun olduğu üniversitesinde, Bilgisayar Programcılığı bölümünün akademik müfredatını güncel sektör ihtiyaçlarına göre şekillendirmek adına Danışma Kurulu'nda görev almaktadır.
WhatsApp Hemen Ara