Setting up (Kurulum)

Oyun mekaniğine geçmeden önce, oyunun içinde yaşayacağı basit bir çerçeve oluşturmamız gerekiyor. Oyun, çoğu önceki bölümlerde tanıtılan birkaç üçüncü taraf kütüphane kullanır. Yeni bir kütüphane gerektiğinde düzgün biçimde tanıtılacaktır.

İlk olarak tüm ilgili render ve oyun kodu içeren uber oyun sınıfı tanımlıyoruz. Böyle bir oyun sınıfının amacı oyun kodunuzu organize etmek ve aynı zamanda tüm pencere kodunu oyundan ayırt etmektir. Bu sayede fazla çaba harcamadan aynı sınıfı tamamen farklı bir pencere kitaplığıyla (SDL veya SFML gibi) kullanabilirsiniz.

circle-info

Oyun/grafik kodunu sınıflar ve nesnelere soyutlamanın ve genelleştirmenin binlerce yolu vardır. Bu bölümlerde görecekleriniz, bu sorunu çözmenin görece basit bir yaklaşımıdır. Daha iyi bir yaklaşım olduğunu düşünüyorsanız kendi iyileştirmenizi geliştirmeyi deneyin.

Oyun sınıfı bir başlatma fonksiyonu, bir güncelleme fonksiyonu, girişi işleyen bir fonksiyon ve bir render fonksiyonu barındırır:

class Game
{
    public:
        // oyun durumu
        GameState    State;	
        bool         Keys[1024];
        unsigned int Width, Height;
        // kurucu/yıkıcı
        Game(unsigned int width, unsigned int height);
        ~Game();
        // oyun durumunu başlat (tüm shader/doku/seviyeleri yükle)
        void Init();
        // oyun döngüsü
        void ProcessInput(float dt);
        void Update(float dt);
        void Render();
};

Sınıf bir oyun sınıfından beklediğiniz özelliklere sahiptir. Oyunu genişlik ve yükseklikle (oynamak istediğiniz çözünürlük) başlatıyoruz; shader'ları, dokuları yüklemek ve tüm oyun durumunu başlatmak için Init fonksiyonunu kullanıyoruz. Keys dizisinde saklanan giriş bilgisini ProcessInput çağırarak işleyebiliriz; tüm oyun olaylarını (oyuncu/top hareketi gibi) Update fonksiyonunda güncelleyebiliriz. Son olarak Render çağırarak oyunu çizebiliriz. Hareket mantığını render mantığından ayırdığımıza dikkat edin.

Game sınıfı aynı zamanda GameState türünde State değişkeni de barındırır:

Bu, oyunun hangi durumda olduğunu takip etmemizi sağlar. Bu sayede mevcut oyun durumuna göre render etmeyi ve/veya işlemeyi ayarlayabiliriz (örneğin oyun menüsündeyken muhtemelen farklı öğeleri render edip işliyoruzdur).

Şimdilik Game sınıfının fonksiyonları tamamen boş; henüz gerçek oyun kodunu yazmadık. Game sınıfının başlıkarrow-up-right ve kodarrow-up-right dosyaları.


Yardımcı Sınıflar (Utility)

Büyük bir uygulama oluşturduğumuzdan, doku ve shader gibi birkaç OpenGL kavramını sık sık yeniden kullanmamız gerekecek. Bu nedenle, daha önce bir shader sınıfı oluşturduğumuz bölüme benzer şekilde bu iki öğe için daha kullanımı kolay bir arayüz oluşturmak mantıklıdır.

İki ya da üç dizeden (geometry shader varsa) derlenmiş bir shader üreten (veya başarısız olursa hata mesajları üreten) bir shader sınıfı tanımlarız. Shader sınıfı ayrıca uniform değerlerini hızla ayarlamak için birçok yararlı yardımcı fonksiyon içerir. Bunun yanı sıra bir byte dizisinden ve belirli genişlik ve yükseklikten 2D doku görüntüsü üreten bir texture sınıfı da tanımlarız.

Sınıfların ayrıntılarına inmeyeceğiz; artık nasıl çalıştıklarını kolayca anlayabilirsiniz. Tamamen yorumlanmış başlık ve kod dosyaları:

Mevcut texture sınıfının yalnızca 2D dokular için tasarlandığını, ancak kolayca alternatif doku türleri için genişletilebileceğini unutmayın.


Kaynak Yönetimi (Resource Manager)

Shader ve texture sınıfları kendi başlarına harika çalışsa da başlatma için ya bir byte dizisi ya da bir dize listesi gerektirirler. Dosya yükleme kodunu sınıfların içine kolayca gömmüş olabilirdik; ancak bu tek sorumluluk ilkesini biraz ihlal eder. Bu sınıfların yalnızca doku veya shader ile ilgilenmesini, dosya yükleme mekanikleriyle değil, tercih ederiz.

Bu nedenle oyunla ilgili kaynakları yüklemek için tasarlanmış kaynak yöneticisi adı verilen tek bir varlık oluşturmak daha organize bir yaklaşım olarak değerlendirilir. Bu bölümde statik yapısı nedeniyle projenin her yerinde her zaman kullanılabilen, tüm yüklenen kaynakları ve ilgili yükleme işlevlerini barındıran bir singleton statik kaynak yöneticisi seçtik.

Singleton sınıfı statik işlevsellikle kullanmak birkaç avantaj ve dezavantaja sahiptir; dezavantajları büyük ölçüde bazı OOP özelliklerinin kaybı ve oluşturma/yıkım üzerinde daha az kontroldür. Ancak bu gibi görece küçük projeler için çalışması kolaydır.

Kaynak yöneticisi dosyası: başlıkarrow-up-right, kodarrow-up-right.

Kaynak yöneticisini kullanarak programa kolayca shader yüklenebilir:

Tanımlanan Game sınıfı, kaynak yöneticisi ve kolay yönetilebilir Shader ve Texture2D sınıfları, sonraki bölümlerin temelini oluşturur; Breakout oyununu uygularken bu sınıfları yoğun biçimde kullanacağız.


Program

Oyun için hâlâ bir pencereye ihtiyacımız var ve OpenGL'in blendingarrow-up-right işlevselliğini kullandığımızdan bazı başlangıç OpenGL durumu ayarlamaları yapıyoruz. Derinlik testini etkinleştirmiyoruz; çünkü oyun tamamen 2D. Tüm vertex'ler aynı z değerleriyle tanımlanır; bu nedenle derinlik testi etkinleştirmek işe yaramaz ve muhtemelen z-fighting'e neden olur.

Breakout oyununun başlangıç kodu görece basittir: GLFW ile pencere oluştururuz, birkaç callback fonksiyonu kayıt ederiz, Game nesnesini oluşturur ve tüm ilgili işlevselliği oyun sınıfına aktarırız. Kod: program.cpparrow-up-right.

Kodu çalıştırdığınızda şu çıktıyı alırsınız:

Boş Breakout penceresi

Artık önümüzdeki bölümler için sağlam bir çerçevemiz var; oyun sınıfını yeni işlevsellik eklemek için sürekli genişleteceğiz. Hazır olduğunuzda sonrakiarrow-up-right bölüme geçin.

Last updated