C++ ve Oyun Motorlarının Kalbi: İşaretçilerle Belleği Dizginlemek

Bir oyun motorunu devasa bir lunapark gibi düşünün: fizik simülasyonu hız trenidir, render sistemi ışıklı dönme dolaptır, yapay zekâ ise pamuk şeker kuyruğunda karar vermeye çalışan NPC’dir. Bu lunaparkın elektriği ise bellektir. C++ burada sahneye çıkar; çünkü işaretçiler, düşük seviyeli bellek kontrolü ve veri yerleşimi sayesinde üç boyutlu dünyaların milisaniyeler içinde hesaplanmasını sağlar.

``

Neden C++ hâlâ oyun motorlarında bu kadar güçlü?

Modern oyun motorlarında her karede binlerce nesnenin konumu güncellenir, çarpışmalar çözülür, animasyonlar karıştırılır ve GPU’ya veri gönderilir. 60 FPS hedefinde bir karenin süresi yaklaşık $16.67ms$’dir. Eğer fizik, render ve oyun mantığı bu küçük zaman penceresine sığmazsa oyuncu takılma hisseder.

C++’ın avantajı, geliştiriciye belleğin nerede, ne zaman ve nasıl kullanılacağını söyleme şansı vermesidir. İşaretçiler sayesinde veriye doğrudan erişebilir, nesneleri kopyalamadan aktarabilir ve donanım önbelleğini daha verimli kullanabiliriz.

Yaklaşım Avantaj Risk
Ham işaretçi T* Çok hızlı, doğrudan erişim Dangling pointer, bellek sızıntısı
std::unique_ptr Tek sahiplik, otomatik temizlik Kopyalanamaz, tasarım dikkat ister
std::shared_ptr Paylaşımlı sahiplik Referans sayımı maliyeti
Nesne havuzu Parçalanmayı azaltır Yönetim kodu karmaşıklaşır

Fizik motorunun arkasındaki basit matematik

Bir fizik motoru temelde şu soruyu her karede tekrar sorar: “Nesneler şimdi nerede olmalı?” En basit hareket güncellemesi şu formülle yapılabilir: $x_{t+dt} = x_t + v_t \cdot dt$. Burada $x$ konum, $v$ hız, $dt$ ise kareler arası zamandır.

Çarpışma tarafında iş daha eğlenceli hale gelir. Bin nesnenin birbirine çarpıp çarpmadığını saf yöntemle kontrol edersek karmaşıklık $O(n^2)$ olur. Yani 1000 nesne için yaklaşık 1 milyon karşılaştırma! Bu yüzden oyun motorları broad phase ve narrow phase gibi aşamalar kullanır.

Aşama Amaç Örnek Teknik
Broad Phase Olası çarpışmaları hızlı elemek Spatial Grid, BVH, Octree
Narrow Phase Gerçek temas hesaplamak SAT, GJK, Raycast
Solver Tepki kuvvetini üretmek Impulse Resolution

İşaretçiler neden performans için kritik?

Oyun motorlarında en büyük düşmanlardan biri gereksiz kopyalamadır. Büyük bir RigidBody nesnesini her fonksiyona değer olarak göndermek pahalıdır. Bunun yerine işaretçi veya referans kullanılır. Böylece nesne bellekte tek yerde durur, sistemler aynı veriye erişir.

struct Vec3 {
    float x, y, z;
};

struct RigidBody {
    Vec3 position;
    Vec3 velocity;
    float mass;
};

void integrate(RigidBody* body, float dt) {
    if (!body) return;

    body->position.x += body->velocity.x * dt;
    body->position.y += body->velocity.y * dt;
    body->position.z += body->velocity.z * dt;
}

Bu kodda RigidBody* body, fizik nesnesinin adresini taşır. Fonksiyon nesneyi kopyalamaz; doğrudan orijinal veriyi günceller. Bu, özellikle binlerce gövdeyi her kare güncelleyen fizik sistemlerinde ciddi fark yaratır.

Bellek havuzları: Motorun gizli turbo modu

Bir sahnede sürekli mermi, parçacık, düşman veya geçici temas noktası oluşturup yok etmek klasik new/delete ile maliyetli olabilir. Çünkü işletim sisteminden sık sık bellek istemek yavaştır ve bellek parçalanmasına yol açar. Çözüm: önceden büyük bir blok ayırmak ve nesneleri bu bloktan dağıtmak.

class BodyPool {
    RigidBody bodies[1024];
    bool used[1024] = {};

public:
    RigidBody* create() {
        for (int i = 0; i < 1024; ++i) {
            if (!used[i]) {
                used[i] = true;
                return &bodies[i];
            }
        }
        return nullptr;
    }

    void destroy(RigidBody* body) {
        int index = static_cast<int>(body - bodies);
        if (index >= 0 && index < 1024) used[index] = false;
    }
};

Bu yapı, nesneleri bitişik bellekte tuttuğu için CPU önbelleğine dosttur. Yani işlemci “bir sonraki veriyi” tahmin edip daha hızlı erişebilir. Karmaşık sahnelerde bu fark, teoriden çıkıp FPS grafiğinde görünür hale gelir.

Sonuç: Güç büyük, sorumluluk da büyük

C++ ile oyun motoru geliştirmek, spor araba kullanmaya benzer: inanılmaz hızlanır ama virajı yanlış alırsanız bariyerle tanışırsınız. İşaretçiler, bellek havuzları, RAII ve veri odaklı tasarım birlikte kullanıldığında fizik motorları daha hızlı, üç boyutlu dünyalar daha akıcı hale gelir. Kısacası C++ hâlâ oyun motorlarının kalbinde atıyorsa sebebi nostalji değil; belleğe yakın durmanın verdiği ham güçtür.

Yorumlar