Bu bölümde oyuna son geliştirmeleri ekleyeceğiz: bir can sistemi, kazanma koşulu ve render edilmiş metin biçiminde geri bildirim. Bu bölüm, daha önce tanıtılan Text Rendering bölümünü yoğun şekilde kullanır; henüz okumadıysanız önce o bölümden geçmenizi öneririm.
Breakout'ta tüm metin render kodu, FreeType kütüphanesinin başlatılması, render yapılandırması ve gerçek render kodunu barındıran TextRenderer sınıfında kapsüllenir:
Metin oluşturucunun fonksiyon içerikleri metin render bölümündeki kodla neredeyse aynı; ancak ekrana glyph render eden kod biraz farklı:
voidTextRenderer::RenderText(std::stringtext,floatx,floaty,floatscale,glm::vec3color){[...] for (c=text.begin(); c !=text.end(); c++){float xpos = x +ch.Bearing.x* scale;float ypos = y +(this->Characters['H'].Bearing.y-ch.Bearing.y)* scale;float w =ch.Size.x* scale;float h =ch.Size.y* scale;// her karakter için VBO güncellefloatvertices[6][4]={{ xpos, ypos + h,0.0f,1.0f},{ xpos + w, ypos,1.0f,0.0f},{ xpos, ypos,0.0f,0.0f},{ xpos, ypos + h,0.0f,1.0f},{ xpos + w, ypos + h,1.0f,1.0f},{ xpos + w, ypos,1.0f,0.0f}};[...] }}
Bunun biraz farklı olmasının nedeni, metin render bölümünde kullandığımızdan farklı bir ortografik projeksiyon matrisi kullanmamız. Metin render bölümünde tüm y değerleri aşağıdan yukarıya doğru sıralanırken Breakout oyununda tüm y değerleri yukarıdan aşağıya doğru sıralanır; 0.0 y koordinatı ekranın üst kenarına karşılık gelir. Bu nedenle dikey ofseti nasıl hesapladığımızı biraz değiştirmemiz gerekiyor.
RenderText'in y parametresinden aşağıya doğru render ettiğimizden dikey ofseti, bir glyph'in glyph alanının üstünden aşağıya ne kadar itildiği olarak hesaplıyoruz. FreeType'dan 'H', 'T' veya 'X' gibi bazı glyphlar her zaman bu üst kenara değer. Bu yüzden bu tepe glyph'lerin bearingY'ını söz konusu glyph'in bearingY'ından çıkararak kırmızı vektörün uzunluğunu hesaplıyoruz:
Glyph dikey ofseti
ypos hesaplamasını güncellemek dışında, mevcut ortografik projeksiyon matrisiyle çarpıldığında tüm vertex'lerin hâlâ ön yüzlü kalmasını sağlamak için vertex sırasını da biraz değiştirdik (yüz ayıklama bölümünde tartışıldığı gibi).
TextRenderer'ı oyuna eklemek kolay:
Metin oluşturucu, buradan indirebileceğiniz OCR A Extended adlı bir fontla başlatılır.
Oyuncu Canları
Topu alt kenara ulaşır ulaşmaz oyunu sıfırlamak yerine oyuncuya birkaç ekstra şans vermek istiyoruz. Başlangıçta 3 can veririz; top alt kenara her değdiğinde can sayısı 1 azalır. Yalnızca can sayısı 0 olduğunda oyunu sıfırlıyoruz:
Oyunun Update fonksiyonunu oyunu sıfırlamak yerine oyuncunun can sayısını azaltacak şekilde güncelliyoruz:
Oyunu/seviyeyi sıfırlarken oyuncunun can sayısını da sıfırlamayı unutmayın:
Oyuncunun şu anki can sayısını göstermek için metin oluşturucuyu kullanıyoruz:
Hayat sayısı render
Seviye Seçimi
Kullanıcı GAME_MENU oyun durumundayken oynanacak seviyeyi seçme kontrolünü vermek istiyoruz. W veya S tuşlarıyla seviyeler arasında geçiş yapabilir; Enter tuşuyla GAME_MENU durumundan GAME_ACTIVE durumuna geçiş yapılır:
Menü durumunda oyuncuya talimat ve seçilen seviyeyi arka planda gösteriyoruz:
Seviye seçim ekranı
W veya S tuşuna basıldığında oyunun birden fazla kare boyunca tuş basışını kaydetmesi nedeniyle seviyeler hızla kayar. Bunu çözmek için GUI sistemlerinde yaygın bir numarayı kullanıyoruz: yalnızca basılı tuşları kaydetmekle kalmaz, bir kez işlenen ve bırakılana kadar tekrar işlenmeyen tuşları da saklarız:
if (this->State == GAME_MENU)
{
if (this->Keys[GLFW_KEY_ENTER])
this->State = GAME_ACTIVE;
if (this->Keys[GLFW_KEY_W])
this->Level = (this->Level + 1) % 4;
if (this->Keys[GLFW_KEY_S])
{
if (this->Level > 0)
--this->Level;
else
this->Level = 3;
}
}
void Game::Render()
{
if (this->State == GAME_ACTIVE || this->State == GAME_MENU)
{
[...] // oyun render kodu
}
if (this->State == GAME_MENU)
{
Text->RenderText("Press ENTER to start", 250.0f, Height / 2, 1.0f);
Text->RenderText("Press W or S to select level", 245.0f, Height / 2 + 20.0f, 0.75f);
}
}
class Game
{
[...]
public:
bool KeysProcessed[1024];
}