Arşiv

XNA kategorisi için arşiv

XNA oyunlarında saniyedeki kare sayısını (FPS) hesaplamak

15 Temmuz 2011 1 yorum

Hemen her oyun’da, saniyede çizilen ekran sayısı (FPSFrame Per Second) bilgisi oyun penceresinde gösterilir.

FPS nedir?

FPS, oyunun bir saniye içerisinde oyun penceresine çizebildiği ekran sayısıdır. Bizim animasyon olarak gördüğümüz akışlar aslında ardı ardına hızlı bir şekilde çizilen tek kare resimlerdir.

Sinema filmleri bile aslında ekranda sırayla gösterilen tek kare resimlerden oluşmaktadır.

İnsan gözü, saniye 25 kare’den büyük hızlarda tek kare resimleri, bir akış olarak algılar.

XNA Framework, saniyede 60 kare ekran çizmeye çalışır.

Oyunlarımızda FPS nasıl hesaplarız?

Formül aslında basit, Draw() method’unda bir değişkenin değerini 1 arttıracağız. Update() method’unda her saniye geçişinde değişkeni sıfırlayacağız.

FPS bilgisini ekranda nasıl gösteririz?

Draw() method’unda değişkenin değerini ekranda gösterirsek, bir saniye içerisinde kaç defa Update() method’unun çalıştırıldığını, yani saniyedeki ekran sayısını (FPS) göstermiş oluruz.

Şimdi yazacağımız örnek projede, klavyeden boşluk (space) tuşuna basıldığında FPS bilgisini ekranda göstereceğiz, tekrar basıldığında gizleyeceğiz.

Örnek proje

Öncelikle sınıf seviyesinde değişkenlerimizi oluşturalım;

int SaniyedeKareSayisi = 0;
double FPS = 0;
bool DetayGoster = false;
KeyboardState pks;

Update() method’unda FPS hesaplayalım;

KeyboardState ks = Keyboard.GetState();

if (pks.IsKeyDown(Keys.Space) && ks.IsKeyUp(Keys.Space))
{
	DetayGoster = !DetayGoster;
}

if (DetayGoster && gameTime.TotalGameTime.Milliseconds == 0)
{
	FPS = SaniyedeKareSayisi;
	SaniyedeKareSayisi = 0;
}

pks = ks;

Oyun penceresinde yazı gösterebilmek için, SpriteFont sınıfından bir değişkene ihtiyacımız olacak.

SpriteFont Arial;

Content projemize Arial isminde bir SpriteFont dosyası ekleyelim.

XNA Oyun Programlama - FPS Hesaplama / SpriteFont Ekleme Dialog Kutusu

<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">
    <FontName>Arial</FontName>
    <Size>24</Size>
    <Spacing>0</Spacing>
    <UseKerning>true</UseKerning>
    <Style>Regular</Style>
    <CharacterRegions>
      <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>

LoadContent() method’unda Arial değişkenimize değer atayalım;

Arial = Content.Load("Arial");

Draw() method’unda hesaplanan FPS bilgisini ekranda gösterelim;

spriteBatch.Begin();

if (DetayGoster)
{
	SaniyedeKareSayisi++;

	spriteBatch.DrawString(Arial, "FPS : " + FPS.ToString(), new Vector2(50, 50), Color.Black);
}

spriteBatch.End();

Artık oyun penceremizde saniyedeki ekran sayısını (FPS) gösterebiliriz.

XNA Oyun Programlama - FPS Hesaplama Sonucu

Oyunun kodlarını indirmek için tıklayınız.

XNA ile Karakter Hareketi

11 Temmuz 2011 3 yorum

Bu yazımı okumadan önce XNA ile Oyun Programlama konulu diğer yazılarımı okumanızı tavsiye ederim.

Her oyunda bir ana karakter vardır ve biz oyuncular olarak, bu ana karakteri mouse/klavye/joystik aracılığıyla oyun evreninde kontrol ederiz.

Bu yazımda önce bir oyun evreni oluşturacak, sonra da ana oyun karakterini bu evrende hareket ettireceğiz.

Önce oyunda kullanacağımız görselleri bilgisayarımıza indirelim;

XNA ile Oyun Programlama - Karakter Hareket Çim ArkaPlan XNA ile Oyun Programlama - Karakter Hareket Sprite

Öncelikle arkaplan ve ana karakterimiz için sınıf seviyesinde değişkenlerimizi oluşturalım;

Texture2D KarakterSprite;
Texture2D ArkaplanCim;

Klavye bilgisini alabilmek için KeyboardState sınıfından bir değişken ekleyelim;

KeyboardState ks;

Vector2 sınıfından bir değişken ile, ana karakterimizin oyun penceresinde konumunu tutalım;

Vector2 KarakterKonum;

Oyun sınıfının constructor‘ında (Game1 ismini GameLoop olarak değiştirelim) KarakterKonum değişkenine başlangıç değerini verelim;

public GameLoop()
{
	graphics = new GraphicsDeviceManager(this);
	Content.RootDirectory = "Content";

	KarakterKonum = new Vector2(50, 50);
}

LoadContent() method’unda ArkaplanCim ve KarakterSprite değişkenlerine değer atayalım;

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);

	KarakterSprite = Content.Load<Texture2D>("KarakterSprite");
	ArkaplanCim = Content.Load<Texture2D>("Cim");
}

KarakterSprite görselinde, karakterimizin dört yöne yapacağı hareketlerin tek tek görüntüleri yer alıyor.

Karakterimize yürüme efekti vermek için, aslında birden fazla görselden oluşan tek bir görsel kullanıyoruz. (Böylece hafızaya tek bir görsel yüklendiği için hafıza kullanımını az tutmuş oluyoruz)

Sırayla görselin herbir parçasını ekranda karakterimiz olarak çizdiğimizde, karaktere yürüme efektini vermiş oluyoruz.

Bir Sprite‘ın bir parçasını ekrana çizdirmek için, SpriteBatch sınıfının Draw() method’undan faydalanırız;

SpriteBatch.Draw(Texture2D, Vector2, Rectangle, Color);

Bu method’un parametreleri;

  • Texture2D, ekrana çizdireceğimiz görselin bulunduğu görseli yüklediğimiz değişken
  • Vector2, görselin ekranda çizileceği konum
  • Rectangle, görselin ekrana çizilecek parçası (Left-Top-Width-Height isminde dört özelliği vardır)
  • Color, görsele uygulanacak baskın renk değişkeni

Sınıf seviyesine aşağıdaki değişkenleri ekleyerek devam edelim;

Rectangle Arkaplan;
Rectangle Karakter;

int Kare = 0;

int Yon = 0;

GameLoop sınıfının constructor‘ına;

Arkaplan = new Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);

ekleyelim. Böylece, arkaplan görselinin çizileceği alanı tanımlamış olduk.

Oyun kodlarımızın en önemli iki methodundan biri, Update() method’udur;

ks = Keyboard.GetState();

if (ks.IsKeyDown(Keys.Escape))
{
	this.Exit();
}

if (ks.IsKeyDown(Keys.Down))
{
	Yon = 0;
	Kare++;
	KarakterKonum.Y += 3;
}
else if (ks.IsKeyDown(Keys.Up))
{
	Yon = 1;
	Kare++;
	KarakterKonum.Y -= 3;
}
else if (ks.IsKeyDown(Keys.Left))
{
	Yon = 2;
	Kare++;
	KarakterKonum.X -= 3;
}
else if (ks.IsKeyDown(Keys.Right))
{
	Yon = 3;
	Kare++;
	KarakterKonum.X += 3;
}

if (Kare > 5)
{
	Kare = 0;
}

Her bir yön tuşuna basıldığında Yon değişkenini farklı bir değere atadık. Ayrıca Kare değişkeninin değerini de bir artırdık.

KarakterSprite görselinde karakter’in yürüme efekti için yana doğru 6 görsel kullanıldığını görüyoruz. Kare değişkeni 5 değerinden büyük bir değere ulaştığında 0 değerine eşitleyip değişkeni sıfırlamış oluyoruz.

Her yön tuşuna basıldığında Yon değişkenine verdiğimiz değer, KarakterSprite görselinde ilgili tuşa ait yönün yukarıdan aşağıya sıra numarası aynı zamanda.

Son adımda, Draw() method’unu yazıyoruz;

GraphicsDevice.Clear(Color.CornflowerBlue);

Karakter = new Rectangle(Kare * 24, Yon * 32, 24, 32);

spriteBatch.Begin();

spriteBatch.Draw(ArkaplanCim, Arkaplan, Color.White);
spriteBatch.Draw(KarakterSprite, KarakterKonum, Karakter, Color.White);

spriteBatch.End();

Öncelikle Rectangle tipinde Karakter değişkenine, KarakterSprite değişkeninin hangi parçasını ekrana çizmek istediğimizi belirteceğimiz değeri atıyoruz.

Sonra arkaplan ve karakterimizi oyun penceresine çizdiriyoruz.

XNA ile Oyun Programlama - Karakter Hareket Oyun Ekranı

Oyunun kodlarını indirmek için tıklayınız.

XNA ile Video Oynatmak

01 Temmuz 2011 Yorum yapılmamış

Bu yazıyı okumadan önce XNA ile Oyun Programlama kategorisindeki diğer yazılarımı okumanızı tavsiye ederim.

Belli bir seviyenin üstündeki her oyunda, oyunu tanıtan, oyuna alışmanızı sağlayan, oyunun hikayesini anlatan veya bölümler arasında bilgi veren videolar vardır.

Oyunun kullanıcılarına, oyunun konusunu okumaları için sayfalarca yazı vermektense, kısa bir video ile anlatmak her zaman daha iyidir.

XNA ile bir video nasıl oynatılır?

Öncelikle oyunumuza ekleyeceğimiz video’nun formatına dikkat etmemiz gerekiyor. Varsayılan olarak, XNA Content Pipeline sadece WMV formatındaki videoları destekler.

XNA ile Oyun Programlama - Content Pipeline Desteklenen Video Formatları

Bir video’yu oyun penceresinde oynatmak için, öncelikle Video sınıfından bir instance‘a oynatacağımız video‘yu yüklememiz lazım.

Daha sonra, XNA Framework ile birlikte gelen VideoPlayer sınıfından bir instance ile, yüklediğimiz video‘yu oynatmaya başlayabiliriz.

Sınıf seviyesinde iki değişken tanımlayarak işe başlıyoruz;

Video v;
VideoPlayer vp;

LoadContent() method’unda değişkenlerimize değer atamalarını yapıyoruz;

v = Content.Load<Video>("OyunBaslangicVideo");
vp = new VideoPlayer();

vp.Play(v);

VideoPlayer tipindeki değişkenin Play() method’unu çağırarak video dosyamızın oynatılmasını sağlıyoruz.

Artık tek yapmamız gereken, Draw() method’unda, oynatılan video‘yu kare kare oyun penceresine çizmek;

spriteBatch.Begin();

spriteBatch.Draw(vp.GetTexture(), Vector2.Zero, Color.Blue);

spriteBatch.End();

XNA, VideoPlayer sınıfının GetTexture() method’u ile oynatılan video’nun o anki karesini Texture2D tipinde kullanmamızı sağlar.

SpriteBatch sınıfının Draw() method’u sayesinde ilgili video karesini oyun penceresine çizebiliriz.

XNA ile Hareketli Arkaplan

29 Haziran 2011 Yorum yapılmamış

Bu yazımı okumadan önce XNA ile Oyun Programlama kategorisindeki diğer makalelerimi okumanızı tavsiye ederim.

Birçok oyunda arkaplan görselinin yavaşça kaydığını görmüşüzdür. Bu tarz bir etkiyi kendi oyunlarımızda nasıl sağlayacağımızı bu yazımda inceliyor olacağım.

Her zamanki gibi öncelikle görselleri paylaşarak başlıyorum;

XNA ile Oyun Programlama - Uzay Arkaplan Resmi XNA ile Oyun Programlama - Uçak Resmi

Öncelikle sınıf seviyesinde birkaç değişken/sabit (readonly, const) ile oyun penceremizin özelliklerini ayarlayalım;

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

private readonly Color PENCERE_ARKAPLAN = Color.Black;

Oyun sınıfının constructor‘ında ilgili sabitleri kullanalım;

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

Oyun projemize, Uzay ve Ucak isimli iki tane sınıf (class) ekleyelim. Böylece arkaplan’da kayacak uzay görseli ile ilgili işleri Uzay sınıfında, klavye ile yöneteceğimiz uçak ile ilgili işleri de Ucak sınıfında gerçekleştirebileceğiz.

Ucak.cs dosyasında yeralan Ucak sınıfının yapacağı işler çok basit;

public class Ucak
{
	public Vector2 Position;
	public readonly Vector2 Size;

	public Texture2D Texture { get; private set; }

	public Ucak(Texture2D Texture)
	{
		this.Texture = Texture;
		this.Size = new Vector2(Texture.Width, Texture.Height);
	}
}

Uzay.cs dosyasında yeralan Uzay sınıfı ise, kendi Update() ve Draw() method’larına sahip olacak. Draw() method’unda sürekli arkaplan görselini kaydırarak oyun penceresine çizdirecek.

Bunu yapabilmek için, SpriteBatch sınıfından bir instance’a ihtiyaç duyacağız.

Uzay sınıfının constructor‘ında ihtiyacımız olacak SpriteBatch tipinden bir parametre alacak ve sınıf seviyesinde bir değişkende saklayacağız;

public class Uzay
{
	private readonly Texture2D _Texture;
	private readonly Texture2D _TextureSecond;

	private float _Position = 0f;

	private readonly int _Width;

	private readonly SpriteBatch _SpriteBatch;

	public Uzay(Texture2D Texture, SpriteBatch SpriteBatch)
	{
		this._SpriteBatch = SpriteBatch;

		this._Texture = Texture;
		this._TextureSecond = Texture;

		this._Width = _Texture.Width;

		this._Position = 0;
	}

	public void Update()
	{
		_Position -= 0.5f;

		if (_Position < -_Width)
		{
			_Position += _Width;
		}
	}

	public void Draw()
	{
		_SpriteBatch.Draw(_Texture, new Vector2(_Position, 0), Color.White);
		_SpriteBatch.Draw(_TextureSecond, new Vector2(_Position + _Width, 0), Color.White);
	}
}

Uzay sınıfının Update() method'unda, arkaplan görselini sürekli 0.5 birim sola kaydırıyoruz.

Not : Arkaplan görseli kaydıkça ekrandan dışarı çıkıyor olacak, arkaplansız kalmamak için aslında ekrana yanyana iki tane arkaplan görseli çizdiriyoruz.

Gelelim bu sınıflardan değişkenlerimizi oluşturmaya ve oyun sınıfımıza eklemeye;

private Uzay Arkaplan;
private Ucak SavasUcagi;

protected override void LoadContent()
{
	spriteBatch = new SpriteBatch(GraphicsDevice);

	Arkaplan = new Uzay(Content.Load<Texture2D>("Uzay"), spriteBatch);
	SavasUcagi = new Ucak(Content.Load<Texture2D>("Ucak"));
	SavasUcagi.Position = new Vector2(50, PENCERE_YUKSEKLIK / 2);
}

Oyunumuzun Update() method’unda, klavye tuş vuruş durumlarına göre uçağımızı kontrol ediyor olacağız. Ayrıca Arkaplan değişkeninde bulunan Uzay sınıfının instance‘ından Update() method’unu çağırıyor olacağız. Böylece arkaplan görseli 0.5 birim sola kaymış olacak ve oyun penceresine yeni yerinde çizilecek.

protected override void Update(GameTime gameTime)
{
	KeyboardState ks = Keyboard.GetState();

	if (ks.IsKeyDown(Keys.Escape))
		this.Exit();

	if (ks.IsKeyDown(Keys.Left))
		SavasUcagi.Position.X -= (ks.IsKeyDown(Keys.LeftShift) || ks.IsKeyDown(Keys.RightShift)) ? 6 : 3;

	if (ks.IsKeyDown(Keys.Right))
		SavasUcagi.Position.X += (ks.IsKeyDown(Keys.LeftShift) || ks.IsKeyDown(Keys.RightShift)) ? 6 : 3;

	if (ks.IsKeyDown(Keys.Up))
		SavasUcagi.Position.Y -= 3;

	if (ks.IsKeyDown(Keys.Down))
		SavasUcagi.Position.Y += 3;

	if (SavasUcagi.Position.X < 0)
		SavasUcagi.Position.X = 0;

	if (SavasUcagi.Position.X > PENCERE_GENISLIK - SavasUcagi.Size.X)
		SavasUcagi.Position.X = PENCERE_GENISLIK - SavasUcagi.Size.X;

	if (SavasUcagi.Position.Y < 0)
		SavasUcagi.Position.Y = 0;

	if (SavasUcagi.Position.Y > PENCERE_YUKSEKLIK - SavasUcagi.Size.Y)
		SavasUcagi.Position.Y = PENCERE_YUKSEKLIK - SavasUcagi.Size.Y;

	Arkaplan.Update();

	base.Update(gameTime);
}

Uçağı sağa/sola yönetmek için kullandığımız ok tuşlarına (LeftRight) basılırken, Shift tuşlarından birine (LeftShiftRightShift) basılıyorsa, uçağı normalin iki katı hızlı ilerletiyoruz (3 birim yerine 6 birim)

Son olarak, Draw() method’unda uçağı ekrana çizdirecek ve Uzay sınıfının instance‘ının (Arkaplan değişkeni) Draw() method’unu çağıracağız;

protected override void Draw(GameTime gameTime)
{
	GraphicsDevice.Clear(PENCERE_ARKAPLAN);

	spriteBatch.Begin();

	Arkaplan.Draw();
	spriteBatch.Draw(SavasUcagi.Texture, SavasUcagi.Position, Color.White);

	spriteBatch.End();

	base.Draw(gameTime);
}

Sonuç olarak, yukarıdaki kodları yazdıktan sonra oyun projemizi başlatırsak;

XNA ile Oyun Programlama - Hareketli Arka Plan Ekran Görüntüsü

Hareketli ArkaPlan oyun projemizin kodlarını buradan indirebilirsiniz.

XNA – Kısa sorular, Kısa cevaplar

28 Haziran 2011 1 yorum

Mouse ikonumu bulamıyorum?

Evet, varsayılan olarak XNA, oyun penceresi içerisinde mouse ikonunu gizler. Eğer mouse’u görmek istiyorsanız;

this.IsMouseVisible = true;

kodunu eklemeniz gerekir.

Oyunu farklı çözünürlükte çalıştırmak istiyorum

XNA ile oyun geliştirmeye çalışan her programcının çok kısa süre sonra merak ettiği bir sorudur bu.

Çözüm;

public Game1()
{
	graphics = new GraphicsDeviceManager(this);

	Content.RootDirectory = "Content";

	/// Oyun penceresinin çözünürlüğünü 1920x1080 ayarlar
	this.graphics.PreferredBackBufferWidth = 1920;
	this.graphics.PreferredBackBufferHeight = 1080;
}

Not : Bu konu ile ilgili XNA – Oyun ekranının boyutlarını belirlemek yazımı okumanızı tavsiye ederim.

Oyunumu tam ekran oynatmak istiyorum

Çözünürlüğü değiştirdikten hemen sonra, tam ekran oyun nasıl oynatılır? sorusu gelir;

protected override void Initialize()
{
	/// Eğer tam ekran değilse, tam ekran moduna geç!
	if (!graphics.IsFullScreen)
	{
		graphics.ToggleFullScreen();
	}

	base.Initialize();
}

Not : Bu konu ile ilgili XNA – Oyun ekranının boyutlarını belirlemek yazımı okumanızı tavsiye ederim.

Oyun penceremin aktif olup/olmadığını nasıl anlarım?

Windows oyunları o anda çalışan tek uygulama olmayabilirler. Oyuncular, oyun oynarken, email adreslerini kontrol etmek, internette bir kelime araştırmak veya diğer oyuncular ile farklı ortamlarda sohbet etmek isteyebilirler.

Oyun penceresinden başka bir uygulamaya geçtiklerinde (yani oyun penceresi focus kaybettiğinde) oynadıkları oyunun durmasını (pause moduna geçmesini) isterler.

Oyun penceresinin Active veya InActive olduğunu kontrol etmemiz, Active değilse, Pause moduna geçmemiz gerekmektedir;

Not : Bu konu ile ilgili XNA Oyunu / Çanakkale Geçilmez – 1 yazımı okumanızı tavsiye ederim.

protected override void Update(GameTime gameTime)
{
	if (this.IsActive)
	{
		/// Sadece oyun penceresi aktif iken yapılması gereken işleri
		/// buraya yazıyoruz

		/// Joystik, klavye, mouse hareketlerini algılamak
		/// Yapay zeka görevlerini yürütmek gibi
	}

	base.Update(gameTime);
}

Oyun penceresinin başlığını nasıl değiştiririm?

protected override void Initialize()
{
	this.Window.Title = "Oyun oynamak içindir!";

	base.Initialize();
}

XNA Oyunu / Çanakkale Geçilmez – 2

27 Haziran 2011 Yorum yapılmamış

XNA ile Oyun Programlama serimize, daha önce başlayıp tamamlamadığımız Çanakkale Geçilmez oyununu tamamlayarak devam ediyoruz.

Bu yazıyı okumadan önce XNA konusunda diğer makaleler için XNA kategorisine bir göz atmanızı tavsiye ederim.

Düşman gemilerine yaptığımız atış miktarını ve isabet adedini tutacağımız iki değişkeni sınıf seviyesinde tanımlayalım;

int SKOR_ATIS = 0;
int SKOR_PUAN = 0;

Oyuna, düşman gemileri ve düşman gemilerine atacağımız gülleler eklemek için, sınıf seviyesinde GameObject sınıfında diziler oluşturacağız;

public GameObject[] Gemiler;
public GameObject[] Gulleler;

Ayrıca, düşman gemilerine ve onlara atacağımız güllelere sınırlama getirmek için iki sabiti (const) sınıf seviyesinde tanımlıyoruz;

public const int DUSMAN_ADET = 3;
public const int GULLE_ADET = 6;

Bu sayede, oyunun zorluk derecesini değiştirebilmek için iki alanımız oluyor; aynı anda savaşacağımız düşman gemisi adedi ve onlara atabileceğimiz gülle adedi. Aynı anda attığımız güllelerden bir tanesi hedefi bulmadan veya ekran dışına çıkmadan yeni gülle atamıyoruz.

Bu oyunda, düşman gemisi adedini 3 olarak, onlara aynı anda atabileceğimiz gülle adedini de 6 olarak belirledik.

LoadContent() methodunda bu değişkenlere başlangıç değerlerini atamalıyız;

Gemiler = new GameObject[DUSMAN_ADET];
for (int iLoop = 0; iLoop < DUSMAN_ADET; iLoop++)
{
	Gemiler[iLoop] = new GameObject(Content.Load<Texture2D>("DusmanGemi"));
}

Gulleler = new GameObject[GULLE_ADET];
for (int iLoop = 0; iLoop < GULLE_ADET; iLoop++)
{
	Gulleler[iLoop] = new GameObject(Content.Load<Texture2D>("Gulle"));
}

Update() method'unda boşluk tuşuna (Space) basılınca gülle atılmasını sağlayacak methodu çağıracak kodu ekliyoruz;

if (pks.IsKeyDown(Keys.Space) && ks.IsKeyUp(Keys.Space))
	GulleAtesle();

GulleAtesle() method’unda 6 gülle’nin herbirine tek tek bakarak, ekranda olmayan var mı kontrol ediyoruz.

Eğer o anda ekranda olmayan bir gülle bulursak (IsAlive özelliğinin false değer içermesinden) düşman gemisine doğru fırlatıyoruz.

Fırlatma işlemini yaparken güllenin fırlatma anındaki yeri ve fırlatma güzergahına dikkat etmeliyiz.

Güllenin Position özelliğine Top’umuzun X ve Y eksenindeki yerini veriyoruz, böylece, gülle tam olarak top’umuzun ucundan fırlatılacak.

Velocity özelliği ile güllenin güzergahını belirliyoruz; Top’umuzun baktığı açının sinus‘unu ve kosinus‘unu alarak, hız sabiti olan 5 değeri ile çarpıyoruz.

Not : Top’umuz ekranın altında, gemiler üstünde olduğu için Y eksenindeki hız sabitini -1 ile çarpıp, -5 değerini elde ediyoruz.

Ayrıca, SKOR_ATIS değişkeninin değerini de bir yükseltiyoruz.

private void GulleAtesle()
{
	foreach (GameObject Gulle in Gulleler)
	{
		if (!Gulle.IsAlive)
		{
			SKOR_ATIS++;
			Gulle.IsAlive = true;
			Gulle.Position = new Vector2(Top.Position.X - Gulle.Center.X, Top.Position.Y);
			Gulle.Velocity = new Vector2((float)Math.Sin(Top.Rotation) * 5, (float)Math.Cos(Top.Rotation) * -5);
			break;
		}
	}
}

Böylece, her boşluk (Space) tuşuna basıldığında 6 gülleden birisi topumuzun ucundan düşman gemisine doğru gönderilecektir. Eğer o anda ekranda 6 gülle varsa, gülle gönderilmeyecektir.

Update() method’unda, düşman gemilerini de yönetiyoruz;

foreach (GameObject Gemi in Gemiler)
{
	if (Gemi.IsAlive)
	{
		Gemi.Position += Gemi.Velocity;

		if (Gemi.Position.X < 0)
			Gemi.IsAlive = false;
	}
	else
	{
		Gemi.IsAlive = true;
		Gemi.Position = new Vector2(PENCERE_GENISLIK, MathHelper.Lerp(0, (float)(PENCERE_YUKSEKLIK * 0.6), (float)Rastgele.NextDouble()));
		Gemi.Velocity = new Vector2(MathHelper.Lerp(-2, -4, (float)Rastgele.NextDouble()), 0);
	}
}

Tüm düşman gemileri (DUSMAN_ADET sabitinden dolayı 3 adet) içerisinde dönen bir foreach döngüsü ile ilgili düşman gemisinin o anda hayatta olup/olmadığına bakıyoruz;

Eğer hayatta ise; gemi'nin yerini, gidiş yönünde hızı kadar ilerletiyoruz. Eğer gemi'nin yeni yeri pencere dışında ise, bir sonraki ekran güncellemesinde gemi'yi tekrar kullanabilmek için öldürüyoruz.

Eğer hayatta değilse; Gemi'yi ekrana çıkartabilmek için hayatta durumuna getiriyoruz, rastgele bir yer ve hız veriyoruz.

Rastgele yer belirlerken, pencerenin en üstünden itibaren %60'lık kısmını kullanıyoruz.

Rastgele hız belirlerken, MathHelper sınıfının Lerp() methodundan faydalanıyoruz.

Lerp() method'u, 3 parametre alır;

  • Değer 1 (Kaynak değer)
  • Değer 2 (Kaynak değer)
  • Büyüklük (0 ile 1 arası değer, Değer 2'nin ağırlığı)

Büyüklük parametresine 0 değer vermek, Değer 1'i, 1 değer vermek, Değer 2'yi döndürür.

Biz bu örnek'te düşman gemilerinin ekranın sağında belirmesi ve ekranın soluna doğru ilerlemesi için, -2 ve -4 arası hız'a sahip olmalarını sağladık.

Update() method'unda, ekrandaki gülleleri de yönetiyoruz;

foreach (GameObject Gulle in Gulleler)
{
	if (Gulle.IsAlive)
	{
		Gulle.Position += Gulle.Velocity;
		if (!Pencere.Contains(Gulle.TextureRectangle))
			Gulle.IsAlive = false;

		foreach (GameObject Gemi in Gemiler)
		{
			if (Gemi.TextureRectangle.Contains(Gulle.TextureRectangle))
			{
				SKOR_PUAN++;
				GemiVurulduSes.Play();
				Gemi.IsAlive = false;
				Gulle.IsAlive = false;
				break;
			}
		}
	}
}

Sadece hayatta olan güllelere teker teker bakıyoruz; (GULLE_ADET sabitinden dolayı en fazla 6 adet)

Gülle’nin yerini, hızı ve doğrultusu kadar ilerletiyoruz. Eğer pencere dışına çıktı ise, gülle’yi öldürüyoruz.

Hayatta olan her gülle için, hayatta olan her gemi ile çarpışma testi yapıyoruz. Eğer ilgili gülle gemi’lerden bir tanesi ile çarpışıyorsa, gülle’yi ve gemi’yi öldürüp, SKOR_PUAN değişkenini bir yükseltiyoruz.

Draw() method’unda, Arkaplan, Top, hayatta olan gemiler ve hayatta olan gülleleri ekrana çizdiriyoruz;

GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

spriteBatch.Draw(Arkaplan, Pencere, Color.White);

spriteBatch.Draw(Top.Texture, Top.Position, null, Color.White, Top.Rotation, Top.Center, 1, SpriteEffects.None, 0);

foreach (GameObject Gemi in Gemiler)
{
	if (Gemi.IsAlive)
	{
		spriteBatch.Draw(Gemi.Texture, Gemi.Position, Color.White);
	}
}

foreach (GameObject Gulle in Gulleler)
{
	if (Gulle.IsAlive)
	{
		spriteBatch.Draw(Gulle.Texture, Gulle.Position, Color.White);
	}
}

Skor tablosunu çizdirebilmek için, sınıf seviyesinde iki yeni değişken eklememiz lazım;

Texture2D Karakterler;
Texture2D Sayilar;

LoadContent() method’unda bu değişkenlere değer atıyoruz;

Karakterler = Content.Load("Karakterler");
Sayilar = Content.Load("Sayilar");

Karakterler imajı’nın (0, 0) noktasından itibaren (113, 25) büyüklüğündeki alanı arasında kalan kısmı ekrana çizdirmek için, Draw() method’una;

spriteBatch.Draw(Karakterler, new Vector2((float)(PENCERE_GENISLIK * 0.05), (float)(PENCERE_YUKSEKLIK * 0.90)), new Rectangle(0, 0, 113, 25), Color.White);

Karakterler imajı’nın (0, 25) noktasından itibaren (113, 25) büyüklüğündeki alanı arasında kalan kısmı ekrana çizdirmek için, Draw() method’una;

spriteBatch.Draw(Karakterler, new Vector2((float)(PENCERE_GENISLIK * 0.75), (float)(PENCERE_YUKSEKLIK * 0.90)), new Rectangle(0, 25, 113, 25), Color.White);

Atış adedimizi ve Skor adedimizi ekrana çizdirmek için gerekli method çağrılarını (DrawAtis(), DrawSkor()) Draw() method’unda yapıyoruz.

private void DrawAtis()
{
	string Atis = SKOR_ATIS.ToString();
	for (int iLoop = 0; iLoop < Atis.Length; iLoop++)
	{
		int Rakam = Convert.ToInt32(Atis.Substring(iLoop, 1));
		spriteBatch.Draw(Sayilar, new Vector2((float)(PENCERE_GENISLIK * 0.05) + (23 * iLoop) + 120, (float)(PENCERE_YUKSEKLIK * 0.90)), new Rectangle(Rakam * 23, 0, 23, 25), Color.White);
	}
}

private void DrawSkor()
{
	string Atis = SKOR_PUAN.ToString();
	for (int iLoop = 0; iLoop < Atis.Length; iLoop++)
	{
		int Rakam = Convert.ToInt32(Atis.Substring(iLoop, 1));
		spriteBatch.Draw(Sayilar, new Vector2((float)(PENCERE_GENISLIK * 0.75) + (23 * iLoop) + 120, (float)(PENCERE_YUKSEKLIK * 0.90)), new Rectangle(Rakam * 23, 0, 23, 25), Color.White);
	}
}

Böylece, oyunumuzun kodlamasını bitirmiş olduk.

XNA ile Oyun Programlama - Çanakkale Geçilmez Oyunu

Oyunu tüm kodlarını buradan indirebilirsiniz.

XNA Oyunu / Çanakkale Geçilmez – 1

11 Mayıs 2011 Yorum yapılmamış

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

Bu sefer yapacağımız oyun, 30 Ağustos‘un ruhuna uygun olacak – Çanakkale Geçilmez.

Önce görsellerimizi verelim;

XNA - Çanakkale Geçilmez - Arkaplan XNA - Çanakkale Geçilmez - Düşman Gemisi XNA - Çanakkale Geçilmez - Top XNA - Çanakkale Geçilmez - Gülle XNA - Çanakkale Geçilmez - Karakterler XNA - Çanakkale Geçilmez - Sayılar

İki tane de ses dosyamız var;

Gemi Vuruldu Sesi

Arkaplan Müziği

Başlayalım oyunumuzu yazmaya; Çanakkale Geçilmez projemizi oluşturduktan ve Game1.cs‘in ismini GameLoop.cs olarak değiştirdikten sonra, class seviyesindeki değişkenlerimizi tanımlayalım;

public const int PENCERE_GENISLIK = 800;
public const int PENCERE_YUKSEKLIK = 600;
public const bool TAM_EKRAN = false;

Texture2D Arkaplan;

Oyun penceremizin sınırlarını tutacağımız Rectangle tipinde bir değişkeni class seviyesindeki değişkenlerimize ekleyelim;

Rectangle Pencere;

Gelelim bu değişkenleri kullanmaya, GameLoop class‘ımızın constructor methodunda aşağıdaki kodları yazalım;

graphics.PreferredBackBufferWidth = PENCERE_GENISLIK;
graphics.PreferredBackBufferHeight = PENCERE_YUKSEKLIK;
graphics.IsFullScreen = TAM_EKRAN;
Pencere = new Rectangle(0, 0, PENCERE_GENISLIK, PENCERE_YUKSEKLIK);

Ses dosyalarımız için class seviyesinde değişkenlerimiz tanımlayalım;

SoundEffectInstance ArkaplanSes;
SoundEffectInstance GemiVurulduSes;

LoadContent() method‘unda değişkenlerimize yükleme işlemlerini gerçekleştirelim;

Arkaplan = Content.Load<Texture2D>("Arkaplan");

ArkaplanSes = Content.Load<SoundEffect>("pong").CreateInstance();
GemiVurulduSes = Content.Load<SoundEffect>("ir_begin").CreateInstance();

ArkaplanSes.IsLooped = true;
ArkaplanSes.Volume = 0.3f;
ArkaplanSes.Play();

Ses dosyaları ile yaptığımız bu işlemleri XNA ile Pong oyunu yazalım – 2 yazımdan hatırlayacaksınız.

Bu oyunumuzda aktörleri Nesne Yönelimli Programlama (Object Orriented Programming) kurallarına uyarak oluşturalım; oyun projemize yeni bir class ekleyelim ve ismini GameObject.cs verelim.

Çanakkale Geçilmez oyunumuzda oluşturacağımız tüm nesneleri GameObject class’ından oluşturacağız.

public class GameObject
{
	public bool IsAlive;
	public Texture2D Texture;
	public Vector2 Position;
	public Vector2 Center;
	public Vector2 Velocity;
	public float Rotation;

	private Rectangle _TextureRectangle;
	public Rectangle TextureRectangle
	{
		get
		{
			_TextureRectangle.X = (int)Position.X;
			_TextureRectangle.Y = (int)Position.Y;

			return _TextureRectangle;
		}
	}

	public GameObject(Texture2D Texture)
	{
		this.IsAlive = false;
		this.Texture = Texture;
		this.Center = new Vector2(Texture.Width / 2, Texture.Height / 2);
		this.Position = Vector2.Zero;
		this.Velocity = Vector2.Zero;
		this.Rotation = 0;

		this._TextureRectangle = new Rectangle(0, 0, Texture.Width, Texture.Height);
	}
}

Yukarıdaki kodu incelediğimizde;

  • Oyun nesnesinin hala canlı olup olmadığını anlamak için IsAlive değişkeninin olduğunu
  • Oyun nesnesinin Texture‘unu almak için Texture nesnesinin olduğunu
  • Oyun nesnesinin ekrandaki yerini almak için Position değişkeninin olduğunu
  • Oyun nesnesinin kendi etrafında dönüş miktarını almak için Center ve Rotation değişkenlerinin olduğunu
  • Oyun nesnesinin hızını almak için Velocity değişkeninin olduğunu
  • Oyun nesnesinin ekranda kapladığı alanı bulmak için TextureRectangle özelliğinin olduğunu görürüz.

GameObject sınıfının constructor‘ı sadece bir Texture2D nesnesi alıyor ve bu parametreyi kendi iç değişkenlerinin değerlerini hesaplamada kullanıyor.

Artık GameLoop sınıfımızın class seviyesinde, Top nesnesi için, GameObject sınıfından değişken oluşturabiliriz.

public GameObject Top;

LoadContent() methodunda bu değişkenin atamasını yapalım;

Top = new GameObject(Content.Load<Texture2D>("Top"));

Update() methodunda Top nesnemizin yerini hesaplayalım;

KeyboardState ks = Keyboard.GetState();

if (ks.IsKeyDown(Keys.Left))
	Top.Position.X -= 5;
if (ks.IsKeyDown(Keys.Right))
	Top.Position.X += 5;

Aynı method içerisinde Top nesnemizin dönüş miktarını da hesaplayabiliriz; (Yukarı ve Aşağı tuşlarına basıldıkça 10 derece dönmesini sağlayacağız)

if (ks.IsKeyDown(Keys.Up))
	Top.Rotation += MathHelper.ToRadians(10);
if (ks.IsKeyDown(Keys.Down))
	Top.Rotation -= MathHelper.ToRadians(10);

MathHelper sınıfının static ToRadians fonksiyonu sayesinde, Top’umuzun dönme açısını radian cinsinden kolaylıkla hesaplayabiliyoruz.

Top‘umuzun dönme açısının -90 ile +90 dereceler arasında olduğundan ve ekrandan dışarı çıkmayıp, görünür olduğundan emin olalım;

if (Top.Position.X < 0)
	Top.Position.X = 0;
if (Top.Position.X > PENCERE_GENISLIK)
	Top.Position.X = PENCERE_GENISLIK;

if (Top.Rotation > MathHelper.ToRadians(90))
	Top.Rotation = MathHelper.ToRadians(90);
if (Top.Rotation < MathHelper.ToRadians(-90))
	Top.Rotation = MathHelper.ToRadians(-90);

Oyunun, Windows'ta başka bir pencereye geçilmesi esnasında (focus kaybetme) bekleme moduna geçmesini, tekrar oyuna dönüldüğünde ise (focus alma), kaldığı yerden devam etmesini sağlayalım;

Öncelikle sınıf seviyesindeki değişkenlerimize,

bool OYUN_DEVAM_EDIYOR = true;

ekliyoruz. Microsoft.Xna.Framework.Game sınıfından türetildiği için GameLoop sınıfının OnActivated ve OnDeactivated method’larını override edebiliriz.

OnDeactivated method’unda, oyun penceresinin focus kaybettiğini, OnActivated method’unda ise, oyun penceresinin focus aldığını anlayabiliriz.

protected override void OnActivated(object sender, EventArgs args)
{
  OYUN_DEVAM_EDIYOR = true;
  ArkaplanSes.Resume();
  base.OnActivated(sender, args);
}

protected override void OnDeactivated(object sender, EventArgs args)
{
  OYUN_DEVAM_EDIYOR = false;
  ArkaplanSes.Pause();
  base.OnDeactivated(sender, args);
}

Şu ana kadar oyunumuza hiç düşman eklemedik. Bir sonraki yazımda düşmanları ekleyeceğiz.

Top’umuzla da düşmanları vurup, puan kazanıyor olacağız.

XNA ile Pong oyunu yazalım – 2

11 Mart 2010 Yorum yapılmamış

XNA ile Pong oyunu yazalım – 1 yazıma bu yazı ile devam ediyorum.

XNA ile ekrana yazı yazdırmak için öncelikle Content projesi içerisinde bir spritefont dosyası oluşturmalıyız.

Pong projesi için ben Arial.spritefont ismini verdiğim dosyayı oluşturdum, “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>Arial</FontName>
		<Size>16</Size>
		<Spacing>0</Spacing>
		<UseKerning>true</UseKerning>
		<Style>Regular</Style>
		<CharacterRegions>
			<CharacterRegion>
				<Start>&#32;</Start>
				<End>&#126;</End>
			</CharacterRegion>
		</CharacterRegions>
	</Asset>
</XnaContent>

Gördüğünüz gibi, font dosyası aslında font’un özelliklerini yazdığımız bir Xml dosyası.

GameLoop sınıfının değişkenlerine Arial font dosyamızı yükleyeceğimiz değişkeni de ekleyelim;

SpriteFont Arial;

LoadContent() method’unda Arial.spritefont dosyasını Arial değişkenine yükleyelim;

Arial = Content.Load<SpriteFont>("Arial");

Şimdi Draw() method‘una aşağıdaki kodları ekleyerek, oyuncuların skorlarını ekrana yazdıralım;

spriteBatch.DrawString(Arial, "Oyuncu 1 : " + Oyuncu1Skor, new Vector2((float)(PENCERE_GENISLIK * 0.05), 30), Color.Yellow);
spriteBatch.DrawString(Arial, "Oyuncu 2 : " + Oyuncu2Skor, new Vector2((float)(PENCERE_GENISLIK * 0.80), 30), Color.Yellow);

Bu kod parçası ile, spriteBatch değişkeninde yeralan DrawString() method’unu kullandık. DrawString() method’unun parametrelerine bakacak olursak;

  • Birinci parametre olarak, spritefont tipinde bir değişken alır
  • İkinci parametre, ekrana yazdırmak istediğimiz yazıdır
  • Üçüncü parametre olarak Vector2 tipinde yazıyı nereye yazacağımız bilgisini ister
  • Son olarak, yazının rengini verdiğimiz dördüncü parametre vardır

Biz, Pong oyununda ilk oyuncunun skorunu ekranın %5 kadar içine, ikinci oyuncunun skorunu ise ekranın %20 kadar içine yazdırdık.

Gelelim, oyunda ses çıkartmaya;

Öncelikle wav formatında iki ses dosyasını Content içerisine sürükleyip bırakalım.

Class seviyesindeki değişkenlere iki tane daha ekleyelim;

SoundEffectInstance ArkaplanSes;
SoundEffectInstance SkorSes;

LoadContent() method’unda bu değişkenlerin değerlerini atayalım;

ArkaplanSes = Content.Load<SoundEffect>("Pong").CreateInstance();
SkorSes = Content.Load<SoundEffect>("recycle").CreateInstance();

Gördüğünüz gibi, SoundEffectInstance tipinde iki değişken tanımladık. Bu değişkenlere Content içindeki ses dosyalarını yüklerken, SoundEffect tipinin CreateInstance() method‘unu kullandık.

Böylece ses değişkenlerimiz kullanıma hazır hale geldi.

LoadContent() method’una aşağıdaki kodları da ekleyerek, arkaplanda çalacak ses değişkeninin sesini %20 oranına düşürelim ve loop olmasını sağlayalım (Böylece ses dosyasının sonuna geldiğinde başa dönüp çalmaya devam edecek).

Son olarak Play() methodu ile, ses dosyasını çalmaya başlayalım;

ArkaplanSes.Volume = 0.2f;
ArkaplanSes.IsLooped = true;
ArkaplanSes.Play();

Update() method’unda oyuncuların skor kazandığı kodlara aşağıdaki satırı da ekleyelim;

SkorSes.Play();

Böylece oyuncular skor yaptıkça, SkorSes değişkeninde yüklü olan ses dosyası çalınacak.

İşte Pong oyunundan ekran görüntüsü. Oyunun kaynak kodlarını buradan indirebilirsiniz.

XNA Pong oyunun bitmiş hali

XNA ile Pong oyunu yazalım – 1

07 Mart 2010 6 yorum

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

Her zamanki gibi, öncelikle ihtiyacımız olacak görselleri vereyim;

XNA - Pong Oyunu - Oyuncu 1 XNA - Pong Oyunu - Oyuncu 2 XNA - Pong Oyunu - Top

Ben kırmızı pedalı Oyuncu 1 için, mavi pedalı Oyuncu 2 için, yeşil kareyi ise Top olarak kullanacağım.

Visual Studio 2008 içerisinde yeni bir XNA projesi oluşturalım, Game1.cs‘in ismini GameLoop.cs ile değiştirelim ve class seviyesinde değişkenlerimizi tanımlayalım;

Texture2D Oyuncu1;
Texture2D Oyuncu2;
Texture2D Top;

Vector2 Oyuncu1Yer;
Vector2 Oyuncu2Yer;
Vector2 TopYer;

private int Oyuncu1Skor = 0;
private int Oyuncu2Skor = 0;
Vector2 TopYon;

private const int PENCERE_GENISLIK = 800;
private const int PENCERE_YUKSEKLIK = 600;
private const string PENCERE_BASLIK = "XNA - Pong Oyunu";
private const bool TAM_EKRAN = false;
private readonly Color ARKAPLAN_RENK = Color.Black;

GameLoop class‘ımızın contructor‘ına aşağıdaki kodları ekleyelim;

graphics.PreferredBackBufferWidth = PENCERE_GENISLIK;
graphics.PreferredBackBufferHeight = PENCERE_YUKSEKLIK;
graphics.IsFullScreen = TAM_EKRAN;
this.Window.Title = PENCERE_BASLIK;

Şimdi LoadContent() method’unda görselleri hafızaya yükleyeceğimiz kodları ekleyelim;

Oyuncu1 = Content.Load<Texture2D>("Oyuncu1");
Oyuncu2 = Content.Load<Texture2D>("Oyuncu2");
Top = Content.Load<Texture2D>("Top");

Oyuncu1Yer = new Vector2((float)(graphics.GraphicsDevice.Viewport.Width * 0.07), (float)(graphics.GraphicsDevice.Viewport.Height / 2));
Oyuncu2Yer = new Vector2((float)(graphics.GraphicsDevice.Viewport.Width - (graphics.GraphicsDevice.Viewport.Width * 0.07)), (float)(graphics.GraphicsDevice.Viewport.Height / 2));
TopYer = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2, graphics.GraphicsDevice.Viewport.Height / 2);

TopYon = new Vector2(3, 3);

Draw() methodunda ekrana çizim işlerini yapalım;

spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

spriteBatch.Draw(Oyuncu1, Oyuncu1Yer, Color.White);
spriteBatch.Draw(Oyuncu2, Oyuncu2Yer, Color.White);
spriteBatch.Draw(Top, TopYer, Color.White);

spriteBatch.End();

Update() methodunda her iki oyuncunun ve top’un yerlerini hesaplayalım. Öncelikle klavyenin o andaki durumunu alalım;

KeyboardState ks = Keyboard.GetState();

Hangi tuşların basılı olduğunu kontrol edelim;

if (ks.IsKeyDown(Keys.Escape))
	this.Exit();

if (ks.IsKeyDown(Keys.Up))
	Oyuncu1Yer.Y -= 3;
if (ks.IsKeyDown(Keys.Down))
	Oyuncu1Yer.Y += 3;

if (Oyuncu1Yer.Y < 0)
	Oyuncu1Yer.Y = 0;
if (Oyuncu1Yer.Y > PENCERE_YUKSEKLIK - Oyuncu1.Height)
	Oyuncu1Yer.Y = PENCERE_YUKSEKLIK - Oyuncu1.Height;

if (ks.IsKeyDown(Keys.E))
	Oyuncu2Yer.Y -= 3;
if (ks.IsKeyDown(Keys.D))
	Oyuncu2Yer.Y += 3;

if (Oyuncu2Yer.Y < 0)
	Oyuncu2Yer.Y = 0;
if (Oyuncu2Yer.Y > PENCERE_YUKSEKLIK - Oyuncu2.Height)
	Oyuncu2Yer.Y = PENCERE_YUKSEKLIK - Oyuncu2.Height;

Top‘un ekrandan dışarı çıkmaması için, ekranın üstüne veya altına eriştiğinde, yönünü değiştirecek kodu ekleyelim;

if ((TopYer.Y < 0) || (TopYer.Y > PENCERE_YUKSEKLIK - Top.Height))
	TopYon.Y *= -1;
if ((TopYer.X < 0) || (TopYer.X > PENCERE_YUKSEKLIK - Top.Width))
	TopYon.X *= -1;

Şimdi Oyuncu 1, Oyuncu 2 ve Top için konum ve boyut bilgilerini tutacağımız Rectangle değişkenlerini tanımlayalım;

Rectangle Oyuncu1Rect = new Rectangle((int)Oyuncu1Yer.X, (int)Oyuncu1Yer.Y, Oyuncu1.Width, Oyuncu1.Height);
Rectangle Oyuncu2Rect = new Rectangle((int)Oyuncu2Yer.X, (int)Oyuncu2Yer.Y, Oyuncu2.Width, Oyuncu2.Height);
Rectangle TopRect = new Rectangle((int)TopYer.X, (int)TopYer.Y, Top.Width, Top.Height);

Yapmamız gereken, Top‘un Oyuncu 1 veya Oyuncu 2 ile kesiştiği durumda yönünü ters çevirmek. Ayrıca eğer Oyuncu 1 veya Oyuncu 2‘nin arkasına geçerse diğer oyuncunun skor‘unu bir artırmak ve Top‘un yerini sıfırlamak;

if (TopRect.Intersects(Oyuncu1Rect) || TopRect.Intersects(Oyuncu2Rect))
	TopYon.X *= -1;

if (TopYer.X < Oyuncu1Yer.X)
{
	Oyuncu2Skor++;
	TopYer.X = graphics.GraphicsDevice.Viewport.Width / 2;
}

if (TopYer.X > Oyuncu2Yer.X)
{
	Oyuncu1Skor++;
	TopYer.X = graphics.GraphicsDevice.Viewport.Width / 2;
}

TopYer += TopYon;

Şu anda elimizde oynayabileceğimiz bir Pong oyunu var. Her iki oyuncu için skor bilgisini de tutuyoruz. Fakat skor’u ekrana yazdırmadık.

Ekrana skor yazdırma işini bir sonraki yazıya bırakıyorum. Oyunun kodlarını da bir sonraki yazıda veriyor olacağım.

XNA ile ekranda UzayGemisi yönetmek

06 Mart 2010 3 yorum

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

Öncelikle ihtiyacınız olacak iki görseli buradan indirebilirsiniz;

XNA - Oyun Programlama - Uzay Arkaplanı XNA - Oyun Programlama - Uzay Gemisi

Yapmak istediğimiz;

  • 800 x 600 pencere içerisinde arkaplanda Uzay.jpg görselini göstermek
  • Pencerenin ortasına UzayGemisi.png dosyasını konumlandırmak
  • Klavyenin tuşlarına görevler atayarak, UzayGemisi‘ni Uzay içerisinde yönetmek

Hemen başlayalım, ilk olarak yeni bir XNA projesi oluşturalım. Game1.cs dosyasının adını GameLoop.cs olarak değiştirelim.

Uzay.jpg ve UzayGemisi.png dosyalarını Content içerisine sürükleyip bırakalım. GameLoop.cs dosyasını açarak kod yazmaya başlayalım;

Sabitlerimizi class seviyesinde const ve readonly field’larda tanımlayalım;

private const int PENCERE_GENISLIK = 800;
private const int PENCERE_YUKSEKLIK = 600;
private const string PENCERE_BASLIK = "XNA - Uzay'da UzayGemisi Yönetelim";
private const bool TAM_EKRAN = false;
private readonly Color ARKAPLAN_RENK = Color.Black;

GameLoop class‘ının constructor‘ında bu sabitleri kullanalım;

graphics.PreferredBackBufferWidth = PENCERE_GENISLIK;
graphics.PreferredBackBufferHeight = PENCERE_YUKSEKLIK;
graphics.IsFullScreen = TAM_EKRAN;
this.Window.Title = PENCERE_BASLIK;

Uzay görselimizi 800 x 600 boyutlarındaki oyun penceremize sığdırmak için class seviyesinde Rectangle tipinde bir değişken tanımlayalım, Uzay ve UzayGemisi görsellerimiz için de birer tane Texture2D tipinde değişken tanımlayalım;

Rectangle Pencere;
Texture2D Uzay;
Texture2D UzayGemisi;

LoadContent() method’unda bu değişkenlerin değerlerini atayalım;

Pencere = new Rectangle(0, 0, PENCERE_GENISLIK, PENCERE_YUKSEKLIK);
Uzay = Content.Load<Texture2D>("Uzay");
UzayGemisi = Content.Load<Texture2D>("UzayGemisi");

Artık Uzayımızı ve UzayGemimizi oyun penceresine çizebiliriz. Ama madem uzay gemisini klavye tuşları ile yönetmek istiyoruz, o zaman uzay gemisinin kendi çevresinde dönebilmesini de sağlamamız gerekir.

Eğer bunu yapmazsak, Yukarı tuşu ile yukarı giden uzay gemisi, Aşağı tuşu ile aşağı gelirken yönünü düzeltmez, geri geri geliyormuş gibi gözükür. Hatta yanlara ve çağrazlara giderken iyice anlamsız görünmeye başlar.

Öncelikle uzay gemisinin o anda oyun penceresinin neresinde olduğunu tutacağımız Vector2 sınıfında bir değişkene class seviyesinde ihtiyacımız var, ayrıca uzay gemisinin gideceği yöne göre yönelmesi için, iki değişkene daha ihtiyacımız olacak;

Vector2 Konum = Vector2.Zero;
Vector2 Merkez;
float Yonelme = 0;

Uzay gemisinin doğru bir yönelme yapabilmesi için, ağırlık merkezini doğru vermemiz gerekiyor, LoadContent() methoduna aşağıdaki kodu ekleyelim;

Merkez = new Vector2(UzayGemisi.Width / 2, UzayGemisi.Height / 2);

Önce Draw() methodumuzu yazalım;

GraphicsDevice.Clear(ARKAPLAN_RENK);
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

spriteBatch.Draw(Uzay, Pencere, Color.White);
spriteBatch.Draw(UzayGemisi, Konum, null, Color.White, Yonelme, Merkez, 1, SpriteEffects.None, 0);

spriteBatch.End();
base.Draw(gameTime);

Son olarak Update() methodumuzu yazalım;

Öncelikle klavyenin o andaki durumunu tutacağımız değişkenimizi tanımlıyoruz;

KeyboardState ks = Keyboard.GetState();

Eğer Escape tuşuna basılıyorsa, oyunda çıkıyoruz;

if (ks.IsKeyDown(Keys.Escape))
	this.Exit();

Eğer hem Yukarı, hem de Sağa tuşlarına basılıyorsa, uzay gemisinin X ve Y koordinatlarında güncellemeler yapıp, yönelmesini 45 derecenin radian karşılığına eşitliyoruz;

if (ks.IsKeyDown(Keys.Up) && ks.IsKeyDown(Keys.Right))
{
	Konum.Y -= 3;
	Konum.X += 3;
	Yonelme = MathHelper.ToRadians(45);
}

MathHelper.ToRadians() parametre olarak derece cinsinden değer alır ve geriye float cinsinden radian döner.

Update() methodunun geri kalanında yön tuşlarına göre uzay gemisinin koordinatlarını ve yönelmesini güncelliyoruz;

if (ks.IsKeyDown(Keys.Up) && ks.IsKeyDown(Keys.Right))
{
	Konum.Y -= 3;
	Konum.X += 3;
	Yonelme = MathHelper.ToRadians(45);
}
else if (ks.IsKeyDown(Keys.Up) && ks.IsKeyDown(Keys.Left))
{
	Konum.Y -= 3;
	Konum.X -= 3;
	Yonelme = MathHelper.ToRadians(315);
}
else if (ks.IsKeyDown(Keys.Down) && ks.IsKeyDown(Keys.Right))
{
	Konum.Y += 3;
	Konum.X += 3;
	Yonelme = MathHelper.ToRadians(135);
}
else if (ks.IsKeyDown(Keys.Down) && ks.IsKeyDown(Keys.Left))
{
	Konum.Y += 3;
	Konum.X -= 3;
	Yonelme = MathHelper.ToRadians(225);
}
else if (ks.IsKeyDown(Keys.Up))
{
	Konum.Y -= 3;
	Yonelme = MathHelper.ToRadians(0);
}
else if (ks.IsKeyDown(Keys.Down))
{
	Konum.Y += 3;
	Yonelme = MathHelper.ToRadians(180);
}
else if (ks.IsKeyDown(Keys.Left))
{
	Konum.X -= 3;
	Yonelme = MathHelper.ToRadians(270);
}
else if (ks.IsKeyDown(Keys.Right))
{
	Konum.X += 3;
	Yonelme = MathHelper.ToRadians(90);
}

Son olarak Update() methodunda uzay gemisinin pencereden dışarı çıkmasını engelleyecek kontrol kodlarını da yazıyoruz;

if (Konum.X < 0)
	Konum.X = 0;
if (Konum.X > PENCERE_GENISLIK - UzayGemisi.Width)
	Konum.X = PENCERE_GENISLIK - UzayGemisi.Width;
if (Konum.Y < 0)
	Konum.Y = 0;
if (Konum.Y > PENCERE_YUKSEKLIK - UzayGemisi.Height)
	Konum.Y = PENCERE_YUKSEKLIK - UzayGemisi.Height;

“Oyun” projemizin kodlarını buradan bilgisayarınıza indirebilirsiniz.

Projeyi çalıştırdığınızda aşağıdaki gibi bir sonuç almanız lazım;

XNA - Uzay Oyunu Sonuç Penceresi