Unity Platformer: Dinamik Level Seçimi ve Kaydetme Sistemi
Unity ile platformer oyunları geliştirirken, oyuncuların ilerlemesini takip etmek ve onlara bir sonraki level’a geçme imkanı sunmak kritik öneme sahiptir. Bu makalede, oyununuz için etkileşimli bir level seçme menüsü oluşturmayı ve oyuncunun ilerlemesini kalıcı olarak kaydeden sağlam bir Unity Kaydetme Sistemi uygulamayı detaylıca inceleyeceğiz. Bu sayede oyuncular, oyunu kapatsalar bile kaldıkları yerden devam edebilecek, kilitli levelleri açabilecek ve oyun deneyimleri kesintisiz olacak.
Giriş: Neden Bir Kaydetme Sistemi?
Bir platformer oyununda, oyuncuların yeni levellerin kilidini açması, toplanabilir eşyaları toplaması veya yüksek skorlar yapması gibi birçok ilerleme unsuru bulunur. Bu ilerlemenin kaydedilmemesi, oyuncu için büyük bir hayal kırıklığına neden olur ve oyunun tekrar oynanabilirlik değerini düşürür. Etkili bir Unity Kaydetme Sistemi, oyuncunun motivasyonunu artırır, onlara emeklerinin boşa gitmediği hissini verir ve oyun deneyimini zenginleştirir. Özellikle level seçme menülerinde, hangi levellerin tamamlandığını, hangilerinin kilitli olduğunu göstermek için bu sistem vazgeçilmezdir.
Level Seçim Menüsü Tasarımı ve İşlevselliği
Level seçme menüsü, oyuncunun oyun içindeki haritasıdır. Genellikle her level için bir düğme veya simge içerir. Bu menüde temel olarak şu işlevler bulunmalıdır:
- Level Kilitleme/Kilit Açma: Oyuncu belirli bir level’ı tamamlamadan önce bir sonraki level’ın kilitli olması, ilerlemeyi teşvik eder.
- Level İlerlemesi Gösterimi: Tamamlanan levelleri işaretlemek (örn: altın yıldızlar, bir onay işareti) oyuncuya başarı hissi verir.
- Seçilen Level’a Geçiş: Düğmeye tıklandığında ilgili sahneye yükleme.
Bu işlevleri sağlamak için, her level’ın durumunu (kilitli mi, tamamlandı mı, skor) kaydetmemiz gerekecek. Kaydedilen bu veriler, menü yüklendiğinde okunarak UI öğelerinin (düğme etkileşimi, ikonlar) güncellenmesini sağlar.
Unity Kaydetme Sistemi Temelleri: PlayerPrefs ve JSON
Unity’de veri kaydetmek için birkaç yöntem bulunur. En yaygın ikisi `PlayerPrefs` ve JSON serialization’dır.
PlayerPrefs
Basit ve küçük veri tipleri (int, float, string) için hızlı ve kolay bir çözümdür. Örneğin, oyuncunun en son tamamladığı level’ın numarasını kaydetmek için idealdir:
// Kaydetme
PlayerPrefs.SetInt("SonLevel", 3);
PlayerPrefs.Save(); // Değişiklikleri diske yaz
// Yükleme
int sonLevel = PlayerPrefs.GetInt("SonLevel", 1); // Varsayılan değer 1
Ancak, `PlayerPrefs` karmaşık veri yapılarını (listeler, custom sınıflar) doğrudan desteklemez ve güvenlik açısından hassas veriler için önerilmez. Platformer oyunlarında birden fazla level’ın durumunu veya toplanan eşya listesini kaydetmek gerektiğinde yetersiz kalır.
JSON Serialization (JsonUtility)
Daha karmaşık ve yapılandırılmış veriler için JSON (JavaScript Object Notation) kullanmak çok daha esnektir. Unity’nin `JsonUtility` sınıfı, C# sınıflarını veya struct’larını JSON dizgilerine dönüştürmenize (serialize) ve tersine çevirmenize (deserialize) olanak tanır. Bu, birden fazla level’ın kilit durumunu, skorlarını veya toplanan eşya envanterini kaydetmek için mükemmeldir. `Application.persistentDataPath` dizini, platforma özel, kalıcı veri depolama için güvenli bir konum sağlar.
using System;
using System.Collections.Generic;
[Serializable]
public class SaveData
{
public int lastCompletedLevel = 0;
public List<bool> unlockedLevels = new List<bool>(); // Her level için kilit durumu
public List<int> levelScores = new List<int>(); // Her level için skor
// Kurucu metot ile varsayılan değerler
public SaveData(int totalLevels)
{
for (int i = 0; i < totalLevels; i++)
{
unlockedLevels.Add(false);
levelScores.Add(0);
}
// İlk level her zaman açık olmalı
if (totalLevels > 0)
{
unlockedLevels[0] = true;
}
}
}
Bu `SaveData` sınıfı, oyunumuzun genel ilerlemesini tutacak. `[Serializable]` özniteliği, Unity’nin `JsonUtility` sınıfının bu objeyi JSON’a çevirebilmesi için gereklidir.
Kaydetme ve Yükleme Yöneticisi (SaveManager)
Tüm kaydetme ve yükleme işlemlerini tek bir merkezden yönetmek için bir `SaveManager` sınıfı oluşturmak iyi bir pratiktir. Bu sınıf genellikle bir Singleton olarak tasarlanır.
using UnityEngine;
using System.IO;
public class SaveManager : MonoBehaviour
{
public static SaveManager Instance { get; private set; }
public SaveData CurrentSaveData { get; private set; }
private string savePath;
private const string SAVE_FILE_NAME = "savedata.json";
private int totalGameLevels = 5; // Toplam level sayınız
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
}
else
{
Instance = this;
DontDestroyOnLoad(gameObject);
savePath = Path.Combine(Application.persistentDataPath, SAVE_FILE_NAME);
LoadGame();
}
}
public void SaveGame()
{
string json = JsonUtility.ToJson(CurrentSaveData, true); // true ile okunabilir format
File.WriteAllText(savePath, json);
Debug.Log("Oyun kaydedildi: " + savePath);
}
public void LoadGame()
{
if (File.Exists(savePath))
{
string json = File.ReadAllText(savePath);
CurrentSaveData = JsonUtility.FromJson<SaveData>(json);
Debug.Log("Oyun yüklendi: " + savePath);
}
else
{
CurrentSaveData = new SaveData(totalGameLevels);
Debug.Log("Yeni oyun başlatıldı, kayıt bulunamadı.");
SaveGame(); // Yeni bir kayıt dosyası oluştur
}
}
public void CompleteLevel(int levelIndex, int score)
{
if (levelIndex > CurrentSaveData.lastCompletedLevel)
{
CurrentSaveData.lastCompletedLevel = levelIndex;
}
if (levelIndex < CurrentSaveData.unlockedLevels.Count - 1)
{
CurrentSaveData.unlockedLevels[levelIndex + 1] = true; // Bir sonraki level'ı aç
}
// Skor güncelleme
if (levelIndex < CurrentSaveData.levelScores.Count)
{
if (score > CurrentSaveData.levelScores[levelIndex]) // Sadece daha yüksek skoru kaydet
{
CurrentSaveData.levelScores[levelIndex] = score;
}
}
SaveGame(); // Level tamamlandığında kaydet
}
// Ek olarak, level durumunu güncelleme veya skorları alma metotları eklenebilir.
}
Bu `SaveManager` sınıfı, oyun başladığında kayıtlı veriyi yükler veya yeni bir kayıt dosyası oluşturur. `CompleteLevel` metodu, bir level tamamlandığında çağrılır, oyuncunun ilerlemesini günceller ve bir sonraki level’ın kilidini açar. Her güncelleme sonrası `SaveGame()` çağrılır. Bu, sağlam bir Unity Kaydetme Sistemi için temel bir yapıdır.
Pratik İpuçları
- Kaydetme Sıklığı ve Kullanıcı Geri Bildirimi: Oyuncunun ilerlemesini önemli anlarda (level bitimi, checkpoint) otomatik olarak kaydedin. Uzun oyun seanslarında periyodik otomatik kaydetme de faydalı olabilir. Kayıt işlemi tamamlandığında kısa bir ‘Oyun kaydedildi’ mesajı veya simgesi göstermek, kullanıcı deneyimini artırır.
- Hata Ayıklama ve Kayıt Dosyası Konumu: Geliştirme sırasında kayıt dosyasına erişmek ve içeriğini görmek isteyebilirsiniz. `Debug.Log(Application.persistentDataPath);` kullanarak kayıt dosyasının yolunu konsola yazdırın. JSON dosyaları metin editörleriyle okunabilir olduğundan, verilerinizin doğru kaydedilip kaydedilmediğini kolayca kontrol edebilirsiniz.
- Veri Bütünlüğü ve Yedekleme: Nadiren de olsa kayıt dosyaları bozulabilir. Önemli oyunlar için, oyuncunun mevcut kayıt dosyasının bir yedeğini tutmayı düşünebilirsiniz (örn: `savedata.bak`). Yükleme sırasında ana dosya bozuksa, yedekten geri yüklemeyi deneyebilirsiniz.
- Level Seçim Menüsü UI Güncellemesi: Level seçme menünüz yüklendiğinde, `SaveManager.Instance.CurrentSaveData` üzerinden `unlockedLevels` ve `levelScores` verilerini okuyarak her level düğmesinin durumunu (aktif/pasif, skor gösterimi) güncelleyin. Örneğin, `unlockedLevels[i]` true ise düğmeyi etkileşimli yapın, değilse grileştirin ve bir kilit ikonu gösterin.
Yaygın Hatalar ve Çözümleri
- `FileNotFoundException` veya Veri Yüklenememesi: Kayıt dosyası belirtilen yolda bulunamadığında meydana gelir. `File.Exists(savePath)` kontrolü ile dosyanın varlığını doğrulayın. `Application.persistentDataPath` kullanıldığından emin olun ve dosya adında yazım hatası yapmamaya dikkat edin.
- Veri Deserializasyon Hataları (JSON): `SaveData` sınıfınızın yapısını değiştirdiğinizde (örneğin, yeni bir alan eklediğinizde veya alan adını değiştirdiğinizde) eski kayıt dosyaları yeni yapıya uymayabilir. Bu durumda `JsonUtility.FromJson` hata verebilir veya varsayılan değerler atayabilir. Geliştirme aşamasında sık sık kayıt dosyasını silip yeniden oluşturmak faydalıdır. Yayınlanmış bir oyunda ise, eski sürümlerden yeni sürümlere geçiş için veri migrasyonu (geçiş) mantığı eklemeniz gerekebilir.
- `PlayerPrefs` Limitleri: Karmaşık veya çok sayıda veri kaydetmeye çalışmak `PlayerPrefs`’i yavaşlatabilir veya yönetilemez hale getirebilir. Böyle durumlarda mutlaka JSON serialization gibi daha robust bir Unity Kaydetme Sistemi çözümüne geçin.
Performans ve Optimizasyon Notları
Kaydetme ve yükleme işlemleri genellikle I/O (giriş/çıkış) yoğun işlemlerdir. Büyük veri setleri söz konusu olduğunda oyunun kısa bir süre donmasına neden olabilirler. Bu durumu minimize etmek için:
- Kaydetme Sıklığını Optimize Edin: Sadece gerçekten gerektiğinde (level sonu, checkpoint, uygulamadan çıkış) kaydetme yapın. Çok sık kaydetmek performansı düşürebilir.
- Asenkron Kaydetme (İleri Seviye): Çok büyük kayıt dosyaları için `File.WriteAllText` yerine asenkron dosya yazma işlemlerini (`File.WriteAllTextAsync` veya `Task` API’leri) kullanarak oyunun ana iş parçacığını (main thread) bloke etmemeyi düşünebilirsiniz. Bu, özellikle mobil cihazlarda veya düşük özellikli donanımlarda daha akıcı bir deneyim sunar.
Sonuç
Unity platformer oyununuz için dinamik bir level seçme menüsü ve sağlam bir Unity Kaydetme Sistemi oluşturmak, oyuncu deneyimini derinden etkileyen temel unsurlardır. `JsonUtility` ile veri yapılarınızı tanımlayarak ve `SaveManager` gibi merkezi bir sistem kullanarak, oyununuzun ilerlemesini güvenilir bir şekilde yönetebilirsiniz. Bu rehberdeki adımları takip ederek, oyuncularınızın emeğinin karşılığını veren ve onları oyununuzda daha uzun süre tutan bir yapı kurmuş olacaksınız. İyi eğlenceler ve bol geliştirmeler!




