12 Aralık 2010

Windows Phone 7 ' de XNA Game Component

Merhaba, bu makalemizde Windows Phone 7 'de XNA 'Game Component' kullanımını inceleyeceğiz. Oyunumuz için 'Game Component Template' kullanarak örnek bir button kontrol sınıfı oluşturacağız. Oluşturacağımız button sınıfını kullanarak dilediğimiz kadar button nesnesi oluşturup, oyunumuzda istediğimiz yere ekleyebilir, yerini değiştirebilir ve istediğimiz zaman da silebilir olacağız.
Oyunlarda sıklıkla ya da birden fazla kullanımı gerektiren nesneler için component kullanımı, kodlarımızın karışmasını engelleyecek, kodların daha modüler ve daha okunabilir bir yapıya kavuşmasını sağlayacaktır. Makalemize başlamadan önce Windows Phone 7 ' de XNA , LoadContent, Update, Draw methodları hakkında bilgilere erişmek isteyenler burdan erişebilirler.

Visual Studio 2010 / File / New Project / XNA Game Studio 4.0 / Windows Phone Game Template seçip yeni bir proje oluşturarak örneğimizi geliştirmeye başlayalım. Projemizi oluşturduğumuzda default olarak Game1.cs isimli sınıf ile birlikte ayrıca bir de Content projesinin, otomatik olarak oluştuğunu göreceğiz. Content projesi oyunumuz içerisinde kullanacağımız nesnelerimizi (imaj, spritefont v.s. gibi) eklediğimiz projedir. Ana projemize 'WindowsPhoneGameComponent' adını verelim. Oluşturduğumuz projenin Solution Explorer 'daki ilk görüntüsü aşağıdaki gibi olacaktır.



























'Button Component' imizi eklemek için hemen WindowsPhoneGameComponent Projesine sağ tıklayıp Add / New Item / XNA Game Studio 4.0 / Game Component Template seçelim ve kontrolümüzün adına 'ButtonControl' adını verelim.



























ButtonControl ilk oluşturulduğunda aşağıdaki kod satırlarının otomatik olarak eklenmiş oduğunu göreceğiz. Bu sınıfın içinde Constractor, Initialize ve Update methodları yer almaktadır. Component sınıfımız bu haliyle override Draw methodunu kullanamayacaktır. Base sınıf GameComponent içerisinde Draw methodu yer almamaktadır.


public class ButtonControl : GameComponent
{
    public ButtonControl(Game game)
        : base(game) { }
    public override void Initialize()
    {
        base.Initialize();
    }
    public override void Update(GameTime gameTime)
    {
        base.Update(gameTime);
    }
}
ButtonControl için base sınıf olarak kullanılan GameComponent sınıfını silerek yerine DrawableGameComponent sınıfını yazıyoruz. DrawableGameComponent ' in eklenmesi ile bu sınıfa ait virtual methodları override şekilde kullanmamız mümkün olacaktır. ButtonControl sınıfımızın son hali de aşağıdaki gibi olsun. Ayrıca override yazıldığında base sınıfdan gelen virtual methodlar da aşağıdaki gibi listelenecektir.







public class ButtonControl : DrawableGameComponent
{
    public ButtonControl(Game game)
        : base(game) { }
    public override void Initialize()
    {
        base.Initialize();
    }
    protected override void LoadContent()
    {
        base.LoadContent();
    }
    public override void Update(GameTime gameTime)
    {
        base.Update(gameTime);
    }
    public override void Draw(GameTime gameTime)
    {
        base.Draw(gameTime);
    }
 }
ButtonControl sınıfımızı DrawableGameComponent' den türettikten sonra sınıfımıza özellikler ekleyerek devam edelim. Button nesnesi oluşturmak amacı ile kullanacağımız için bir constractör daha ekleyerek butonumuzun Text değerini buradan belirtebiliriz. Sınıfımıza ait, text özelliği için bir Texture2D ve Vektör2 nesneleri, ayrıca butonumuzu ekrana çizdirebilmek için de SpriteBatch nesnesinden faydalanacağız. Butonumuzun ekranın neresinde yer alacağını, font değerini ve Text'ini public olarak dışarıya açarak, diğer sınıfların değiştirebilmesine imkan vereceğiz. Ayrıca butonumuza tıklanıldığında click eventı yakalayabilmek için de bir Click EventHandler kullanacağız.

private, public ve constractörümüz aşağıdaki gibi olsun;

private SpriteBatch spriteBatch;
private Texture2D aTexture;
private Vector2 textPosition;
public event EventHandler Click;
public Rectangle Destination { setget; }
public SpriteFont SpriteFont { setget; }
public string Text { setget; }
public ButtonControl(Game game, string buttonText)
    : base(game)
{ 
    Text = buttonText; 
}
private değişkenlerimizi, public özelliklerimizi ve bir de Click isimli event ' ı ekledikten sonra, şimdi de LoadContent methodumuzun içeriğini oluşturalım. SpriteBatch nesnesini oluşturup, hemen ardından buton çerçevesini belirleyece bir de texture nesnesi oluşturuyoruz;

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(this.GraphicsDevice);
    aTexture = new Texture2D(this.GraphicsDevice, 1, 1);
    aTexture.SetData<uint>(new uint[] { Color.White.PackedValue });
    base.LoadContent();
}
Şimdi de sırasıyla Update ve Draw methodlarımızın içeriğini oluşturalım.  Butonumuzun ekranda yalnızca bir yerde çakılı kalmaması, oyunun akışı içerisinde  dilersek yerini değiştirebilmek için Update methodu içerisinde Destination  özelliğimizden faydalanıyoruz. Update ' den hemen sonra çalışacak Draw methodumuz  içerisinde de ekrana çizim yapacağımız kod satırlarımızı ekleyelim.


public override void Update(GameTime gameTime)
{
    if (SpriteFont != null && !String.IsNullOrEmpty(Text))
    {
        Vector2 textSize = SpriteFont.MeasureString(Text);
        textPosition = 
        new Vector2((int)(Destination.Left + (Destination.Width - textSize.X) / 2),
                    (int)(Destination.Top + (Destination.Height - textSize.Y) / 2));
    }
    base.Update(gameTime);
}
 
public override void Draw(GameTime gameTime)
{
    spriteBatch.Begin();
         
    if (Destination != null) {
        spriteBatch.Draw(aTexture, Destination , Color.Black);
    }
 
    if (SpriteFont != null && !String.IsNullOrEmpty(Text))
    {
        spriteBatch.DrawString(SpriteFont, Text, textPosition, Color.White);
    }
    spriteBatch.End();
    base.Draw(gameTime);
}
Click event ' ımızı oluşturmak için public bir method hazırlayacağız.  Methodumuz bir TouchLocation nesne parametresi alacak. Parametre olarak gelen  TouchLocation, eğer bizim butonumuzun bulunduğu yerin (Destination) içinde ise  Click event'ımızı burada oluşturacağız. Aynı zamanda bu methodumuz Click  event'ın oluşup oluşmadığına dair bool bir değer dönecek. Bu methodumuz için bir  de interface kullanarak tıklanan kontrolün aynı zamanda bir button olduğunu da  daha rahat bir şekilde anlayabileceğiz. Methodumuz ve interface aşağıdaki gibi  olsun;


public bool ButtonTouch(Microsoft.Xna.Framework.Input.Touch.TouchLocation touch)
{
    bool touchHandled = false;
    bool isInside = Destination.Contains((int)touch.Position.X, (int)touch.Position.Y);
    if (isInside && Click != null)
    {
        touchHandled = true;
        Click(thisnull);
    }
    return touchHandled;
}
public interface IButtonTouch
{
    bool ButtonTouch(TouchLocation touch);
}
public class ButtonControl : Microsoft.Xna.Framework.DrawableGameComponentIButtonTouch


ButtonControl sınıfımız DrawableGameComponent ve IButtonTouch interface'ini base olarak kullanacak.
Artık ButtonControl' ümüzü kullanabilir durumdayız. Game1.cs içinde private bir ButtonControl değişken olarak tanımlayarak ana sınıfımız içerisinde bu kontrolü kullanmaya başlayalım. Game1.cs isimli ana sınıfımız ve private değişkenleri aşağıdaki gibi olsun;


public class Game1 : Microsoft.Xna.Framework.Game
private GraphicsDeviceManager graphics;
private SpriteBatch spriteBatch;
private ButtonControl btnTamam;
private bool exitTheGame;
Content projemiz içerisine de bir SpriteFont dosyası eklemeyi unutmayalım.  ButtonFont, Font Size özelliğini de 18 olarak değiştirelim.









Game1.cs içindeki methodların içerisine de aşağıdaki kod satırlarını ekleyelim. Initialize methodumuzdan başlıyoruz. Burada button kontrölümüzü oluşturarak Game1 components collection içerisine ekliyoruz.


protected override void Initialize()
{
    btnTamam = new ButtonControl(this"Oyundan Cikis");
    btnTamam.Click += new EventHandler(btnTamam_Click);
    this.Components.Add(btnTamam);
    base.Initialize();
}
btnTamam isminde bir ButtonControl nesnesi oluşturduk. Butonumuzun Text özelliğinde "Oyundan Cikis" yazacak ve eğer bu butona tıklanırsa öncelikle bir onay mesajı gelecek. Evet seçilirse oyundan çıkılacak hayır seçilirse oyunda kalacağız. btnTamam_Click event handler içerisindeki kod satırlarını da aşağıdaki gibi oluşturalım.


private void btnTamam_Click(object sender, EventArgs e)
{
    if (Guide.IsVisible)
        return;
 
 
    List<string> msgBoxButtons = new List<string>();
    msgBoxButtons.Add("evet");
    msgBoxButtons.Add("hayır");
 
    string msg = "Oyundan çıkılacak.\nÇıkmak istiyor musunuz?";
    Guide.BeginShowMessageBox("Onay", msg, msgBoxButtons, 0, MessageBoxIcon.Alert, 
                              result =>
                              {
                                  int? response = Guide.EndShowMessageBox(result);
                                  if (response.HasValue && response.Value == 0)
                                      exitTheGame = true;
                                  else
                                      exitTheGame = false;
                              }, 
                              null );
}
Guide.BeginShowMessage methoduna parametre olarak IEnumarable tipinde 'evet' ve 'hayır' string değerlerini gönderdik. ilk gönderdiğimiz string değerinin indeksi 0 ve diğerleri 1 artarak devam edecektir. Kullanıcı hangi değeri sonuç onun indeksini dönecektir. Yukarıdaki kod satırlarında Responce.Value == 0 ise evet, 1 ise hayır seçildiği anlamına gelmektedir.


























Şimdi de sırasıyla Game1.cs sınıfının LoadContent, Update ve Draw methodları kod satırlarını da ekleyerek Game1.cs sınıfını tamamlayalım. LoadContent içerisinde kullandığımız content dosyasını ve butonumuzun açılışta ekranın neresinde konumlanacağını (Destination) belirtelim. Update methodu her çalıştığında ekrana tıklanma durumunu da elde edeceğiz. Ekran üzerindeki her bir kontrolü döngü içinde gezerek bu kontrolün IButtonTouch türünden olup olmadığını ve eğer aradığımız kontrol buton türünde bir kontrol ise bu kontrolün ButtonTouch methoduna parametre olarak elde edilen TouchLocation değerini göndereceğiz. Bool ButtonTouch methodu bize true değerini dönerse kontrole tıklanıldığı anlamına gelecek ve Click event handler da yakalanacaktır. Onay mesajında evet denilirse exitTheGame private değişkenimize true atayarak bir sonraki Update methodunda oyunu Exit() ile sonlandıracağız.


protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
 
    SpriteFont fontTamam = Content.Load<SpriteFont>("ButtonFont");
    btnTamam.SpriteFont = fontTamam;
 
    Vector2 vektorTamam = fontTamam.MeasureString(btnTamam.Text);
 
    int buttonWith = (int)vektorTamam.X;
    int buttonHeight = (int)vektorTamam.Y;
 
    Viewport vport = GraphicsDevice.Viewport;
    btnTamam.Destination = new Rectangle(vport.Bounds.Left + 10,
                                         vport.Bounds.Top + 10,
                                         buttonWith + 60,
                                         buttonHeight + 20);
}
 
protected override void Update(GameTime gameTime)
{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || exitTheGame)
        this.Exit();
 
    TouchCollection touches = TouchPanel.GetState();
            
    foreach (TouchLocation touch in touches)
    {
 
        bool touchHandled = false;
        foreach (GameComponent component in this.Components)
            if (component is IButtonTouch && (component as IButtonTouch).ButtonTouch(touch))
            {
                touchHandled = true;
                break;
            }
        if (touchHandled)
            continue;
    }
 
    base.Update(gameTime);
}
Uygulamamızı emulatör üzerinde F5 ile çalıştırarak buton kontrolümüzü  belirlediğimiz koordinatda, renkte ve büyüklükte görebiliriz.























Kolay gelsin.
gokhanmanduz@hotmail.com
gmanduz@gmail.com

Hiç yorum yok: