Olayları Yönetmek

Pencereleri nasıl açacağımızı öğrendik ama henüz nasıl durduracağımızı bilmiyoruz. Şimdi pencere olaylarını nasıl yakalayacağımızı ve onları düzgün bir şekilde nasıl yöneteceğimizi göreceğiz.


Olayları Almak
Temel olarak, olaylar pencereleme sisteminden iki şekilde alınır:
  • Pencereden her döngüde olaylar için beklemesini istersiniz -ki buna kuyruklama (polling) denir-.
  • Pencereye bir fonksiyon işaretçisi verebilir ve sonra pencerenin bir olay aldığı zaman onu çağırması için bekleyebilirsiniz. Buna sanırım "geri çağırma fonksiyonları kullanımı" diyebiliriz. 

SFML, olayları almak için bir kuyruklama sistemi kullanır. GetEvent, bu iş için kullanılır. Bu fonksiyon, bir sf::Event kopyası doldurur ve bekleyen olaylar varsa true, bekleme yığını boşsa false döndürür.
// App'ın bir sf::Window kopyası / örneği olduğunu hatırlayın
sf::Event Event;
if (App.GetEvent(Event))
{
    // Olayı işle
}
Her karede alınması gereken birden çok olay olabilir ve eğer biz yalnızca bir tanesini alırsak olaylar birikir ve hiçbir zaman işlenmez. Tüm bekleyen olayları almak için en doğru yol, tüm olayları alabileceğimiz döngü kurmaktır:
sf::Event Event;
while (App.GetEvent(Event))
{
    // Olayı işle
}
"Ama bir saniye, nereye ekleyeceğim ben bu kod parçasını?" sorusunun tam zamanı şimdi. Olayların her karede işlenmesi gerektiğinden olay yönetimine ait kodları ana döngünün başına ekleyebilirsiniz:
while (Running)
{
    sf::Event Event;
    while (App.GetEvent(Event))
    {
        // Olayı işle
    }
    App.Display();
}


Olayları İşlemek
Bir olay alındığında bakılacak ilk şey Type üyesi içindeki biçimini kontrol etmektir. SFML aşağıdaki olay biçimlerini destekler:

  • Closed (Kapatıldı)
  • Resized (Boyutlandırıldı)
  • LostFocus (OdakKaybedildi)
  • GainedFocus (OdakKazanıldı)
  • TextEntered (MetinGirildi)
  • KeyPressed (TuşaBasıldı)
  • KeyReleased (TuşBırakıldı)
  • MouseWheelMoved (FareTekeriHareketettirildi)
  • MouseButtonPressed (FareTuşunaBasıldı)
  • MouseButtonReleased (FareTuşuBırakıldı)
  • MouseMoved (FareHareketettirildi)
  • MouseEntered (FareGirdi)
  • MouseLeft (FareÇıktı)
  • JoyButtonPressed (OyunçubuğuTuşunaBasıldı)
  • JoyButtonReleased (OyunçubuğuTuşuBırakıldı)
  • JoyMoved (OyunçubuğuHareketettirildi)

Olay örnekleri, olayın biçimine göre farklı parametreler alabilir:

Boyutlandırma olayları (Resized)
  • Event.Size.Width piksel olarak yeni pencere genişliğini içerir
  • Event.Size.Height piksel olarak yeni pencere yüksekliğini içerir
Tuş olayları (KeyPressed, KeyReleased)
  • Event.Key.Code basılan / bırakılan tuşun kodunu içerir
  • Event.Key.Alt Alt tuşunun basılıp basılmadığını söyler
Fare düğmesi olayları (MouseButtonPressed, MouseButtonReleased)
  • Event.MouseButton.Button fare tuşuna basılıp basılmadığını (ya da bırakılıp bırakılmadığını) içerir
Fare hareketi olayları
  • Event.MouseMove.X yerel kordinatlarda (pencere içinde) fare imlecinin o anki X konumunu içerir
  • Event.MouseMove.Y yerel kordinatlarda (pencere içinde) fare imlecinin o anki Y konumunu içerir
Fare tekeri olayları (MouseWheelMoved)
  • Event.MouseWheel.Delta fare tekeri hareketini içerir (ileri için pozitif, geri için negatif değer)
Tuş kodları SFML'ye özgüdür. Her klavye tuşu Events.hpp başlık dosyasında tanımlı bir sabit değerle ilişkilendirilmiştir. Örneğin F5, sf::Key::F5 ile tanımlanmıştır.

Fare içinse beş sabit tanımlanmıştır: sf::Mouse::Left, sf::Mouse::Right, sf::Mouse::Middle (tekerlek tuşu), sf::Mouse::XButton1 ve sf::Mouse::XButton2.

Uygulamalarımıza geri dönelim ve uygulamamıza olayları yönetebilmesi için gerekli eklemeleri yapalım. Kullanıcı kapatma düğmesine veya ESC tuşuna bastığında uygulamayı durdurmak için:
sf::Event Event;
while (App.GetEvent(Event))
{
    // Pencere kapatıldı
    if (Event.Type == sf::Event::Closed)
        Running = false; 
    // ESC tuşuna basıldı
    if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
        Running = false;
}


Bir Pencereyi Kapatmak
SFML ile bir süre uğraştıktan sonra fark edeceksiniz ki kapat düğmesine basmak bir Closed olayı yaratıyor fakat pencereyi yok etmiyor. Bu, kullanıcıya pencere kapatılmadan önce istediği kodları çalıştırması (yapılanların saklanmak istenip istenmediğinin sorulması, vb.) ya da bu işlemi iptal etmesi için fırsat vermektedir. Bir pencereyi gerçekten kapatmak için sf::Window örneğini yok etmeli ya da o pencerenin Close() fonksiyonunu çağırmalısınız.

Bir pencerenin açık olup olmadığını anlamak için IsOpened() fonksiyonunu çağırabilirsiniz.

Şimdi daha güzel görünen bir ana döngü kuralım:
while (App.IsOpened())
{
    sf::Event Event;
    while (App.GetEvent(Event))
    {
        // Pencere kapatıldı
        if (Event.Type == sf::Event::Closed)
            App.Close(); 
        // ESC tuşuna basıldı
        if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
            App.Close();
    }
}


Gerçek Zamanlı Girdiler Almak
Bu olay sistemi, pencere kapatmak ya da tek bir tuşu takip etmek için yeterince iyi. Bununla birlikte, sürekli hareket halindeki bir karakterin kontrolü gibi örneklerde, bir yön tuşuna bastığınızda bir problemle karşılaşacaksınız: her hareket arasında işletin sistemince tanımlanmış bir gecikme yaşanacak.

Bu gibi durumlar için daha iyi bir strateji izlenmelidir. Bunun için bir ikilik değişkene, bir tuşa basıldığında true değeri verilebilir ve tuş serbest bırakıldığında bu değişken sıfırlanabilir. Böylece döngü her yeniden başladığında eğer değişken sıfır değilse karakter hareket eder. Bu iş için fazladan bir değişken kullanılması can sıkıcı olabilir, özellikle onlarla baş edemeyeceğiniz kadar çoğaldıklarında. Bunun için SFML, sf::Input ile gerçek zamanlı girdilere erişmek için kolay bir yol sunar.

sf::Input örnekleri kendi başlarına yaşayamazlar, bir pencereye eklenmelidirler. Gerçekte her sf::Window, kendi sf::Input örneğini yönetir ve bunlara sadece istediğinizde sahip olursunuz. Pencereyle ilişkilendirilmiş bir girdiden referans almak için GetInput fonksiyonu kullanılır.
const sf::Input& Input = App.GetInput();
Daha sonra, istediğiniz zaman sf::Input örneğiyle fare, klavye ve oyun çubuğu durumları alınabilir:
bool         LeftKeyDown     = Input.IsKeyDown(sf::Key::Left);
bool         RightButtonDown = Input.IsMouseButtonDown(sf::Mouse::Right);
bool         Joy0Button1Down = Input.IsJoystickButtonDown(0, 1);
unsigned int MouseX          = Input.GetMouseX();
unsigned int MouseY          = Input.GetMouseY();
float        Joystick1X      = Input.GetJoystickAxis(1, sf::Joy::AxisX);
float        Joystick1Y      = Input.GetJoystickAxis(1, sf::Joy::AxisY);
float        Joystick1POV    = Input.GetJoystickAxis(1, sf::Joy::AxisPOV);
Bu eğitselde kısaca pencere girdilerini yönetmeyi ve gerçek zamanlı klavye ve fare durumlarını kullanmayı öğrendik.

Farenin pencere üzerindeki konumunu konsol üzerinden söyleyen bir örnekle eğitseli tamamlayalım:



Bu eğitselle ilgili diğer örneklere sfml-gelistirme deposundan ulaşabilirsiniz.


Kaynak: Window - Handling events