Endless Runner (Sonsuz Koşucu) oyunları, mobil oyun dünyasının en popüler türlerinden biridir. Bu oyunlar, oyuncuyu sürekli ileri doğru hareket eden bir karakteri kontrol ederken, engellerden kaçınma ve puan toplama üzerine kuruludur. Subway Surfers, Temple Run gibi örneklerle milyonlarca oyuncuya ulaşan bu türün kalbinde, sonsuzluk hissini veren dinamik ve rastgele içerik üretimi yatar. Bu makalede, Unity kullanarak bir Endless Runner geliştirme sürecinde yol ve engel üretimini nasıl ele alacağımızı, temelden orta seviyeye detaylarıyla inceleyeceğiz.
Endless Runner Geliştirme Temelleri
Bir Endless Runner oyununun temel amacı, oyuncunun olabildiğince uzun süre hayatta kalmasını sağlamaktır. Bu, genellikle karakterin sabit bir hızda ilerlemesi veya oyuncunun hızını artırmasıyla gerçekleşir. Oyunun “sonsuz” hissini vermesi için, sahnenin hiçbir zaman bitmemesi, sürekli olarak yeni yol parçacıklarının ve engellerin oluşturulması gerekir. Bu dinamik üretim mekaniği, oyunun tekrar oynanabilirliğini artırır ve her oynayışta farklı bir deneyim sunar.
Temel olarak, bir Endless Runner oyununda şu bileşenlere ihtiyacımız var:
- Oyuncu Karakteri: Genellikle sabit bir Z ekseni hızıyla ilerleyen, sola/sağa hareket edebilen bir karakter.
- Kamera: Oyuncuyu takip eden ve sabit bir mesafede duran bir kamera.
- Yol Parçacıkları (Segmentler): Üzerinde oyuncunun koştuğu, tekrar eden veya farklı varyasyonlara sahip platformlar.
- Engeller: Oyuncunun kaçınması veya üzerinden atlaması gereken nesneler.
Bu makaledeki ana odak noktamız, yol parçacıklarının ve engellerin nasıl dinamik olarak üretileceğidir. Bu, etkili bir Endless Runner geliştirme için kritik bir adımdır.
Yol Parçacıklarının (Segment) Dinamik Üretimi
Sonsuz bir yol oluşturmanın en verimli yolu, sahneye sürekli yeni yol segmentleri eklerken, geride kalan eski segmentleri kaldırmaktır. Bu döngüsel sistem, sahnedeki nesne sayısını yönetilebilir bir seviyede tutar.
Temel Mimari
Yol segmentlerini yönetmek için basit bir yapı kurabiliriz:
- Yol Prefabrikleri: Farklı tasarımlara sahip (düz, dönüşlü, boşluklu, farklı engel yerleşimlerine sahip) yol segmentleri oluşturun ve bunları birer prefabrik olarak kaydedin. Her segmentin uzunluğunu ve genişliğini standart tutmak, entegrasyonu kolaylaştıracaktır.
RoadGeneratorBetiği: Tüm yol üretim mantığını yönetecek merkezi bir C# betiği oluşturun. Bu betik, genellikle sahnedeki boş birGameObjectüzerine eklenir.- Spawn Noktası: Yeni yol segmentlerinin nerede başlayacağını belirleyen bir referans noktası (genellikle son segmentin bitiş noktası).
Üretim Mantığı
RoadGenerator betiği aşağıdaki adımları içermelidir:
- Başlangıç Segmentleri: Oyun başladığında, oyuncunun ilerlemesi için yeterli sayıda yol segmentini sahneye yerleştirin.
- Oyuncu Takibi: Oyuncunun Z ekseni üzerindeki konumunu sürekli olarak takip edin.
- Yeni Segment Üretimi: Oyuncu belirli bir mesafeyi (örneğin, mevcut en sondaki segmentin ortasını) geçtiğinde, yeni bir yol segmentini mevcut son segmentin bitiş noktasına spawn edin. Bu, oyuncunun her zaman önünde yeterli yol olmasını sağlar.
- Eski Segmentleri Kaldırma: Oyuncu belirli bir mesafeyi (örneğin, birkaç segment) geçtikten sonra, sahneden uzaklaşan ve artık görünmeyen en eski segmenti yok edin. Bu adım, performans açısından kritik öneme sahiptir.
Aşağıdaki basit kod bloğu, bu mantığın bir kısmını göstermektedir:
using UnityEngine;
using System.Collections.Generic;
public class RoadGenerator : MonoBehaviour
{
public GameObject[] roadPrefabs; // Farklı yol segmenti prefabrikleri
public Transform playerTransform; // Oyuncunun Transform'u
public float roadLength = 30f; // Bir yol segmentinin uzunluğu
public int roadsOnScreen = 5; // Ekranda aynı anda kaç yol segmenti olacağı
private List<GameObject> activeRoads = new List<GameObject>();
private float spawnZ = 0f; // Yeni yol segmentinin spawn edileceği Z konumu
void Start()
{
// Başlangıçta yeterli yol segmenti oluştur
for (int i = 0; i < roadsOnScreen; i++)
{
SpawnRoad();
}
}
void Update()
{
// Oyuncu ilerledikçe yeni yol üret ve eskileri yok et
if (playerTransform.position.z - (activeRoads[0].transform.position.z + roadLength) > 0f)
{
DeleteOldestRoad();
SpawnRoad();
}
}
void SpawnRoad()
{
int randomIndex = Random.Range(0, roadPrefabs.Length);
GameObject newRoad = Instantiate(roadPrefabs[randomIndex], new Vector3(0, 0, spawnZ), Quaternion.identity);
activeRoads.Add(newRoad);
spawnZ += roadLength;
}
void DeleteOldestRoad()
{
Destroy(activeRoads[0]);
activeRoads.RemoveAt(0);
}
}
Dinamik Engel Üretimi
Yol segmentleri kadar, engellerin de dinamik olarak üretilmesi, oyunun sürekli taze kalmasını sağlar. Engeller genellikle yol segmentlerinin üzerine yerleştirilir.
Engel Yerleşimi
Engelleri yerleştirmenin birkaç yolu vardır:
- Segment Başına Spawner: Her yol segmenti prefabriğinin içine, üzerinde engel spawn noktaları tanımlanmış bir
ObstacleSpawnerbetiği ekleyebilirsiniz. Segment spawn edildiğinde, bu betik kendi engellerini rastgele olarak oluşturur. - Merkezi Spawner:
RoadGeneratorgibi merkezi bir betik, yol segmenti spawn edildikten sonra o segment üzerine engeller yerleştirebilir. İlk yöntem genellikle daha modülerdir.
Engel prefabrikleri oluştururken, farklı boyutlarda, şekillerde ve hatta hareket mekaniklerine sahip engeller tasarlayabilirsiniz. Her bir segment üzerinde birden fazla olası spawn noktası (örneğin, sol, orta, sağ şeritler için) tanımlamak esneklik sağlar.
Rastgelelik ve Zorluk Ayarı
Engellerin yerleşimi tamamen rastgele olmamalıdır; oyuncunun geçebileceği bir yol bırakılmalıdır. Ayrıca, oyun ilerledikçe zorluğun artması, oyuncunun ilgisini canlı tutar. Bu, iyi bir Endless Runner geliştirme stratejisidir.
- Rastgele Konumlandırma: Bir segment üzerindeki olası spawn noktalarından bazılarını seçerek engel yerleştirin. Tüm noktaları aynı anda doldurmaktan kaçının.
- Spawn Şansı: Her segmentte engel spawn etme olasılığını bir yüzde ile belirleyin. Oyun ilerledikçe bu yüzdeyi artırabilirsiniz.
- Zorluk Eğrisi: Oyun süresine veya oyuncunun puanına bağlı olarak, daha sık engel, daha karmaşık engel desenleri veya daha hızlı hareket eden engeller üretin.
Aşağıdaki kod bloğu, bir segment üzerinde basit bir engel yerleşimini göstermektedir:
using UnityEngine;
public class ObstacleSpawner : MonoBehaviour
{
public GameObject[] obstaclePrefabs; // Farklı engel prefabrikleri
public Transform[] spawnPoints; // Segment üzerindeki olası spawn noktaları
[Range(0f, 1f)]
public float spawnChance = 0.7f; // Engel yerleştirme olasılığı
void Start()
{
// Her segment spawn edildiğinde çağrılır
// Belirli bir şansla engel yerleştir
if (Random.value < spawnChance)
{
SpawnObstacle();
}
}
void SpawnObstacle()
{
// Hangi spawn noktasına ve hangi engeli koyacağımızı rastgele seçelim
int spawnPointIndex = Random.Range(0, spawnPoints.Length);
int obstacleIndex = Random.Range(0, obstaclePrefabs.Length);
GameObject obstacle = Instantiate(obstaclePrefabs[obstacleIndex],
spawnPoints[spawnPointIndex].position,
Quaternion.identity);
obstacle.transform.SetParent(this.transform); // Engeli segmentin çocuğu yap
}
}
Pratik İpuçları
Etkili bir Endless Runner geliştirme süreci için bazı önemli ipuçları:
1. Object Pooling Kullanımı
Yukarıdaki kod örneklerinde Instantiate ve Destroy kullandık, ancak bu işlemler performans açısından maliyetlidir. Özellikle mobil platformlarda, saniyede birçok nesnenin oluşturulup yok edilmesi takılmalara (stuttering) neden olabilir. Bunun yerine, Object Pooling (Nesne Havuzlama) tekniğini kullanmalısınız. Bir nesne havuzu, önceden belirli sayıda nesneyi oluşturur ve ihtiyaç duyulduğunda onları aktif hale getirir, işi bittiğinde ise pasif hale getirerek havuza geri gönderir. Bu, Instantiate ve Destroy çağrılarını minimuma indirir ve performansı artırır.
2. Zorluk Eğrisi Yönetimi
Oyunun başında kolay, sonrasında zorlaşması oyuncuyu motive eder. Bu zorluk eğrisini yönetmek için:
- Oyun süresine veya oyuncunun puanına bağlı olarak engel spawn şansını artırın.
- Daha karmaşık engel desenleri veya daha hızlı hareket eden engelleri belirli zaman aralıklarında devreye sokun.
- Oyuncunun hızını yavaşça artırın.
3. ScriptableObjects ile Veri Yönetimi
Farklı yol segmenti türleri veya engel setleri için verileri yönetmek adına ScriptableObjects kullanmak oldukça faydalıdır. Örneğin, bir RoadSegmentData ScriptableObject‘i, o segmentin hangi engelleri içerebileceğini, uzunluğunu veya özel davranışlarını tanımlayabilir. Bu, tasarımcılara kod yazmadan yeni segmentler ve zorluk seviyeleri oluşturma esnekliği sunar.
4. Görsel Yardımcılar (Gizmos)
Editörde yol segmentlerinin başlangıç/bitiş noktalarını, engel spawn noktalarını veya tetikleyici alanları görmek için OnDrawGizmos fonksiyonunu kullanın. Bu, geliştirme sırasında düzeni ve hizalamayı kontrol etmenizi çok kolaylaştırır.
Yaygın Hatalar ve Çözümleri
Endless Runner geliştirme sürecinde sıkça karşılaşılan bazı hatalar ve bunlara yönelik çözümler:
1. Performans Sorunları
Hata: Oyun takılıyor, FPS düşüyor. Genellikle sürekli Instantiate ve Destroy çağrılarından kaynaklanır.
Çözüm: Yukarıda bahsedilen Object Pooling tekniğini mutlak suretle uygulayın. Sahnedeki aktif nesne sayısını her zaman minimumda tutun.
2. Tekrarlayan Desenler ve Sıkıcılık
Hata: Oyuncular bir süre sonra yolun ve engellerin hep aynı olduğunu hissediyor.
Çözüm: Daha fazla yol segmenti ve engel prefabriği varyasyonu oluşturun. Rastgele üretim algoritmalarını daha akıllı hale getirin (örneğin, belirli bir desenin arka arkaya gelmesini engelleyin, belirli zorluk seviyeleri için farklı segment setleri kullanın). Oyuna yeni mekanikler veya güçlendirmeler ekleyerek çeşitliliği artırın.
3. Fizik Problemleri ve Çarpışma Hataları
Hata: Oyuncu engellerden geçerken garip çarpışmalar oluyor veya bazı engeller algılanmıyor.
Çözüm: Oyuncu karakterinde bir Rigidbody olduğundan ve Collider‘larının doğru ayarlandığından emin olun. Engellerin de uygun Collider‘lara sahip olduğundan ve Is Trigger ayarlarının beklediğiniz gibi olduğundan emin olun. Katmanları (Layers) ve çarpışma matrisini (Collision Matrix) doğru yapılandırmak, istenmeyen etkileşimleri önler.
4. Bellek Sızıntıları
Hata: Oyun uzun süre oynandığında bellek kullanımı sürekli artıyor.
Çözüm: Sahneden uzaklaşan eski yol segmentlerinin ve üzerindeki engellerin gerçekten yok edildiğinden veya Object Pooling ile havuza geri gönderildiğinden emin olun. Bazen Destroy çağrılsa bile, başka bir betik tarafından referansı tutulan nesneler bellekten atılamaz.
Performans ve Optimizasyon Notları
Endless Runner geliştirme sürecinde performansı göz önünde bulundurmak hayati öneme sahiptir:
- Object Pooling Önceliği: Her zaman bir numaralı optimizasyon kuralınız bu olmalı.
- Küçük ve Hafif Segmentler: Yol segmentlerini çok karmaşık hale getirmeyin. Gereksiz poligon sayısından, ışıklandırmadan veya ağır shader’lardan kaçının.
- Sahnedeki Nesne Sayısı: Ekranda aynı anda görünen aktif yol segmenti ve engel sayısını mümkün olduğunca düşük tutun. Sadece oyuncunun görmesi gerekenleri yükleyin.
UpdateFonksiyonundan Kaçınma: Her karede çalışması gerekmeyen ağır hesaplamalarıUpdateyerine daha seyrek çalışanInvokeRepeatingveya Coroutine’ler ile yönetin. Örneğin, oyuncu konumunu her karede değil, her 0.1 saniyede bir kontrol edebilirsiniz.- Statik Batching: Eğer yol segmentlerinizdeki bazı nesneler (örneğin dekoratif kenar elemanları) tamamen statikse, Unity’nin statik batching özelliğinden faydalanarak çizim çağrılarını azaltabilirsiniz. Ancak dinamik üretilen nesnelerde bu her zaman mümkün değildir.
Unity’de bir Endless Runner geliştirme, dinamik içerik üretimi ve performans optimizasyonu becerilerinizi geliştirmeniz için harika bir fırsattır. Bu makaledeki teknikler ve ipuçlarıyla kendi sonsuz koşu oyununuzu inşa etmeye başlayabilir, oyuncularınıza unutulmaz bir deneyim sunabilirsiniz. Başarılar dileriz!



