Makale Özeti

Bu makalede sizlere DirectX ile bir oyun iskeletinin nasıl oluşturulup çalıştırılacağını anlatacağım.

Makale

DirectX ile Oyun Programlama İskeleti Oluşturma

Bu makalede sizlere DirectX ile bir oyun  iskeletinin nasıl oluşturulup çalıştırılacağını anlatacağım.

Windows işletim sistemlerinde bilgisayar bileşenlerini(ses, grafik, ağ vs.) yönetmek için kullanılan uygulama geliştirme arayüzü DirectX'dir. DirectX biz programcılar için bilgisayar bileşenlerini yönetmek için bir standart getirdi. En uzun soluklu directx 9 dan sonra vista ile gelen ve vista içerisinde kullanılan yeni DirectX'in adı DirectX 10 dur. Özellikle oyun programlarında DirectX çok fazla kullanılmaktadır. DirectX 10 ile geliştirilmiş oyunlara baktığımızda (Örn Halo2, Crysis vs.) oyunlardaki ayrıntıların daha fazlalaştığını ve genel oyun kalitesinin arttığı görülmektedir. DirectX içerisinde Direct3D, DirectSound, DirectPlay  ve DirectInput, vb. gibi bileşenleri vardır. Oyun programlama yapılırken bu bileşenlerden faydalanılır. Bu bileşenlerin ne işlere yaradıklarına bakalım;

  • Direct3D (veya yeni adıyla WGF: Windows Graphics Foundations - Windows Grafik Temelleri) hem iki hem üç boyutlu grafikleri ekrana dökmeye yarar.
  • DirectSound hem iki hem üç boyutlu ses için kullanılır. Windows altında çalışabilen her ses kartı en azından yazılım ile DirectSound ve DirectSound 3D desteğine sahiptir.
  • DirectInput, klavye, fare ve oyun çubuğu gibi bileşenlerin giriş ve çıkışlarını basit bir şekilde yönetebilmek için olan arabirimdir.
  • DirectPlay, TCP/IP, modem ve Bluetooth gibi birçok protokol üzerinden bağlantı ve mesajlaşma bileşenlerini içerir.

DirectX ile .net ile yazılım yapmak için Microsoft.DirectX veMicrosoft.DirectX.Direct3D kütüphanelerini kullanmanız gerekmektedir. Bu dll leri bilgisayarınıza DirectX kurarak getirebilirsiniz.  Eğer sharpdevelop kullanıyorsanız directX projesi yapmak için New Project içerisinde Direct3D Application'ı seçerek otomatik olarak yapabilirsiniz. Visual Studio kullanıyorsanız ise yukarıdaki iki kütüphaneyi projenize eklemeniz gerekmektedir.

Visual studio içerisinde projeye directX dll lerini ekleme.

Sharp develop içerisinde proje olarak Direct3D Application seçildiğinde directX dll leri otomatik olarak eklenmiş olarak proje açılır.

Oluşturacağımız bir DirectX iskeleti aşağıdaki gibi olacaktır.

 

using System;
using System.Collections.Generic;
using
System.ComponentModel;
using System.Drawing;
using
System.Windows.Forms;
using Microsoft.DirectX;
using
Microsoft.DirectX.Direct3D;
namespace
gamer
{

    public
class MainClass : Form{
       
Device device = null;
       
public MainClass(){
           
this.ClientSize = new System.Drawing.Size(640, 480);
            this
.Text = "Mehmet Ali ECER";
        }

        public
bool InitializeGraphics
(){
               
try {
                        PresentParameters presentParams = new PresentParameters();
                       
presentParams.Windowed = true;
                       
presentParams.SwapEffect = SwapEffect.Discard;
                        device
= new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
                        device
.DeviceLost += new EventHandler(this.InvalidateDeviceObjects);
                       
device.DeviceReset += new EventHandler(this.RestoreDeviceObjects);
                       
device.Disposing += new EventHandler(this.DeleteDeviceObjects);
                       
device.DeviceResizing += new CancelEventHandler(this.EnvironmentResizing);
                       
return true;
            }
catch (DirectXException) {
                        return false;
            }
        }
    protected virtual void InvalidateDeviceObjects(object sender, EventArgs e){MessageBox.Show("invalidate device");}

    protected virtual void RestoreDeviceObjects(object sender, EventArgs e){MessageBox.Show("restore device");}

    protected virtual void DeleteDeviceObjects(object sender, EventArgs e){MessageBox.Show("delete device");}

    protected virtual void EnvironmentResizing(object sender, CancelEventArgs e){MessageBox.Show("environment resize");}

    protected virtual void FrameMove(){}

    protected virtual void Render(){
           
if (device != null) {
               
device.Clear(ClearFlags.Target, Color.Aqua, 1.0f, 0);
               
device.BeginScene();
                device
.EndScene();
               
device.Present();}
        }

        public void Run(){
            while
(Created) {
               
FrameMove();
                Render
();
               
Application.DoEvents();
                                        }
            }

        protected override void OnPaint(PaintEventArgs e){this.Render();}

        protected override void OnKeyPress(KeyPressEventArgs e){
               
base.OnKeyPress(e);
                if
((int)e.KeyChar == (int)System.Windows.Forms.Keys.Escape) {
                   
this.Close(); }
        }

        static void Main(){
           
using (MainClass mainClass = new MainClass()) {
               
if (!mainClass.InitializeGraphics()) {
                       
MessageBox.Show("Hata Direct3D");
                       
return;
                }

        mainClass
.Show();
       
mainClass.Run();
        }
        }
    }
}

Programda System, System.Collections, System.ComponentModel,System.Drawing, System.Windows.Forms,Microsoft.DirectX,Microsoft.DirectX.Direct3D isim uzayları kullanılmıştır. Bu isim uzayları window bazlı programlamı, çizimde, formlarda, directx, direct3d sınıflarını kapsar.Main sınıfının kurucusunda oluşacak olan pencerenin boyutları ve pencerenin başlığı yazılmaktadır. InitializeGraphics fonksiyonunda ise yeni bir grafik aygıtı tanımlanmaktadır. Yeni bir aygıt tanımlarken alınan parametreleri başka bir makalede ayrıntılı olarak inceleyeceğim için fazla üzerinde durmanıza gerek yok, şu an için kafa karıştırıcı olabilir. Burada en önemli nokta delegate vasıtasıyla event handlerların tanımlanmasıdır. Bu eventhandler vasıtası ile  program işletim sisteminden gelen kesmelere cevap verebilecektir bir nevi işletim sistemi ile haberleşebilecektir. Bu event handlerların ne zaman tetiklendiğini görmek için her bir eventın içerisine message box konularak uyarı alınmıştır. Bu uyarılar ile programı çalıştırdığınızda hangi event'ın nasıl ne zaman tetiklendiğini görebilirsiniz. Şimdi bu eventları inceleyelim.
device.DeviceLost += new EventHandler(this.InvalidateDeviceObjects); eventı pencere kaybolduğunda pencere üzerinde bir işlem yapıldığında tüm objelerin geçersiz duruma gelmesi halinde tetiklenir.Bu event'ı kullanarak pencerenin kullanıcı tarafından kapatılmasını ya da pencerenin büyüklüğünde yapılan değişiklikleride yakalayabilirsiniz. device.DeviceReset += new EventHandler(this.RestoreDeviceObjects) eventi aygıt üzerinde yapılan bir değişiklik sırasında ya da form resize edilirken tetiklenir. device.Disposing += new EventHandler(this.DeleteDeviceObjects); eventı ile objeler toplanıp finalize edilip garbage collectera gönderileceği zaman çağrılır.device.DeviceResizing += new CancelEventHandler(this.EnvironmentResizing); aygıtın boyutunun değiştirilmesinde tetiklenir.Render ise nesnelerin hazırlanması, sahneye çizilmesi aşamasıdır. Kodların nesnelere dönüşmesi, oyun görüntülerinin oluşmasını sağlayan kısımdır. Kodda önce device temizlenip daha sonra tekrar oluşturulmaktadır. Şimdi düşünün bir oyun esnasında yanlışlıkla formu kapatmak zorunda kaldınız eğer form hemen kapanırsa o ana kadar veriler kaybolabilir işte bu gibi durumlarda yukarıdaki eventlar vasıtası ile kullanıcıya oyunu kaydetmek isteyip istemediğini sorabileceksiniz. FrameMove() fonksiyonunda oyun içeriği ile işlemler yapılabilir. Mesale yapay zeka, network uygulamaları oyun ile ilgili işlemler yapılabilir. Form içerisinde OnKeyPress eventı ile program içerisinde klavye hareketleri yakalanabilmektedir. Program içerisinde eğer esc(escape) tuşuna basılırsa formun kapanması sağlanmıştır. Kullanıcının klavye,mouse vs üzerindeki hareketlerinin yakalanması ve buna göre programın akışını değiştirmek oyun programlamada çok önemli olacaktır. Form üzerinde zaman işlemleri için micsoft'un hazırladığı ve directX sdk sı içerisinde gelen DXUtil.cs dosyasını kullanabilirsiniz. Bu dosyada bulunan static fonksiyonlar ile programınız içerisinde zaman tutabilir, iki komut arasındaki zaman farkını bulabilirsiniz. Bu  FrameMove fonksiyonu içerisine aşağıdaki kodu yazarak formun başlığında zamanlayıcıyı sürekli olarak yazdırabilirsiniz.

        float time = DXUtil.Timer( DirectXTimer.GetAbsoluteTime );

        this.Text=time.ToString();

Timer nesnesinin aşağıdaki fonksiyonları vardır.

    Reset Uygulama zamanını resetler ve 0 yapar

    Start Uygulama zamanı durdurulduktan sonra çalıştırılmasını sağlar

    Stop Uygulama zamanını durdurur

    Advance Uygulama zamanını 0.1 saniye ileriye alır.

    GetAbsoluteTime Mutlak sistem saatini verir

    GetApplicationTime Uygulama zamanını verir

    GetElapsedTime İki işlem arasında geçen süre hesaplamada kullanılır. Örnek olarak

            DXUtil.Timer( DirectXTimer.GetElapsedTime );

            f = DXUtil.Timer( DirectXTimer.GetElapsedTime );

şeklindeki bir işlemde ilk satırda süre sayılmaya başlanır ikinci satırda bu sayılan süre alınır.

 

 

Makalede ya da kodlarda bulunan hatalar ya da sorularınız için mehmetaliecer@gmail.com adresinden bana ulaşabilirsiniz. İyi çalışmalar.

Mehmet Ali ECER

www.mehmetaliecer.com