uEngine4 – Matrix, Düşen Harf Uygulaması – Güncelleme

GÜNCELLEME:

Evet dostlar CMake betikleri de geldi artık Linux ve diğer platformlar için de derleyebilirsiniz. Aşağıda, WSL’den aldığım ekran görüntüsü mevcut (evet WSL üzerinden grafiksel uygulamaları da çalıştırabilirsiniz, wsl2 sağolsun 😉

Açıkçası asıl vakit alan SDL kütüphanelerini adam etmek oldu. Bunun için de, https://github.com/yazilimperver/uEngine4/blob/main/code/src/ext/sdl2_linux/PrepareSDL2ForLinux.sh betiğini ekledim. Bu betikle, SDL’ yi indirip, kurabilirsiniz.

ORJİNAL YAZI:

Bir süredir muzdarip olduğum rahatsızlıktan ötürü sizler ile paylaşım yapamadım ama bugün bunu bozuyoruz.  Evet sevgili dostlar bir süre önce sizler ile paylaştığım uEngine4 ile örnek uygulamalar geliştirmenin zamanı geldi. Bu yazım bunlardan ilki olacak. Bu örnek uygulamanın amacı, çok karmaşık olmayan ama elle de tutulur bir örnek ile uEngine4 tarafından sunulan kabiliyetleri keşfediyor olmak.

Örnek uygulama, 90’ların sonunda hepimizin hayatına giren Matrix filmindeki akan yazılar animasyonu olacak. Bu uygulamada, Matrix filmindeki kayan yazılara benzer bir animasyonu nasıl elde edebileceğimize bakıyor olacağız ve bunu nasıl özelleştirebileceğimize. Öncelikle hemen bir ekran görüntüsü koyalım aşağıya:

Arkadaşlar, herhangi bir proje yaparken, bu proje ile neyi yapmayı hedeflediğinizi, sınırlarınızı belirlemenizi şiddetle öneririm. Çok basit uygulamalarda bile bazen kendinizi, kaybolmuş bulabilirsiniz. Bu da size zaman ve motivasyon kaybettirir. Şimdi bu örnek için kapsamımızı kısaca bir sıralayalım:

  • Düşen harfler olsun 😊,
  • Harfler Matriks filmindeki font olsun,
  • Ekranda her sütun boyunca, harfker rastgele bir şekilde oluşsun, gittikçe silikleşsin ve bir süre sonra kaybolsun,
  • Hızları farklı olsun,
  • Alpha (trasnparanlık) değerleri de farklı olsun,
  • Harfler düşerken değişsin,
  • Parlama efekti olsun,
  • Hız, uzunluk, renk gibi parametreler değiştirilebilsin.

Giriş

Şimdi, kütüphanemiz ile nasıl uygulama geliştireceğimize bir bakalım. Bu ve benzeri yazılarımda, kod ile metin arasındaki ilişkiyi kurmanıza yardımcı olması adına parantez içerisinde sayıları hem metine hem de koda koyuyor olacağım. Koda, aşağıdaki adresten ulaşabilirsiniz, kısa bölümleri yazı içerisine de ekliyor olacağım:

https://github.com/yazilimperver/uEngine4/tree/main/code/src/apps/falling_letters

Şimdi koda dönelim. İlk olarak bir uygulama sınıfı yazıyoruz. Bu sınıf SdlApplication‘dan türetiliyor olacak (1). Bu temel sınıf bizler için, SDL ilklendirme, döngü yönetimi, girdi kontrolü ve benzeri ortak işlevleri gerçekliyor. Aslına bakarsanız, main.cpp dosyası içerisine de bakarsanız, sadece bu sınıfın oluşturularak başlatıldığını görebilirsiniz.

SdlApplication ve yardımcı sınıfları, sdl_application kütüphanesi ile sunulmakta. Bu kütüphane içerisinde, SDL’e özel tipler, SDL uyarlama parametreleri ve okuma/yazma sınıfları bulunuyor. Bu sınıf da aslında ApplicationBase sınıfından türetilmekte (application_base kütüphanesi) ve ortak olaylar, tipler de bu kütüphanede. Şu an için sadece SdlApplication sınıfı var ama kendi uygulama sınıflarınızı da kullanabilirsiniz.

Bunun ile birlikte ihtiyaç duyacağımız girdilere ilişkin olayları da dinleyebilmek için arayüz sınıfımızı türettiğimiz sınıflara ekliyor olacağız. Bu örnek için şimdilik sadece klavye girdilerine ihtiyaç duyduğumuz için KeyboardEventListener’dan türetiyor olacağız (2).

Klavye girdileri (ve benzeri diğer girdiler), basitçe XXXXEvent() fonksiyonlarını (klavye için KeyboardEvent) tanımlayarak elde edebilirsiniz (3). Tabi, öncelikle dinlemeye yönelik isteğinizi beyan edip, abone olmanız gerekmekte. Bunu da yine yapıcı içerisinde hallediyoruz (3)

Şimdi gelelim uygulamamızın en önemli bileşenine ki o da grafiksel unsurlar oluyor. Bunun için ClientGraphicApplication sınıfından türetilen FallingLettersGraphicApp sınıfını oluşturarak (4), SDL uygulamasına veriyor olacağız (5). Uygulama çalışırken de, aslında kullanılan grafiksel uygulamanızı değiştirebilirsiniz, bunun için de (5)’de sunulan UpdateGraphicApplication() API’sini kullanabilirsiniz.

Şimdi burada neden iki farklı sınıf ile bunu yapıyoruz sorusu aklınıza geliyor ki bu gayet normal aslında her zaman SDL ile grafiksel uygulama yapıyor olacağımız farz ederek, SdlApplication aracılığı ile de uygulamalarımızı geliştiriyor olabilirdik. Açıkçası bu benim başta aldığımz tasarım kararlarından birisiydi. Bu sayede sadece grafiksel değil, konsol uygulamaları için de bu alt yapı kullanılabilecek ama çok da elzem değil.

Sonuç olarak, grafik uygulamasına ilişkin sınıfımız aşağıdaki gibi olacak:

Haydi şimdi piksellere biraz raks ettirelim 😊. Öncelikle temel bir takım işlevlere bakalım. Daha önce geliştirme yazılarımı takip edenler hatırlayacaktır, aslında, bu uygulamalarda işlevleri temel olarak ikiye bölüyorduk. Bunlar, veri/durum güncelleme -> görselleştirme şeklindeydi. Grafik uygulaması sınıfı da, sizden güncelleme ve görselleştirme işlevlerinizi Update/Display içerisine gömenizi istiyor.
Bu fonksiyonlar da girdi olarak, bir önceki çağrıdan bu yana geçen süreyi milisaniye cinsinden sunuyor. Şimdi artık uygulama özel işlevlere eğilebiliriz.

Kabiliyetler

Sanırım yeterince kabiliyet ekledik. Şimdi bunları nasıl gerçeklediğimize bir göz atalım. İlk olarak FallingLettersGraphicApp sınıfına göz atalım.

Öncelikle her bir harf sütununu temsil edecek bir veri yapısı tanımlayalım: LetterDrop (6). Burada açıkçası, harfleri ayrı modellemedim (modellenebilir), mevcut veri yapısı iş görüyor gibi. Peki, ne lazım bize:

  • Harf sütunu başlangıç konumu. Diğer harflerin konumunu bu konumdan bulabiliriz,
  • Harfler,
  • Şu an aktfi mi değil mi?
  • Düşüş hızı (aslında yön için de kullanılabilir),
  • İlk saydamlık değeri,
  • Rengi. Tabi renk sabit ama saydamlık bilgisi de renk üzerinden verilir,
  • Güncelleme adeti. Bu değişkeni, harfi ne zaman güncelleyeceğimizi yönetmek için kullanacağız.

Bu sütunları da STL vector konteyneri içerisinde tutacağız.

Tabi, harfler ile iş yapıp font bilgisinden kopuk olamayız. En önemli verileri de: Font dosyası ve boyut bilgileri. Bunlar da FontData veri yapısı ve mFontData değişkeninde tutulmakta (7). Bunları, görselleştirme kütüphanesi ve konum benzeri hesaplamalarda kullanıyor olacağız.

Geri kalan değişkenlere baktığımızda, her güncellemede, saydamlığı ne kadar azaltacağımıza ilişkin değişken (8), karakterleri kaç güncelleme sonrasında güncelleyeceğiz (9).

Rastgelelik için kullandığımız std::mersenne_twister_engine motoru (10). Bu arada bu konuya da C++ 11 yazılarımda detaylı değinmediğimi fark ettim.

Kısaca değinmek gerekirse, mersenne_twister_engine, Mersenne Twister algoritmasına dayalı bir rastgele sayı motorudur. Verilen aralıkta yüksek kaliteli işaretsiz tamsayı üretir. Bunu nasıl kullandığımızı anlatıyor olacağım. Ayrıca bir değişken ile, bu örnek kapsamında, daha önce sunulan rand() API’si ile Mersenne Twister arasında geçişe de izin veriyor olacağız.

mRenderer değişkeni SDL kullanarak çizim yapmak için kullanacağımız bir nesne, buna çok kafa yormanıza gerek yok (11). SDL detaylarına girmek isteyenler ise, SDL dokümanlarına göz atabilirler.

mParameters değişkeni uygulamamız için kullandığımız pencere özelliklerini içeriyor. Bunu SdlApplication sınıfı arka planda okuyarak uygulamalara sunuyor olacak (12). Peki, bunları nasıl değiştirebilirim? Bu da kolay sdl_application_configuration.json betiği içerisinden, ilgili parametreler ile uygulamanıza ilişkin bu parametreleri değiştirebilir ve GetWindowParametrs() ile bunlara ulaşabilirsiniz.

Gelelim son değişkenimize, mPainter (13). Evet dostlar, Painter sınıfı, SDL kabiliyetleri kullanarak ihtiyaç duyabileceğiniz temel görselleştirme işlevlerini sunan bir sınıftır. painter kütüphanesi içerisinde tanımlanan bu sınıf, Qt ile sunulan QPainter‘a benzer işlevler sunuyor. bunu yaparken, ayrıca SDL yan kütüpahnelerinden olan gfx ve fontcache kütüphanelerini de kullanıyor. Bu örnek kapsamında ise aslında sadece metin görselleştirmesi yapıyor olacağız. Bu sınıf ile sunulan bütün özellikleri görmek için, sdl_example uygulaması içerisindeki, SdlPainterSample, grafik uygulamasına göz atabilirsiniz.

Bunu ilklendirmek için de Initialize içerisinde:

  • SDL_Renderer nesnesini geçiriyoruz,
  • Matrix font verisine ilişkin bilgileri veriyoruz,
  • Kullanılacak fontu atıyoruz.

Sınıf üyelerine baktıktan sonra şimdi geldik temel işlevlere: ilklendirme, güncelleme, görselleme ve sonra olarak da verilen aralık içerisinde rastgele sayı üretme.

İlklendirme

İlklendirmeye dair işlevleri, Initialize() fonksiyonu içerisinde yapıyoruz. Bunların bir kısmından yukarıda bahsettik. Gelelim bahsetmediklerimize.
İlk önce, rastgele sayı üretme motoru üretme nesnesini ilklendiriyoruz (14). Bunun için de std::random_device nesnesini, mRandEngine nesnesine veriyoruz. Bunu “seed” olarak düşünebilirsiniz. Bu nesne her platform için farklı yöntemler kullansa da, rastgele motorları için ihtiyaç duyulan “seed” bilgisini standart bir şekilde sunmakta. C++ 11 öncesinde, rastgeleliği sağlamak adına “seed” olarak time(NULL) kullanırdık. Detaylı bilgi için https://en.cppreference.com/w/cpp/numeric/random/random_device‘e göz atabilirsiniz.

Şimdi gelelim, harf sütunlarımızı ilklendirmeye (15). Öncelikle sütun sayısını buluyoruz. Bunun için font genişliği ve ekran genişliğini kullanıyoruz. Bir diğer ifade ile, uygulama, pencere genişliğine göre sütun sayısını ayarlıyor, daha sonra da dolduruyoruz. Bunun için de, yine C++ 11 ile sunulan std::generate_n API’sini kullanıyoruz. Bu API, kendisine verilen fonksiyonu kullanarak, ki bizim durumumuzda lambda ifadesi oluyor, geçirilen konteyneri dolduruyor. Doldurmayı da, sona ekleyerek, std::back_inserter ile yapıyoruz. Şu an için sadece x konumlarını girip bırakıyoruz.

Güncelleme

Güncellemeye dair işlevleri, Update() fonksiyonu içerisinde yapıyoruz. Buna ilişkin hususlara geçmeden önce sıkça kullanacağımız ve verilen aralıkta rastgele sayı üretme konusuna bakacağız. Başta da bahsettiğim gibi, uygulamanın doğası gereği, rastgeleliği oldukça sık kullanıyor olacağız. C++ 11 öncesi direk bir aralıkta rastgele sayı üretmek için RandomInBetween fonksiyonu içerisindeki else kısmı gibi bir yaklaşım izlemeniz gerekiyordu (16). C++ 11 ile sunulan kütüphane kabiliyetleri ile aralıklı rastgele sayı üretebiliyoruz. Bunun için de std::uniform_int_distribution‘ı kullanıyoruz. Diğer dağıtımlar için https://en.cppreference.com/w/cpp/numeric/random adresine başvurabilirsiniz. Hatta, uygulama içerisinde onları kullanarak, davranışın nasıl değiştiğine bakabilirsiniz.

Harfleri güncellemek için ilk bakacağımız, şu an ilgili sütunda bir harf damlasının aktif olup/olmadığı. Bu kısımlar açıkçası biraz uygulamaya yönelik olduğu için kısaca değinip geçeceğim. Zaten koda bakarak da genel akışı görebilirsiniz. Eğer, ilgili harf damlası aktif ise?

  • Dikey konum güncellenir (yani aşağı inmesi),
  • Saydamlık azaltılır,
  • Eğer güncelleme sırası geldi ise de harf güncellenir,
  • Ve en önemlisi eğer pencerenin altına geldiyse ya da saydamlık belirli bir seviyenin altına düşerse, artık aktif değil olarak güncellenir.

Eğer, ilgili harf damlası aktif değil ise?

  • Dikeyde rastgele bir konum ata,
  • Rastgele bir harf, saydamlık ve hız ata,
  • Aktifleştir ve güncelleme sırasını sıfırla.

Evet sevgili dostlar. Örnek uygulamamızın bütün mekaniği bu. Şimdi de görselleştirme kısmına bakalım.

Görselleştirme

Motorun sunduğu Painter sınıfının kendini gösterdiği kısma geldik. Görselleştirmeye dair işlevleri, Display() fonksiyonu içerisinde yapıyoruz.
Öncelikle, önceki çizimleri bir silelim (evet bu örnek için bir anlamı olmayabilir ama çizim işlevleri öncesinde bu tarz bir kullanımda fayda var). Harf damlası içerisindeki harfleri, alttan üste doğru çiziyoruz. Her aktif harf için (aktif olmayanları çizmiyoruz):

  • İlk çizilen harfin saydamlığını diğerlerinden daha düşük yapıyoruz ve bunu Pen nesnesi ile Painter‘a geçiriyoruz. Painter her zaman en son atanan Pen ve Brush nesnesini kullanmakta. Şekil ve metin çizimlerde Pen nesnesi, doldurma işlemlerinde de Brush nesnesi kullanılıyor,
  • İlk harfi Text() API’si ile çizdiriyoruz,
  • Diğer harfleri ise azalan saydamlık ile benzer şekilde çizdiriyoruz,
  • Tamamen transparan harfleri çizdirmiyoruz.

Uygulamamızın görselleştirme kısmı da bu kadar. Umarım, faydalı olmuştur. Örnek uygulamayı alarak çeşitli güncellemeler ya da uyarlamalar yapabilirsiniz. Ör. aynı sütunda birden fazla harf damlası ya da farklı renkler ya da yatay akışlar, vb. Bunları da paylaşırsanız sevinirim.

Bu arada örnek uygulamaya ilişkin henüz CMake betiklerini eklemedim ama en kısa sürede ekliyor olacağım. Şimdilik VS2022 proje dosyaları ile uygulamayı çalıştırabilirsiniz. Buna da aşağıdaki adresten ulaşabilirsiniz:

https://github.com/yazilimperver/uEngine4/tree/main/code/project/vs2022/uengine4/app/falling_letters

Bir sonraki yazımda görüşmek dileğiyle. Kendinize iyi bakın.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.