Windows Phone 8 - XNA Oyunu / PlaneHunter

Bu yazımı okumadan önce Windows Phone ve XNA konusundaki diğer makalelerimi okumanızı öneririm.

Her zamanki gibi önce görseller;

PlaneHunter : BackgroundPlaneHunter : CoinPlaneHunter : Target1PlaneHunter : Target2PlaneHunter : Target3PlaneHunter : Target4PlaneHunter : Target5PlaneHunter : Target6PlaneHunter : Target7

Bir tane de Sprite Font dosyamız var, HitCountFont.spritefont ismini verdiğim dosyanın, “yorum satırları kaldırılmış halini” aşağıdaki gibi düzenledim;

<?xml version=”1.0” encoding=”utf-8”?> <XnaContent xmlns:Graphics=”Microsoft.Xna.Framework.Content.Pipeline.Graphics”> <Asset Type=”Graphics:FontDescription”> <FontName>Segoe UI Mono</FontName> <Size>14</Size> <Spacing>0</Spacing> <UseKerning>true</UseKerning> <Style>Regular</Style> <CharacterRegions> <CharacterRegion> <Start> </Start> <End>~</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent></pre>

İlk olarak XNA Game Studio 4.0 grubundaki Windows Phone Game şablonundan PlaneHunter isimli projeyi oluşturalım;

Windows Phone : XNA Game Project Template

Game1.cs dosyasının ismini GameLoop.cs olarak değiştirdikten sonra, Target isminde yeni bir class ekleyelim;

public class Target
{
    public Texture2D Texture;

    public Vector2 Position;

    public Vector2 Velocity;

    public Rectangle Area;

    public SpriteEffects Effect;

    public bool IsAlive;

    public Target(Texture2D texture)
    {
        this.Texture = texture;

        this.IsAlive = true;

        this.Area = new Rectangle(0, 0, texture.Width, texture.Height);
    }
}

Target sınıfı sayesinde, ekrana getireceğimiz hedef‘lerin ekranın neresinden çıkıp, hangi yöne doğru gideceğini, hedefin vurulup/vurulmadığını, çarpışma testi yapabilmek için ekranda kapladığı alanı ve hızını bileceğiz.

GameLoop sınıfına geri dönelim ve sınıf seviyesindeki değişkenlere aşağıdakileri ekleyelim;

private const int PENCERE_GENISLIK = 800;
private const int PENCERE_YUKSEKLIK = 480;

private Texture2D backgroundTexture;

private readonly Texture2D[] targetTextures = new Texture2D[7];

private Texture2D coinTexture;
private Rectangle coinPart;
private int coinPartIndex = 0;

private SpriteFont hitCountFont;
private int targetHitCount = 0;

private readonly List targetList = new List();

private TimeSpan lastTargetSpawnTime = TimeSpan.Zero;

private readonly Rectangle screenArea = new Rectangle(0, 0, PENCERE_GENISLIK, PENCERE_YUKSEKLIK);

private readonly Random r = new Random();

Yukarıdaki kodlar için daha önce yazmış olduğum Windows Phone ve XNA konusundaki diğer makalelerimi okumanızı öneririm.

GameLoop sınıfının constructor‘ında aşağıdaki atama işlerini yapalım;

graphics.PreferredBackBufferWidth = PENCERE_GENISLIK;
graphics.PreferredBackBufferHeight = PENCERE_YUKSEKLIK;
graphics.IsFullScreen = true;

graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;

SupportedOrientations özelliğine DisplayOrientation enum’ından LandscapeLeft ve LandscapeRight değerlerini atayarak, oyun ekranının telefon dikey değil, yatay tutulduğunda düzgün görüntüleneceğini tanımlamış olduk.

LoadContent method’unda Texture2D tipindeki değişkenlerimize değer atayalım;

backgroundTexture = Content.Load<Texture2D>("PlaneHunterBackground");

coinTexture = Content.Load<Texture2D>("PlaneHunterCoin");
hitCountFont = Content.Load<SpriteFont>("PlaneHunterHitCountFont");

for (int i = 0; i < targetTextures.Length; i++)
{
    targetTextures[i] = Content.Load<Texture2D>("PlaneHunterTarget" + (i + 1));
}

Update method’unda son hedef üretilme zamanından itibaren yeterli süre geçmişse, yeni hedef oluşturmamız lazım;

</pre><pre class="brush:csharp">lastTargetSpawnTime += gameTime.ElapsedGameTime;

if (lastTargetSpawnTime > TimeSpan.FromMilliseconds(1500)) { Target t = new Target(targetTextures[r.Next(0, 7)]); switch (r.Next(0, 4)) { case 0: default: t.Position = new Vector2(r.Next(100, 300), 480); t.Velocity = new Vector2((float)r.NextDouble() * 8 + 2, (float)r.NextDouble() * -4 - 2); t.Effect = SpriteEffects.FlipHorizontally; break; case 1: t.Position = new Vector2(r.Next(500, 700), 480); t.Velocity = new Vector2((float)r.NextDouble() * -8 - 2, (float)r.NextDouble() * -4 - 2); t.Effect = SpriteEffects.None; break; case 2: t.Position = new Vector2(r.Next(100, 300), 0); t.Velocity = new Vector2((float)r.NextDouble() * 8 + 2, (float)r.NextDouble() * 4 + 2); t.Effect = SpriteEffects.FlipHorizontally; break; case 3: t.Position = new Vector2(r.Next(500, 700), 0); t.Velocity = new Vector2((float)r.NextDouble() * -8 - 2, (float)r.NextDouble() * 4 + 2); t.Effect = SpriteEffects.None; break; }

targetList.Add(t);

lastTargetSpawnTime = TimeSpan.Zero; }</pre>

Yeni hedef ekleyeceğimiz zaman öncelikle hedef’i ekranın dört köşesinden rastgele birtanesine koyup, rastgele hız veriyoruz. Böylece hedefler ekranın rastgele bir yerinden çıkıp rastgele hızla rastgele bir yöne doğru hareket edecek.

Update method’unda o esnada ekranda olan hedeflerin yerlerini ve ekranda kapladıkları alanı güncelliyoruz;

foreach (Target hedef in targetList)
{
    hedef.Position += hedef.Velocity;

    hedef.Area.X = (int)hedef.Position.X;
    hedef.Area.Y = (int)hedef.Position.Y;
}

Windows Phone oyunlarında, oyuncunun ekrana dokunduğu noktaların listesini TouchPanel sınıfının static GetState methodundan dönen TouchCollection ile alabilmekteyiz.

TouchCollection koleksiyonunun her bir elemanı TouchLocation tipindedir, State özelliğinin TouchLocationState enum’ından Pressed değerinde olduğunu kontrol ederek, ilgili noktaya dokunulduğu durumu yakalayabilir, eğer bir hedef ile kesişiyorsa, hedefi öldürebiliriz;

TouchCollection tc = TouchPanel.GetState();
foreach (TouchLocation tl in tc)
{
    if (tl.State == TouchLocationState.Pressed)
    {
        Rectangle touchArea = new Rectangle((int)tl.Position.X, (int)tl.Position.Y, 1, 1);

        foreach (Target hedef in targetList)
        {
            if (hedef.Area.Intersects(touchArea))
            {
                hedef.Velocity = Vector2.Zero;

                hedef.IsAlive = false;

                targetHitCount++;
            }
        }
    }
}

Update method’unda, ekran sınırları dışına çıkan hedefleri listeden çıkartmalıyız. Eğer ekran dışına çıkan hedefleri listeden temizlemezsek, liste zamanla çok büyüyecek, telefonun kısıtlı olan hafızasını dolduracak ve oyunun önce yavaşlamasına sonra kapanmasına yol açabilecektir.

foreach (Target hedef in targetList)
{
    if (!hedef.Area.Intersects(screenArea))
    {
        targetList.Remove(hedef);

        break;
    }
}

Draw method’unda elimizdeki arkaplan görselini, hayatta olan hedefleri, hayatta olmayan hedefler için kendi etrafında dönen altın görselini ve toplam kaç hedefin vurulduğunu gösteren skor‘u ekrana çizdireceğiz;

spriteBatch.Begin();

spriteBatch.Draw(backgroundTexture, Vector2.Zero, Color.White);

foreach (Target hedef in targetList)
{
    if (hedef.IsAlive)
    {
        spriteBatch.Draw(hedef.Texture, hedef.Position, null, Color.White, 0, Vector2.Zero, 1, hedef.Effect, 0);
    }
    else
    {
        spriteBatch.Draw(coinTexture, hedef.Position, coinPart, Color.White);
    }
}

spriteBatch.DrawString(hitCountFont, "Hit Count : " + targetHitCount, new Vector2(10, 10), Color.Black);
spriteBatch.DrawString(hitCountFont, "Hit Count : " + targetHitCount, new Vector2(9, 9), Color.White);

spriteBatch.End();


Oyunumuzdan bir kare;

![Windows Phone - XNA Oyunu : PlaneHunter](/assets/uploads/2013/02/PlaneHunter-2.png)

Oyunun kodlarını buradan indirebilirsiniz.

blog comments powered by Disqus

Engin Polat hakkında

Senior Software Engineer, @Microsoft

Ada ve Ege'nin babası ;)

Kategoriler

İstatistik

Makale Adedi: 484

Creative Commons Lisansı