> DirectX > DirectInput Programlama – Bölüm 2

DirectInput Programlama – Bölüm 2

Bu makalede, birinci bölümde öğrendiklerimizi, uygulamaya nasıl geçireceğimizi öğreneceğiz. Ayrıca bir aygıttan nasıl veri okuyacağımızı da göreceğiz. Bunun için örnek bir klavye ve bir mouse nesnesi oluşturacağız. Tabi ki, oluşturduğumuz bu nesneleri, uygulamalarımız ile nasıl ilişkilendirebileceğimizi de göreceğiz. Bu makale, önceki sürümünden biraz farklılık gösteriyor. Önceki makalede eksik gördüğüm bazı yerlerde düzenlemeler yaptım.

İçindekiler

Bölüm 1

Bölüm 2

Hepsini Birleştirelim

Aygıtın Odaklanma(Focus) Durumu

Aygıt Verilerinin Okunması

Uygulamamızda Kullanalım

Mouse Aygıtı

Başka Konular

Sonuç Olarak

Hepsini Birleştirelim

Daha önceden bir pencere oluşturup, Direct3D gibi olayları atladığınızı varsayıyorum.

Bu makalemizde uygulamaımızı temsil eden bir adet cApplication nesnesi olsun. Bu sınıfa DirectInput nesnesini oluşturacak bir fonksiyon ekleyelim. Hatırlatmak gerekirse, başlamadan önce dinput8.lib ve d3dguid.lib kütüphanelerini projemize eklemiş olmamız lazım. Fonksiyonumuz şu şekilde olsun:



		

DirectInput nesnesini oluşturma kısmını zaten Bölüm-1’den hatırlıyoruz. En sondaki ShowError fonksiyonu, hata mesajları göstermek için tarafımca yazılan bir fonksiyon. Siz burada MessageBox fonksiyonunu kullanabilirsiniz. DirectInput nesnesini oluşturduktan sonra iki adet listener sınıfı hazırlayacağız birisi klavye için diğeri mouse için. İkisi de zor değil ama klavye’de mouse göre daha fazla yapılacak iş var. İsterseniz ilk başta klavyeden başlayalım. Klavye için ilk önce bize özel bir sınıf oluşturalım ve işimizi kolaylaştıracak metodlar tanımlayalım.



		

Listener oluşturmayı zaten Bölüm-1’den gördük. Ek olarak buradaki bazı metodların açıklanması gerek. Öncelikle tuşların basıldı basılmadı gibi durumlarını KeyStates isimli bir dizide tutuyoruz. Constructor’ın aldığı ilk parametre olan Owner, cApplication sınıfından bir nesne alıyor. Bunu almamızın sebebi, aygıtı oluştururken gerekecek olan DirectInput nesnesi ve Pencerenin Handle değeridir. Bu nesneler, cApplication sınıfımızda bulunmaktadır. İkinci parametre olan Receiver, bizim tanımlamış olduğumuz klavye arayüzünden türeyecek olan bir nesnedir. İstenirse bu işlem sadece fonksiyonlarla da halledilebilir. Ama biz nesne tabanlı programlama mantığına göre gitmek istiyoruz. Üstelik ileride bunun işmiz ne kadar kolaylaştırdığını da göreceksiniz. Diğer fonksiyonları kodlarını verdikten sonra anlatalım. Å?imdi sınıfımızın constructor ‘ını yazalım.



		

Aslında burada bize yabancı olan pek bir şey yok. Çünkü birinci bölümde aygıt nesnesini oluşturmayı öğrenmiştik. Burada ek olarak bunları bir sınıf tanımlaması içinde görüyoruz. Merak edebileceğiniz diğer bir nokta ise, pRecevier nesnemizi, SetReceiver gibi ayrı bir fonksiyon ile de alabilirdik. Ama ikinci bir fonksiyon çağırımı olmasın diye bu işi de constructor’a yerleştirdim. Bize yabancı olan tek şey sondaki ClearTable() metodudur. Bu da bizim tanımladığımız sınıfın bir metodudur. Bunu da şu şekilde ifade edelim:



		

Burada tek yaptığımız işlem tuşların durmlarını saklayan dizimizi sıfırlamak. Yani kalvye tuşlarını tutan dizimizi ilk haline çeviriyoruz. Böylece bu fonksiyon ile istediğimiz zaman tuşları ilk haline yani basılmamış haline çevirebiliriz. Kalvye sınıfımızdaki geriye kalan metodlarımızı da aşağıda verelim:



		

İlk başta KeyPressed metodumuza bakalım. Bu fonksiyon, KeyStates dizisinin o anda Key ile ifade edilen sıradaki tuşun basılı olup olmadığını kontrol eder. Yani eğer tuş basılı durumdaysa sonuç true olarak dönecektir.

Update metoduna bakacak olursak, uzun bir kod yığını görürüz. Bu metod, ileride de göstereceğimiz gibi sahneyi çizdirmeden hemen önce çağırabiliriz. Genel olarak bu fonksiyonun yaptığı iş, tuşlara basılı olup olmadıklarını kontrolle beraber, herhangi bir klavye olayında Receiver nesnemizin ilgili fonksiyonuna gerekli verileri yollamaktır. Evet böyle uzun bir cümle ile anlatmaya çalışınca işler biraz daha karışabiliyor. Bu metod, hemen hemen esas işi yaptığından dolayı ayrıntılarını ayrı başlıklar halinde açıklamak istiyorum. Å?imdi bu metodun ayrıntılarına geçelim.

Aygıtın Odaklanma Durumu

Windows programlamadan bildiğiniz Focus hadisesi, burada da geçerli. Mesela bir bir mouse nesnesi, bir pencereye focus olduğunda ya da buradaki değimi ile Acquire() olduğunda, pencere üzerindeki icraatlarından dolayı söz hakkına sahip olmuş olur. Veya mesela bir klavye nesnesi, bir textbox’a odaklanmamış ise, ya da buradaki ifadesi ile acquire olmamış ise o textbox, klavyeden verileri okuyamaz. Hangi nesne klavye odağına sahipse o klavye verilerini alacaktır. Eğer bir nesne Acquire olmamış ise herhangi bir veri alamayız. Tabi ki focus’u yani acquire işlemini, nesnemiz silindiğinde iptal etmemiz gerekmektedir. Bunun için, destructor’da (cKeyboard::~cKeyboard) gördüğünüz Unacquire() fonksiyonunu kullanıyoruz. Aksi halde, mesela mouse DISCL_EXCLUSIVE olarak tanımlanmış ise, hatırlayacağınız üzere, Unacquire() yapmadığınız sürece Windows mouse’dan mesaj alamayacaktır.

Aygıt Verilerinin Okunması

Tekrar Update metoduna dönelim ve metodun başlarında bulunan Poll metoduna bakalım. Farkında iseniz, defa Poll ve GetDeviceState fonksiyonu iki defa çağrılıyor. Aygıt verilerini okumak için ilk başta, aygıt nesnesinin Poll() metodunu çağırmalıyız. Ardından GetDeviceState ile verileri çekiyoruz. Veriler çekilemediğinde, anlamalıyız ki Acquire işlemi yapılmamış demektir. Acquire olmadıysa, yani focus yapılmadıysa kalvyeden veri alamayız. Bu yüzden tekrar acquire yapmalıyız. Her defasında Acquire yapmak performansı düşürecektir. Bu yüzden yukarıdaki kodlarda da gördüğünüz gibi sadece veri çekilemediğinde Acquire işlemi yapılıyor. Ardından tekrar veriler çekiliyor. Yine bir hata oluşursa ve de Acquire yapıldığından emin isek, hata olarak çıktı veriyoruz. Å?imdi aşağıdaki kodlara bakalım:


		

Burada Poll işleminden sonra gelen verileri alabilmek için GetDeviceState metodunu kullanıyoruz. ilk parametre veri formatımızın boyutunu, ikinci parametre de LPVOID cinsinden veri formatımızın referansını almaktadır. Bu örnekte, klavye durumları yeni bir diziye alınıp o şekilde işlem yapılıyor. Bu iki metoddan birisi başarısız olduğunda FAILED makrosu ile hatalı olarak çıkıyoruz.

Ardından gelen for döngüsündeki açıklama satırları kafi geleceği kanaatindeyim bu yüzden ek açıklama yapmıyorum.

Å?imdi bu bilgiler eşliğinde ilk başta Update olmak üzere diğer fonksiyonları da gözden geçirelim. Ardından biraz ara verelim, gözlerimizi dinlendirelim. Çünkü gerçekten çok işleri hallettik. Ardından bunu uygulamamızda nasıl kullanacağımızı görelim.

Uygulamamızda Kullanalım

Buraya kadar herşey güzel de, bunu uygulamamızda nasıl kullanacağız? Tek yapmamız gereken önceden de dediğimiz gibi Receiver arayüzlerinden sınıfımızı türetmek olacaktır. Aşağıda cApplication nesnemizden türeyen bir sınıf mevcut. Burada Direct3D’ye girmeyeceğiz. Sadece DirectInput’u uygulamamızda nasıl kullanacağız onu görelim.



		

Gördüğünüz gibi fazla yapılacak bir şey yok. Hangi aygıtı kullanacaksak, ona ait Receiver arayüzlerinden nesnemizi türetiyoruz. Ardından ona ait metodları kullanabiliyoruz. Tabiki Keyboard ve Mouse nesnelerimizi oluşturduktan sonra… Keyboard ve Mouse nesnelerini oluştururken verdiğimiz iki parametre’ye dikkat edin. İkisi de this‘dir. Çünkü cExample nesnesi hem cApplication sınıfındandır hem de IKeyboardReceiver ve IMouseReceiver özelliklerini taşır. Böylece cExample sınıfı ile hem Direct3D fonskiyonlarını çalıştırıp çizim yapabiliyoruz hem de kalvye ve mouse aygıtlarını kullanabiliyoruz. Ama burada eksik kalan tek şey, mouse aygıtının tanımlanmasıdır. Å?imdi dilerseniz, klavye’ye ye göre farkllarını görelim.

Mouse Aygıtı

Önceden de bahsettiğim gibi bu aygıtın programlaması, klavyeye göre daha basittir. Farklı olan yanlarını şimdi göstermeye çalışalım.

 


		

Klavyeden farklı olarak, eğer tam ekran çalışıyorsak, mouse sadece bize hizmet etmelidir. Tabi ki uygulamamız aktif olduğunda. İsteğe göre DISCL_NOWINKEY sabiti çıkartılabilir. Bu fonksiyonu birinci bölümde görmüştük. Ayrıca MouseDevice nesnesinin nasıl oluşturulacağını da görmüştük.

Klavye tuşlarının durumlarını ifade ederken 256 elemanlı bir dizi kullanmıştık. Mouse’da ise verileri tutabilmek için DIMOUSESTATE isimli hazır bir struct’ı kullanıyoruz. İstersek bunu kendimiz de oluşturabilirz. Bununla ilgili olarak 1. Bölümde bir şeyler anlatmıştık.



		

Bunu Mouse için sınıf tanımlamasında bir yere yerleştirebiliriz. Ardından aygıt nesnemizi oluşturduktan sonra bu struct’ı sıfırlayalım.



		

Buradaki ilk üç öğenin mouse’un 3 ekseni, diğer 4 öğenin de mouse’un 4 tuşu olduğunu anlamışsınızdır. Örnek olarak DIMOUSESTATE2 yapısnı da SDK yardım dosyalarından bulup inceleyebilirsiniz. Duruma göre bu struct değişik biçimlerde tanımlanabilir. Ama bu verdiğimiz struct, 3 eksenli 4 tuşlu bir mouse içindir. Günümüzdeki mouse’lar için yeter artar bile.

Å?imdi mouse için Update metoduna bir göz atalım:



		

Aslında klavyeden pek farkı yok. Tek yaptığımız ilk başta aygıt nesnemizin Poll metodunu çağırmak ve ardından GetDeviceState ile istediğimiz formatta verileri almak. Buradaki diğer kısımlar tuşun basılıp basılmaması ile ilgili mantık kısımlarıdır. Fazla anlaşılması zor değiller. Klavyede olduğu gibi KeyPressed gibi ekstra bir metod tanımlayabilir ve durumları 80h sayısal değeri ile and işlemine tabi tutabilirdik. Son durumu ifade eden LastState struct’ını oluşturabilmek için, CurrentState gibi aynı yapıda geçici bir struct tanımlıyoruz. Ve işlemleri bununla yapıyoruz. Bu Update metodunun çağrıldığı yeri cExample örnek sınıfımızda Render metodunda göstermiştik. Böylece mouse verilerini de uygulamamızda rahatlıkla işleyebiliyoruz.

Başka Konular

Normalde şu haliyle uygulamamız kalvye ve mouse verilerini işleyebilmektedir. Ama isteğe göre bu veriler özelleştirilebilir. Mesela klavyede tuşların basılı tutulma hadisesini iptal etmek istiyoruz. Yani klavyeden bir tuşa sürekli basıldığında bir çok işlem yerine sadece bir işlem yaptırtmak istiyoruz. Bunun için kullanacağınız mantık; tuşların adedi kadar bir boolean dizi tanımlayıp, her tuş kontrolünde bu diziyi de kontrol edilmesidir. Yani, "A" tuşuna ne kadar basılı tutulursa tutulsun, sanki tek bir A harfine basmış gibi algılatmak için; boolean dizimizdeki A harfine denk gelen elemanı, A harfine basıldığında true yapmalı, sonra da bunu istenilen yerde kontrol etmek yeterlidir. OnKeyDown metodunda A’yı algıladığımızda, dizimizde A’nın kodu olan 65’inci elemanı true yapıyoruz. OnKeyDown metodunda switch case’e tekrar A harfi gelirse bu sefer kontrol ettiriyoruz, acaba dizimizdeki 65’inci eleman true’ mu eğer true ise hiç bir işlem yapmıyoruz, false ise dizimizdeki 65’inci elemanı true yapıp, A’ya basılınca yapılması gereken işlemleri yapıyoruz.

Bunun dışında karşılaşabileceğiniz olaylardan birisi de joystick kullanımıdır. Önceden de dediğim gibi joystick aygıtı tanımlaması klavye ve mouse ile aynıdır. Buradaki bilgililer eşliğinde SDK help dosyalarının da yardımı ile joystick ve joypadler sizin için kolay olacaktır.

Sonuç Olarak

Artık makalenin sonuna geldik. Buraya kadar anlattıklarımız, üzerinde uygulama yapılması gereken bilgilerdir. DirectX’in hiç bir konusu yüzeysel okunup geçilecek şeyler değildir. Bu yüzden bol bol örnek incelemeli ve sdk yardım dosyalarını karıştırmalısınız.

Bir başka makalede görüşmek dileği ile.

Fatih Tolga Ata © 2007

» Tags: , , , , ,

1 Yorum

  • At 2009.08.03 09:58, Anonim said:

    teşekkürler

    (Required)
    (Required, will not be published)