19 Aralık 2010

Windows Phone 7 Push Notification Types

Bu makalemizde Windows Phone 7 işletim sistemli telefonlarda kullanılan Push Notification (Türkçede 'bildirim itme' ya da 'dürtme' anlamında) konusunu 3 farklı bildirim tipi (notification types) ile ele alacağız.
Bu bildirim tiplerini sırasıyla;

1. Tile Notification
2. Toast Notification
3. Raw Notification


olarak 3 farklı gruba ayırarak inceleyelim. Öncelikle, kısaca ve ekran görüntüleri ile bu bildirim tiplerinin ne olduğunu anlamaya çalışalım.

1. Tile Notification


Windows Phone 7 açılış ekranında gösterimi yapılacak olan bildirim tipidir. Görsel olarak dikkat çekici konumdadır ve uygulamaları çalıştırdığımız ana ekranda yer alacaktır. Windows Phone 7 içerisinde her uygulama için ana ekranda bir 'Tile' (giriş ekranında yer alan ve application quick launchers olarak adlandırılan ekran ) bulunabilir.













Bir Tile Backgraund Image, Count ve Title'dan oluşur. Backgraund Image: Arka plan resmi için local veya bir uzak sunucuda bulunan imaj dosyası kullanılabilir. Tabiiki burada daha fazla performans istiyorsak local dosyada olması tercih nedeni olacaktır. Count: Integer sayıdır. Bildirim sayısını göstermek için kullanacağız. Title: Alt kısımda yer alan ve yalnızca bir satırlık string bilgi gösterilebilecek alandır. Bu sebepten dolayı tile genişliğini aşmayacak uzunlukta string bildirimler gönderilmesi gerekecektir.



2. Toast Notification (Tost :)

Bir bildirim geldiğinde ekranın üst kısmında belirecektir. Aşağıdaki resimde de görüleceği gibi turuncu arka plan renginde bir title ve bir de sub-title'dan oluşur. Title bold font, sub-title ise normal font olacaktır.







3. Raw Notification


Eğer yukarıda belirttiğimiz Tile ve Toast bildirimde bulunmak yerine direk olarak uygulamıza bildirimde bulunmak istiyorsak raw notification kullanacağız. Eğer gönderim sırasında uygulamamız kapalı ise Microsoft Push Notification servisinde bu bildirim iptal edilecektir ve cihaza iletilmeyecektir.

( Önemli Not : Tüm bu bildirimlerle ilgili UI tasarlamadan önce Microsoft' un belirttiği UI Guidelines'ı bir sıkıntı yaşamamak için (Phone Marketplace için) incelemekte fayda var. İlgili Guidelines'ı buradan indirebilirsiniz. )

Bildirim türlerinden kısaca bahsettikten sonra şimdi de bir örnek ile notification türlerini daha yakından
incelemeye çalışalım. Örneğimiz için 2 farklı proje oluşturacağız. Öncelikle bir WPF projesi ile Notification Sender tarafını daha sonrada Windows Phone projesi ile bildirimlerin cihaza nasıl ulaştığını ve cihazda nasıl gösterildiğinden bahsedeceğiz.

Notification Sender Side (WPF Application)


Visual Studio 2010 / File / New / Project / Visual C# / Windows / WPF Application seçeneği ile yeni bir proje oluşturarak notification göndereceğimiz tarafı oluşturmaya başlayalım. MainWindows ekranımızı aşağıdaki gibi oluşturalım.

MainWindows XAML
<Window x:Class="SenderApplication.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Sender Application" Height="433" Width="563">
  <Grid>
      <Border BorderBrush="#FF8AC0C0" BorderThickness="1" Height="43"
              HorizontalAlignment="Left" Margin="13,10,0,0" Name="border1" VerticalAlignment="Top" Width="507">
          <TextBlock Height="21" Name="textBlock1" Text="Windows Phone 7 URI" Width="133" HorizontalAlignment="Left" Margin="5" />
      </Border>
      <TextBox Height="23" HorizontalAlignment="Left" Margin="152,21,0,0" Name="txtUri" VerticalAlignment="Top" Width="357" />
      <GroupBox Header="Tile Notification" Height="100" HorizontalAlignment="Left"
                Margin="13,59,0,0" Name="groupBox1" VerticalAlignment="Top" Width="513">
          <Grid>
              <TextBox Height="23" HorizontalAlignment="Left" Margin="133,6,0,0" 
                     Name="txtTileTitle" VerticalAlignment="Top" Width="142" />
              <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,8,0,48" 
                         Name="txt" Text="Title" Width="91" />
              <TextBox Height="23" HorizontalAlignment="Left" Margin="133,35,0,0" 
                       Name="txtTileBackgraundimage" VerticalAlignment="Top" Width="252" />
              <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,35,0,21" 
                         Name="textBlock3" Text="Basckbraund Image" Width="109" />
              <TextBox Height="23" HorizontalAlignment="Left" Margin="331,5,0,0" 
                       Name="txtCount" VerticalAlignment="Top" Width="54" />
              <TextBlock Height="21" HorizontalAlignment="Left" Margin="281,7,0,49" 
                         Name="textBlock4" Text="Count" Width="44" />
              <Button Content="Gönder" Height="23" HorizontalAlignment="Left" Margin="415,48,0,0" 
                      Name="btnSendTile" VerticalAlignment="Top" Width="75" Click="btnSendTile_Click" />
          </Grid>
      </GroupBox>
      <GroupBox Header="Toast Notification" Height="100" HorizontalAlignment="Left" Margin="13,165,0,0" 
                Name="groupBox2" VerticalAlignment="Top" Width="513">
          <Grid>
              <TextBox Height="23" HorizontalAlignment="Left" Margin="133,10,0,0" 
                       Name="txtToastValue1" VerticalAlignment="Top" Width="252" />
              <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,10,0,46" 
                         Name="textBlock2" Text="String Value" Width="124" />
              <TextBox Height="23" HorizontalAlignment="Left" Margin="133,39,0,0" 
                       Name="txtToastValue2" VerticalAlignment="Top" Width="252" />
              <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,39,0,17" 
                         Name="textBlock5" Text="Sub-String Value" Width="124" />
              <Button Content="Gönder" Height="23" HorizontalAlignment="Left" Margin="415,48,0,0" 
                      Name="btnSendToast" VerticalAlignment="Top" Width="75" Click="btnSendToast_Click" />
          </Grid>
      </GroupBox>
      <GroupBox Header="Raw Notification" Height="100" HorizontalAlignment="Left" Margin="13,271,0,0"
                Name="groupBox3" VerticalAlignment="Top" Width="513">
          <Grid>
              <TextBox Height="23" HorizontalAlignment="Left" Margin="133,10,0,0" 
                       Name="txtRawValue" VerticalAlignment="Top" Width="252" />
              <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,10,0,46" 
                         Name="textBlock6" Text="String Value" Width="124" />
              <Button Content="Gönder" Height="23" HorizontalAlignment="Left" Margin="415,48,0,0" 
                      Name="btnSendRaw" VerticalAlignment="Top" Width="75" Click="btnSendRaw_Click" />
          </Grid>
      </GroupBox>
  </Grid>
</Window>






























UI hazırladıktan sonra Tile, Toast ve Raw buttonların click eventları altına ilgi notification gönderim kodlarını ekleyelim. Tile notification özelliklerine uygun olacak şekilde bir title, count ve arkaplanda çıkacak imaj adresini parametre olarak olan methodumuzu hazırlayalım. Imaj olarak ApplicationIcon.png dosyasını kullanabiliriz. "ApplicationIcon.png" dışında client windows phone uygulamamız tarafına content olarak eklenmiş farklı bir imaj dosyası da kullanabilirdik. (Windows Phone projesine eklenecek imaj dosyasının build action özelliğinin content olmasına dikkat edelim.)

Tile Notification Sender Method ve Button Click Event;

private void sendTile(Uri phoneUri, string title, int count, string backgroundImage)
{
    string xmlMsg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                    "<wp:Notification xmlns:wp=\"WPNotification\">" +
                    "<wp:Tile>" +
                    "<wp:BackgroundImage>" + backgroundImage + "</wp:BackgroundImage>" +
                    "<wp:Count>" + count.ToString() + "</wp:Count>" +
                    "<wp:Title>" + title + "</wp:Title>" +
                    "</wp:Tile>" +
                    "</wp:Notification>";
    byte[] data = new UTF8Encoding().GetBytes(xmlMsg);
 
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(phoneUri);
    request.Method = WebRequestMethods.Http.Post;
    request.ContentLength = data.Length;
    request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
 
    request.Headers["X-WindowsPhone-Target"] = "token";
    request.ContentType = "text/xml";
    request.Headers["X-NotificationClass"] = "1";

    Stream requestStream = request.GetRequestStream();
    requestStream.Write(data, 0, data.Length);
    requestStream.Close();
    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
}
private void btnSendTile_Click(object sender, RoutedEventArgs e)
{
    int count = Convert.ToInt32(txtCount.Text);
    sendTile(new Uri(txtUri.Text), txtTileTitle.Text, count ,txtTileBackgraundimage.Text);
    count++;
    txtCount.Text = count.ToString();
}
Yukarıdaki sendTile methoduna parametre olarak vereceğimiz phoneUri değerinin de önceden WindowsPhone tarafından verilmiş olması gerekiyor. uri adresinin nasıl elde edileceğini Windows Phone Application tarafında göreceğiz. Şimdi de Toast notification gönderecek olan methodumuzu ve click event'ımızı ekleyelim.

private void sendToast(Uri phoneUri, string str, string subStr)
{
    string msg =
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
        "<wp:Notification xmlns:wp=\"WPNotification\">" +
        "<wp:Toast>" +
        "<wp:Text1>" + str + "</wp:Text1>" +
        "<wp:Text2>" + subStr + "</wp:Text2>" +
        "</wp:Toast>" +
        "</wp:Notification>";
    byte[] data = new UTF8Encoding().GetBytes(msg);
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(phoneUri);
    request.Method = WebRequestMethods.Http.Post;
    request.ContentLength = data.Length;
    request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
    request.Headers["X-WindowsPhone-Target"] = "toast";
    request.ContentType = "text/xml";
    request.Headers["X-NotificationClass"] = "2";
    Stream requestStream = request.GetRequestStream();
    requestStream.Write(data, 0, data.Length);
    requestStream.Close();
    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
}
private void btnSendToast_Click(object sender, RoutedEventArgs e)
{
    sendToast(new Uri(txtUri.Text), txtToastValue1.Text, txtToastValue2.Text);
}
Yukarıdaki kod satırlarında görüleceği gibi Toast gönderime uygun olacak şekilde sendToast parametre değerine bir string ve sub-string değer gönderiyoruz. Tile gönderimde olduğu gibi burada da uri adresini windows phone tarafında elde ediyor olacağız.

HttpWebResponse response = request.GetResponse() as HttpWebResponse

HttpWebResponse dönen kod satırında ise eğer dilerseniz gönderim sonrası durum bilgilerini ekrana yazdırabilirsiniz. Örneğin response.Header["X-DeviceConnectionStatus"] ile cihaz bağlantı durum bilgisi elde edilebilir. Raw Notification gönderimi için de aşağıdaki kod satırlarını ekleyelim;

private void sendRaw(Uri phoneUri, string str)
{
    MemoryStream stream = new MemoryStream();
    BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8);
    writer.Write(str);
    byte[] data = stream.ToArray();
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(phoneUri);
    request.Method = WebRequestMethods.Http.Post;
    request.ContentLength = data.Length;
    request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
    request.Headers["X-NotificationClass"] = "3";
    Stream requestStream = request.GetRequestStream();
    requestStream.Write(data, 0, data.Length);
    requestStream.Close();
}
private void btnSendRaw_Click(object sender, RoutedEventArgs e)
{
    sendRaw(new Uri(txtUri.Text), txtRawValue.Text);
}
Raw notification gönderiminde de benzer şekilde string değerimiz ile windows phone kanal adresimizi parametre olarak gönderiyoruz. Şimdi de Windows Phone tarafında yeni bir bağlantı kanalı oluşturarak sender
uygulamıza verelim. Hemen ardından bildirimlerin nasıl gözükeceğine göz atalım.

Windows Phone Application Side


Öncelikle private kanal değişkenimizi, kanala vereceğimiz adı ve constractorümüz içerisinde Loaded event ekleyerek başlayalım. HttpNotificationChannel using Microsoft.Phone.Notification namespace altında bulunmaktadır. bu namespace default olarak projemize eklenen Microsoft.Phone.dll altında bulunmaktadır.


private HttpNotificationChannel httpChannel;
const string channelName = "MyPushChannel";
const string serviceName = "MyPushServis";
public MainPage()
 {
     InitializeComponent();
     this.Loaded += MainPage_Loaded;
 }

MainPage Loaded event handler altında çalışacak kod satırlarımızı ekleyelim. Tanımladığımız isimle yeni bir notification kanalı oluşturulmasını sağlayalım. Eğer bu isimde bir kanal tanımlı ise hazır durumda olan kanalımızı debug modda iken output penceremize yazdıracağız(Debug.WriteLine ile). Bu event handler içerisinde, yeni bir kanal oluşturma ve mevcut kanalın kontrol edilmesi Microsoft Push Notification Servisi tarafından
verilmektedir. Bu servisin nasıl çalıştığı konusunda bilgi almak isteyenler buradan ulaşabilirler.

private  void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    httpChannel = HttpNotificationChannel.Find(channelName);
    if (null == httpChannel)
    {
        Debug.WriteLine("Yeni kanal oluşturuldu.");
 
        httpChannel = new HttpNotificationChannel(channelName, serviceName);
        httpChannel.ChannelUriUpdated += 
            new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);
        httpChannel.Open();
    }
    else
    {
        Debug.WriteLine("Var olan kanal : " +
                        httpChannel.ChannelUri.ToString());
    }
 
    // Raw event handler ekleniyor
    httpChannel.HttpNotificationReceived += httpChannel_HttpNotificationReceived;
           
    // Toast event handler ekleniyor
    httpChannel.ShellToastNotificationReceived += httpChannel_ShellToastNotificationReceived;
 
    // Hata için event handler
    httpChannel.ErrorOccurred += httpChannel_ErrorOccurred;
}
ChannelUriUpdated event handler Microsoft Push Notification servisinden gelen yanıtları yakalayacaktır;

private void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
    Debug.WriteLine("Güncellenen kanal:" + httpChannel.ChannelUri.ToString());
    httpChannel.BindToShellToast();
    httpChannel.BindToShellTile();
}
Şimdide sırasıyla Raw ve Toast bildirimleri yakalayan event handler kod satırlarımızı ekleyelim. Gelen bildirimler asenkron olarak çalışacağı için Windows Phone ekranına koyacağımız bir textblock'da görüntüleyebilmek için textblock'un Dispatcher.BeginInvoke methodundan faydalanacağız.


private void httpChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
{
    BinaryReader reader = new BinaryReader(e.Notification.Body, System.Text.Encoding.UTF8);
    string ntext = reader.ReadString();
    txtMessageBox.Dispatcher.BeginInvoke(new Action(delegate() {
        txtMessageBox.Text += "Raw Notification" + Environment.NewLine;
        txtMessageBox.Text += ntext + Environment.NewLine;
     } ) );
}
 
private void httpChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
{
    txtMessageBox.Dispatcher.BeginInvoke(new Action(delegate() {
        txtMessageBox.Text += "Toast Notification:" + Environment.NewLine;
    }));
    if (e.Collection != null)
    {
        Dictionary<stringstring> collection = (Dictionary<stringstring>)e.Collection;
        foreach (string elementName in collection.Keys)
        {
            txtMessageBox.Dispatcher.BeginInvoke(new Action(delegate() {
              txtMessageBox.Text += elementName + " - " + collection[elementName] + Environment.NewLine;
            }));
        }
    }
}
ErrorOccured event handler da bir hata oluşması durumunda, hatayı yakalayacak ve kanalı kapatıp açarak tekrar bağlantının kurulmasını deneyecektir.

private void httpChannel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
{
    Debug.WriteLine("Hata oluştu: " + e.Message);
    Debug.WriteLine("Kanal tekrar açılacak...");
    httpChannel.Close();
    httpChannel.Open();
}
Windows Phone kod satırlarımızı da eklediğimize göre, şimdi debug modda F5 ile projemizi çalıştırıp, olup biteceklere göz atalım. Windows Phone 7 Emulatör seçili durumda ve debug modda uygulamamızı çalıştıralım.  MainWindows Loaded event handler altına eklediğimiz Debug.WriteLine kod satırları ile gönderdiğimiz bilgilere output penceresinden bakıyoruz.















Output penceresinden Microsoft Push Notification Servisinin gönderdiği kanal adresini kopyalayarak sender programımızı çalıştırıp ilgili URI alanına, kanal adresini kopyalayalım.






























Raw Notification string alanına "Merhaba Dünya :)" yazarak Gönder butonu ile bildirimimizi gönderelim. Windows Phone uygulamamız açık ve ilgili event handler eklenmiş olduğu için gönderilen bildirim aşağıdaki ekran görüntüsünde olduğu gibi ekrana yansıyacaktır.








































Raw notification ile gönderdiğimiz bildirim mesajı yalnızca uygulama ekranı açık olduğunda iletilecektir. Toast ve Tile notification'lar için uygulama ekranının açık olmasına gerek yoktur. İlgili bildirim Toast ise ekranın üst
kısmında belirecek, Tile ise quick launch ekranında uygulama tile alanında yazdırılacaktır.



























Aşağıdaki ekranda Windows Phone Toast notification görüntüsü yer alıyor. Phone uygulamamızın icon özelliğini project properties'den "warning.png" olarak değiştirdiğimiz için bir ünlem işareti ile toast bildirimi almış olduk ve ilgili bildirim mesajıyla "Ekmek Almayı unutma :)" mesajını göndermiş olduk.








































Kolay gelsin.
Faydalı Linkler:
http://msdn.microsoft.com/en-us/library/ff941124(v=vs.92).aspx
http://msdn.microsoft.com/en-us/wp7trainingcourse_usingpushnotificationslab_topic1
http://create.msdn.com/en-US/education/catalog/sample/push_notifications

Gökhan Manduz
http://gokhanmanduz.blogspot.com/
gmanduz@gmail.com

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

8 Aralık 2010

Silverlight 5 Beta

Microsoft Silverlight 5 Beta 'nın 2011 yılı ilk yarısında hazır olacağını duyurdu. Konuyla ilgilenenler http://www.microsoft.com/silverlight/ adresini ziyaret edebilirler.

Silverlight 5 Beta ile ilgili özelliklerin anlatıldığı video ya buradan erişilebilir.

5 Aralık 2010

Windows Phone 7' de XNA - 2 (2D Rectangle & Per-Pixel Collisions)

Bu makalemizde Windows Phone 7 'de XNA başlıklı yazdığımız makalemize kaldığımız yerden devam ediyoruz. XNA Game Studio 4 kütüphanelerinden faydalanarak Texture2D nesnesini ve bu nesnelerimize Windows Phone 7 cihazımızın ekranında hareket kabiliyeti kazandırmak için de Vektor2D nesnesini kullanmıştık. Önceki makalemizde olduğu gibi konuyu temel düzeyde ele alarak kullandığımız Texture nesnelerinin çarpışmasını (Collision Detection) 2D Rectangle Collision ve 2D Per-Pixel Collision olarak ele alacağız.
Oyunlarda nesnelerin çarpışması (ya da kesişmesi diyebiliriz) kritik öneme sahiptir. Özellikle yarış oyunlarında oyuncuların çarpışmamak, ya da yoldan çıkmamak için ne ölçüde çaba harcadıklarını, hırslandıklarını gözlemleyebiliriz. Böyle bir oyunda elbette çarpışmanın kalitesi ve gerçeğe yakın olması da oyuncuyu daha az çileden çıkaracaktır.
Bu makalemizde de örnek bir senaryo üzerinden devam edeceğiz. Ekran üzerinde bulunacak 2 adet kırmızı topun ekranın farklı yerlerinden rastgele çıkarak aracımızın üzerine doğru farklı hızlarda hareket etmesini sağlayacağız. Aracımızı (car.png) ekranın üzerinde parmağımızla hareket ettirerek bu toplardan kaçırmaya çalışacağız. Toplar ile aracın çarpışması durumunda oyundan 1 hakkımız eksilecek. Toplam 3 hakkımız olacak ve 3 hakkımızı da kaybedersek oyun sonlanmış olacak.


2D Rectangle Collision

Adından da anlaşılacağı üzere 2 boyutlu düzlemde 2 dikdörtgenin kesişmesi durumudur. Nesnelerimizin görüntüsünün dikdörgen olmamasının bir önemi yoktur. Burada önemli olan imaj nesnesinden dikdörtgen veya dikdörtgenler elde edilmesidir. Aşağıdaki resimlerde 2 boyutlu düzlemde dikdörtgenlerin kesişme durumlarına örnekler görebiliriz ya da resimde gösterdiğimiz uçak imajında olduğu gibi bir imajı birden fazla dikdörtgene bölebiliriz.
Aşağıdaki araç ve kırmızı top resimlerinde açıkça görüldüğü gibi dikdörtgenlerin kesişmesinde, asıl kesişim noktaları nesnelerin ekranda görünen yanlarından farklı olarak imajın dikdörtgen olarak belirlenen kısımlarının kesişmesidir.



Araç için car.png, toplar için ball-red-48.png ve yol kenar çizgileri içinde blackLine.png imajlarını kullanacağız. Aşağıdaki dosyaları sağ tıklayarak bilgisayarınıza kaydedebilirsiniz.




Windows Phone 7'de XNA konulu ilk makalemizde olduğu gibi nesnelerimizi solution explorer altında oluşturulan content projesi içerisine ekliyoruz. Ayrıca Imaj dosyaları dışında bir de spritefont dosyasını content projemize ekleyelim. Bu dosyanın nasıl ekleneceğini de Windows Phone 7'de XNA konulu ilk makalemizde belirtmiştik.
Şimdi C# ile ön hazırlığımızı yaparak, oyunumuz için gerekli olacak kodlarımızı yazmaya başlayalım. Öncelikle private olarak Game1.cs sınıfımızda kullanacağımız Texture2D , Vektor2D ve SpriteFont nesnelerimizi oluşturalım.

 GraphicsDeviceManager graphics;
 SpriteBatch spriteBatch;
 
 // Araba objesi için kullanılacak Texture ve Vektörü
 Texture2D carTexture;
 Vector2 carPosition;
 
 //Top 1 objesi için kullanacağımız Texture ve Vektörü
 Texture2D ball_1Texture;
 Vector2 ball_1Position;
 
 //Top 2 objesi için kullanacağımız Texture ve Vektörü
 Texture2D ball_2Texture;
 Vector2 ball_2Position;
 
 //Ekrana ekleyeceğimiz text bilgi için SpriteFont ve Vektörü
 SpriteFont textFont;
 Vector2 textPosition;
 const string TEXT_TEMPLATE = "Gokhan Manduz Car Game - {0} ";
 string TEXT = "";
 
 //Yolu belirtmek için kullanılacak Texture ve Vektörler
 Texture2D line1Texture;
 Vector2 line1Position;
 Texture2D line2Texture;
 Vector2 line2Position;
 
 bool collisionDetected;
 short game = 3;
 Random random = new Random();
 int ballSpeed = 10;
Game1 constractor içerisinde herhangi bir değişiklik yapmıyoruz. Default  olarak belirtilen TargetElapsedTime zaman aralığı ile update ve  draw methodlarımız 30 fps zaman aralığı ile sürekli çalışacaktır.


 public Game1()
 {
     graphics = new GraphicsDeviceManager(this);
     Content.RootDirectory = "Content";
     TargetElapsedTime = TimeSpan.FromTicks(333333);
 }
Şimdide Content projemize eklediğimiz nesnelerimizi LoadContent override  methodumuz içerisinde oyunumuzda kullanmak üzere hazırlayarlayalım oyun  içerisinde bu nesnelerin hangi pozisyonda (koordinatta) başlayacağını  belirleyelim.

 protected override void LoadContent()
 {
     spriteBatch = new SpriteBatch(GraphicsDevice);
 
     carTexture = Content.Load<Texture2D>("car");
     Viewport viewPort = graphics.GraphicsDevice.Viewport;
     carPosition = new Vector2( (viewPort.Width - carTexture.Width) / 2,  (viewPort.Height - carTexture.Height) / 2 );
 
     ball_2Texture = ball_1Texture = Content.Load<Texture2D>("Ball-red-48");
     ball_1Position = new Vector2(-150, 30);
     ball_2Position = new Vector2(-150, 260);
 
     textFont = this.Content.Load<SpriteFont>("CarGameFont");
     textPosition = new Vector2(2, 25);
 
     line2Texture = line1Texture = Content.Load<Texture2D>("blackLine");
     line1Position = new Vector2(-800, 0);
     line2Position = new Vector2(-800, 470);
 }
Tabiki oyunumuzun kilit kodları Update override methodu içerisinde yer  alacaktır. Update methodu her çalıştırıldığında nesnelerimizin yeni pozisyonunu, yer  değiştirmelerden sonra herhangi bir çarpışma olup olmadığını ve bir takım  hesaplamalarımızı buraya yazacağız. Öncelikle Update methodumuz içerisinde 2D  Rectangle ile çarpışma olup olmadığını kontrol edelim. Eğer çarpışma meydana  gelmiş ise ekranımızın arka plan rengini de kırmızıya boyayalım. 3 çarpışmadan  sonra oyunumuz sonlanacaktır. Kırmızı toplarımız X koordinatında soldan sağa  doğru farklı hızlarda ve her defasında farklı Y koordinatından gelerek aracımıza  doğru ilerleyecektir. Aracımıza bir yol üzerinde gidiyormuş izlenimi kazandımak  için de yol kenarlarında bulunan çizgilerimizi sürekli olarak X koordinatında  soldan sağa doğru hareket ettireceğiz. Update methodu içerisindeki kod  satırlarını comment satırlarına dikkat ederek inceleyelim.


 protected override void Update(GameTime gameTime)
 {
     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
         this.Exit();
 
     switch (game)
     {
         case 3:
             //-- 3 oyun hakkımız var.
             TEXT = string.Format(TEXT_TEMPLATE, "|||");
             break;
         case 2:
             //-- 2 oyun hakkımız kaldı.
             TEXT = string.Format(TEXT_TEMPLATE, "||");
             break;
         case 1:
             //-- 1 oyun hakkımız kaldı.
             TEXT = string.Format(TEXT_TEMPLATE, "|");
             break;
         case 0:
             //-- 0 oyun hakkı. Oyundan çıkılacak :)
             this.Exit();
             break;
     }
            
     //-- 13 ve 20 birim arasında rastgele topların hızlarını değiştirelim.
     //   (X koordinatında)
     ballSpeed = random.Next(13, 20);
     ball_1Position.X += ballSpeed;
     ball_2Position.X += (ballSpeed - 3);
     //-- 
 
 
     //-- X koordinatındaki pozisyon 900'den fazla ise toplar X'de tekrar başa dönsün.
     //   ve Y ekseninde toplar rasgele bir pozisyonda başlasın.
     if (ball_1Position.X > 900) {
         ball_1Position.X = -60;
         int ball1_Y = random.Next(ball_1Texture.Height, (480 - ball_1Texture.Height));
         ball_1Position.Y = ball1_Y;
     }
     if (ball_2Position.X > 900)
     {
         ball_2Position.X = -60;
         int ball2_Y = random.Next(ball_2Texture.Height, (480 - ball_1Texture.Height));
         ball_2Position.Y = ball2_Y;
     }
     line1Position.X += 10;
     line2Position.X += 10;
     if (line1Position.X > -2)
     {
         line1Position.X = -800;
         line2Position.X = -800;
     }
          
     //-- Ekrana dokunulduysa hangi pozisyona dokunulduğu bilgisi alınır.
     //   ve aracı o yönde ve mesafeye göre hızı ayarlanarak aracın X,Y değerleri arttırılır.
     TouchCollection touchCollection = TouchPanel.GetState();
     if (touchCollection.Count > 0) {
         TouchLocation t1 = touchCollection[0];
         double x = t1.Position.X - (carPosition.X + (carTexture.Width / 2));
         double y = t1.Position.Y - (carPosition.Y + (carTexture.Height / 2));
         double speed = Math.Sqrt(x * x + y * y) / 10;
         double angle = (float)Math.Atan2(y, x);
         carPosition.X += (float)(speed * Math.Cos(angle));
         carPosition.Y += (float)(speed * Math.Sin(angle));
     }
 
 
     //-- Collision Detaction kod satırları
     Rectangle carRectangle = new Rectangle((int)carPosition.X, (int)carPosition.Y, carTexture.Width, carTexture.Height);
     Rectangle ball1Rectangle = new Rectangle((int)ball_1Position.X, (int)ball_1Position.Y, ball_1Texture.Width, ball_1Texture.Height);
     Rectangle ball2Rectangle = new Rectangle((int)ball_2Position.X, (int)ball_2Position.Y, ball_2Texture.Width, ball_2Texture.Height);
     if (carRectangle.Intersects(ball1Rectangle) || carRectangle.Intersects(ball2Rectangle))
     {
         collisionDetected = true;
     }
     else {
         if (collisionDetected) {
             //Oyun hakkımızı 1 azaltıyoruz
             game--;
         }
         collisionDetected = false;
     }
     //--
 
     base.Update(gameTime);
 }
Yukarıdaki kod satırlarına dikkat edecek olursak çarpışma olup olmadığını  rectangle nesnemizin Intersects methodunu kullanarak gerçekleştiriyoruz. 
Şimdi de aynı örneğimiz üzerinden yalnızca Collision Detection kod satırlarımızı düzenleyerek 2D Per-Pixel Collision Detaction ile çarpışma olup olmadığını kontrol edeceğiz.


2D Per-Pixel Collision

2 boyutlu düzlemde yer alan nesnelerimizin piksel piksel kontrol edilmesiyle çarpışma olup olmadığının anlaşıldığı yöntemdir. Bu yöntem Rectangle Collision yöntemine göre çok daha sağlıklı sonuçlar verecektir. Hatta tam olarak istediğimiz şekillerin çarpışmasını en ince detaya kadar kontrol edebiliriz. Bu yöntemde imaj içerisinde yer alan nesnelerimizi piksel bazında üst üste gelecek renk değişimini kontrol ederek çarpışma oldup olmadığını belirleyeceğiz. Aşağıdaki resimlerde imaj dosyalarımızın içeriğini piksel bazında detaylı olarak görebiliriz. (VS 2010 content projemiz içerisindeki imaj dosyalarına çift tıklayarak hangi renge sahip piksellerden oluştuğunu görebiliyoruz.)





Piksel piksel bölünmüş olan nesnelerimizin aslında bazı piksel renklerinin transparent, bazılarının ise transparent dan farlı olarak bir renge sahip olduğunu görebiliyoruz. Per-Pixel Collision da nesnelerimizin kesişmesini piksellerindeki renk farklılıklarını kontrol ederek yakalayacağız. Imajlar kesiştiğinde kesişen bölgelerdeki renklerin transparent ya da saydam olması muhtemeldir. Bu sebepten dolayı imajların kesişiminde transparent renkten farklı olan renklerin aynı piksel üzerinde yer almasını dikkate alacağız. C# kodlarımızı bu özelliğe uygun şekilde hazırlayalım.




Öncelikle private olan değişkenlerimizin olduğu kısıma Color array olarak carTextureData, ball1TextureData ve ball2TextureData isimli nesnelerimizi tanımlıyoruz. Hemen ardından LoadContent override methodumuz içerisinde de color array nesnelerimizi aşağıdaki gibi oluşturalım.

private değişkenler;

 Color[] carTextureData;
 Color[] ball1TextureData;
 Color[] ball2TextureData;
LoadContent methoduna eklememiz gereken c# kod satırlarımız.

carTextureData = new Color[carTexture.Width * carTexture.Height];
ball2TextureData = ball1TextureData = new Color[ball_1Texture.Width * ball_1Texture.Height];
 
carTexture.GetData(carTextureData);
ball_1Texture.GetData(ball1TextureData);
ball_2Texture.GetData(ball2TextureData);
Ve Update override methodumuzun son hali ile static bool IntersectPixel  (Piksel çakışması) methodumuzu aşağıdaki gibi hazırlayalım.

protected override void Update(GameTime gameTime)
{
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();
 
    switch (game)
    {
        case 3:
            //-- 3 oyun hakkımız var.
            TEXT = string.Format(TEXT_TEMPLATE, "|||");
            break;
        case 2:
            //-- 2 oyun hakkımız kaldı.
            TEXT = string.Format(TEXT_TEMPLATE, "||");
            break;
        case 1:
            //-- 1 oyun hakkımız kaldı.
            TEXT = string.Format(TEXT_TEMPLATE, "|");
            break;
        case 0:
            //-- 0 oyun hakkı. Oyundan çıkılacak :)
            this.Exit();
            break;
     }
            
    //-- 13 ve 20 birim arasında rastgele topların hızlarını değiştirelim.
    //   (X koordinatında)
    ballSpeed = random.Next(13, 20);
    ball_1Position.X += ballSpeed;
    ball_2Position.X += (ballSpeed - 3);
    //-- 
 
 
    //-- X koordinatındaki pozisyon 900'den fazla ise toplar X'de tekrar başa dönsün.
    //   ve Y ekseninde toplar rasgele bir pozisyonda başlasın.
    if (ball_1Position.X > 900) {
        ball_1Position.X = -60;
        int ball1_Y = random.Next(ball_1Texture.Height, (480 - ball_1Texture.Height));
        ball_1Position.Y = ball1_Y;
    }
    if (ball_2Position.X > 900)
    {
        ball_2Position.X = -60;
        int ball2_Y = random.Next(ball_2Texture.Height, (480 - ball_1Texture.Height));
        ball_2Position.Y = ball2_Y;
    }
 
    line1Position.X += 10;
    line2Position.X += 10;
    if (line1Position.X > -2)
    {
        line1Position.X = -800;
        line2Position.X = -800;
    }
        
    //-- Ekrana dokunulduysa hangi pozisyona dokunulduğu bilgisi alınır.
    //   ve aracı o yönde ve mesafeye göre hızı ayarlanarak aracın X,Y değerleri arttırılır.
    TouchCollection touchCollection = TouchPanel.GetState();
    if (touchCollection.Count > 0) {
        TouchLocation t1 = touchCollection[0];
        double x = t1.Position.X - (carPosition.X + (carTexture.Width / 2));
        double y = t1.Position.Y - (carPosition.Y + (carTexture.Height / 2));
        double speed = Math.Sqrt(x * x + y * y) / 10;
        double angle = (float)Math.Atan2(y, x);
        carPosition.X += (float)(speed * Math.Cos(angle));
        carPosition.Y += (float)(speed * Math.Sin(angle));
    }
 
 
    //-- Per-Pixel Collision Detaction kod satırları
    Rectangle carRectangle = new Rectangle((int)carPosition.X, (int)carPosition.Y, carTexture.Width, carTexture.Height);
    Rectangle ball1Rectangle = new Rectangle((int)ball_1Position.X, (int)ball_1Position.Y, ball_1Texture.Width, ball_1Texture.Height);
    Rectangle ball2Rectangle = new Rectangle((int)ball_2Position.X, (int)ball_2Position.Y, ball_2Texture.Width, ball_2Texture.Height);
    if (IntersectPixels(carRectangle, carTextureData, ball1Rectangle, ball1TextureData) || IntersectPixels(carRectangle, carTextureData,ball2Rectangle, ball2TextureData))
    {
        collisionDetected = true;
    }
    else {
        if (collisionDetected) {
            //Oyun hakkımızı 1 azaltıyoruz
            game--;
        }
        collisionDetected = false;
    }
    //--
 
    base.Update(gameTime);
}
 
static bool IntersectPixels(Rectangle rectangleCar, Color[] dataCar,
                           Rectangle rectangleBall, Color[] dataBall)
{
    int top = Math.Max(rectangleCar.Top, rectangleBall.Top);
    int bottom = Math.Min(rectangleCar.Bottom, rectangleBall.Bottom);
    int left = Math.Max(rectangleCar.Left, rectangleBall.Left);
    int right = Math.Min(rectangleCar.Right, rectangleBall.Right);
 
    for (int y = top; y < bottom; y++)
    {
        for (int x = left; x < right; x++)
        {
            Color colorA = dataCar[(x - rectangleCar.Left) +
                                 (y - rectangleCar.Top) * rectangleCar.Width];
            Color colorB = dataBall[(x - rectangleBall.Left) +
                                 (y - rectangleBall.Top) * rectangleBall.Width];
 
            // Eğer her 2 piksel rengide transparent dan farklı ise kesişme var
            if (colorA.A != 0 && colorB.A != 0)
            {
                return true;
            }
        }
    }
    // Kesişme bulunamadı ise false
    return false;
}
Oyunumuzda aracımızı hareket ettirirken TouchCollection nesnesinden  faydalandık. Eğer istersek Windows Phone 7 cihazlarında bulunan Akselerometreden  faydalanarak da aracımızı hareket ettirebiliriz.  Akselerometre ile ilgili  detaylı bilgiye  buradan erişilebilir.

Son olarak Draw override methodumuzda nesnelerimizi ekrana çizdirecek olan C# kodlarımızı yazarak makalemizi sonlandıralım. Eğer kodları ilgili methodlarımızın içerisine yazıp F5 ile Windows Phone 7 emulator üzerinde çalıştıracak olursak aşağıdaki ekran görüntüsünü elde edeceğiz.


protected override void Draw(GameTime gameTime)
{
    if(collisionDetected)
        GraphicsDevice.Clear(Color.Red);
    else
       GraphicsDevice.Clear(Color.White);
 
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    spriteBatch.Draw(carTexture, carPosition, collisionDetected ? Color.Red : Color.White);
    spriteBatch.End();
 
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    spriteBatch.Draw(ball_1Texture, ball_1Position, Color.White);
    spriteBatch.End();
 
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    spriteBatch.Draw(ball_2Texture, ball_2Position, Color.White);
    spriteBatch.End();
 
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    spriteBatch.DrawString(textFont, TEXT, textPosition, Color.Blue);
    spriteBatch.End();
 
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    spriteBatch.Draw(line1Texture, line1Position, Color.White);
    spriteBatch.End();
 
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    spriteBatch.Draw(line2Texture, line2Position, Color.White);
    spriteBatch.End();
 
    base.Draw(gameTime);
}























Windows Phone 7 de keyifli oyunlar... Kolay gelsin.

gokhanmanduz.blogspot.com
gokhanmanduz@hotmail.com
gmanduz@gmail.com