Hepinize merhaba,

Bu ders ile yeni bir serinin (umarım beğeneceğiniz bir seri olur) başlangıcını yapıyorum: Klonluyoruz! Bu seride insanların rağbet gösterdiği oyunların arasından, kendi çapımda yapılabilir bulduklarımı seçip onları Unity’de sıfırdan tekrar programlamaya çalışacağım.

Yaptığımız klon oyunlar orijinal oyundan görsel anlamda ve bazı noktalarda oynanabilirlik anlamında çok farklı olabilir, ama orijinal tasarıma olabildiğince sadık kalmaya çalışacağımdan emin olabilirsiniz.

Seriye Flappy Bird oyunuyla başlangıç yapıyoruz çünkü hem karmaşık olmayan bir oyun kendisi hem de bu proje ile ben de Unity’e yeni gelen 2D desteğine ilk adımımı atmak istiyorum.

Oyunumuzda bulunacak özellikler şunlar:

1- Ekrana tıklayınca kuşun zıplaması

2- Kuşun kanat çırpması (bir animasyona sahip olması)

3- Engellerin gerçek-zamanlı rastgele şekilde oluşturulması

4- Engele çarpınca ölmek

5- Yüksekskorun cihazda kayıt altına alınması

6- Çeşitli ses efektleri

Derse başlamadan önce Unity’nin arayüzüne ve script yazmaya az da olsa aşina olmanız çok işinize yarayacaktır.

NOT: Ders beni de şaşırtıp Word’de tam 46 sayfa tutmuştur. Olabildiğince yaptığım her şeyi anlatmaya çalıştım. Eğer dersi siteden değil de bir PDF’ten okumak isterseniz sizi şu linke alayım (sizlerden aldığım geridönüşlerle zaman zaman sitedeki dersi güncelliyorum ancak PDF’i güncellemiyorum, o yüzden tavsiyem dersi siteden takip etmeniz): https://app.box.com/s/g6wjpjhc9x5xmfveh0fp (Alternatif link)

NOT2: Projenin bitmiş halini indirip takıldığınız yerlerde ona bakmak istiyorsanız: https://app.box.com/s/pduuj3duocmx4c8izxjj (Alternatif link)

Yeni bir proje oluşturarak derse başlayalım. Projeye istediğiniz ismi verin ancak “Import the following” kısmında hiçbir şeyi işaretlemeyin ve aşağıdaki “Setup defaults for” seçeneğini 2D yapın.

Eğer şimdi Game paneline geçiş yaparsanız arkaplanın boğuk bir mavi renkte olduğunu göreceksiniz. Ben bu rengi biraz açmak istiyorum. Siz de istiyorsanız Main Camera‘yı seçip Background değerini değiştirin (bendeki yeni RGB değeri 28,191,214):

resim1

Başta oyunun görsellerini bir şekilde kendim hazırlamayı düşünüyordum ama sonradan bu işte yetersiz olduğumu fark ettim ve lanica.co/flappy-clone/‘dan oyunun bir klonunun görsellerini aldım. Klon oyunumuzda da bu görselleri kullanacağız. Bu projeyi öğrenme amaçlı yapacağımız için sorun çıkmayacaktır. Ben proje için gerekli tüm görselleri ve sesleri bir Winrar arşivinde depoladım. Siz direkt onu indirin: https://app.box.com/s/kct22wtzp25s4bi2h1ag (Alternatif link)

NOT: Bir alttaki paragrafı okumadan bu notu okumanızın önemli olduğunu düşünüyorum. Unity’nin yeni sürümleriyle beraber Project panelinin tarzı değişti. Artık iki farklı şekilde görebiliyoruz Project panelini: tek hücreli ve iki hücreli olarak. Yeni bir proje açtığınız zaman Project paneli otomatik olarak iki hücreli şekilde gelir ama ben bu yeni stili sevmiyorum ve eski tek hücreli stili kullanıyorum. Dersi anlatırken de tek hücreli stili kullanarak anlatıyorum. Eğer herhangi bir sıkıntı yaşamak istemiyorsanız siz de tek hücreli Project paneline geçiş yapın. Bunu yapmak çok kolay: Project panelinin sağında bir kilit simgesi, onun da sağında garip bir simge var. İşte o garip simgeye tıklayın ve One Column Layout seçeneğini seçin:

resim2

Şimdi Project panelinde Create-Folder yolunu izleyerek Sprites adında yeni bir klasör oluşturun ve Winrar arşivindeki sprite’leri bu klasöre atın. Eğer sürükle-bırak işe yaramıyorsa Assets-Import New Asset… yolunu izleyerek tüm sprite’leri tek tek import edin (ben tek tek import etmek zorunda kaldım).

Sonra flappy1 spritesini seçip Inspector panelinde yer alan Format kısmını Truecolor olarak değiştirin ve alttan Apply butonuna basın. Bu işlemi flappy2 ve flappy3 spriteleri için de yapın. Eğer bunu yapmazsak kuş biraz eğim aldığında etrafında istenmeyen siyah çizgiler oluşuyor ama artık böyle bir sorun yaşamayacağız.

resim2_3

Kuşu Oluşturmak ve Kanat Çırpmasını Sağlamak

Projenin ilk aşamasında planım kuş objemizi oluşturmak ve ona animasyon vermek. Kuşumuzun kanat çırpma animasyonu üç kareden oluşuyor. İlk karede kanat yukarı kalkmış, ikinci karede kanat ortada ve son karede kanat aşağı inmiş vaziyette. Eğer animasyonumuzu 1-2-3-1-2-3-… diye oynatırsak kuşun kanadı yukarıdan aşağıya iner ve ardından direkt yukarıya geri zıplar. ama eğer 1-2-3-2-1-2-3-… diye zikzak yaptırarak oynatırsak kanat önce aşağı iner, sonra yukarı çıkar ve sonra yine aşağı iner ve bu böyle devam eder. Fark edeceğiniz üzere ikinci yol daha sağlıklı. Bu yüzden bu yolu izleyeceğiz.

Kuşa animasyonunu vermek için script yazmamız gerekecek. Scriptin başlıca avantajı kuşun animasyon hızını istediğimiz gibi değiştirebilmek ve animasyonun oynama sırasına karar verebilmek (1-2-3-2-1-… diye). O halde önce kuşumuzu oluşturalım. GameObject-Create Other-Sprite yolunu izleyin. Ardından “flappy1” sprite’sini Assets klasöründen tutup Sprite Renderer‘daki Sprite kısmına sürükleyin:

resim3

Bu aşamada kuş ekranınızda belirecektir. Eğer belirmemişse Scene panelinde biraz dışarı doğru zoom yapın. Ya da Hierarchy panelinden New Sprite‘yi seçip fareyi Scene paneline getirin ve klavyeden F tuşuna basın. Kamera kuşa odaklanacaktır. Şimdi Inspector panelinden New Sprite‘nin adını FlappyBird olarak değiştirin. Hemen sonrasında ise Transform componentinin sağında yer alan dişli çarka tıklayıp Reset‘e dokunarak kuşu 3 boyutlu uzayın tam merkezine (origin) yerleştirin:

resim4

Bu işlemin ardından Scene panelinde F tuşuna basarak kuşa tekrar odaklanmanız gerekebilir.

Şimdi sıra geldi kuşa animasyon vermeye. Bunun için yeni bir Javascript oluşturun (Assets-Create-Javascript) ve adını KusAnimasyon koyun. Bu scripti Project panelinden tutup sürükleyerek Hierarchy‘deki FlappyBird objesine atayın. Artık scripti düzenlemeye hazırız. Scripte çift tıklayarak onu açın ve içini şöyle değiştirin:

#pragma strict

public var saniyedeKareSayisi : int = 10;
public var animasyonKareleri : Sprite[];

private var sonrakiAnimasyonDegismeAni : float;
private var animasyonYonu : boolean = true;
private var mevcutAnimasyonKaresi : int = 0;

function Start()
{
	if( animasyonKareleri.Length < 2 )
		Destroy( this );

	sonrakiAnimasyonDegismeAni = Time.time + 1f / saniyedeKareSayisi;
}

function Update()
{
	if( Time.time >= sonrakiAnimasyonDegismeAni )
	{
		if( animasyonYonu )
		{
			if( mevcutAnimasyonKaresi == animasyonKareleri.Length - 1 )
			{
				mevcutAnimasyonKaresi--;
				animasyonYonu = false;
			}
			else
			{
				mevcutAnimasyonKaresi++;
			}
		}
		else
		{
			if( mevcutAnimasyonKaresi == 0 )
			{
				mevcutAnimasyonKaresi++;
				animasyonYonu = true;
			}
			else
			{
				mevcutAnimasyonKaresi--;
			}
		}

		GetComponent( SpriteRenderer ).sprite = animasyonKareleri[ mevcutAnimasyonKaresi ];
		sonrakiAnimasyonDegismeAni = Time.time + 1f / saniyedeKareSayisi;
	}
}

Bu 50 satırlık scripti hızlıca açıklamaya çalışayım. En başta 5 adet değişken tanımlıyoruz:

saniyedeKareSayisi: Bu değişkenin varsayılan değeri 10. Bunun anlamı bir saniyede kuş animasyonundaki kareler arasında toplam 10 kere geçiş olacak. Bu değeri artırırsanız kuş daha hızlı kanat çırpar. Kendisi public bir değişken olduğu için Inspector’dan değerini değiştirerek en uygun değeri kolayca bulabilirsiniz.

animasyonKareleri: Bu bir Sprite dizisi (array). Flappy Bird’in her bir animasyon karesine bir sprite diyoruz. Yani bu değişken kuşun kanat çırpma animasyonundaki kareleri depoluyor. Elimizde üç tane sprite var: flappy1, flappy2 ve flappy3. Şimdi Inspector’da Kus Animasyon (Script) yazan yerin altındaki Animasyon Kareleri‘nin üzerine tıklayın. Kendisi genişleyecektir. Gelen değerlerden Size‘ı 3 yapın. Bu değer array’de toplam kaç eleman depolayacağınızı belirtir. Değeri 3 yapınca Element’lerin sayısı otomatik olarak 1’den 3’e çıkacaktır. Şimdi sırayla flappy1, flappy2 ve flappy3 sprite’lerini Project panelinden sürükleyerek bu Element’lerin üzerine bırakın:

resim5

Eğer şu anda Play butonuna basarsanız kuşun ekranda kanat çırptığını göreceksiniz. Biz değişkenleri tanımaya devam edelim.

sonrakiAnimasyonDegismeAni: Animasyonun karesinin bir sonraki değişme anının kaçıncı saniyeye (salisesiyle beraber) denk geldiğini depolar. Oyunda o saniyeye ulaştığımızda kuşun animasyonundaki bir sonraki kareye geçiş yapılır ve bu sonrakiAnimasyonDegismeAni değişkeninin değeri artar.

animasyonYonu: Değeri true ise kuşun animasyonu Element 0’dan Element 2’ye doğru akarken değeri false olursa kuşun animasyonu Element 2’den Element 0’a doğru akar. Bu değişkeni kullanma sebebimiz kuşun animasyonunu 1-2-3-2-1-2-3-… şeklinde oynatacak olmamız.

mevcutAnimasyonKaresi: animasyonKareleri isimli, kuş animasyonunun spritelerini depolayan dizideki kaçıncı spritede bulunduğumuzu depolar. Değeri 0 ise o anda animasyonun Element 0’ıncı karesinde, 1 ise Element 1’inci karesinde, 2 ise Element 2’nci karesinde yer aldığımızı anlarız.

Gelelim Start fonksiyonuna. Burada yer alan animasyonKareleri.Length değeri animasyonKareleri array’inin büyüklüğünü, yani Inspector’dan girdiğimiz Size değerini bize döndürür. Eğer bu değer 2’den küçükse Destroy(this); komutu ile scripti FlappyBird objesinden siliyorum çünkü Size değerinin 2’den küçük olması demek animasyon olmaması demektir ve bu durumda bu scriptin gereksiz çalışmasına gerek yok.

NOT: Destroy(this) komutu ile Destroy(gameObject) komutunu karıştırmayın. Destroy(this) komutu bir scripti objeden atmaya yararken Destroy(gameObject) komutu scriptin yazıldığı objeyi yok etmeye yarar.

Start fonksiyonunda ikinci olarak da sonrakiAnimasyonDegismeAni değişkenine ilk değerini veriyoruz. Time.time komutu oyun ilk başladığında 0 değerini alır ve oyun başlatıldıktan itibaren kaç saniye geçtiğini depolar. Örneğin oyunu açtıktan tam 5 saniye sonra Time.time’ın değeri 5 olur. Biz de tam üzerinde bulunduğumuz Time.time saniyesine 1f / saniyedeKareSayisi‘ni ekliyoruz. Buradaki 1f sayının float olduğunu belirtir. İstersek oraya 1.0 da yazabilirdik. Yaptığımız işlem basit: eğer 1 saniyede saniyedeKareSayisi kadar kare gösterilecekse bir kare 1 / saniyedeKareSayisi kadar saniyede gösterilir. Eğer saniyedeKareSayisi‘nı 1f’e değil de 1’e bölseydik bu bir integer bölmesi olurdu ve bulunan sayının küsüratlı kısmı silinirdi. Yani bölme işlemi hep 0 döndürürdü.

Update fonksiyonu oldukça basit. Önce üzerinde bulunduğumuz saniyenin (Time.time) sonrakiAnimasyonDegismeAni’ndan büyük olup olmadığına bakıyoruz. Eğer büyükse anlarız ki animasyonun sonraki karesine geçmenin zamanı gelmiştir. Eğer animasyonYonu true ise animasyonu Element 0’dan Element 2’ye doğru oynatıyoruz. Ama eğer o an Element 2’deysek (mevcutAnimasyonKaresi == animasyonKareleri.Length – 1) o zaman animasyonYonu’nü tersine çeviriyoruz ve mevcutAnimasyonKaresi’ni artırmak yerine 1 azaltıyoruz. Animasyonun sonraki oynatılacak karesine karar verdikten sonra ise objemizin SpriteRenderer component’indeki Sprite kısmının değerini script vasıtasıyla ilgili animasyon karesiyle değiştiriyoruz ve sonrakiAnimasyonDegismeAni‘nın değerini güncelliyoruz. Umarım her şey anlaşılmıştır. Unutmayın: bu ders hiç script bilgisi olmayanlar için değildir, eğer buraya kadar dediklerimden hiç bir şey anlamadıysanız önce başka tutorialler vasıtasıyla biraz UnityScript (Javascript) aşinalığı kazanın.

Kuşu oluşturup ona animasyon verdiğimize göre sonraki kısma geçebiliriz. Önce şimdiye kadar yaptığımız aşamayı kaydedelim. Bunun için File-Save Scene yolunu izleyin ve sahneye Oyun adını verin.

Kuşu Zıplatmak, Yerçekimi ve Kuşun Dönmesi

KusHareket adında yeni bir Javascript oluşturun, scripti FlappyBird objesine atın ve içini şöyle değiştirin:

#pragma strict

public var yercekimi : float = 4;
public var ziplamaGucu : float = 2.5;
public var yatayHiz : float = 1.5;
private var dikeyHiz : float = 0;

function Update ()
{
	dikeyHiz -= yercekimi * Time.deltaTime;

	if( Input.GetMouseButtonDown( 0 ) )
		dikeyHiz = ziplamaGucu;

	var egim : float = 90 * dikeyHiz / yatayHiz;

	if( egim < -50 ) egim = -50;
	else if( egim > 50 ) egim = 50;

	transform.eulerAngles = Vector3( transform.eulerAngles.x,
					transform.eulerAngles.y, egim );

	transform.Translate( Vector3( 0, dikeyHiz, 0 ) * Time.deltaTime, Space.World );
}

Eğer Play tuşuna basarsanız kuşun bu kod vasıtasıyla yerçekimine kapılıp aşağı düştüğünü ve ekrana tıklayınca biraz yükseldiğini göreceksiniz. Kuşun dikey hızına bağlı olarak eğiminin de değiştiğini fark etmiş olmalısınız. Şimdi kodu inceleyelim:

yercekimi: Kuşa etki eden yerçekiminin gücü. Değerini artırırsanız kuş daha çabuk yere düşer.

ziplamaGucu: Ekrana dokununca kuşun dikey hızını bu değere eşitliyoruz. Eğer değeri artırırsanız ekrana tıklayınca kuş daha yukarı zıplar.

yatayHiz: Kuşun yatay eksende (x-ekseni) ne kadar hızlı gideceğini belirleyen değer. Henüz tam bir işe yaramıyor.

dikeyHiz: Kuşun dikey eksende (y-ekseni) ne kadar hızlı hareket ettiğini belirleyen değer. Bu değişkenin private olduğuna dikkat edin. Bunun sebebi yerçekiminin ve ekrana tıklamalarımızın etkisiyle değerinin script vasıtasıyla değiştiriliyor olması.

Update fonksiyonuna girecek olursak; ilk satırda dikeyHiz‘a yerçekiminin etki ettiğini görebilirsiniz. Burada Time.deltaTime devreye giriyor. Eğer bilmiyorsanız diye kısaca açıklayayım. Update fonksiyonunda bir değişkenin değerini saniyede 1 artırmak istiyorsanız o değişkene Time.deltaTime eklerseniz. Eğer değerini 5 azaltmak istiyorsanız o değişkenden 5 * Time.deltaTime çıkarırsınız. Yani Time.deltaTime, Update fonksiyonunda bir değişkenin değerinin bir saniyede belli bir miktar değiştirilmesine yardımcı olur. Biz Update fonksiyonunda dikeyHiz’dan yercekimi * Time.deltaTime’ı çıkarıyoruz. Bunun anlamı dikeyHiz’ın değeri her saniyede yercekimi kadar azalacaktır.

İkinci olarak if( Input.GetMouseButtonDown( 0 ) ) ile ekrana o anda farenin sol tuşuyla tıklayıp tıklamadığımıza bakıyoruz ve eğer tıklamışsak kuşun dikeyHiz‘ını ziplamaGucu‘ne eşitliyoruz. Bu da kuşun yönünün aniden yukarı doğru değişmesine sebep oluyor.

Devam edecek olursak kodda egim diye bir değişkenin tanımlandığını görebiliriz. Bu değişken FlappyBird objesinin dikeyHiz‘ının değerine göre eğim almasına yardımcı oluyor. Önce dikeyHiz’ın yatayHiz’a oranını buluyor, sonra bu değeri 90 ile çarparak eğimi bir açıya çeviriyoruz. Ardından iki if koşulu ile bu eğimi -50 ile 50 derece arasında tutuyoruz. Eğer açı 50 ise buradan anlayabiliriz ki kuşun burnu 50 derece yukarı bakıyordur. Eğer bu iki if koşulunu silersek kuşa yerçekimi etki ettikçe kuş dönmeye devam edecektir ve hoş olmayan bir görüntü ortaya çıkacaktır.

Elimizde bir egim değişkeni var. Şimdi bu değişkeni kuşun Transform component’ine uygulayarak kuşun sprite‘sine eğim vermeliyiz. Bunun için transform.eulerAngles değerini kullanıyoruz. Bu değişken Inspector’da Transform component’i altında yer alan Rotation değerlerini temsil etmektedir. 2 boyutlu uzayda bir objeye eğim vermek için daima bu Rotation’ın Z değeri değiştirilmelidir. Biz de bunun için transform.eulerAngles’ın x ve y değerlerini aynen bırakıyoruz.

Son olarak transform.Translate komutu ile objeyi dikey eksende (y-ekseni) saniyede dikeyHiz kadar hareket ettiriyoruz (burada da Time.deltaTime kullandığıma dikkat edin). Bu kodda dikkatinizi Space.World parametresi çekmiş olabilir. Hemen açıklayayım: normalde Translate fonksiyonuna girdiğiniz değerler objenin eğimine göre değiştirilerek öyle objeye uygulanır. Yani mesela Translate vasıtasıyla objeyi yukarı yönde 5 birim hareket ettiriyorum diyelim. Eğer objenin herhangi bir eğimi yoksa kod objeyi ekranda gerçekten yukarı yönde 5 birim hareket ettirir. Ama eğer obje bir eğime sahipse, yani mesela 90 derece sağa dönmüşse (kuşun burnu yere bakıyorsa) o zaman objeyi yukarı yönde 5 birim hareket ettirmeye çalışınca obje kendisine göre yukarı ama bize göre sağa doğru (burnu aşağı bakan bir kuşun yukarı yönü bize göre sağ olur) hareket eder. İşte Translate fonksiyonunun sonuna Space.World ibaresini ekleyerek bu durumu engelliyor ve kuşun eğimi ne olursa olsun hep bize göre yukarı yönde hareket etmesini sağlıyoruz.

Kuşu neden henüz yatay eksende hareket ettirmediğimi merak ediyor olabilirsiniz. Öyle yaptım çünkü Flappy Bird oyununda bildiğiniz üzere kamera kuşu yatay eksende takip eder ama dikey eksende takip etmez. Yani kuş ne kadar yukarı ya da aşağı hareket ederse etsin kamera yukarı-aşağı oynamaz. Unity’de kameranın kuşu takip etmesinin en kolay yolu kuşu kameranın parent objesi olarak atamaktır. Böylece kamera hep kuşu takip edecektir. Ama bunun dezavantajı ise kamera kuşu gerçek anlamda takip edecektir, yani kuş yukarı-aşağı hareket ettikçe onunla beraber yukarı-aşağı inecek, kuş eğim kazandıkça kamera da eğim kazanacaktır. İşte bu duruma engel olmak için (kameranın kuşu sadece yatay eksende takip etmesi ve kuşla beraber eğim almaması için) biraz farklı bir hiyerarşi oluşturacağız.

GameObject-Create Empty ile boş bir GameObject oluşturun. Empty GameObject’in Inspector‘da Transform panelinin sağındaki dişli çarka tıklayıp Reset deyin. Sonrasında Hierarchy‘den FlappyBird objesini bu objenin üzerine sürükleyerek Empty GameObject’i FlappyBird’ün parent’ı yapın. Aynı işlemi Main Camera‘ya uygulayarak Empty GameObject’i Main Camera’nın da parent’ı yapın:

resim6

Artık FlappyBird objesi ve kameramız Empty GameObject‘i takip edecekler. Eğer kuşumuzun yatayHiz‘ını bu Empty GameObject’e uygularsak amacımıza ulaşmış olacağız: Empty GameObject sadece yatay eksende hareket etmiş olacak ve beraberinde FlappyBird ile Main Camera’yı da yatay eksende taşıyacak (çünkü onların parent’ı). Buna ek olarak FlappyBird kendi başına dikey eksende de hareket edecek (dikeyHiz sayesinde) ama kamera bu dikey eksendeki hareketi takip etmeyecek çünkü kameranın FlappyBird ile hiçbir direkt bağlantısı yok. O halde yapmamız gereken kuşun yatayHiz’ını Empty GameObject’e vermek. Bunun için KusHareket scriptinde en alttaki Translate fonksiyonunun bir altına şu satırı ekleyin:

transform.parent.Translate( yatayHiz * Time.deltaTime, 0, 0 );

Yazdığımız bu kod ile FlappyBird’ün parent’ına (yani Empty GameObject‘e) ulaşıp onu yatay eksende saniyede yatayHiz kadar hareket ettiriyoruz. Burada Space.World kullanmadık çünkü Empty GameObject hiçbir zaman eğim almıyor ve bu yüzden ona göre sağ hep bize göre de sağ oluyor.

Şu anda arkaplanda başka obje olmadığı için kuşun yatayda hareket edip etmediğini anlayamayabilirsiniz. Bunu test etmenin en basit yolu sahnede geçici bir küp oluşturmak: GameObject-Create Other-Cube. Şimdi Play butonuna basarsanız küp objesinin sola doğru kaydığını görebilirsiniz. Harika!

Bir sonraki aşamaya geçmeden önce küpü silmeyi ve ardından File-Save Scene ile sahneyi kaydetmeyi unutmayın.

Arkaplanı Oluşturmak ve Sonsuza Dek Devam Etmesini Sağlamak

Aslında bu tutoriale başlarken aklımda arkaplana birşey koymak yoktu. Hatta o yüzden dersin başında kameranın arkaplanındaki rengi açık maviye çevirdik. Ama bu kısımda kararımı değiştirdim ve bu yüzden de birlikte oyuna bir arkaplan ekleyecek ve arkaplanın hiç bitmemesini, sürekli kendini tekrar etmesini sağlayacağız. Bu işi ise tamamen script yardımıyla yapacağız.

Arkaplan iki ayrı sprite’den oluşmakta: gökyüzü ve toprak. Bu ikisinin ayrı sprite’ler olmasının sebebi gökyüzünün toprağa nazaran daha yavaş hareket edecek olması. Buna parallax scrolling deniyor oyun terminolojisinde. Yani yakındaki arkaplan objelerinin uzaktaki arkaplan objelerine nazaran daha hızlı hareket etmesine deniyor. Bu da daha gerçekçi ve hoş bir görüntü oluşturuyor.

Bizim yazacağımız script oyun başladığında ekrana gökyüzü ve toprak sprite’lerinden ne kadar sığıyorsa o kadarını güzelce yerleştirecek ve kuşumuz sağa doğru hareket ettikçe kameranın dışında kalan soldaki arkaplan sprite’lerini en sağa taşıyarak arkaplanın sanki hiç bitmiyormuşçasına kendini tekrar etmesini sağlayacak.

İlk önce gökyüzü ve toprak spritelerini uygun şekilde düzenleyelim. Bunun için Project panelindeki Sprites klasörünüzden gokyuzu sprite’sini seçin ve Inspector’dan Pivot özelliğini Top Left olarak değiştirin. Ardından aşağıdan Apply butonuna basın. Aynı şeyi toprak sprite’si için de yapın:

resim7

Burada yaptığımız şey çok basit: artık gokyuzu ve toprak objelerini Unity’e attığımızda bu objelerin Inspector’daki Transform component’inde gördüğümüz position değerleri tam sol üst köşelerinin pozisyonunu temsil edecek. Eskiden Pivot‘un değeri Center iken position değeri objenin merkezini temsil ediyordu. Böyle yapmamız kod yazarken işimize yarayacak.

Şimdi gokyuzu ve toprak spritelerini birer prefab‘a çevirelim. Bunun için önce gokyuzu spritesini Project panelinden sürükleyerek Scene paneline bırakın. Sahnede gökyüzünü gösteren yeni bir GameObject oluşacaktır. Bu objeyi seçin ve Transform component’indeki değerleri sıfırlayın (dişli çarka tıklayıp Reset‘e basın). Şimdi bu objeyi Hierarchy panelinden tutup sürükleyerek Project paneline bırakın. Harika! Artık gokyuzu prefabımız oluştu. Aynı şeyi toprak sprite’si için de yapın. İşiniz bitince Project panelinde Prefablar isminde yeni bir klasör oluşturup toprak ve gokyuzu prefab’larını içine atın. Böylece projemiz daha düzenli olacak. Artık sahnedeki gokyuzu ve toprak objelerine ihtiyacımız kalmadı, onları Hierarchy panelinden veya Scene panelinden seçerek silin.

resim8

NOT1: Üstteki resimde Sprites klasöründe ustEngel sprite’si eksik. Ama sizde ustEngel sprite’sinin de olması lazım. Hata benim resmimde, sizin yanlış yaptığınız bir şey yok.

NOT2: Bu safhada Main Camera‘nın Transform component’indeki position değerinin 0,0,-10 olduğundan emin olun yoksa script düzgün çalışmayabilir.

Artık scripti yazmaya hazırız. Arkaplan adında yeni bir Javascript oluşturup scripti Main Camera objesine atayın. Ardından içeriğini şöyle değiştirin:

#pragma strict

public var gokyuzu : GameObject;
public var toprak : GameObject;

private var arkaplanSayisi : int;

private var kameraUnityEbatlar : Vector2;
private var gokyuzuUnityEbatlar : Vector2;
private var toprakUnityEbatlar : Vector2;

function Start()
{
	gokyuzuUnityEbatlar = Vector2( ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.width, ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.height ) / 100;
	toprakUnityEbatlar = Vector2( ( toprak.renderer as SpriteRenderer ).sprite.rect.width, ( toprak.renderer as SpriteRenderer ).sprite.rect.height ) / 100;

	camera.orthographicSize = ( gokyuzuUnityEbatlar.y + toprakUnityEbatlar.y ) / 2;
	arkaplanSayisi = Mathf.CeilToInt( ( camera.orthographicSize * 2 * camera.aspect ) / gokyuzuUnityEbatlar.x ) + 1;

	kameraUnityEbatlar = Vector2( camera.orthographicSize * camera.aspect, camera.orthographicSize );

	for( var i = 0; i < arkaplanSayisi; i++ )
	{
		var xKoordinati : float = transform.position.x - kameraUnityEbatlar.x + i * gokyuzuUnityEbatlar.x;
		Instantiate( gokyuzu, Vector3( xKoordinati, kameraUnityEbatlar.y, 0 ), Quaternion.identity );
		Instantiate( toprak, Vector3( xKoordinati, kameraUnityEbatlar.y - gokyuzuUnityEbatlar.y, 0 ), Quaternion.identity );
	}
}

Bu script biraz karmaşık, kabul ediyorum. Ancak hayıflanmadan önce scripti kaydedin ve Main Camera‘dan Arkaplan (Script) component’indeki Gokyuzu ve Toprak kısımlarına değer olarak gokyuzu ve toprak prefablarını verip oyunu test edin.

resim9

Arkaplanın tam oturduğunu ve kameranın arkaplanı tam oturtmak için kendi boyutunu değiştirdiğini göreceksiniz. Tek sorun bir süre sonra arkaplan sona eriyor çünkü henüz geride kalan arkaplan objelerini ileriye ışınlamıyoruz. Onu sonra yapacağız. Şimdi elimden geldiğince scripti açıklamaya çalışacağım.

NOT: Eğer kuş, arkaplan sprite’lerinin arkasında kalıyor ve ancak arkaplanlar bitince görünüyorsa FlappyBird‘ün Position‘ının Z değerini -2 yapın.

resim10

gokyuzu: Gökyüzü prefabının depolandığı değişken. Oyun başlarken Instantiate ile sahneye gökyüzü objeleri eklerken kullanıyoruz.

toprak: Toprak prefabının depolandığı değişken. Tıpkı gokyuzu gibi; oyun başlarken toprak objeleri oluştururken kullanıyoruz.

arkaplanSayisi: Kameraya kaç tane arkaplan objesinin sığabildiğini depoluyor. Oyun başladığında bu değişkenin değeri kadar yanyana arkaplan objesi oluşturuyoruz. Aslında değişkenin gerçek değeri kameraya sığan arkaplan objesi sayısının 1 fazlası. Çünkü oyun sırasında her zaman arkaplanın ekranda görünür olmasını, kameranın kendi açık mavi arkaplanın hiç gözükmemesini istiyoruz. Bunun için kameranın dışında kalan arkaplan objelerini soldan sağa ışınlıyoruz ama eğer bu değişkenin değeri normalden 1 fazla olmasaydı en soldaki arkaplan objesi kameranın solundan dışarı çıkıp sağına geri ışınlanana kadar kamera sağdaki diğer arkaplan objelerini geride bırakıp bir süreliğine kendi açık mavi arkaplanını gösterecekti bize. Ama bu değeri 1 artırdığımız için artık bu boşlukta kameranın mavi arkaplanı gözükmeyecek, yine bir başka arkaplan objesi gözükecek.

kameraUnityEbatlar: Son üç değişkeni açıklarken iki farklı terimden faydalanacağım ve bu terimleri yazının devamında da kullanacağım. O yüzden dikkatle dinleyin:

— 1 Uzay Birimi: Bildiğiniz gibi oyunumuzu boş bir uzayda tasarlayıp bu uzaya objeler ekleyerek sahnemizi oluşturuyoruz. Peki uzay biriminden kastım nedir? Şöyle açıklayayım: Project panelinden sahnenize bir gokyuzu sprite’si sürükleyip objenin Transform‘unun position‘ının X değerini 0 yapın. Kamerayı biraz uzaklaştırın ve şimdi gokyuzu’nün position’ının X değerini 1 yapın. Gökyüzü uzayda bir miktar sağa kaydı değil mi? İşte gökyüzünün uzaydaki bu kayma miktarına 1 uzay birimi diyoruz. (Sahnedeki gokyuzu’nü geri silmeyi unutmayın)

— 1 Sprite Birimi: Bu birim bir sprite’nin genişliğinin kaç pixel olduğuna denk gelmiyor, hayır. Bu birim sprite’deki kaç pixelin 1 uzay birimine denk geldiğini belirtiyor. Ve bu birimin değerini bulmak çok kolay: Hemen Project panelinden gokyuzu sprite’sini veya herhangi başka sprite’yi seçin. Inspector panelinde Pixels to Units diye bir değer var. İşte orada yazan değer tam olarak 1 sprite birimine eşit. Varsayılan olarak 1 sprite birimi 100 pixeldir. Yani bir spritedeki 100 pixel 1 uzay birimine denk gelir. Yani tam 100 pixel genişliğinde bir sprite’den sahneye iki tane koyarsanız ve birinin X değerini 0, ötekinin X değerini 1 yaparsanız bu iki sprite mükemmel bir şekilde yan yana otururlar. Umarım bu iki birim de anlaşılmıştır.

NOT: Sprite Birimi‘ni bu ders için ben uydurdum. Gerçekte böyle bir birim olduğunu sanmıyorum. Dersin daha rahat anlaşılması için böyle bir yola başvurdum.

Şimdi kameraUnityEbatlar değişkenini açıklamaya başlayabilirim. Bu değişken bir Vector2, yani X ve Y değerlerine sahip. Değişkenin X değerinde kameranın tam ortası ile tam solu arasının kaç Uzay Birimi‘ne denk geldiği kaydedilirken Y değerinde kameranın tam ortası ile tam tepesi arasının kaç Uzay Birimi‘ne denk geldiği kaydediliyor. Yani X değeri kameranın genişliğinin kapladığı Uzay Birimi’nin yarısını, Y değeri de kameranın yüksekliğinin kapladığı Uzay Birimi’nin yarısını depoluyor.

gokyuzuUnityEbatlar: Bu da bir Vector2. Değişkenin X değeri gokyuzu sprite’mizin genişliğinin kaç Uzay Birimi‘ne denk geldiğini, Y değeri ise gokyuzu sprite’mizin yüksekliğinin kaç Uzay Birimi‘ne denk geldiğini depoluyor. Bu değişkenin değerini hemen şimdi beraber hesaplayalım ki konu aklınıza daha da yatsın. Soruyorum size: 1 Sprite Birimi kaçtır? Ve soruyu sorduğum gibi umarsızca kendim cevaplıyorum: 100! Evet, 1 Sprite Birimi 100 pixele eşit (daha 3 paragraf önce gördük, unuttum demeyin). Bizim gokyuzu sprite’mizin ebatları tamı tamına 288×414 pixel. Deminden beridir 1 Sprite Biriminin 100 pixele eşit olduğundan da bahsediyoruz. Ve 1 Sprite Biriminin tanımını hatırlayın: Spritedeki kaç pixelin 1 Uzay Birimi‘ne denk geldiğini belirten birim. E hesaplayın o zaman 288×414 pixellik gokyuzu spritemiz yatayda ve dikeyde kaç Uzay Birimi‘ne denk geliyor? Çok kolay, değil mi? Tek yapmamız gereken 288’i 100’e bölmek ve şıp diye cevabı buluyoruz: 2.88 . Evet, sprite yatayda 2.88 uzay birimine denk geliyor. Dikeyde de 414 / 100 = 4.14 uzay birimine denk geliyor. Kendinizi alkışlayabilirsiniz. Peki bu bulduğumuz değerler ne demek? Şöyle ki eğer bir gokyuzu sprite’sini X=0 konumuna koyduysak diğerini tam olarak X=2.88’e koyduğumuz vakit bu iki sprite tam olarak yan yana oturmuş olacak ve sprite’mizin sağıyla solu sanki birbirini devam ettiriyormuş gibi güzel resmedildiği için yan yana duran bu iki sprite birbirini devam ettirecek.

toprakUnityEbatlar: Ve yine Vector2 türünde bir değişken! Bu değişken de artık tahmin edebileceğiniz üzere toprak sprite’sinin yatayda ve dikeyde kaç Uzay Birimi‘ne denk geldiğini depoluyor. Spritenin 288×32 pixel olduğunu belirttikten sonra bu değişkenin X değerinin 2.88, Y değerinin 0.32 olduğunu ben bu cümleyi bitirmeden hesapladığınızı umuyorum.

Oh be! Değişkenleri anlatabildim sonunda. Umarım hepsini bir güzel anlamışsınızdır. Şimdi scriptteki tek fonksiyon olan Start fonksiyonuna bakalım.

İlk iki satırda gokyuzuUnityEbatlar‘ın ve toprakUnityEbatlar‘ın değerlerini aldığını görüyoruz. İstesem gokyuzuUnityEbatlar’a direkt Vector2( 2.88, 4.14 ) değerini verebilirdim ve emin olun oyun yine düzgün şekilde çalışırdı. Ama ben daha genel bir çözüm yoluna gittim ve spritenin ebatlarını bilmediğimizi varsaydım. Bu yüzden de spritenin ebatlarını kod vasıtasıyla çektim Unity’den ve bu ebatları 100’e (1 Sprite Birimi) bölerek spritenin kaç Uzay Birimine denk geldiğini buldum. Örneğin gokyuzu sprite’sinin kaç pixel genişliğinde olduğunu nasıl bulduğumu anlatayım: Bizim sprite’lerimizin Sprite Renderer diye bir component’i var. Bu component o objedeki sprite’yi ve başka birkaç değeri depoluyor. Her sprite’nin ise ebatlarının depolandığı kendi değişkeni bulunur ve bu değişkenin adı rect‘tir. Yani gokyuzu’nün kaç pixel genişliğinde olduğunu bulmak için yapmam gereken gokyuzu prefab’ındaki Sprite Renderer component’inde yer alan sprite’ye erişmek ve onun rect değişkeninin width (genişlik) değerini okumak. Bir objenin Sprite Renderer component’ine erişmek için yazmanız gereken kod da şöyle: gokyuzu.renderer as SpriteRenderer. rect değişkeninden genişliği width ile okuyoruz, yüksekliği ise height ile okuyoruz. Artık bu kısmı anladığınızı varsayıyorum.

Gelelim Start‘ın 3. satırına. Burada camera.orthographicSize değişkeninin değerini ayarlıyoruz. Bu değişken de neyin nesi? Bu değişken her Orthographic render yapan kamerada bulunur ve kameranın orta noktası ile tepe noktası arasının kaç Uzay Birimi‘ne denk geldiğini depolar. Yani bizim kameraUnityEbatlar değişkeninin Y değerinde depolayacağımız değerle birebir aynı değeri depolar. Peki değişkene değerini nasıl veriyoruz? gokyuzu sprite’sinin ve toprak sprite’sinin kaç Uzay Birimi yüksekliğinde olduğunu topluyoruz ve çıkan değeri 2’ye bölüyoruz çünkü dediğim gibi, orthographicSize değişkeni kameranın sadece ortası ile tepesi arasındaki yüksekliğin kaç Uzay Birimi olduğunu depoluyor.

Peki arkaplanSayisi‘nı nasıl hesaplıyoruz? Burada şu bilgiyi vermem lazım: Orthographic kameralarda (Unity 2D’de varsayılan kamera tipimiz) kameranın yüksekliğinin aldığı Uzay Birimi değerini camera.aspect isimli bir değişkenle çarparsanız kameranın genişliğinin kaç Uzay Birimine karşılık geldiğini bulursunuz. Kameranın yüksekliğinin yarısının kaç uzay birimine denk geldiğini biliyoruz: camera.orthographicSize. O halde bu değişkeni 2 ile çarpınca kameranın yüksekliğini, çıkan değeri de camera.aspect ile çarpınca kameranın genişliğini bulmuş oluyoruz.

Yaptığımız şey önce kameranın genişliğini gokyuzu sprite’sinin genişliğine bölmek ve çıkan değeri Mathf.CeilToInt() fonksiyonu ile yukarı yuvarlamak (yani sonuç 0.5, 0.6 ve hatta 0.1 çıksa bile 1 döndürülür). Ardından bu değere 1 eklemek. Niçin 1 eklediğimizi değişkeni açıklarken anlatmaya çalışmıştım. Ama şimdi sizinle ufak bir simülasyon yaparak konuyu pekiştireceğiz: Diyelim ki ekranımızın genişliği çok küçük. O kadar küçük ki bir gokyuzu sprite’sini bile tamamen göstermeye yetmiyor. Bu durumdayken arkaplanSayisi’nı hesaplayalım: kameranın genişliği bir gokyuzu sprite’sini bile göstermeye yetmiyorsa o zaman kameranın genişliğini gokyuzu sprite’sinin genişliğine bölersek 0 ile 1 arasında bir sayı elde ederiz. Mathf.CeilToInt ile bu sayıyı yukarıya yuvarlayıp 1 elde ediyoruz ve ardından sonuca 1 ekliyoruz. Yani bu durumda arkaplanSayisi‘nın değeri 2. Bu da demek oluyor ki oyun başladığında yan yana 2 tane arkaplan objesi oluşturacağız. Peki bu 2 arkaplan objesi bizim hiç kameranın kendi mavi arkaplanını görmememize yetecek mi? Elbette evet! Düşünün hele; kamera 1 arkaplanı bile tam olarak sığdıramıyor ekrana. Bu da demek oluyor ki kamera ikinci arkaplan objesinin ortalarına geldiğinde soldaki arkaplan objesi kameranın dışında kalacak ve ileride düzenleyeceğimiz script vasıtasıyla ikinci arkaplanın tam sağına ışınlanacak. Bu sayede kamera ikinci arkaplanın sonuna ulaşıp biraz daha sağa kaydığında kendi mavi arkaplanını görmek yerine eskiden solda olan ama artık sağa ışınlanmış olan birinci arkaplan objesini görecek. Ve bu döngü böyle devam edecek.

Şimdi bir satır daha aşağı inelim. Burada kameraUnityEbatlar‘a değeri veriliyor. Bu satırı artık açıklamayacağım çünkü şu ana kadar hem kameraUnityEbatlar’ın neyi depoladığından, hem camera.orthographicSize‘dan hem de camera.orthographicSize’ı camera.aspect’le çarparsak ne olur defalarca bahsettim.

Start‘ın en altında arkaplanSayisi kadar çalışan bir for döngüsü görüyoruz. Bu for döngüsünde Instantiate komutu ile arkaplan objelerini (gokyuzu ve toprak) ekliyoruz. Kameranın transform.position değeri kameranın tam merkezinin uzaydaki konumunu depolar. Bu değerin X bileşeninden kameraUnityEbatlar.x‘i çıkarırsak kameranın en sol noktasının koordinatını, bu değerin Y bileşenine kameraUnityEbatlar.y‘yi eklersek kameranın en üst noktasının koordinatını buluruz (Y koordinatı aşağıdan yukarıya doğru artar). Kameramızın Y değeri zaten 0 olduğu için kameraUnityEbatlar.y diyince direkt kameranın en tepe noktasının koordinatını elde ediyoruz.

Hatırlarsanız gokyuzu ve toprak objelerinin Pivot‘larını Top Left yaparak onların position değişkenlerinin en sol üst noktaları için hesaplanmasını sağlamıştık. Ve yine hatırlarsanız kameranın orthographicsSize’ını öyle ayarlamıştık ki kameranın yüksekliği bir gokyuzu ile bir toprak sprite’sini alt alta tam olarak sığdıracak kadardı. O zaman sahnemizdeki gokyuzu sprite’lerinin Y koordinatları ekranın tam tepesine denk gelmeli ve toprak sprite’lerinin Y koordinatları da yerleştirdiğimiz bu gokyuzu sprite’lerinin tam en alt noktalarının uzaydaki koordinatına denk gelmeli. Bir başka deyişle kameranın en tepesinin koordinatından gokyuzu’nün yüksekliğinin Uzay Birimi cinsinden dengini çıkarınca elde edeceğimiz sonuç toprak sprite’lerinin Y koordinatlarına denk gelmiş olacak.

Peki X koordinatı nasıl hesaplanıyor? Bir kere gokyuzu ve toprak tam alt alta oldukları için ikisinin de X koordinatı aynı olacak, bu konuda hemfikiriz umarım. En soldaki gokyuzu ve toprak ikilisinin X koordinatı kameranın en sol noktasının koordinatına eşit olacak ve bu da kameranın kendi X koordinatından (position.x) kameraUnityEbatlar.x‘i çıkarınca bulduğumuz değer (aslında kameranın X koordinatı başta 0 olduğu için orada transform.position.x ifadesine gerek yokmuş). Soldan 2. arkaplan objelerini (arkaplan objelerinden kastım her zaman için toprak ve gokyuzu objeleri) nereye ekliyoruz? 1. arkaplan objelerinin hemen sağına. Bu nereye denk geliyor? Ekranın sol noktasının koordinatına gokyuzu’nün yatayda kapladığı Uzay Birimi’ni ekleyince bulduğumuz koordinata denk geliyor. Ve bu obje oluşturma döngüsü böyle devam edip gidiyor (arkaplanSayisi kadar kez).

NOT: Instantiate‘e yazdığım Quaternion.identity‘nin nolduğunu merak ediyor olabilirsiniz. Bir objeyi Instantiate ederken eğim olarak Quaternion.identity verirseniz o objenin Transform component’indeki Rotation değeri 0,0,0 olur. Bu kadar basit!

Veee bitti! Bu ders boyunca karşılaşacağınız en karmaşık scripti böylece bitirmiş olduk. Umarım hemen her şeyi anlamışsınızdır. Bu kadar çok uğraştık yok Script Birimi‘dir yok Uzay Birimi‘dir yok kameranın orthographicSize‘ıdır ile ama inanın buna değdi. Çünkü artık oyunu hangi ama hangi ekran çözünürlüğünde oynarsak oynayalım arkaplan ekrana hep tam olarak oturacak ve birazdan düzenleyeceğimiz script ile arkaplan sanki hiç bitmiyormuş havası alacağız.

Kameranın Dışında Kalan Arkaplanları En Sağa Işınlamak

Bu bölümde yapacağımız iş de arkaplanla alakalı olduğu için son bölümde yazdığımız Arkaplan.js scriptinde işlem yapmaya devam edeceğiz. Bu bölümde çok az bir iş yapacağımız için bittiğini anlamayacaksınız bile.

Arkaplan.js scriptini açın ve içeriğini şöyle güncelleyin:

#pragma strict

public var gokyuzu : GameObject;
public var toprak : GameObject;

private var arkaplanSayisi : int;

private var kameraUnityEbatlar : Vector2;
private var gokyuzuUnityEbatlar : Vector2;
private var toprakUnityEbatlar : Vector2;

private var gokyuzuObjeleri : Transform[];
private var toprakObjeleri : Transform[];
private var bastakiArkaplanObjesi : int = 0;

function Start()
{
	gokyuzuUnityEbatlar = Vector2( ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.width, ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.height ) / 100;
	toprakUnityEbatlar = Vector2( ( toprak.renderer as SpriteRenderer ).sprite.rect.width, ( toprak.renderer as SpriteRenderer ).sprite.rect.height ) / 100;

	camera.orthographicSize = ( gokyuzuUnityEbatlar.y + toprakUnityEbatlar.y ) / 2;
	arkaplanSayisi = Mathf.CeilToInt( ( camera.orthographicSize * 2 * camera.aspect ) / gokyuzuUnityEbatlar.x ) + 1;

	kameraUnityEbatlar = Vector2( camera.orthographicSize * camera.aspect, camera.orthographicSize );

	gokyuzuObjeleri = new Transform[ arkaplanSayisi ];
	toprakObjeleri = new Transform[ arkaplanSayisi ];

	for( var i = 0; i < arkaplanSayisi; i++ )
	{
		var xKoordinati : float = transform.position.x - kameraUnityEbatlar.x + i * gokyuzuUnityEbatlar.x;
		gokyuzuObjeleri[i] = Instantiate( gokyuzu, Vector3( xKoordinati, kameraUnityEbatlar.y, 0 ), Quaternion.identity ).GetComponent( Transform );
		toprakObjeleri[i] = Instantiate( toprak, Vector3( xKoordinati, kameraUnityEbatlar.y - gokyuzuUnityEbatlar.y, 0 ), Quaternion.identity ).GetComponent( Transform );
	}
}

function Update()
{
	if( transform.position.x - kameraUnityEbatlar.x >= gokyuzuObjeleri[bastakiArkaplanObjesi].position.x + gokyuzuUnityEbatlar.x )
	{
		gokyuzuObjeleri[bastakiArkaplanObjesi].position.x += arkaplanSayisi * gokyuzuUnityEbatlar.x;
		toprakObjeleri[bastakiArkaplanObjesi].position.x += arkaplanSayisi * gokyuzuUnityEbatlar.x;
		bastakiArkaplanObjesi++;

		if( bastakiArkaplanObjesi == gokyuzuObjeleri.Length )
			bastakiArkaplanObjesi = 0;
	}
}

Şimdi scripti kaydedip oyunu çalıştırın. Arkaplan artık hiç bitmeyecektir (daha doğrusu öyle bir izlenim uyandıracaktır.). Dilerseniz oyunu çalıştırdıktan sonra Scene paneline geçiş yapın ve kamera objesini ekrandan takip edin. En soldaki arkaplan objesi kameranın görüş alanından çıktığı anda en sağa ışınlanmakta. Ve inanın bunu yapması hiç de zor değildi. Şimdi siz de göreceksiniz.

Scriptte 3 yeni değişken tanımladım:

gokyuzuObjeleri: Instantiate kullanarak oyunun başında oluşturduğumuz gokyuzu objelerini depolayan bir array (dizi). Fark ettiyseniz türü Transform çünkü gokyuzu objelerini bu şekilde tutunca onların Transform component’inin position değerine direkt position diyerek ulaşabileceğiz. Eğer array’in türü GameObject olsaydı her seferinde transform.position demek zorunda kalacaktık. Peh!

toprakObjeleri: Oyunun başında Instantiate kullanarak oluşturduğumuz toprak objelerini depolayan Transform türünde bir array.

bastakiArkaplanObjesi: Bu değer elimizdeki iki array‘deki hangi sıradaki arkaplan objesinin o anda en solda yer aldığını depoluyor. Oyunun en başında en soldaki arkaplan objeleri arrayin 0. elemanı, onun sağındaki arkaplan objeleri 1. elemanı oluyor ve bu böyle gidiyor. Bu değişkenin ilk değeri de göreceğiniz üzere 0. Yani oyunun en başındayken en soldaki arkaplan objesini temsil ediyor. Ardından diyelim ki kamera biraz ilerledi ve en soldaki arkaplan objeleri en sağa ışınlandı. Artık array’in 1. elemanları (oyun başlarken soldan ikinci sırada olan) en başta oldu. Bu yüzden biz de tam bu anda bastakiArkaplanObjesi değerini artırıp 1 yapıyoruz (birazdan göreceksiniz).

Gelelim Start fonksiyonuna. Burada for döngüsünden hemen önce gokyuzuObjeleri ve toprakObjeleri‘ni initialize ediyorum (yani oluşturuyorum). Bir array’in nasıl oluştuğunu burada görebilirsiniz: gokyuzuObjeleri = new Transform[ arkaplanSayisi ];

Önce new takısı geliyor, ardından array‘in türü yazılıp köşeli parantezler arasına array’in alacağı eleman sayısı yazılıyor. Böylece işletim sistemi bize hafızadan o kadar elemanı sığdıracak büyüklükte bir yer ayarlıyor ve biz de elemanlarımızı bu hafızaya kaydediyoruz (tabi bu hafıza ile ilgili kısımları hep işletim sistemi hallettiği için biz görmüyoruz bile). Bizim array’lerimizin eleman sayısı arkaplanSayisi kadar çünkü for döngüsü içinde o kadar arkaplan objesi oluşturuyoruz.

for döngüsüne girecek olursak ufacık bir fark göreceğiz. Artık Instantiate ile oluşan arkaplan objelerinin Transform component’lerini GetComponent( Transform ) kodu ile alıyor ve bunu array’lerimizin ilgili elemanına değer olarak atıyoruz.

Update() fonksiyonu scripte yeni geldi. Bu fonksiyon bir tane if‘ten ibaret. Bu if koşulunun içinde kameranın en sol noktasının koordinatının (transform.position.x – kameraUnityEbatlar.x) arkaplan objelerinin en solda yer alanının en sağ noktasının koordinatından (gokyuzuObjeleri[bastakiArkaplanObjesi].position.x + gokyuzuUnityEbatlar.x) büyük olup olmadığına bakıyoruz. Bir başka deyişle en soldaki arkaplan objesinin kameranın görüş alanının dışına çıkıp çıkmadığına bakıyoruz. Eğer çıkmışsa if‘in içine giriyoruz.

if koşulunun içinde yaptığımız şeyler çok basit: en soldaki gokyuzu ve toprak objelerinin position‘larının X değerini en soldan en sağa ışınlanacak şekilde artırıyoruz. Bunu nasıl yapıyoruz? Çok basit: sahnedeki toplam arkaplan objesi sayısını bir arkaplan objesinin genişliği ile çarpıyoruz ve bu değeri soldaki arkaplan objesinin position.x‘ine ekliyoruz. Hemen ardından bastakiArkaplanObjesi değerini 1 artırıyoruz. Yani Unity’e diyoruz ki artık en soldaki arkaplan objesi 0. indexteki değil 1. indexteki (oyunun en başındaki durumu referans alırsak). Çünkü bir satır önce yaptığımız ışınlama sonucu artık 0. indexteki arkaplan objeleri en sağdaki arkaplan objeleri oldu.

Peki diyelim oyunda biraz ilerledik, en soldaki arkaplan objeleri sürekli en sağa ışınlandı ve oyunun başında en solda yer alan arkaplan objesi yine en sola geldi. Şimdi bastakiArkaplanObjesi‘nin değerini geri 0 yapmalıyız. Bunun için de bu değişkenin değerinin gokyuzuObjeleri.Length‘e, yani gokyuzuObjeleri array‘inin uzunluğuna eşit olup olmadığına bakıyoruz. Eğer eşitse anlıyoruz ki array’deki tüm elemanların üzerinden geçmiş ve array’in sonuna gelmişiz. E haliyle bastakiArkaplanObjesi’ni geri sıfırlıyoruz.

Böylece bir başka bölümü daha geride bırakıyoruz.

BONUS – Gökyüzü Arkaplanının Daha Yavaş Sola Kayması (Parallax Scrolling)

Bu bölümde ekrandaki gokyuzu sprite’lerinin toprak sprite’lerine nazaran daha yavaş sola kaymasını sağlayacağız (çünkü gokyuzu mantıken toprak’tan daha uzak kameraya ve uzaktaki şeyler gerçek hayatta yakındaki şeylere göre daha yavaş görüş alanımızın dışına doğru kayar).

NOT: Bu bölümden vazgeçip vazgeçmemek arasında çok gittim çünkü ne kadar uğraşırsam uğraşayım bu özelliği oyuna ekledikten sonra bazen iki gökyüzü sprite’si arasında ufacık bir boşluk oluyordu birkaç milisaniyeliğine ve bu boşluk hemen ardından kapanıyordu. Ama bu beni rahatsız etti. Scriptte birkaç deneme yanılma yoluyla en son yaptığım değişiklik sonucu bu boşluk sorunu çok az bir düzeye indi ve ben de bu yüzden bu bölümden vazgeçmemeye karar verdim. Ama eğer isterseniz bu bölümü es geçebilirsiniz, biraz görsellikten kaybedersiniz o kadar. Önemli bir kaybınız olmaz.

Kameranın sağa doğru gitme hızı sabit, yani kamerayı gokyuzu için yavaş, toprak için hızlı bir şekilde sağa kaydıramayız. Peki napacağız? Ufak bir hileye başvuracağız ve gokyuzu sprite’lerine biraz sağa doğru yatay hız vereceğiz. Ama bu hız kameranın sağa kayma hızından yavaş olacak, böylece kameranın gokyuzu sprite’lerini yakalaması zorlaşacak ve sanki gokyuzu daha yavaş sola kayıyormuş gibi bir izlenim uyanacak bizde.

Hemen şimdi Arkaplan.js scriptini açın ve içeriğini şöyle güncelleyin:

#pragma strict

public var gokyuzu : GameObject;
public var toprak : GameObject;

public var gokyuzuSagaKaymaHizi : float = 1.0;

private var arkaplanSayisi : int;

private var kameraUnityEbatlar : Vector2;
private var gokyuzuUnityEbatlar : Vector2;
private var toprakUnityEbatlar : Vector2;

private var gokyuzuObjeleri : Transform[];
private var toprakObjeleri : Transform[];
private var bastakiGokyuzuArkaplanObjesi : int = 0;
private var bastakiToprakArkaplanObjesi : int = 0;

private var gokyuzuParent : Transform;

function Start()
{
	gokyuzuUnityEbatlar = Vector2( ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.width, ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.height ) / 100;
	toprakUnityEbatlar = Vector2( ( toprak.renderer as SpriteRenderer ).sprite.rect.width, ( toprak.renderer as SpriteRenderer ).sprite.rect.height ) / 100;

	camera.orthographicSize = ( gokyuzuUnityEbatlar.y + toprakUnityEbatlar.y ) / 2;
	arkaplanSayisi = Mathf.CeilToInt( ( camera.orthographicSize * 2 * camera.aspect ) / gokyuzuUnityEbatlar.x ) + 1;

	kameraUnityEbatlar = Vector2( camera.orthographicSize * camera.aspect, camera.orthographicSize );

	gokyuzuObjeleri = new Transform[ arkaplanSayisi ];
	toprakObjeleri = new Transform[ arkaplanSayisi ];

	gokyuzuParent = new GameObject().GetComponent(Transform);

	for( var i = 0; i < arkaplanSayisi; i++ )
	{
		var xKoordinati : float = transform.position.x - kameraUnityEbatlar.x + i * gokyuzuUnityEbatlar.x;
		gokyuzuObjeleri[i] = Instantiate( gokyuzu, Vector3( xKoordinati, kameraUnityEbatlar.y, 0 ), Quaternion.identity ).GetComponent( Transform );
		gokyuzuObjeleri[i].parent = gokyuzuParent;
		toprakObjeleri[i] = Instantiate( toprak, Vector3( xKoordinati, kameraUnityEbatlar.y - gokyuzuUnityEbatlar.y, 0 ), Quaternion.identity ).GetComponent( Transform );
	}
}

function Update()
{
	if( transform.position.x - kameraUnityEbatlar.x >= gokyuzuObjeleri[bastakiGokyuzuArkaplanObjesi].position.x + gokyuzuUnityEbatlar.x )
	{
		gokyuzuObjeleri[bastakiGokyuzuArkaplanObjesi].localPosition.x += arkaplanSayisi * gokyuzuUnityEbatlar.x;
		bastakiGokyuzuArkaplanObjesi++;

		if( bastakiGokyuzuArkaplanObjesi == gokyuzuObjeleri.Length )
			bastakiGokyuzuArkaplanObjesi = 0;
	}

	if( transform.position.x - kameraUnityEbatlar.x >= toprakObjeleri[bastakiToprakArkaplanObjesi].position.x + toprakUnityEbatlar.x )
	{
		toprakObjeleri[bastakiToprakArkaplanObjesi].position.x += arkaplanSayisi * gokyuzuUnityEbatlar.x;
		bastakiToprakArkaplanObjesi++;

		if( bastakiToprakArkaplanObjesi == gokyuzuObjeleri.Length )
			bastakiToprakArkaplanObjesi = 0;
	}

	gokyuzuParent.position.x += gokyuzuSagaKaymaHizi * Time.deltaTime;
}

Gelelim scriptte neler nelerin yenilendiğine. Maalesef ki gökyüzünün daha yavaş sola kaymasını sağlamak düşündüğümden daha zor oldu ama sonuçta ortaya daha güzel bir görüntü çıktı (bence).

gokyuzuSagaKaymaHizi: Scripte yeni eklenen değişkenlerden biri. Bu değişkenin değerini ne kadar artırırsanız gokyuzu objeleri sahnede (Scene paneli) o kadar hızlı sağa kadar ve haliyle Game panelinde o kadar yavaş sola kayıyor izlenimi uyandırır. Yalnız eğer değişkenin değeri kusHareket scriptindeki yatayHiz değişkeninin değerini geçerse bu sefer arkaplan gerçek anlamda sağa kaymaya başlar (Game panelinde de sağa kayar). Bu istemediğimiz bir durum olduğu için script vasıtasıyla buna izin vermiyorum da zaten (Start‘ta göreceksiniz).

bastakiGokyuzuArkaplanObjesi ve bastakiToprakArkaplanObjesi: Eskiden bu ikisinin değeri aynıydı ve bastakiArkaplanObjesi değişkeninde tutuluyordu. Ama artık gökyüzü daha yavaş sola kaydığı için gökyüzü arkaplanı objelerinin kameranın solundan dışına çıkıp sağına ışınlanması daha uzun sürecek ve haliyle bir süre sonra en soldaki gokyuzu objesinin array‘deki sırası (index) ile en soldaki toprak objesinin array’deki sırası (index) farklı olacak. Bu yüzden bastakiArkaplanObjesi değişkenini böyle ikiye ayırdık.

gokyuzuParent: Bu değişken içinde bir Empty GameObject depolayacak ve bu Empty GameObject sahnedeki tüm gokyuzu objelerinin parent‘ı olacak. Bu sayede tüm gokyuzu objelerini tek tek sağa oynatmak yerine sadece gokyuzuParent objesini sağa oynatmamız yetecek (unutmayın; A objesi B objesinin parent objesi ise A objesi ne yöne hareket ederse B objesi de otomatik olarak o yöne hareket eder.). Parent obje kullanmanın bir başka avantajı da bir şekilde bu yöntemin ekranda oluşan boşlukların (bölümün başında bahsettiğim şey) sayısını çok aza indirgemesi. Sebebini bilmiyorum, ben de deneme-yanılma yoluyla bu yöntemin iyi olduğunu fark ettim.

Şimdi Start fonksiyonunun içine girelim. Burada yaptığım ilk değişiklik 2 satırlık bir if koşulu eklemek oldu. Bu if koşulunun içinde gokyuzuSagaKaymaHizi‘nın FlappyBird objemizdeki KusHareket scriptinde yer alan yatayHiz değişkeninin değerinden büyük olup olmadığına bakıyoruz. gokyuzuSagaKaymaHizi değişkenini tanıtırken belirttiğim gibi; eğer gökyüzünün sağa kayma hızı kameranın sağa kayma hızından büyük olursa (KusHareket scriptindeki yatayHiz değeri) arkaplan gerçek anlamda sağa kayar. Biz bunu istemediğimizden bu durumda gokyuzuSagaKaymaHizi‘nın değerini KusHareket’teki yatayHiz’a eşitliyorum. Böylece arkaplan sağa doğru kaymak yerine kamera ile birlikte hareket edecek (ne sola kayacak ne sağa kayacak).

if koşulunun hemen altında “gokyuzuParent = new GameObject().GetComponent(Transform);” kodunu görebilirsiniz. gokyuzuParent‘ın bir Empty GameObject olduğundan bahsetmiştim. Ama bu Empty GameObject’in oyunun başında script vasıtasıyla oluşturulduğundan bahsetmedim. Bu işlemi yapmak için ise (oyun sırasında sahnede Empty GameObject oluşturmak) Unity’nin şu komutunu kullanıyoruz: new GameObject(). Bu komut sahnede 0,0,0 position koordinatlarında yeni bir Empty GameObject oluşturup onu bize döndürüyor. Ben de hemen ardından bu GameObject’in Transform component’ine erişiyor ve onu gokyuzuParent‘a atıyorum (gokyuzuParent’ın türünün Transform olduğuna dikkat edin).

Start fonksiyonundaki son değişiklik ise for döngüsünün içindeki yeni bir kod satırı: “gokyuzuObjeleri[i].parent = gokyuzuParent;“. Bu kod sayesinde oluşturduğumuz gokyuzu objelerine parent olarak gokyuzuParent objesini atıyoruz. İşte bu kadar basit!

Update fonksiyonuna gelelim. Hatırlarsanız burada eskiden sadece bir if koşulu vardı ve o da en soldaki gokyuzu objesinin kameranın görüş alanından çıkıp çıkmadığına bakıyordu. Eğer çıkmışsa en soldaki gokyuzu ve toprak objelerini en sağa ışınlıyordu (gokyuzu ve toprak o sıralar aynı hızda hareket ettikleri için biri dışarı çıkarsa bu ötekinin de dışarı çıktığı anlamına geliyordu). Artık gokyuzu ve toprak arkaplan objelerinin sola kayma hızları farklı olduğu için en soldaki gokyuzu ve en soldaki toprak objelerinin kameranın dışına çıkıp çıkmadıklarını ayrı ayrı kontrol etmeliyiz. Bu yüzden de Update‘teki tek if koşulunu ikiye ayırdım. İlk if koşulu en soldaki gokyuzu objesinin, ikinci if koşulu da en soldaki toprak objesinin kameranın dışına çıkıp çıkmadığını kontrol ediyor.

Update‘in en altına ise yeni bir kod ekledik. Bu kodun tek yaptığı şey gokyuzuParent objesini (haliyle bu objenin child objesi olan tüm gokyuzu objelerini) yatay koordinat düzleminde saniyede (Time.deltaTime kullandığıma dikkat edin) gokyuzuSagaKaymaHizi kadar sağa kaydırmak. Hiç de karışık değilmiş, öyle değil mi!

Böylece bir başka bölümün daha sonuna varmış bulunmaktayız.

Engelleri Rastgele Şekilde Oluşturmak

Bu bölümde oyuna meşhur engellerimizi ekleyeceğiz. Engellerin yüksekliği rastgele olarak ayarlanacak ve kameranın dışında kalan engeller tıpkı arkaplan objelerinde olduğu gibi en sağa ışınlanacak. Bu da demek oluyor ki oyun boyunca kullanacağımız tüm engelleri de arkaplan objeleri gibi oyunun en başında oluşturacağız.

Bu iş için Engeller adında yeni bir Javascript oluşturup bunu da Main Camera‘ya atayın. Hatırlarsanız gokyuzu ve toprak objelerinin Pivot‘larını Top Left yaparak position değerlerinin en sol üst noktalarını temsil etmesini sağlamıştık. Şimdi benzer şekilde altEngel sprite’si için de Pivot’u Top Left yapın (Apply demeyi unutmayın) ama bu sefer ustEngel objesi için Pivot’u Bottom Left yapın:

resim11

Neden böyle yaptık hemen açıklayayım. Yeni yazacağımız scriptimizde engelleri oluştururken kullandığımız algoritma şöyle: kameranın üst noktasının koordinatı ile alt noktasının koordinatı arasında rastgele bir koordinat seçiyoruz ve bu koordinatı üst ve alt engel sprite’leri arasındaki boşluğun sol üst noktası olarak kabul ediyoruz (bu noktaya yKoordinati diyelim). Eğer ki ustEngel‘in Pivot‘u da Top Left olsaydı üst engeli yerleştirirken engelin Y koordinatını yKoordinati + ustEngel sprite’sinin yüksekliğinin denk geldiği Uzay Birimi şeklinde yapacaktık. Böylece ustEngel’in en alt noktası bu boşluğun başlangıcına denk gelecekti. Ama artık Pivot’u Bottom Left yaptığımız için ustEngel’in transform.position değeri sprite’sinin en sol alt noktasını temsil edecek. Yani objeyi direkt yKoordinati‘na yerleştirmek işimizi görecek.

Bu kısacık açıklamanın ardından scripti açın ve içeriğini şöyle düzenleyin:

#pragma strict

public var ustEngel : GameObject;
public var altEngel : GameObject;

public var altUstEngelArasiBosluk : float = 0.8;
public var ikiEngelArasiMesafe : float = 2;

private var engelSayisi : int;

private var kameraUnityEbatlar : Vector2;
private var engelUnityEbatlar : Vector2;

private var ustEngelObjeleri : Transform[];
private var altEngelObjeleri : Transform[];
private var bastakiEngelObjesi : int = 0;

function Start ()
{
	engelUnityEbatlar = Vector2( ( ustEngel.renderer as SpriteRenderer ).sprite.rect.width, ( ustEngel.renderer as SpriteRenderer ).sprite.rect.height ) / 100;
	kameraUnityEbatlar = Vector2( camera.orthographicSize * camera.aspect, camera.orthographicSize );

	engelSayisi = Mathf.CeilToInt( ( camera.orthographicSize * 2 * camera.aspect ) / ( engelUnityEbatlar.x + ikiEngelArasiMesafe ) ) + 1;

	ustEngelObjeleri = new Transform[ engelSayisi ];
	altEngelObjeleri = new Transform[ engelSayisi ];

	for( var i = 0; i < engelSayisi; i++ )
	{
		var xKoordinati : float = transform.position.x + kameraUnityEbatlar.x + i * ( engelUnityEbatlar.x + ikiEngelArasiMesafe );
		var yKoordinati : float = Random.Range( -kameraUnityEbatlar.y + altUstEngelArasiBosluk + 0.6, kameraUnityEbatlar.y - 0.6 );
		ustEngelObjeleri[i] = Instantiate( ustEngel, Vector3( xKoordinati, yKoordinati, 0 ), Quaternion.identity ).GetComponent( Transform );
		altEngelObjeleri[i] = Instantiate( altEngel, Vector3( xKoordinati, yKoordinati - altUstEngelArasiBosluk, 0 ), Quaternion.identity ).GetComponent( Transform );
	}
}

function Update()
{
	if( transform.position.x - kameraUnityEbatlar.x >= ustEngelObjeleri[bastakiEngelObjesi].position.x + engelUnityEbatlar.x )
	{
		var yKoordinati : float = Random.Range( -kameraUnityEbatlar.y + altUstEngelArasiBosluk + 0.6, kameraUnityEbatlar.y - 0.6 );

		ustEngelObjeleri[bastakiEngelObjesi].position.x += engelSayisi * ( engelUnityEbatlar.x + ikiEngelArasiMesafe );
		altEngelObjeleri[bastakiEngelObjesi].position.x += engelSayisi * ( engelUnityEbatlar.x + ikiEngelArasiMesafe );

		ustEngelObjeleri[bastakiEngelObjesi].position.y = yKoordinati;
		altEngelObjeleri[bastakiEngelObjesi].position.y = yKoordinati - altUstEngelArasiBosluk;

		bastakiEngelObjesi++;

		if( bastakiEngelObjesi == ustEngelObjeleri.Length )
			bastakiEngelObjesi = 0;
	}
}

Tam bu noktada ufak ayarlamaları ve script’i tanıtmayı yapmadan önce bir şey yapmamız lazım: Arkaplan.js scriptini geri açın ve Start fonksiyonunu Awake fonksiyonu olarak değiştirin (function Start() satırını function Awake() olarak değiştirin.). Bunu yapmanız çok önemli çünkü Awake fonksiyonu her zaman Start‘tan önce çalışır ve bizim oyunumuzda Arkaplan.js scriptinin Engeller.js scriptinden önce çalışması lazım. Peki neden? Çünkü Engeller.js scriptinde de kameraUnityEbatlar diye bir değişken var ve bu değişkenin değerini düzgün alması için önce Arkaplan.js scriptindeki camera.orthographicsSize = blabla; satırının çalışması gerekiyor.

Şimdi ustEngel ve altEngel‘i birer prefab‘a çevirip Main Camera‘daki Engeller (Script) component’ine parametre olarak atamamız lazım. Nasıl gokyuzu ve toprak’i prefab yaptıysak bunları da aynı şekilde prefab yapıyoruz. Sırayla Scene paneline sürükleyip Transform component’indeki değerleri resetliyoruz. Ardından Hierarchy‘den tutup sürükleyerek Project paneline bırakıyoruz. Prefablarımız oluşunca Hierarchy’deki ustEngel ve altEngel’i siliyoruz (artık onlara gerek kalmadı) ve oluşan prefablarımızı projenin düzenli durması için Prefablar klasörüne atıyoruz.

Artık prefab’larımız hazır olduğuna göre onları Engeller (Script) component’indeki yerlerine sürükleyin ve oyunu çalıştırın.

Oyun alanında rastgele yükseklikte engeller çıkacaktır. Siz ilerledikçe yeni engeller çıkmaya devam edecek. Ama henüz engele çarpsanız da bir şey olmuyor. Bu aşamada önemli olan engellerin oluşması.

resim12

Yazdığımız scriptin Arkaplan.js scriptine benzediğine dikkat ettiniz mi? Değişken isimlerinden tutun kodun içeriğine kadar çok benzer yönleri var. Bakalım scriptimizde neler varmış:

ustEngel ve altEngel: Engel prefab‘larını depolayıp Instantiate metoduyla oyunun başında engelleri oluşturmakta kullanılan değişkenler

altUstEngelArasiBosluk: ustEngel ile altEngel sprite’leri arasında kaç Uzay Birimi kadar boşluk olması gerektiğini belirten değer. Değerini Inspector‘dan artırırsanız engellerin arası açılır, oyun kolaylaşır.

ikiEngelArasiMesafe: Bir engel oluşturulduktan sonra ardından gelen engelin (engelden kastım ustEngel ve altEngel’in bir arada oluşturmuş olduğu engel) ne kadar Uzay Birimi sonra geleceğini belirler. Eğer değerini Inspector‘dan artırırsanız iki engel arası uzaklık artar, bir engel geçtikten sonra ötekinin gelmesi için daha çok beklersiniz. Yani oyun kolaylaşır.

engelSayisi: Nasıl Arkaplan.js‘de arkaplanSayisi varsa bu scriptte de engelSayisi var. Oyunun başında kaç tane engel oluşturursak bu engellerin oyun boyunca bize yeteceğini depoluyor kendisi.

kameraUnityEbatlar: Arkaplan.js‘deki aynı isimli değişkenin değeriyle birebir aynı değeri alıyor.

engelUnityEbatlar: Bir ustEngel sprite’sinin yatayda ve dikeyde kaç Uzay Birimi ebatlarında olduğunu depolar. ustEngel ile altEngel’in boyutları aynı olduğu için aynı zamanda altEngel’in de ebatlarını depolamış olur.

ustEngelObjeleri ve altEngelObjeleri: Sahnede oluşturulan ustEngel objelerini ve altEngel objelerini depolayan array‘ler.

bastakiEngelObjesi: Ekranımızda görünür vaziyetteki engellerden en soldakinin ustEngelObjeleri ve altEngelObjeleri array‘lerinde kaçıncı index‘te yer alan engel olduğunu depolar.

Start fonksiyonunda ilk iki satırda engelUnityEbatlar‘a ve kameraUnityEbatlar‘a değerlerini veriyoruz. Burada kameraUnityEbatlar’ın değerini camera.orthographicSize‘ı kullanarak aldığına dikkat edin. Arkaplan.js scriptinde de böyleydi ama öncesinde camera.orthographicSize’ın değerini değiştiriyorduk. İşte Arkaplan.js’deki Start fonksiyonunu Awake ile değiştirmemizin sebebi de tam burada yatıyor. Önce Arkaplan.js’de camera.orthographicSize’ın belirlenmesini sağlıyoruz. Böylece artık Engeller.js scriptinde kameraUnityEbatlar‘a değerini verirken camera.orthographicSize‘ın değerinin güncel değer olduğundan emin oluyoruz.

Bu scriptte engelSayisi‘nı Arkaplan.js‘deki arkaplanSayisi‘na çok benzer bir şekilde buluyoruz. Tek fark şu: arkaplanSayisi’nda bir arkaplanın genişliğini gokyuzuUnityEbatlar.x olarak alırken burada bir engelin genişliğini “engelUnityEbatlar.x + ikiEngelArasiMesafe” olarak alıyoruz. Böyle yapıyoruz zira her engelden sonra ikiEngelArasiMesafe kadar da bir boşluk olmasını istiyoruz.

Gelelim for döngüsüne. Oyun başlarken engellerin kameranın en sağından başlamasını istiyoruz, o yüzden xKoordinatı transform.position.x – kameraUnityEbatlar.x‘ten değil transform.position.x + kameraUnityEbatlar.x‘ten başlıyor. Böylece ilk engelin X koordinatını kameranın en sağ noktası olarak belirliyoruz. Burada ekstradan yKoordinati diye bir şey var. Bildiğiniz üzere engelleri yerleştirirken ustEngel ile altEngel arasında boşluk olmasını istiyoruz (ki kuş bu aralıktan geçebilsin, değil mi!). İşte yKoordinati tam olarak bu boşluğun tepe noktasının Y koordinatına denk geliyor. Bu da demek oluyor ki bu boşluğun en alt noktasının Y koordinatını bulmak istersek tek yapmamız gereken yKoordinati – altUstEngelArasiBosluk işlemini çözmek (Y koordinatı yukarıdan aşağı indikçe azalır). Hatırlarsanız ustEngel‘in Pivot noktasını Bottom Left yaptık, yani engelin Inspector‘daki Position değeri bize sol alt noktasının koordinatını veriyor. Benzer şekilde altEngel‘in Pivot‘unu da Top Left yapmıştık. Yani ustEngel’in position değeri sol üst noktasını temsil ediyor. O halde yapmamız gereken şey ustEngel’i tam yKoordinati‘na, altEngel’i de yKoordinati – altUstEngelArasiBosluk noktasına koymak. Instantiate komutlarına bakarsanız da tam olarak bunu yaptığımızı göreceksiniz (açıklama resimden sonra devam ediyor).

resim13

Peki yKoordinati‘nı nasıl bulduk? Hepimiz biliyoruz ki Flappy Bird’de engellerin yüksekliği rastgele belirleniyor. Bizim oyunumuzda da bu durum geçerli. Random.Range ile belli bir aralıkta rastgele bir float döndürülüyor ve bu değer yKoordinati‘na atanıyor. Peki bu “belli aralık” ne? Bu belli aralığın bir minimum ve maksimum değeri var. Döndürülen sayı da bu aralığın içinden seçiliyor. Aralığın minimum değerini bulmak için Random.Range’in ilk parametresine bakmanız yeterli: “-kameraUnityEbatlar.y + altUstEngelArasiBosluk + 0.6“. Bildiğiniz gibi -kameraUnityEbatlar.y kameranın görüş alanının en alt noktasının koordinatını veriyor. Bizim yKoordinati‘mız boşluğun tepe koordinatını depoluyordu. Bu yüzden bu değere altUstEngelArasiBosluk‘u ekliyoruz ve engeller arası boşluğun bulunabileceği en dip noktanın koordinatını buluyoruz. Ama bu değere bir de 0.6 Uzay Birimi ekliyoruz. Eklemezsek ne olur? Eğer rastgele sayımız tam “-kameraUnityEbatlar.y + altUstEngelArasiBosluk” olarak döndürülürse boşluk kameranın en dibinde yer alır ve ekranda sadece ustEngel gözükür. altEngel kameranın dışında kalır. Bunu engellemek için boşluğun kameranın dibinden en azından 0.6 Uzay Birimi kadar uzak olduğundan emin oluyoruz. Dilerseniz bu değeri kendiniz de değiştirebilirsiniz. Ne kadar artırırsanız engeller arası boşluğun kameranın en alt noktasından o kadar uzakta başlamasını garantilersiniz.

Fark etmiş olabilirsiniz, Start fonksiyonunda çoğu şeyi anlatmadım çünkü yaptığımız şeyler Arkaplan.js ile neredeyse birebir aynıydı. Sadece önemli noktaların üzerinden geçtim. Aynı şeyi şimdi Update için yapacağım. Update’in en başında en soldaki engel objesinin kameranın görüş alanından çıkıp çıkmadığına bakıyoruz. Eğer çıkmışsa yeni bir rastgele yKoordinati buluyoruz (böylece engel sağa ışınlandığında farklı bir yüksekliğe sahip olacak). Ardından en soldaki ustEngel ile altEngel‘i en sağa ışınlayıp position‘larının Y değerini uygun şekilde değiştiriyoruz. Geri kalan işlemler Arkaplan.js ile birebir aynı.

Bu bölümle beraber oyunun büyük çoğunluğunu bitirdik. Şu haliyle bile istediğiniz zorlukta bir Flappy Bird oyunu oluşturmak için pek çok değişken emrinizde: KusHareket.js scriptindeki yercekimi, ziplamaGucu ve yatayHiz; Engeller.js scriptindeki altUstEngelArasiBosluk ve ikiEngelArasiMesafe.

Bölümü bitirmeden önce isterseniz ufak bir ayar yapalım: Fark ettiyseniz altEngel sprite’si hep toprak sprite’sinin üzerinde çiziliyor. Ama aslında toprak’ın daha üstte olması lazım. Bunu düzeltmek tahmin ettiğinizden daha kolay. Unity’nin 2D motorunda şöyle bir olay var: iki obje aynı koordinatta kesişiyorsa position.z değeri küçük olan obje ötekinin üzerine çizilir. altEngel sprite’sinin Z değeri 0. O zaman toprak’ın Z değerini -1 yaparsak toprak her zaman için altEngel’in üzerinde çizilir. Tam da istediğimiz şey! Bunun için Arkaplan.js scriptini açıp Start‘taki for döngüsünde toprak Instantiate ettiğimiz satırda oluşturduğumuz toprak objelerine Z değeri olarak -1 verelim. Yani “Vector3( xKoordinati, kameraUnityEbatlar.y – gokyuzuUnityEbatlar.y, 0 )” kısmını “Vector3( xKoordinati, kameraUnityEbatlar.y – gokyuzuUnityEbatlar.y, -1 )” olarak değiştirin. İşte bu kadar!

NOT: İsterseniz Z değerini değiştirmeyi bir de kuşa uygulayabilir ve kuşun toprağın üzerinde gösterilmesini sağlayabilirsiniz. Şu anda kuşun Z koordinatı 0 ama toprağınki -1. Yani kuş toprağın arkasında kalıyor. Eğer kuşun Z‘sini -2 yaparsanız sorun çözülmüş olur.

Kuş Engele Çarpınca Oyunun Bitmesi

Bu bölümde yapacağımız tek şey şu olacak: kuşun bir engele çarpıp çarpmadığını kontrol edeceğiz ve eğer çarpmışsa kuşun artık sağa gitmesini engelleyip toprağa düşmesini sağlayacağız. Kuş toprağa düştükten sonra oyuncu ekrana tıklarsa oyunu yeniden başlatacağız.

Unity’de iki objenin temas edip etmediğini test etmeye yarayan bir fonksiyon var: OnCollisionEnter(). Bu fonksiyonun çalışması için ise hem temas eden objede hem de temas edilen objede Collider adında bir component olması gerekiyor. Buna ek olarak temas eden objede Rigidbody component’i de olması gerekiyor. Temas eden obje burada FlappyBird olurken temas edilen obje ise ustEngel veya altEngel oluyor.

İşe FlappyBird objesine Collider2D ve Rigidbody2D ekleyerek başlayalım. Bunun için kuşu seçin ve yukarıdan Component-Physics 2D-Rigidbody 2D yolunu izleyin. Ardından Component-Physics 2D-Circle Collider 2D yolunu izleyin.

İlk önce kuşa Unity’nin 2D motorunda çalışan Rigidbody versiyonunu, Rigidbody 2D‘yi ekledik. Ardından Circle Collider 2D ile kuşun başka objelerle temas eden alanını bir daire olarak belirledik. Bu temas alanını Scene panelinde kuşu çevreleyen yeşil bir dairesel hat olarak görebilirsiniz. Bence bu alan biraz büyük, bu yüzden bunu küçülttüm ben. Bunun için Inspector’dan Circle Collider 2D component’i altında yer alan Radius (yarıçap) değerini 0.115 olarak değiştirdim. Kuşu çevreleyen hat haliyle küçüldü:

resim14

Unity’nin 2D motorunda şu anda ufak bir bug var: eğer Rigidbody 2D‘de Is Kinematic‘i işaretlersek obje başka objelerle temas etmiyor. Ama eğer bu seçeneği seçmezsek de varsayılan olarak obje yerçekiminden etkileniyor. Bizse kuşun yerçekiminden etkilenmesini istemiyoruz çünkü kuşun tüm hareket işlerini kod vasıtasıyla kendimiz hallediyoruz. Bu sorunu çözmek ise kolay: Rigidbody 2D’deki Gravity Scale değerini 0 yapmanız yeterli. Artık yerçekimi kuşa etki etmeyecek. Buna ek olarak Linear Drag ve Angular Drag değerlerini de 0 yapın. Ne işe yaradıklarını bilmiyorum ama Rigidbody 2D’nin objenin başka objelerle temas etmesini sağlamak dışında bir iş yapmadığından emin olalım biz.

resim14_15

Şimdi kuşun temas edebileceği objelere, yani ustEngel‘e, altEngel‘e ve toprak‘a da birer Collider 2D ekleyelim. Başlangıcı ustEngel ile yapalım. Şu anda herhangi bir ustEngel objesi sahnede yer almadığından ustEngel’e ekleyeceğimiz Collider’ın dış hattını gözle görerek değiştiremeyiz. Sahnede referans bir ustEngel objesi lazım bize. Bunun için Project panelinden ustEngel prefabını seçip sürükleyerek Scene paneline bırakın. Referansımız hazır:

resim15

Şimdi sahneye eklediğiniz ustEngel seçiliyken yukarıdan Component-Physics 2D-Box Collider 2D yolunu izleyin. Bu, dikdörtgen şeklinde bir temas alanı oluşturmak için kullanılır. ustEngel’in çevresinde yeşil bir dikdörtgen hat göremeyebilirsiniz çünkü o hat objeyi saran gri hatla çakışıyor, gri hattın gerisinde kalıyor.

Bu safhada bir şeyi daha değiştirmemiz gerekiyor. Şu anda ustEngel’in Box Collider 2D component’inde Is Trigger seçili değil. Bunun anlamı şu: kuş objemiz ustEngel ile temas edince onun içinden geçemeyecek. Aslında bizim istediğimiz de bu gibi duruyor ama değil. Bizim istediğimiz kuşun bir engele çarpınca yatay eksende hareket etmeyi kesip toprağa düşmesi ve toprakta hareketinin tamamen son bulması (dikey eksende de hareketinin kesilmesi). Bu yüzden kuş bir engele çarpınca toprağa düşene kadar diğer engellerin içinden geçebilmeli. Bunu sağlamak için de Collider’da Is Trigger seçeneğini işaretleyin.

Engel objemize collider ekledik. Şimdi bu collider’ı Project panelindeki prefab’a uyarlayalım ki Instantiate metoduyla oluşturduğumuz tüm ustEngel objelerinde collider yer alsın. Bunu yapmak çok kolay. Sahnedeki ustEngel seçili iken Inspector‘dan Apply butonuna basın. Bunu yaptığınızda objedeki tüm değişiklikler onu oluşturan prefab‘a da uygulanır.

resim16

Artık sahnedeki ustEngel‘e ihtiyacımız kalmadığından onu silebilirsiniz. Sonrasında ise altEngel ve toprak prefab‘larını da ayrı ayrı sahneye taşıyıp Box Collider 2D verin (altEngel’de Is Trigger‘ı işaretleyin ama toprak’ta işaretlemeyin, çünkü kuşun hareketinin toprak’a çarpınca tamamen son bulmasını istiyoruz) ve değişiklikleri prefab‘larına uygulayın. Artık teması tetikleyen tüm component’ler objelerimizde yer almakta.

Şimdi kod yazma zamanı. Kuşun temas olaylarını ayarlamak için yeni bir script oluşturmamıza gerek yok. KusHareket.js scriptini açın ve en alta şu iki fonksiyonu ekleyin:

function OnTriggerEnter2D( temas : Collider2D )
{
	Destroy( Camera.main.GetComponent( Arkaplan ) );
	Destroy( GetComponent( KusAnimasyon ) );
	yatayHiz = 0;
}

function OnCollisionEnter2D( temas : Collision2D )
{
	Destroy( Camera.main.GetComponent( Arkaplan ) );
	Destroy( GetComponent( KusAnimasyon ) );
	yatayHiz = 0;
}

OnTriggerEnter2D: Obje collider‘ında Is Trigger işaretli bir objeyle temas edince çalıştırılır.

OnCollisionEnter2D: Obje collider‘ında Is Trigger işaretli olmayan bir objeyle temas edince çalıştırılır.

Kuş engele çarpınca yaptığımız işlem çok basit: Önce Arkaplan.js scriptini kameradan çıkarıyoruz ve böylece gokyuzu‘nün sağa doğru kaymasını sonlandırmış oluyoruz. Bununla beraber FlappyBird‘deki KusAnimasyon component’ini de siliyoruz ve böylece kuş artık kanat çırpmıyor. Ardından yatayHiz‘ı sıfırlıyoruz ve böylece artık kuş yatay eksende hareket etmeyi sonlandırıyor. Peki neden içinde birebir aynı kod olan iki ayrı fonksiyon kullandık? Çünkü OnTriggerEnter2D kuş ustEngel ve altEngel‘e çarpınca, OnCollisionEnter2D ise sadece kuş toprak‘a çarpınca gerçekleştiriliyor. Kuş engele çarpınca zaten yatayHiz’ı OnTriggerEnter2D’de sıfırlanıyor, niye bir de OnCollisionEnter2D’de sıfırlanıyor diyebilirsiniz. Bunun sebebi ise kuşun hiçbir engele çarpmadan direkt toprağa çarparak da ölmesinin mümkün olması.

Scripti kaydedip oyunu test ederseniz bir gariplik fark edeceksiniz: Kuş toprağa çarpınca sapıtıyor, olduğu yerde kalmıyor. Çünkü kuştaki Rigidbody 2D ve Collider 2D kuşu temas ettiği yerde tutmaya çalışırken KusHareket.js scriptinin Update fonksiyonundaki Translate fonksiyonu kuşa yerçekimi uyguluyor ve bu iki güç birbiriyle çatışma haline giriyor. Ama neticede yerçekimi yeniyor ve kuş ekranımızdan çıkıyor. Buna engel olmalıyız. Ve inanın çözümü çok basit. Tek yapmamız gereken kuş toprağa çarpınca artık KusHareket.js scriptindeki Update fonksiyonunun çalışmasını engellemek, böylece artık Translate komutu çalıştırılmayacak. KusHareket.js scriptini geri açın ve scripti şöyle güncelleyin:

#pragma strict

public var yercekimi : float = 4;
public var ziplamaGucu : float = 2.5;
public var yatayHiz : float = 1.5;
private var dikeyHiz : float = 0;

private var oyunBitti : boolean = false;

function Update ()
{
	if( !oyunBitti )
	{
		dikeyHiz -= yercekimi * Time.deltaTime;

		if( Input.GetMouseButtonDown( 0 ) )
			dikeyHiz = ziplamaGucu;

		var egim : float = 90 * dikeyHiz / yatayHiz;

		if( egim < -50 ) egim = -50;
		else if( egim > 50 ) egim = 50;

		transform.eulerAngles = Vector3( transform.eulerAngles.x,
						transform.eulerAngles.y, egim );

		transform.Translate( 0, dikeyHiz * Time.deltaTime, 0, Space.World );
		transform.parent.Translate( yatayHiz * Time.deltaTime, 0, 0 );
	}
}

function OnTriggerEnter2D( temas : Collider2D )
{
	Destroy( Camera.main.GetComponent( Arkaplan ) );
	Destroy( GetComponent( KusAnimasyon ) );
	yatayHiz = 0;
}

function OnCollisionEnter2D( temas : Collision2D )
{
	Destroy( Camera.main.GetComponent( Arkaplan ) );
	Destroy( GetComponent( KusAnimasyon ) );
	yatayHiz = 0;
	oyunBitti = true;
}

Scriptin en başında oyunBitti adında bir değişken tanımladık, varsayılan değeri false. Kuş toprağa değince (OnCollisionEnter2D) bu değişkeni true yapıyoruz. Ardından Update fonksiyonunun hepsini kaplayan if( !oyunBitti ) koşulu artık sağlanmayacağı için Update fonksiyonunun içindeki kodların hiçbiri artık çalıştırılmıyor.

Scripti kaydedip test edin. Sonuç tam istediğimiz gibi oldu. Bense tam bu noktada çok önemli bir bug’ı atladığımızı keşfettim. Eğer bir engele çarptıktan sonra kuş toprağa değmeden mouse ile ekrana tıklarsanız kuş zıplamaya devam ediyor. Bunun için en uygun çözümün kuş bir engele çarpınca başka bir boolean değişkenin değerini true yapmak ve bu değişken true ise kuşun kanat çırpmasını engellemek olduğunu düşünüyorum. Yapmanız gereken scriptin başında “private var engeleCarptim : boolean = false;” değişkenini tanımlamak ve Update fonksiyonunda şu değişikliği yapmak:

if( Input.GetMouseButtonDown( 0 ) && !engeleCarptim )
	dikeyHiz = ziplamaGucu;

Eğer oyuncu ekrana tıklarsa engeleCarptim değişkeninin false olup olmadığına bakıyoruz ve false ise kuşu zıplatıyoruz.

Buna ek olarak OnTriggerEnter2D‘nin en sonuna da “engeleCarptim = true;” komutunu ekleyin ve oyunu test edin. Artık bu sorundan kurtulmuş olmanız lazım.

Scripte bir de oyunu öldükten sonra yeniden başlatma kodu eklersek tam olacak. Bunun için Update fonksiyonunu şöyle güncelleyin:

if( !oyunBitti )
{
...
}
else
{
	if( Input.GetMouseButtonDown( 0 ) )
		Application.LoadLevel( "Oyun" );
}

Eğer kuş toprağa çarpmışsa (oyunBitti true ise) ve oyuncu ekrana tıklamışsa Oyun sahnemizi (scene) yeniden yüklüyoruz, yani restart atıyoruz levele. Hiç de zor değilmiş.

Kuş Kameranın Dışına Çıkınca Ölmesini Sağlamak

Bilirsiniz, normal Flappy Bird’de kuşu çok zıplatıp kameranın tepesine çarptırırsanız oyun biter. Biz de bunu yapacağız bu bölümde. Sandığınızdan da kısa sürecek!

Tüm işi KusHareket.js scriptinde halledeceğiz. Scripti açıp başında şu değişkeni tanımlayın: “private var kameraUnityEbatlar : Vector2;“. Sonra tıpkı Engeller.js scriptindeki gibi Start fonksiyonuna şu satırı ekleyin (daha doğrusu KusHareket.js‘de Start olmadığı için Start fonksiyonu oluşturup içine şu kodu yazın):

kameraUnityEbatlar = Vector2( Camera.main.orthographicSize * Camera.main.aspect, Camera.main.orthographicSize );

Hatırlarsanız kameraUnityEbatlar.y bize kameranın en üst noktasının koordinatını veriyordu. Eğer kuşun Y koordinatı bu koordinattan büyük ise kuş ekranın dışına çıkmış demektir. Bu durumda kuşu öldüreceğiz. Update fonksiyonunu açıp “if( !oyunBitti )” koşulunun içinde istediğiniz yere şu kodu ekleyin:

if( transform.position.y > kameraUnityEbatlar.y )
{
	Destroy( Camera.main.GetComponent( Arkaplan ) );
	Destroy( GetComponent( KusAnimasyon ) );
	yatayHiz = 0;
	engeleCarptim = true;
}

Eğer kuş kameranın dışına çıkmışsa kuşa sanki engele çarpmış muamelesi yapıyoruz (kod OnTriggerEnter2D‘dekiyle birebir aynı) ve kuşun hareketlerine son veriyoruz.

Böylece çok kısa bir sürede bu bölümü de bitirmiş olduk.

Skor Sistemi

Şimdi sıra geldi kuşun engelleri aştıkça skor yapmasına ve oyun bitiminde yüksekskorun güncellenmesine.

Kuş ne zaman skor yapacak? İki engelin arasını tam yarılamışken skor yapacak. Yani alttaki resimdeki kırmızı çizgiye değdiği an skor yapacak:

resim17

Bir başka deyişle engelin yarısında yer alan dikey bir çizgiyle temas edince skor yapacağız. Tabi bu çizgi görünmez olacak. Unity’nin 2D motorunda Edge Collider 2D diye bir component var. Nasıl Box Collider 2D bir dikdörtgen şeklindeyse Edge Collider 2D de bir çizgi şeklinde temas alanı temsil eder.

GameObject-Create Empty yoluyla yeni bir GameObject oluşturun ve hemen ardından objeye Component-Physics 2D-Edge Collider 2D yolunu izleyerek bir Edge Collider 2D verin. Obje resimdeki gibi gözükecektir:

resim18

Burada bir sorunumuz var: çizgi şeklindeki temas alanımız dikey değil, yatay. Bunun dışında bir sorunumuz daha var aslında: bu çizginin uzunluğunu bilmiyoruz. Bu iki sorunu da kod yazarak halledeceğiz. Çizginin ilk noktası kameranın tepe koordinatına denk gelirken ikinci noktası kameranın en alt noktasının koordinatına denk gelecek. Yani çizginin uzunluğu kameranın yüksekliği kadar olacak.

Kod kısmına geçmeden önce Inspector‘dan Edge Collider 2D component’i altındaki Is Trigger seçeneğini işaretleyin. Böylece kuş çizgiyle temas edince çizginin içinden geçecek. Son olarak çizgi objemize bir Tag (etiket) verelim. Bunu yapmamızın sebebi şu: kuş objemizdeki OnTriggerEnter2D hem ustEngel ve altEngel için hem de şimdi oluşturduğumuz Edge Collider 2D için çalışacak. Hangisiyle temas ettiğimizi anlamak için temas edilen objenin tag‘ına bakacağız. Objeye Tag vermek için Inspector‘dan Tag-Add Tag… yolunu izleyin:

resim19

Gelen sayfadan Tags sekmesini açın ve Element 0‘a değer olarak SkorTemasAlani verin:

resim20

Şimdi Edge Collider 2D‘li objeyi tekrar seçin ve Inspector‘dan Tag olarak az önce oluşturduğumuz SkorTemasAlani tag’ını verin:

resim21

Artık yapabileceğimiz her şeyi yaptık. Geri kalan kısımları script yoluyla halledeceğiz. Objenin ismini SkorEdgeCollider olarak değiştirin ve objeyi Hierarchy panelinden Project paneline sürükleyerek bir prefab‘a çevirin. Oluşan prefab’ı Prefablar klasörüne atın. Sahnedeki objeyle işimiz bittiğinden sahnedeki SkorEdgeCollider‘ı silin.

Edge Collider objesini oyun sahnemize ekleme işlemini Engeller.js scriptinde yapacağız. Scripti açıp üst kısımda “public var skorCollider : GameObject;” adında yeni bir değişken tanımlayın. Sonra Start fonksiyonunun en altındaki for‘u şöyle güncelleyin:

for( var i = 0; i < engelSayisi; i++ )
{
	var xKoordinati : float = transform.position.x + kameraUnityEbatlar.x + i * ( engelUnityEbatlar.x + ikiEngelArasiMesafe );
	var yKoordinati : float = Random.Range( -kameraUnityEbatlar.y + altUstEngelArasiBosluk + 0.6, kameraUnityEbatlar.y - 0.6 );
	ustEngelObjeleri[i] = Instantiate( ustEngel, Vector3( xKoordinati, yKoordinati, 0 ), Quaternion.identity ).GetComponent( Transform );
	altEngelObjeleri[i] = Instantiate( altEngel, Vector3( xKoordinati, yKoordinati - altUstEngelArasiBosluk, 0 ), Quaternion.identity ).GetComponent( Transform );

	var temasAlani : EdgeCollider2D = Instantiate( skorCollider, Vector3( xKoordinati + engelUnityEbatlar.x / 2, kameraUnityEbatlar.y, 0 ), Quaternion.identity ).GetComponent( EdgeCollider2D );
	var cizgi : Vector2[] = new Vector2[2];
	cizgi[0] = Vector2( 0, 0 );
	cizgi[1] = Vector2( 0, -2 * kameraUnityEbatlar.y );
	temasAlani.points = cizgi;
	temasAlani.transform.parent = ustEngelObjeleri[i];
}

Yeni eklediğim kod “var temasAlani : …” satırından başlıyor. Eklediğim kod parçası karmaşık durabilir ama sizi temin ederim çok basit bir mantığı var. Önce sahnede yeni bir SkorEdgeCollider oluşturuyoruz ve bunun Edge Collider 2D component‘ini temasAlani isimli bir değişkende tutuyoruz. Böyle yaparak objenin Edge Collider 2D’sinin değişkenlerine direkt ulaşabilirim. Objeyi oluşturduğum konum önemli. X koordinatı olarak en son oluşturduğumuz engelin tam orta noktasını (xKoordinati + engelUnityEbatlar.x / 2) ve Y koordinatı olarak kameranın tepe noktasını (kameraUnityEbatlar.y) veriyorum.

Sonra cizgi adında 2 elemanlı bir Vector2 array‘i oluşturuyorum. İki elemanlı çünkü bir düz çizginin sadece iki noktası vardır. cizgi‘nin ilk elemanını 0,0 noktasına konumlandırıyorum. Bunun anlamı çizginin başlangıç noktası tam SkorEdgeCollider objemizin bulunduğu noktada olacak. cizgi’nin ikinci elemanın Y koordinatını ise -2 * kameraUnityEbatlar.y olarak belirliyorum. Bunun anlamı ise çizginin bitiş noktası SkorEdgeCollider objesinin Y koordinatından 2 * kameraUnityEbatlar.y kadar aşağıda olacak. SkorEdgeCollider’ın kameranın en üst noktasında yer aldığını düşünürsek çizginin bitiş noktasının kameranın en alt noktasına denk geldiğini tahmin etmek zor değil. cizgi array‘inin elemanlarına değerlerini verdikten sonra temasAlani‘nın points isimli değişkenine değer olarak cizgi’yi veriyorum. Edge Collider 2D component’inin points isminde bir değişkeni bulunmakta ve bu değişken çizgi şeklindeki temas alanının noktalarının koordinatlarını depolamakta. Az önce bu değişkene değer olarak yeni oluşturduğumuz çizgiyi verdim. Son olarak temasAlani’na parent olarak for‘un içinde en son oluşturulan ustEngel objesini veriyorum. Böylece temasAlani objemiz o ustEngel objesiyle beraber hareket edecek, ustEngel kameranın dışına çıkıp sağa ışınlanınca temasAlani da sağa ışınlanacak.

Engeller.js scriptindeki işimiz bitti. Scripti kaydedin, Main Camera‘yı seçin ve Inspector‘dan Skor Collider değişkenine değer olarak SkorEdgeCollider‘ı verin:

resim22

Oyunu başlatın ve hemen ardından pause edin. Scene paneline geçiş yapıp ustEngel objelerinin tekini seçin. Engelin tam ortasından geçen ve sahnenin yüksekliği kadar uzunlukta olan dikey bir temas alanı göreceksiniz. Görevimiz başarılıyla sonuçlandı!

resim23

Şimdi yapmamız gereken şey kuş bu çizgiyle temas edince ne olacağını ayarlamak. Bu işlemi ise KusHareket.js scriptinde ayarlayacağız. Script’i açıp yukarıda “private var skor : int = 0;” şeklinde skor değişkenini tanımlayın. Sonrasında OnTriggerEnter2D fonksiyonunu şu şekilde güncelleyin:

function OnTriggerEnter2D( temas : Collider2D )
{
	if( temas.tag == "SkorTemasAlani" )
	{
		if( !engeleCarptim && !oyunBitti )
			skor++;
	}
	else
	{
		Destroy( Camera.main.GetComponent( Arkaplan ) );
		Destroy( GetComponent( KusAnimasyon ) );
		yatayHiz = 0;
		engeleCarptim = true;
	}
}

Eğer temas ettiğimiz objenin tag‘ı “SkorTemasAlani” ise (yani SkorEdgeCollider‘a temas ettiysek) ve oyun hâlâ devam ediyorsa (engeleCarptim ve oyunBitti false ise) skoru 1 artırıyoruz. Eğer objenin tag’ı “SkorTemasAlani” değilse o zaman anlıyoruz ki bir engele çarpmışız ve bu durumda gereğini yerine getiriyoruz.

Oyunu şimdi test ederseniz skorun artıp artmadığını anlayamazsınız çünkü skor private bir değişken ve değerini ekranda bir yere yazdırmıyoruz henüz. Bu sorunu aşmak için skoru ekranda gösterelim. Bizim oyunumuzda skor ekranın sağ üst köşesinde gözükecek (biraz kolaya kaçacağız).

Ben genelde GUIText component’i yerine scriptlerimde OnGUI fonksiyonunu kullanarak ekrana GUI elemanlarını çizdiriyorum. Bu sefer de öyle yapacağım. KusHareket.js script’inin en altına şu fonksiyonu ekleyin:

function OnGUI()
{
	GUI.skin.box.alignment = TextAnchor.UpperRight;
	GUI.skin.box.fontSize = 30;
	GUI.skin.box.fontStyle = FontStyle.Bold;
	GUI.color = Color.green;
	GUILayout.BeginArea( Rect( 0, 25, Screen.width - 25, 200 ) );
	GUILayout.BeginHorizontal();
	GUILayout.FlexibleSpace();
	GUILayout.Box( "SKOR: " + skor );
	GUILayout.EndHorizontal();
	GUILayout.EndArea();
}

Oyunu test ederseniz sağ üstte skoru görebilirsiniz. Kodda ne yaptığımızı açıklayayım: ilk satırda ekrana yazdığımız yazının sağ üste dayalı olmasını ayarlıyorum. İkinci satırda yazının fontunu 30 punto yapıyorum ve üçüncü satırda yazıyı kalın punto yapıyorum. Dördüncü satırda yazının rengini yeşil yapıyorum. Beşinci satırda yazının ekrana yazdırılacağı alanı belirliyorum. Rect()‘in içine sol üst noktası 0,25 pixel koordinatında olan ve genişliği ekranın genişliğinden 25 pixel az (Screen.width – 25), yüksekliği 200 pixel olan bir dikdörtgensel alan giriyorum. Yazı bu dikdörtgensel alanın sağ üst köşesine dayalı olarak yazdırılacak ekrana. Buradaki 6. 7. ve 9. satırlar yazıyı sağa dayalı yazdırmak için gerekli başka komutlar. Önemli olan 8. satır (GUILayout.Box). Bu satırda yazıyı bir kutucuğun içinde ekrana yazdırıyoruz.

Sıra geldi yüksekskora. Yüksekskor round bitiminde gerekli olursa güncellenecek ve cihaza kaydedilecek. Böylece oyunu sonradan açınca yine aynı yüksekskorla karşılaşacağız. Yüksekskor için KusHareket.js scriptinde “private var yuksekSkor : int = 0;” değişkeni tanımlayın. Sonra OnCollisionEnter2D fonksiyonunu şöyle güncelleyin:

function OnCollisionEnter2D( temas : Collision2D )
{
	Destroy( Camera.main.GetComponent( Arkaplan ) );
	Destroy( GetComponent( KusAnimasyon ) );
	yatayHiz = 0;
	oyunBitti = true;

	if( skor > yuksekSkor )
		yuksekSkor = skor;

	PlayerPrefs.SetInt( "YuksekSkor", yuksekSkor );
	PlayerPrefs.Save();
}

Hatırladığınız üzere OnCollisionEnter2D kuş toprak‘a çarpınca, yani round bitince çalışıyordu. Yaptığımız şey fonksiyonun sonunda eğer skor yüksekskordan büyükse yüksekskoru skora eşitlemek ve cihaza “YuksekSkor” ismiyle kaydetmek. PlayerPrefs.SetInt cihaza bir tamsayı değeri kaydetmeye yarar. İlk parametresine kaydedilen değer için bir isim girilir ve ikinci parametresine kaydedilecek değer girilir. Bu komutu kullandıktan sonra PlayerPrefs.Save komutu ile değerin cihaza tam o anda kaydedilmesini sağlıyoruz. Aksi taktirde yuksekSkor‘un değeri cihaza oyundan çıkarken kaydedilir.

Geriye oyunun başında yüksekskoru cihazdan çekmek kaldı. Bunun için de KusHareket.js‘nin Start fonksiyonunu şu şekilde güncelleyin:

function Start()
{
	kameraUnityEbatlar = Vector2( Camera.main.orthographicSize * Camera.main.aspect, Camera.main.orthographicSize );
	yuksekSkor = PlayerPrefs.GetInt( "YuksekSkor" );
}

PlayerPrefs.GetInt komutu cihazdaki kayıtlı bir tamsayı değerini çekmeye yarar. Parametre olarak kaydedilen değere verilen ad girilir. Eğer cihazda böyle bir değer kaydedilmemişse 0 döndürülür.

Şimdi haklı olarak yüksekskoru da ekranda görmek isteyeceksiniz. Bunun için OnGUI fonksiyonundaki GUILayout.Box fonksiyonunu şu şekilde değiştirin:

GUILayout.Box( “SKOR: ” + skor + “\nYÜKSEKSKOR: ” + yuksekSkor );

“\n” komutu yazıda bir satır aşağı inmeye yarar. Yaptığımız şey skorun bir satır altına yüksekskorun değerini yazdırmak. Oyunu iyice test edin, yüksekskordan büyük bir skor elde edip ölün, yeniden başlayın. Oyunu kapatıp tekrar açın. Yüksekskorun her yeni oyunda değerini koruması lazım.

resim23_24

Ses Efektleri Eklemek

Oyun boyunca çalacağımız 4 ses var:

kanatSes: ekrana tıklayınca çalan kanat çırpma sesi

skorSes: skor yapınca çalan ses

olumSes: bir engele veya toprağa çarpınca çıkan ses

dusmeSes: bir engele çarpınca çıkan düşme sesi

Ses çaldırma işlemlerinin hepsini KusHareket.js scriptinde halledeceğiz. Ama önce FlappyBird objemize Component-Audio-Audio Source yoluyla Audio Source component’i vermemiz lazım. Böylece FlappyBird objesindeki scriptler ses dosyası çalma yetkisine sahip olacaklar.

Şimdi de kullanacağımız ses dosyalarını Unity’e import edelim. Önce Project panelinde “Sesler” adında yeni bir klasör oluşturun ve Assets-Import New Asset… yoluyla dersin başında paylaştığım Winrar arşivindeki tüm dört ses dosyasını da tek tek Unity’e import edin:

resim24

Şimdi import ettiğiniz ses dosyalarını tek tek seçin ve Inspector‘dan 3D Sound‘un yanındaki işareti kaldırıp Apply butonuna basın:

resim25

3D seslerin şiddeti onları çaldıran AudioSource component’inin bulunduğu konuma göre artar ya da azalır ama 2D seslerin şiddeti her zaman aynıdır.

Sıra geldi script vasıtasıyla uygun sesleri uygun zamanda çaldırmaya. Bunun için KusHareket.js scriptinin en başında şu dört değişkeni tanımlayın:

public var kanatSes : AudioClip;
public var skorSes : AudioClip;
public var olumSes : AudioClip;
public var dusmeSes : AudioClip;

Sonra OnTriggerEnter2D ve OnCollisionEnter2D fonksiyonlarını şöyle değiştirin:

function OnTriggerEnter2D( temas : Collider2D )
{
	if( temas.tag == "SkorTemasAlani" )
	{
		if( !engeleCarptim && !oyunBitti )
		{
			skor++;
			audio.PlayOneShot( skorSes );
		}
	}
	else
	{
		Destroy( Camera.main.GetComponent( Arkaplan ) );
		Destroy( GetComponent( KusAnimasyon ) );
		yatayHiz = 0;

		if( !engeleCarptim )
			EngelOlumSesiCal();

		engeleCarptim = true;
	}
}

function OnCollisionEnter2D( temas : Collision2D )
{
	Destroy( Camera.main.GetComponent( Arkaplan ) );
	Destroy( GetComponent( KusAnimasyon ) );
	yatayHiz = 0;
	oyunBitti = true;

	if( skor > yuksekSkor )
		yuksekSkor = skor;

	PlayerPrefs.SetInt( "YuksekSkor", yuksekSkor );
	PlayerPrefs.Save();

	if( !engeleCarptim )
		audio.PlayOneShot( olumSes );
}

function EngelOlumSesiCal()
{
	audio.PlayOneShot( olumSes );
	yield WaitForSeconds( 0.25 );
	audio.PlayOneShot( dusmeSes );
}

audio.PlayOneShot komutu bir ses dosyası çalmaya yarar. Bu komutu kullanarak skor yapınca skorSes‘i çaldırıyoruz. Bir engele çarpınca eğer engeleCarptim false ise EngelOlumSesiCal isimli bir fonksiyon çağırıyoruz. Bu fonksiyon önce olumSes‘i çalıyor ve 0.25 saniye sonra dusmeSes‘i çalıyor. İkisini aynı anda çalarsak çıkan ses çok güzel durmadığından böyle bir yöntem kullanıyoruz. if( !engeleCarptim ) kullanmamızın sebebi kuşun ölme sesinin sadece bir kere çalınacağından emin olmak. Kuş direkt toprak‘a çarparsa da sadece olumSes‘i çaldırıyoruz.

Şimdi kanat çırpma sesi verelim. Bunun için Update‘teki “if( Input.GetMouseButtonDown( 0 ) && !engeleCarptim )” kodunu şöyle güncelleyin:

if( Input.GetMouseButtonDown( 0 ) && !engeleCarptim )
{
	dikeyHiz = ziplamaGucu;
	audio.PlayOneShot( kanatSes );
}

Ses çaldırmadığımız tek bir yer kaldı: kuş ekranın dışına çıkarsa öldüğü zaman. Bunun için de Update() fonksiyonunda “if( transform.position.y > kameraUnityEbatlar.y )” koşulunun içini şöyle güncelleyin:

Destroy( Camera.main.GetComponent( Arkaplan ) );
Destroy( GetComponent( KusAnimasyon ) );
yatayHiz = 0;

if( !engeleCarptim )
	EngelOlumSesiCal();

engeleCarptim = true;

Scripti kaydedin ve FlappyBird‘ün Inspector paneline gelin. Buradan Kus Hareket (Script) component’indeki yeni eklediğimiz değişkenlere uygun ses dosyalarını Project panelinden sürükleyerek değer olarak verin:

resim26

Şimdi oyunu çalıştırın. Uygun zamanda uygun ses dosyaları çalınacaktır.

Son Rötuşlar

Flappy Bird’ü az çok oynamışsanız fark etmişsinizdir; bizim oyunumuzda kuşun hızı olsun zıplama yüksekliği olsun orijinal Flapp Bird’den biraz daha farklı. Bunu oyunun yarılarındayken ben de fark ettim ama düzeltmek için dersin sonuna gelmeyi bekledim. Deneye yanıla aşağıdaki değişkenlere Inspector‘dan yanlarındaki değeri verince oyunun orijinaline daha da benzediğini buldum. İsterseniz siz de değerleri benimki gibi değiştirebilirsiniz ya da tamamen kendi zevkinize göre kişiselleştirebilirsiniz.

Kus Hareket (Script)

Yercekimi: 8

Ziplama Gucu: 4

Yatay Hiz: 1

Arkaplan (Script)

Gokyuzu Saga Kayma Hizi: 0.8

Engeller (Script)

Alt Ust Engel Arasi Bosluk: 1.45

Iki Engel Arasi Mesafe: 1.2

Buna ek olarak oyuna ESC tuşuyla oyundan çıkma özelliği de eklesek iyi olacak. Bu özellik Unity Editor’de çalışmayacak ama oyunu Build edince çalışacak. Tek yapmanız gereken KusHareket.js scriptinin Update fonksiyonunun en altına şu kodu yazmak:

if( Input.GetKeyDown( KeyCode.Escape ) )
	Application.Quit();

Oyunu Android’e Build Etmek

Çoğunuz oyunu Android cihazınızda da test etmek istiyorsunuzdur. Bunu anlayışla karşılıyorum. Size iyi bir haberim var: Android için tek bir satır yeni kod yazmamıza gerek yok. Çünkü Input.GetMouseButtonDown(0) komutu Android’de ekrana parmakla dokununca da true dönüyor. Ve Input.GetKeyDown(KeyCode.Escape) komutu da Android cihazda geri tuşuna basınca true dönüyor.

File-Build Settings yolunu izleyin. Gelen pencerede Platform olarak Android‘i seçin ve Switch Platform deyin. Sonra oradaki Player Settings… butonuna tıklayın.

resim27

Inspector‘da açılan sayfada Other Settings altındaki Bundle Identifier‘ı “com.Unity.FlappyBird” olarak değiştirin:

resim28

Şimdi Build Settings‘teki Build butonuna basıp oyunun APK şeklindeki son versiyonunu bir yere kaydedin (Bu işlemden önce Android SDK kurmanız lazım. Kurmadıysanız şu derse göz atın: https://yasirkula.com/2013/07/17/unity-android-sdk-kurulumu-resimli-anlatim/ ).

Build işlemi bitince APK dosyasını Android cihazınıza atıp kurun ve oyunu test edin. Elinize sağlık!

The End

Bu uzunca dersin böylece sonuna gelmiş bulunmaktayız. Daha başka derslerde görüşmek dileğiyle, hoşçakalın 🙂

yorum
  1. zahir dedi ki:

    Amma uzun yazmışşın ya. İyi sabır var valla

  2. Birdal dedi ki:

    Çok Yararlı olmuş. Teşekkürler.

  3. hilal dedi ki:

    elinize saglık

  4. udede dedi ki:

    usta ellerinize sağlık unity orjinal eğitimlerine alt yazı ekleyecektiniz bu projeniz ne oldu??

    • yasirkula dedi ki:

      Bir iki videonun daha altyazılarını çevirdim ama sonra bi duraklama dönemine girdim. Emin olamıyorum o videolara çevirdiğim altyazıların yeterince faydalı olup olmadığına.

  5. serkan dedi ki:

    EMeğine sağlık çok faydalı. Altyazı olayı iyi fikrmiş.

  6. yusufaydinnn dedi ki:

    Desene bu anlatımla ben bile Flappy Bird oyunu yapabileceğim 🙂

    • yasirkula dedi ki:

      Script yazma konusunda biraz tecrübesi olanları hedef aldım, tecrübeniz yoksa yine yaparsınız ama yazdığım herşeyi anlayamayabilirsiniz. 😀

      • yusufaydinnn dedi ki:

        Tabi iddialı değilim.Sadece Web projelerimde Javascript kullanıyorum.

  7. Emre dedi ki:

    Emeğine sağlık yararlı bir ders olmuş, bayağı da uğraşmışsın 🙂

  8. Emre dedi ki:

    Abi arkaplan scriptini oluşturduğumda gökyüzü ve toprak kısmı (https://yasirkula.files.wordpress.com/2014/05/resim9.png burdaki gibi) gelmiyor. Neden acaba?

    • yasirkula dedi ki:

      arkaplan ve gokyuzu “public var” olduğu için gelmesi lazım. Scripti Main Camera’ya attıktan sonra Main Camera’nın Inspector’unda bu iki değişkeni bulamadığından emin misin?

      • Emre dedi ki:

        Şuan yeni geldi şimdi de engeller scriptindeki değişkenler gelmedi daha. Birazdan gelir herhalde 😀

  9. Akın dedi ki:

    hocam ellerinize sağlık sağolun güncel bir blog bulmak zor zaten sizde başkada bu denli yok başarılarınızın devamını ve getirilerinin bol olması dileğiyle iyi çalışmalar..

  10. Hamdi Can dedi ki:

    elinize sağlık ama linkler ölmüş sanırım, (proje dl için) çalışan başka bir link varmıydı? teşekkürler.

  11. ali dedi ki:

    teşekkürler ancak verdiğiniz linkler hep silinmiş

    • ali dedi ki:

      bir önceki cevabınızda vermişsiniz, projenin boyutu nasıl bu kadar az tuttu optimizasyon mu yaptınız, unityengine in kendisi 6mb diyorlar?

      • yasirkula dedi ki:

        Android’e build ettiğiniz APK dosyasının boyutu büyük oluyor, projenin boyutu o kadar büyük olmayabiliyor.

  12. Hüseyin Sönmez dedi ki:

    Linkler kırık

  13. lal13 dedi ki:

    Unitynin kendi sitesinde kodlamada yardımcı olan bir sayfa vardı yanılmıyorsam o sayfanın adresini biliyormusunuz?

  14. Ahmet Öz dedi ki:

    Yasir abi ben kuş yerine başka bişey yaptım aynısı olmasın tam öğrenmek için ama benim yaptığım karakter animasyonda geri sarma yapıyor.. kuşta 1-2-3-2-1 şeklinde gidiyor ya ben 1-2-3-1-2-3 şeklinde kodlaması nasıl olacak kodlamada neyi değiştirmem gerekir.

    • yasirkula dedi ki:
      #pragma strict
       
      public var saniyedeKareSayisi : int = 10;
      public var animasyonKareleri : Sprite[];
       
      private var sonrakiAnimasyonDegismeAni : float;
      private var mevcutAnimasyonKaresi : int = 0;
       
      function Start()
      {
          if( animasyonKareleri.Length < 2 )
              Destroy( this );
       
          sonrakiAnimasyonDegismeAni = Time.time + 1f / saniyedeKareSayisi;
      }
       
      function Update()
      {
          if( Time.time >= sonrakiAnimasyonDegismeAni )
          {
      		if( mevcutAnimasyonKaresi == animasyonKareleri.Length - 1 )
      			mevcutAnimasyonKaresi = 0; // 1-2-3 yaparken eğer son kare 3 ise 1'e geri dön (böylece 1-2-3-1-2-3 diye gitsin) (rakamlar temsilidir)
      		else
      			mevcutAnimasyonKaresi++;
               
              GetComponent( SpriteRenderer ).sprite = animasyonKareleri[ mevcutAnimasyonKaresi ];
              sonrakiAnimasyonDegismeAni = Time.time + 1f / saniyedeKareSayisi;
          }
      }
      

      Bu script tahminimce iş görecektir. Orijinal scriptle kıyaslayarak neleri değiştirdiğimi görebilirsiniz.

      • Ahmet Öz dedi ki:

        mantığı anladım yasir abi bir şey daha sormak istiyorum. karakter ileri doğru otomatik gitmesi için yazılan kod nasıl olacak android ekrana tıklanınca zıplayacak zıplama esnasında spritelerin birinde durmasını sağlamak için yazılan kodu nasıl yapabilirim…

        birde zemin göstermek için ne zapıcam hani marioda havada duran nesnelerin üzerinde de hareket ediliyor o nasıl yapılır. kod gerekir mi onun için ?

      • Ahmet Öz dedi ki:

        ben marioya benzer bir şey yapıyorum.

      • yasirkula dedi ki:

        Ben karakteri transform.Translate ile hareket ettirdim. Zıplarken spritenin donması için zıplama anında KusAnimasyon scriptinin enabled’ını false yapabilirsin. Yere düşünce geri enabled = true; yaparsın.

        Zeminde ve karakterde collider varsa ikisi birbiriyle temas ederler.

      • Ahmet Öz dedi ki:

        ileriye doğru hareket için bu kodları kullanıyorum ama otomatik değil..

        #pragma strict

        function Start () {

        }

        function Update () {
        if (Input.GetKey (KeyCode.RightArrow))
        this.transform.Translate (0.15,0,0);
        }

      • yasirkula dedi ki:

        function Update () {
        transform.Translate (15,0,0) * Time.deltaTime;
        }

        Doğrusu böyle olmalı.

  15. DursunCan dedi ki:

    gayet güzel bir eğitim olmuş teşekkürler Süleyman Bey… tek bir eksik var oda giriş, skor tabela şekli ve ölümden sonra öldüğüne dahil bir yazı ve tekrar oyna gibi butonlar…

  16. murat dedi ki:

    if( !oyunBitti )
    {

    }
    else
    {
    if( Input.GetMouseButtonDown( 0 ) )
    Application.LoadLevel( “Oyun” );
    }

    bu yer bende bir türlü çalışmıyor..

    bu şekilde koydum ben… tekrar başlatma yapmıyor. olduğu gibi kalıyor

    function Update ()
    {
    if( !oyunBitti )
    {
    dikeyHiz -= yercekimi * Time.deltaTime;

    if( Input.GetMouseButtonDown( 0 ) && !engeleCarptim )
    dikeyHiz = ziplamaGucu;

    var egim : float = 90 * dikeyHiz / yatayHiz;

    if( egim 50 ) egim = 50;

    transform.eulerAngles = Vector3( transform.eulerAngles.x,
    transform.eulerAngles.y, egim );

    transform.Translate( 0, dikeyHiz * Time.deltaTime, 0, Space.World );
    transform.parent.Translate( yatayHiz * Time.deltaTime, 0, 0 );
    }
    else
    {
    if( Input.GetMouseButtonDown( 0 ) )
    Application.LoadLevel( “Oyun” );
    }
    }

    • yasirkula dedi ki:

      Kodu doğru yazmışsınız. Kuş ölünce ekrana tıkladığınızdan ve sahnenizin isminin Oyun olduğundan emin olun. Eğer konsolda hata alıyorsanız hatayı yazın.

  17. Dursun44 dedi ki:

    Grafikleri biraz kötü gözüküyor. ben yüksek grafikli temalar kullanarak denedim ama bu seferde arka plan dağıldı yani arka plan aşırı derecede büyüdü… bu kod diziliminde arka planı nasıl düzeltebilirim yani boyutu belirleyen nedir.

    #pragma strict

    public var gokyuzu : GameObject;
    public var toprak : GameObject;

    private var arkaplanSayisi : int;

    private var kameraUnityEbatlar : Vector2;
    private var gokyuzuUnityEbatlar : Vector2;
    private var toprakUnityEbatlar : Vector2;

    function Start()
    {
    gokyuzuUnityEbatlar = Vector2( ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.width, ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.height ) / 100;
    toprakUnityEbatlar = Vector2( ( toprak.renderer as SpriteRenderer ).sprite.rect.width, ( toprak.renderer as SpriteRenderer ).sprite.rect.height ) / 100;

    camera.orthographicSize = ( gokyuzuUnityEbatlar.y + toprakUnityEbatlar.y ) / 2;
    arkaplanSayisi = Mathf.CeilToInt( ( camera.orthographicSize * 2 * camera.aspect ) / gokyuzuUnityEbatlar.x ) + 1;

    kameraUnityEbatlar = Vector2( camera.orthographicSize * camera.aspect, camera.orthographicSize );

    for( var i = 0; i < arkaplanSayisi; i++ )
    {
    var xKoordinati : float = transform.position.x – kameraUnityEbatlar.x + i * gokyuzuUnityEbatlar.x;
    Instantiate( gokyuzu, Vector3( xKoordinati, kameraUnityEbatlar.y, 0 ), Quaternion.identity );
    Instantiate( toprak, Vector3( xKoordinati, kameraUnityEbatlar.y – gokyuzuUnityEbatlar.y, 0 ), Quaternion.identity );
    }
    }

    • yasirkula dedi ki:

      Oyun mobil cihazlara odaklanmış durumda. O yüzden arkaplan texture’leri çok büyük değil. Eğer gokyuzu spritesi yerine yüksekliği daha fazla olan bir sprite eklerseniz arkaplan çok fazla büyümez.

      Kamera görüş alanının büyüklüğünü arkaplanın yüksekliğine göre ayarlıyor oyun başında ve yüksekliği daha fazla olan arkaplan demek oyun sırasında arkaplanın daha az büyütülmesi demek.

      Kameranın kendisini ayarladığı satır şu:

      camera.orthographicSize = ( gokyuzuUnityEbatlar.y + toprakUnityEbatlar.y ) / 2;

      • Dursun44 dedi ki:

        tamamdır çözümü buldum.. ben şöyle bir şey yaptım orjinal oyundaki gibi bir başlama ekranı ekrandaki tuşa basıldığında oyun sahnesine geçmek için

        if( Input.GetMouseButtonDown( 0 ) )
        Application.LoadLevel( “Oyun” );

        bu kodu kullandım ama olmadı nerede yanlış yaptım ?

      • yasirkula dedi ki:

        Kodu Update’e yazacaksınız.

  18. Dursun44 dedi ki:

    #pragma strict

    function Start () {

    }

    function Update () {
    if( Input.GetMouseButtonDown( 0 ) )
    Application.LoadLevel( “Oyun” );
    }

    bu şeklide yapıyorum ama ” All compiler errors have to be fixed before you can enter playmode!” diye bir hata veriyor . benim yapmaya çalıştığım ekrandaki bir nesneye basıldığın başka sahneye geçmesini sağlamak acaba yanlış mı yaptım nerede hata yapıyorum.

    • yasirkula dedi ki:

      Verdiğiniz kodda sorun yok. Aklıma gelen tek sıkıntı “Oyun”u çevreleyen tırnak işaretleri. Onları silip tekrar oluşturmayı deneyin.

      Verdiğiniz kod ekranda herhangi bir yere fareyle tıklanınca çalışır. Bir objeye tıklayınca çalışması için tıklanmasını istediğiniz objeye bir script verin ve scriptte OnMouseDown() fonksiyonunu kullanın.

      • Dursun44 dedi ki:

        #pragma strict

        function Start () {

        }

        function OnMouseDown () {
        Application.LoadLevel(“Oyun”);
        }

        bu şekilde yaptım hata vermeyi bıraktı ama geçiş yapmıyor. ben şunu merak ediyorum ekrandaki nesneye doğrudan atama yaptım scripti nesne üzerine birşey eklemem gerekiyormuydu. ” collider gibi ” ??

      • yasirkula dedi ki:

        Evet Collider lazım.

  19. Dursun44 dedi ki:

    Collider atama yaptıktna sonra kodlama satırında nasıl dahil edebilirim ? bir kaç kez denedim ama işe yaramadı.

    • yasirkula dedi ki:

      Collider olması yeterli. Ekstra kod gerekmiyor. Belki collider’da Is Trigger işaretli olursa sorun olabilir.

      • Dursun44 dedi ki:

        malesef olmuyor. bunun ile ilgi bir ders hazırlabilir misin ? benim gibi bu tür sorunlar yaşayanlar için çok iyi olur..

      • yasirkula dedi ki:

        Ama gerçekten başka bir şeye gerek yok. Collider’lı bir objeye tıklandığında o objedeki scriptlerde yer alan OnMouseDown() fonksiyonunun çalışması lazım.

  20. Dursun44 dedi ki:

    acaba benim yaptığım işlemde sorun olabilir mi ? benim yaptığım işlem şöyle. nesneyi ekrana koydum. sonra nesneye Component-Physics 2d- circle collider 2d yatım ( Is trigger işaretli değil ). sonra java scprip dosyası açtım adını giris butonu yaptım. ardından kavascrip dosyasına

    #pragma strict

    function Start () {

    }

    function OnMouseDown () {
    Application.LoadLevel(“Oyun”);
    }

    bu kodu yazdım.ardından çalıştır yaptığımda hiç birşey olmadı.

    • yasirkula dedi ki:

      Önce scripti objeye verdiğinizden, sonra Circle Collider’ın objenin etrafını iyice sarmaladığından emin olun. Ardından objeye tıkladığınızda konsola hata gelmediğinden emin olun. Son olarak “Oyun” scene’inin Build Settings’teki “Scenes In Build” listesine dahil olduğundan emin olun.

      • Dursun44 dedi ki:

        TAMAMDIR YASİR HOCAM… SONUNDA OLDU SORUN KODDA DEĞİLMİŞ YAZILIŞ ŞEKLİNDE DE DEĞİLMİŞ.SORUN BENİM HER İKİ SAHNEYİ DE BUİLD EKRANINA EKLEMEMEMDEN KAYNAKLANIYORMUŞ. GÜNLERDİR RAHATSIZ ETTİM KUSURA BAKMAYIN… YARDIMLARINIZ İÇİN TEŞEKKÜR EDERİM.

      • yasirkula dedi ki:

        Rica ederim 😉

  21. özgür dedi ki:

    Ben arkaplanın scriptini yazdıkdan sonra başlattım. arka plan gayet düzgün bir şekilde devam etti. fakat kuş ekranda gözükmüyor. scripti kaldırdığım zaman tek başına hareket ediyor.

  22. özgür dedi ki:

    teşekkür ederim oldu 🙂

  23. özgür dedi ki:

    abi KusHareket.js altına yazacağımız kodu bi yazarmısın sunu eleyin falan demisinde kafam karısdı

  24. Dursun44 dedi ki:

    build aldıktan sonra böyle bir hata benzer sorun var var acaba bu sıkıntı yaratır mı… çalışma konusunda sorun yapmıyor. bu hata beni ürpertti sadece ileride sorun olur mu ?

    Game scripts or other custom code contains OnMouse_ event handlers. Presence of such handlers might impact performance on handheld devices.
    UnityEditor.HostView:OnGUI()

    • yasirkula dedi ki:

      Hayır sorun oluşturmaz.

      • Dursun44 dedi ki:

        teşşekkürler beni korkutmuştu nerede hata yaptım diye.. bir şey daha sormak istiyorum. ben oyun içerisinde bir menü yerleştirdim giriş menüsüne dönmek için ama ölüm gerçekleştiğinde ekrana tıkladığında tekrar oynatma nedeniyle menü çalışmıyor menünün olduğu yeri tıklama dışına nasıl alabilirim. ?

      • yasirkula dedi ki:

        Menü GUITexture ile hazırlandıysa guiTexture.HitTest, OnGUI’de hazırlandıysa Rect.Contains fonksiyonlarını araştırın.

  25. mertcan dedi ki:

    abi ekrana tıklanınca tekrar oyun sahnesine dönmesini otomatik nasıl yapabiliriz. ekrana tıklamaya gerek kalmadan kendisi otomatik tekrar edebilmesi…

    • yasirkula dedi ki:

      OnCollisionEnter2D fonksiyonundaki “oyunBitti = true;” satırının altına şu kodu eklerseniz 1 saniye sonra otomatik yeniden başlar:

      yield WaitForSeconds(1);
      Application.LoadLevel( “Oyun” );

  26. mertcan dedi ki:

    evet ses geliyor ama bu seferde 1 saniye sonra tekrar oyun sahnesini açıyor ölmeden yapıyor havada uçarken 1 saniye sonra başa dönüyor.

  27. mertcan dedi ki:

    function OnCollisionEnter2D( temas : Collision2D )
    {
    Destroy( Camera.main.GetComponent( Arkaplan ) );
    Destroy( GetComponent( KusAnimasyon ) );
    yatayHiz = 0;
    oyunBitti = true;

    if( skor > yuksekSkor )
    yuksekSkor = skor;

    PlayerPrefs.SetInt( “YuksekSkor”, yuksekSkor );
    PlayerPrefs.Save();

    if( !engeleCarptim )
    audio.PlayOneShot( olumSes );
    }

    yield WaitForSeconds(1);
    Application.LoadLevel( “Oyun” );

    • yasirkula dedi ki:

      Ben size OnCollisionEnter2D fonksiyonunun sonuna koymanızı söyledim, siz class’ın sonuna koymuşsunuz. Dediğimi yaparsanız çalışacak. Yani o iki satırı audio.PlayOneShot( olumSes ); satırından sonraki } işaretinin hemen öncesine yazın.

  28. fatih dedi ki:

    function OnTriggerEnter2D( temas : Collider2D )
    {
    Destroy( Camera.main.GetComponent( Arkaplan ) );
    Destroy( GetComponent( KusAnimasyon ) );
    yatayHiz = 0;
    }

    function OnCollisionEnter2D( temas : Collision2D )
    {
    Destroy( Camera.main.GetComponent( Arkaplan ) );
    Destroy( GetComponent( KusAnimasyon ) );
    yatayHiz = 0;
    oyunBitti = true;
    }
    kushareket.js ekleyince sorun çıkıyor oynatmıyor

  29. derya dedi ki:

    Son Rötuşlar yaparken bende Inspector‘dan değerler değişmiyor sadece kodlar gözüküyor. birde kodları değiştirdiğimde de değerler aynıymış gibi duruyor sanki hiç verilen değerler etki etmemiş gibi

    • yasirkula dedi ki:

      Son Rötüşlar’daki değişiklikleri, scripti verdiğiniz objeyi Hierarchy panelinden seçip onun Inspector’unda uygulamalısınız. Yani scripti Project’ten seçmek işe yaramaz. Ayrıca public bir değişkenin değerini scriptten değil Inspector’dan değiştirin.

  30. Dursun44 dedi ki:

    yasir hocam menüyü yaptım güzel bir şekilde çalışıyor bide ses ekledim ama orada bir sıkıntı yaşadım.. ses menüye tıklanmadan çalıyor böyle bir kod yazdım.

    #pragma strict

    public var menuSesleri : AudioClip;

    function Start () {

    }

    function OnMouseDown () {
    audio.PlayOneShot( menuSesleri );
    }

    menüye tıklandıktan sonra çalmasını nasıl sağlarım

  31. Dursun44 dedi ki:

    Evet yasir hocam oldu ama menüye tıklayınca ses çıkmıyor. onu nasıl yapıcam

  32. mertcan dedi ki:

    tamamdır oldu scripte ses vermemişim 😀 kusura bakmayın hocam sizi yine gereksiz yere rahatsız ettim …

  33. mertcan dedi ki:

    Ses le ilgili sorulunca benim de aklıma şu geldi kısa olan bir sesi sürekli tekrar etmesini nasıl yapıcaz?

    • yasirkula dedi ki:

      Audio Source’taki loop’u seçeceksiniz. Ardından şu kodu yazacaksınız:

      audio.clip = muzikDosyasi;
      audio.Play();

      • mertcan dedi ki:

        başlarken 2 kere yapıyor oyun esnasında tekrarlamıyor sesi

        #pragma strict

        public var kanatSes : AudioClip;

        function Start () {

        }

        function Update () {
        audio.clip = kanatSes;
        audio.Play();
        }
        bu şekilde yazdım

      • yasirkula dedi ki:

        Loop açık olduğu halde tekrarlanmıyorsa bilmiyorum.

  34. turan_temel dedi ki:

    skor bölümünü başka bir sayfada nasıl gösterilir. yani en yüksek skoru başka sayfada göstermek istesek nasıl yaparız.

  35. Abbas dedi ki:

    Aslında bu oldukça basit bir işlem Yasir Hocamın da dediği gibi biraz araştırsanız rahatlıkla bulabilirsiniz…. araştırmayı seviyorsanız iş başka ben nasıl yapılacağını anlatayım… Ama size pek faydası olmaz hazırcılığın..öncelikle yeni bir java scrip açıp içerisine bu kodu yerleştirin.. ( Yüksek scoru başka bir yerde göstermek istiyorsanız bunu yapın aksi taktirde ellemeyin kodu bozarsanız karışmam 😀 )

    #pragma strict

    private var skor : int = 0;
    private var yuksekSkor : int = 0;

    function Start () {
    yuksekSkor = PlayerPrefs.GetInt( “YuksekSkor” );
    }

    function Update () {

    }
    function OnGUI()
    {
    GUI.skin.box.alignment = TextAnchor.UpperRight;
    GUI.skin.box.fontSize = 30;
    GUI.skin.box.fontStyle = FontStyle.Bold;
    GUI.color = Color.green;
    GUILayout.BeginArea( Rect( 0, 25, Screen.width – 25, 200 ) );
    GUILayout.BeginHorizontal();
    GUILayout.FlexibleSpace();
    GUILayout.Box(“\nYÜKSEKSKOR: ” + yuksekSkor );
    GUILayout.EndHorizontal();
    GUILayout.EndArea();
    }

    kaydedip kapatın.. sonra sayfadaki kus objesine atama yapın.. ardından kuşhareket scribi içerisindeki GUILayout.Box( “SKOR: ” + skor + “\nYÜKSEKSKOR: ” + yuksekSkor ); bulunduğu satırı bulup + “\nYÜKSEKSKOR: ” + yuksekSkor bunu silin kaydedip kapatın… bu kadar.. ben böyle yaptım oldu… bu arada benim en çok yaptığım hata ” işaretleridir.. ona da dikkat edin .. yüksek ihtimal en büyük hatalar tırnak işaretlerini yanlış yapmaktan geliyor 😀

    bu arada Yasir Hocam sayenizde bu güzel derste oldukça hoş olmuş… kendi projeme admob eklemenin farklı bir yolunu buldum… flappy bird de benzerde bir oyun yaptım benzer derken tarz olarak 😀 kısmetse yakında google play’e yüklemeyi düşünüyorum… yardımlarınız için teşekkür ederim…

  36. Melih dedi ki:

    2 dil bilmeme ramen arkaplan scriptini zar zor anladım, çünkü yazdıklarımızın sonunda ne olacağını söylememişsin. mesela for döngüsünü anlatırken “Bu komut arkaplan objelerinin kameraya tam oturmasını sağlıyor” deseydin 1 saat düşünmezdim

    • yasirkula dedi ki:

      for döngüsü arkaplanların yanyana yerleştirilmesini sağlıyor. Arkaplanların yüksekliğinin kameranın yüksekliğine eşit olmasını sağlayan kod camera.orthographicSize.

  37. Melih dedi ki:

    ingilizceyi iyi biliyon da türkçede çünkü diye bi kelime var kullanıverseydin ya her yaptığımız şeyde

  38. Hamza dedi ki:

    Abi cihazı unitye bağlayıp build etmeden debug yapabilirmiyiz ??

  39. Kerim dedi ki:

    abi benim hiç bilgim yoktu programlama oyun falan hakkında yaptım ama programdayken oyun sorunsuz çalışıyor oyunu apkya sorunsuz çevirdi ama telefona atıyorum kurarken hata veriyor uygulama yüklenmedi diyor sorun ne olabilir ?

  40. Kerim dedi ki:

    abi telefon babamın normal samsung galaxy mini s55701 di galiba en eskilerden 😀 başka telefonda dener sonucu söylerim yine de çok teşekkürler abi emeğin için ve ios için nasıl build edecez bilgin var mı ?

  41. erkan dedi ki:

    iosa blackberry ye flash playere falan hepsine bakıyorum switch platforma player settingede tıklıyor ama androidde sadece player settingse tıklıyor ne yapmalıyım yardım edin

  42. murat dedi ki:

    hocam direkt unity projesini verseniz olurmu?New Unity Project’ten açacak şekilede

  43. murat dedi ki:

    hocam anlatamadım herhalde.direkt oyun oynama sayfası açılsa.asset storede örnek projeler gibi

    • yasirkula dedi ki:

      Ben hâlâ anlamadım. Projeyi açıp ardından Project panelinden “Oyun.scene” asset’ini açınca oyun oynanmıyor mu zaten? Sorun nerede, daha net olur musunuz?

  44. mahmut dedi ki:

    hocam build edip play store atabilirmiyiz

    • yasirkula dedi ki:

      Eğer bu örnek projeyi Play Store’a koymanın mahsuru var mı diye soruyorsanız benim için mahsuru yok, koyabilirsiniz. Ancak kullandığınız görsellerin hazır olması sorun çıkarırsa ben karışmam.

  45. mahmut dedi ki:

    Ancak kullandığınız görsellerin hazır olması sorun çıkarırsa ben karışmam. ne gibi sorun çıkabilir

  46. mehmet dedi ki:

    hocam multiplayer yapabilirmiyiz?

  47. tarik dedi ki:

    Hocam unityde yukarda pivot global falan yaziyor nedir onlar

    • yasirkula dedi ki:

      Pivot: Objeyi pivot’undan mı yoksa merkezinden mi (Center) hareket ettirmek istediğimizi belirlediğimiz kısım.

      Global: Global uzayda ya da yerel uzayda (Local) işlem yapmaya yarayan buton.

  48. tarık dedi ki:

    hocam game over yazısı yok.yani kuş engele çarptığınde game over yazısı çıkacak bir kod varsa game over menüsü bende var.sadece kuş engele çarptğında o game over yerine göndersin

  49. melek212 dedi ki:

    hocam peki game over sahnesine bir efekt verebilirmiyiz.mesale düz çıkmak yerine yukardan aşağı dogru gelsin

  50. melek dedi ki:

    hocam gökyüzünün değilde topragın sağa gitmesini istiyorum.nasıl yapabilirim

  51. göksu dedi ki:

    hocam kuşun başta yere düşüyor.ama ben get ready yazılı ekran vardı.kuş sürekli sağa gidiyordu.biz ekrana tıklayınca başlıyordu.ekrana tıklayınca başlıyor ama kuş havalanması lazım.yere dogru hareket ediyor.

  52. göksu dedi ki:

    hocam hani bir tuşa basdığımızda scren değişiyoruzya.ona zaman koymak istiyorum.yeni scriptin içine yazmak için

  53. fevdun dedi ki:

    hocam oyun ekranındaki yüksekskor ve skoru game over menüsüne taşımak istiyorum.yani skor neyse game overde görünsün

    • yasirkula dedi ki:

      KusHareket’teki skor ve yuksekSkor’u public yapın. Bu skorlara erişmek için “GameObject.Find( “FlappyBird” ).GetComponent( KusHareket ).yuksekSkor” komutunu kullanabilirsiniz. Bu skorları menüde göstermek size kalmış, bu konuda yardımcı olmayacağım.

  54. Mustafa Emektar dedi ki:

    Ben bu işe yeni başladım. Sizin sayenizde javascript i daha iyi anladım ve daha pratik kullanıyorum artık. Her yeni paylaşımınızı devamını dört gözle bekliyorum. Sende 17 yaşında olduğunda göre Teşekkürler Kardeşim dememin bir sakıncası yoktur herhalde :).

  55. yasinkalalı dedi ki:

    hocam başlangıçta yerçekimi 0 olsun istiyorum çünkü menüden start butonuna basınca oyun hemen başlıyorum.ben ekrana tıklamadan önde yerçekimi 0 olsun istiyorum.sonra yerçekimi eski haline dönsün istiyorum.

    if(mouse falan vardı

    • yasirkula dedi ki:

      Yeni bir boolean tanımlayın ve başlangıç değerini false yapın. Kuşa yerçekimini ancak bu değer true ise uygulayın. Update’te if( Input.GetMouseButtonDown(0) )’ın içinde boolean’ın değerini true yapın.

      Bundan daha fazla yardımcı olamam, herşey çok açık.

  56. Mustafa Emektar dedi ki:

    Hocam eğer vaktiniz varsa sıradaki derslere ana menü ve game over sahnesi yapımını işlerseniz çok sevinirim.

    • yasirkula dedi ki:

      Menü tasarımında iyi değilim. Sonraki derslerimde büyük olasılıkla daha başka konulara değinirim. Siz menü tasarımını internetten video ders izleyerek halletmeye çalışın.

  57. hakantarik dedi ki:

    yasir bey skor her ekrana uymuyor.bazı ekranlarda sağda bazılardına üstte falan

  58. hakantarik dedi ki:

    Game scripts or other custom code contains OnMouse_ event handlers. Presence of such handlers might impact performance on handheld devices.
    UnityEditor.HostView:OnGUI()

    hocam bu hata nedir önemlimi

  59. hakantarik dedi ki:

    hocam ekrana tıkladıktan sonra ekrandaki nesnenin gitmesini istiyorum.mesela Go yazısı ekledim.ekrana tıklayınca go yazısı gitsin istiyorum

  60. mustafa dedi ki:

    hocam kus engele carptıgında beyaz ekran çıksın istiyorum.hafif beyaz ekran 0.50 sn

  61. mustafa dedi ki:

    peki hocam 50-55 levelli oyunum var.her level atladığında geçişlere siyah bir geçiş eklemek istiyorum

  62. mercime dedi ki:

    hocam flappy harekete başlayınca hep benim belirlediğim ses çalssın istyiroum.sesi import ettim.kodu yapamadım

    • yasirkula dedi ki:

      Bahsettiğiniz ses arkaplan müziği gibi birşey ise Audio Source component’indeki Play On Awake seçeneğini kapatın ve müziği ne zaman başlatmak isterseniz audio.Play fonksiyonunu kullanın.

  63. memduh dedi ki:

    hocam playerprefabs kushareketde ama ben game over scene almayı istiyorum.yani hem oyun scenede hem gameover scenede olmasını istiyorm.hocam yardımcı olursanız sevinirim

    • yasirkula dedi ki:

      Game Over scene’inde de yuksekSkor değişkeni oluşturup değerini yine “yuksekSkor = PlayerPrefs.GetInt( “YuksekSkor” );” şeklinde verebilirsiniz.

  64. Fırat dedi ki:

    bende Sutunlar gidip geliyor Layerlarıda ayarladım ve çoğu zaman üst Sutunlar kameranın dışında kalıyor nasıl düzelteceyim

  65. gökhan dedi ki:

    hocam yine bir sorum olcaktı şöyle bir kod yazdım :
    using UnityEngine;
    using System.Collections;

    public class enemyAI : MonoBehaviour {
    public Transform target;
    public int moveSpeed;
    public int rotationSpeed;
    public int maxDistance;

    private Transform myTransform;

    void Awake () {
    myTransform = transform;
    }

    // Use this for initialization
    void Start () {
    GameObject go = GameObject.FindGameObjectWithTag (“Player”);
    target = go.transform;
    maxDistance = 2;
    }

    // Update is called once per frame
    void Update () {
    Debug.DrawLine (target.position, myTransform.position, Color.yellow);
    //hedefe bakmak
    myTransform.rotation = Quaternion.Slerp (myTransform.rotation, Quaternion.LookRotation (target.position – myTransform.position), rotationSpeed * Time.deltaTime);

    if(Vector3.Distance(target.position, myTransform.position) > maxDistance) {
    myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
    }
    }
    }
    kodun amacı Player taglı objenin yanına gitmek fakat benim oyunumda birden fazla Player taglı obje olacak ve bunlardan en yakın olanına gidecek bunu nasıl yapabilirim?
    yardımlarınız için teşekkürler

    • yasirkula dedi ki:
      void Start()
      {
      	GameObject[] objeler = GameObject.FindGameObjectsWithTag( "Player" );
      	GameObject go = objeler[0];
      
      	if( objeler.Length > 1 )
      	{
      		Vector3 pozisyon = transform.position;
      		float enKisaMesafe = ( objeler[0].transform.position - pozisyon ).sqrMagnitude;
      
      		for( int i = 1; i < objeler.Length; i++ )
      		{
      			float mesafe = ( objeler[i].transform.position - pozisyon ).sqrMagnitude;
      			
      			if( mesafe < enKisaMesafe )
      			{
      				go = objeler[i];
      				enKisaMesafe = mesafe;
      			}
      		}
      	}
      	
      	target = go.transform;
      	maxDistance = 2;
      }
      
  66. Burak dedi ki:

    Yasir abi merhabalar, öncelikle rahatsız ettiğim için özür dilerim. Tutorial’ı sonuna kadar takip ettim ve güzel bir çalışma oldu lakin uygulamayı telefonda çalıştırdığımda engeller görünmez oluyor ancak bir kere yandıktan sonra oyun düzeliyor. Neden olduğunu bulamadım sizin bir fikriniz olabilir mi?
    Bir de oyun açıldıktan sonra ekrana bir kez dokunulunca oyun başlasın istedim ama bir türlü beceremedim şu an oyuna telefondan tıkladığım anda oyun başlıyor. Eğer sizin için kolay bir şeyse bu konuda da yardımcı olabilir misiniz?
    Ayrıca Tutorial için harcadığınız emekler için çok teşekkür ediyorum. Saygı ve merakla sizi ve paylaşımlarınızı takip ediyorum. İyi çalışmalar.

    • yasirkula dedi ki:

      Bahsettiğiniz sorunu ben de yaşadım ve sorunun kaynağını da bulamadım.

      Oyunun ekrana tıklayınca başlaması için kameraya bir script verin ve Start fonksiyonunda Time.timeScale=0f; yapın. Update fonksiyonunda ise şu kodu yazın:

      if( Input.GetMouseButtonDown(0) )
      Time.timeScale=1f;

  67. Burak dedi ki:

    hocam bazen engelleri geçiyorum aniden alt ve ust engel yukarı çıkıyor.yani geçilmez oluyor.ne kadar zıplasam yukarı çıkamıyorum.engeller çok rastgele olmuş.sınır koyabilirmiyiz

    • yasirkula dedi ki:

      Engeller.js scriptinin 31. satırındaki yKoordinati’nın rastgele aldığı değer aralığını küçültebilirsiniz (bunun için 0.6’yı örneğin 1.5 yapmayı deneyin).

  68. gökhan dedi ki:

    hocam son bir sorum daha olcaktı (sürekli rahatsız ettiğim için özür dilerim.)

    *Kod çok uzun olduğundan admin tarafından silindi.*

    bu kodu objeye atınca raycast çalışıyor fakat cameraya atınca çalışmıyor.(Sadece raycast çalışmıyor.)

  69. mahsun dedi ki:

    hocam kuş havaya gidiyor ama aniden aşagı iniyor.

    var egim : float = 90* dikeyHiz / yatayHiz;

    if( egim 50 ) egim = 50;

    bu kodu nasıl değiştirmem gerek

  70. osman dedi ki:

    selamun aleyküm hocam cok tesekkür ederim cok istifade ettik cok sey ögrendik sizden ricam pandra run gibi oyunlarda tek ziplama yapabiliyorsun baska ziplayamiyorsun birde iki tiklarsan iki ziplama (double jump) bundan baksa ziplayamiyorsun böyle birsey nasil yapariz ? bildigniz bir anlatim varmi tesekkrüler!

    • yasirkula dedi ki:

      Bildiğim bir anlatım yok. Elinizde “ziplamaSayisi” adında bir int değişken olsun, ilk değeri 0 olsun. Zıplama tuşuna basınca bu değişken 2’den küçükse zıplayın ve değişkenin değerini 1 artırın. OnCollisionEnter fonksiyonunda ise zemine temas etmişsek değişkenin değerini sıfırlayın.

  71. Selçuk dedi ki:

    Merhaba, öncelikle emeklerine sağlık.

    Blender 3d kullanıcısıyım ve Unity öğrenmek ve bu iki eşsiz programın nimetlerini birlikte kullanmak istiyorum. Senin blogun çok faydalı ve eminim ki bana ve benim gibi Unity öğrenmek isteyenlere ışık tutacak.

    Sorum şu kardeşim:

    Her bir platform(android, windows v.b) için ayır ayrı programlama yapmak mı gerekiyor yoksa Unity tüm kodları platforma göre portluyor mu?

  72. FuaT dedi ki:

    apk ya cevirince bende ekran dikey çıkıyor. Bunu yatay nasıl yapabilirim?

    • fuatmercan dedi ki:

      kameraUnityEbatlar : (7.8, 2.2)
      gokyuzuUnityEbatlar: (2.9, 4.1)
      (gokyuzu.renderer as SpriteRenderer ).sprite.rect.width: 288
      ( gokyuzu.renderer as SpriteRenderer ).sprite.rect.height: 414

    • yasirkula dedi ki:

      Edit-Project Settings-Player-Resolution and Presentation-Default Orientation’ı Landscape Left yapabilirsiniz.

  73. feviz dedi ki:

    if( skor > yuksekSkor )
    klon.renderer.enabled = true;}

    hocam bu kodu yazdım ama klon ekrana çıkmıyor.yani amacım skor yuksek skordan cok olunca ekrana new yazısı çıksın new yazısını tanımladım.herşey tamam ama görünmüyor

  74. fevzi dedi ki:

    hocam yani skor yüksek skordan büyük olunca new yazısı çıksın istiyorum.new yazısının resmini aldım.değişken tanımladım herşey hazır ama skor yüksek skordan büyük olunca yazı çıkmıyor nasıl yapacabilirim

    • yasirkula dedi ki:

      Eğer klon objesinde new yazısı depolanmışsa kodun düzgün çalışmasını beklerdim ama garip bir şekilde çalışmıyormuş. Eğer new yazısı bir GUITexture ise klon.guiTexture.enabled = true; yapmayı deneyin.

  75. fevzi dedi ki:

    hoca kodu böyle anlatayım.yani guitexture değil kodu kısalttım

    var klon: GameObject;

    function Start()
    {
    klon.renderer.enabled = false;
    }
    function Update ()
    {

    if( skor > yuksekSkor )
    yuksekSkor = skor;
    klon.renderer.enabled = true;

    PlayerPrefs.SetInt( “SKOR”, skor );
    PlayerPrefs.Save();

    PlayerPrefs.SetInt( “YuksekSkor”, yuksekSkor );
    PlayerPrefs.Save();

    }

  76. fevzi dedi ki:

    hocam bu seferde skor yüksek skordan büyük olmasada çıkıyor

  77. fevz dedi ki:

    hocam swing copters yapılırmı bu oyun üzerinden mesela engeller yana değilde üstte dogru kuşta aşagı degilde saga sola dogru gitsin.yapılabilirmi

  78. murat dedi ki:

    Hocam selamlar tutorial için gerçekten çok teşekkurler bana unityi siz sevdrdiniz diyebilirm hobby olarak başladm bu işe.Bu oyun denemesini anca yapabildim işlerden dolayı benim bi sorum olcaktı arkaplan scriptini eklediğm zaman kuş arkada kalıyor görüntü olarak yani game işliyor ama kuş arkada daha tutorialda 13.sayfadaym aynn yazılanları yaptım fakat böyle bi hata çıktı sizce nerde nasıl bi yanliş yapmış olurum. Şimdiden yardımınz içn teşkkrler.

  79. mfed dedi ki:

    hocam swing copters klonunu ne zaman paylaşacaksınız

  80. fevdun dedi ki:

    hocam alt üst engel arasına altın koyacagımda tam olarak nereye yazacağım kodu

  81. fevdun dedi ki:

    hocam öncelikle sorumu cevapladıgınız için teşekkürler.engeller.js kaçıncı satırına bakayım.

  82. fevdun dedi ki:

    nasıl ekleyeceğim mesela bir örnek varmı hocam.altın resmi hazır

  83. zxc dedi ki:

    yasir bey arka planın sola doğru kayması değilde aşağıya doğru kayması için nerelerin değişmesi gerekiyor.

  84. fevzi dedi ki:

    hocam hakkınızı helal edin bu oyunu alıp play storede yayınlasam yada bu oyundan para kazansam falan hakkınızı helal edin içim rahat olsun

  85. günay dedi ki:

    hocam selamun aleyküm

    hocam ben ekranın sol tarafının çalışmasını istiyorum.mesela oyunu oynayan sağ tarafa tıklayınca kuş hareket etmesin ekranın sol tarafına tıklayınca kuş hareket etsin istiyorum getmousebuttonu nasıl değiştirmem lazım

    • yasirkula dedi ki:

      Aleykümselam. O if’i şöyle değiştirin:

      if( Input.GetMouseButtonDown(0) && !engeleCarptim && Input.mousePosition.x < Screen.width / 2 )

  86. günay dedi ki:

    hocam getmousebuttondown kodları androidde çalışıyormu?birde sağ taraf içinde hangisini kullanacağım

  87. günay dedi ki:

    hocam aşağıdaki kodu android için yazmıştım bunu nasıl değiştireceğim

    if( Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Began && !engeleCarptim )

  88. günay dedi ki:

    Input.touchPosition.x < Screen.width / 2 ) ile deneyince olmadı mouse yerine touch kullandım

  89. günay dedi ki:

    hocam mousedown kullandıgında androidde bug falan çıkıyormuş doğrumu onun için touch kullandım acaba mousedown bug çıkarırmı

  90. günay dedi ki:

    Input.touchPosition.x < Screen.width / 2 ) ile deneyince olmadı mouse yerine touch kullandım.hata verdi

  91. günay dedi ki:

    hocam klonluyoruz 2. serisi geliyormuş dogrumu acaba hangi oyun

  92. gunay dedi ki:

    güncel oyunlardanmı peki .fifa 2015 ultimate mi yoksa

  93. günay dedi ki:

    hocam multiplayer hakkında hiç türkçe kaynak yok.google play game services hizmeti varmış.google sunucularıyla beraber multiplayer oyun yapılıyormuş androidde.buna dair biraz yazı yazmayı düşünüyormusnuz

  94. memde dedi ki:

    hocam 2.klon bitmedimi bitmediyse ne zaman biter

  95. mehmdug dedi ki:

    abi bu kuş yere çarpınca ölmemesi için ne yapabiliriz

  96. battalgazi dedi ki:

    hocam kuşu sadece ileriye götürüp, sadece zıplatmak için nasıl bir kodlama yapıcaz ben kuşun dönmesini istemiyorum. yer çekimide olmasını istemiyorum… ileri gidip sadece tıklama yapınca zıplamasını istiyorum.. bunu nasıl yapabilirim…

  97. battalgazi dedi ki:

    yasir abi ben bu oyunlar konusunda çok meraklı biriyimdir kendi çapımda bir şeyler yapıyorum.tasarım yaptım ama kodlamada sorunlarım var bir iki yerden kaynak inceleyerek bir yere kadar geldim.. ama bir yerde sorun yaşıyorum.. benim yaptığım karakter ileri doğru gidiyor ve zıplıyor.. engele çarıncada duruyor.. anca ben karakter zıplatamıyorum yani.. iler giderken ekrana tıklandığında zıplayı aynı yerine gelmesi için nasıl bir kodlama yapıcam..

  98. battalgazi dedi ki:

    yasir abi şunu denedim..

    #pragma strict

    public var yercekimi : float = 4;
    public var ziplamaGucu : float = 2.5;
    public var yatayHiz : float = 1.5;
    private var dikeyHiz : float = 0;

    function Update ()
    {
    dikeyHiz -= yercekimi * Time.deltaTime;

    if( Input.GetMouseButtonDown( 0 ) )
    dikeyHiz = ziplamaGucu;

    var egim : float = 90 * dikeyHiz / yatayHiz;

    if( egim 00 ) egim = 00;

    transform.eulerAngles = Vector3( transform.eulerAngles.x,
    transform.eulerAngles.y, egim );

    transform.Translate( Vector3( 0, dikeyHiz, 0 ) * Time.deltaTime, Space.World );
    }

    kuşun zıplama kodu bu işe yarıyor.. karakterin dönmesine engel oldum ama her tıklamaya havaya yükseliyor.. birde yerde yol şeklinde bir zeminim var ve o zemin üzerinde durmuyor.. orda durmasını sağlamak ve tek bir kere zıplamasını sağlamak için bu kod üzerinde nasıl bir değişiklik yapıcam..

    • yasirkula dedi ki:

      Bu kod 2D bir karakter olan Flappy Bird için yazıldı. 3D bir dünyada işe yaramaz. Bence Unity’nin hazır Character Controller scriptiyle ilgili tutorialler araştırın.

      • battalgazi dedi ki:

        yaptığım oyunda 2d yasir abi…
        bu konuda yardımcı olursan çok sevinirim.. benim 2d karakterim bu kodla çalışıyor.. ama tek zıplama yerine her bastığında daha çok zıplıyor birde yerdeki zemin üzerinde durduramıyorum sürekli aşağı düşüyor.

      • yasirkula dedi ki:

        Bu kodu belki de sıfırdan yazmanız gerekir onun için. Kodla çok aykırı şeyler istiyorsunuz. Siz en iyisi Youtube’da “Unity 2D platform game” diye arayın.

  99. Serhat dedi ki:

    Hocam, öncelikle verdiğiniz ders çok başarılı, Bir sorum olacak;
    Oyunda Flappy Bird Yere çarptığı zaman ekrana GuiTexture çıksın bu GuiTexture ‘nin herhangi bir işlevi olmasını istemiyorum sadece çıksın. Bunu nasıl yapa bilirim. Teşekkürler 🙂

    • yasirkula dedi ki:

      Bir GUITexture objesi oluşturup ekranda istediğiniz şekilde konumlandırın. Ardından objenin GUITexture component’inin solundaki işareti (Inspector panelinden) kaldırın. Oyun bitince guiObjem.enabled = true; komutunu çalıştırın. Bunun çalışması için aynı scriptte “public var guiObjem : GUITexture;” isminde bir değişken oluşturup Inspector’dan değerini verin.

      • Serhat Akgül dedi ki:

        Dediğinizi yaptım ama olmadı 😦

        Kusharaket.js ye ekledim. Önce dediğinz gibi bir GuiTexture oluşturdum. Bu gui Texturenin Tik işaretini kaldırdım.

        Kodlara geçtim : “public var guiObjem : GUITexture;” diye bir değişken oluştudum. Sonra OnTriggerEnter2D fonksiyonun Else kısmının altına ” guiObjem.enabled = true; ” komutunu yapıştırdım ama olmadı. Yanlış bir şeymi yaptım acaba.

        OYUNA Giriyorum hata yok. Ama Console ‘da şöyle bir hata var UnassignedReferenceException: The variable guiObjem of KusHareket has not been assigned.
        You probably need to assign the guiObjem variable of the KusHareket script in the inspector.
        KusHareket.OnTriggerEnter2D (UnityEngine.Collider2D temas) (at Assets/KusHareket.js:92)

      • Serhat Akgül dedi ki:

        Tamam yaptım oldu. Ama bu sefer Panel ‘e çarpınca çıkıyor. Ben toprağa çarpınca çıkmasını istiyorum. Bu kodu nereye eklim ?

  100. Serhat Akgül dedi ki:

    Tamam bunuda yaptım Çok teşekkür ederim 🙂

  101. Unity3d dedi ki:

    Hocam kaydetmek istediğimde ”The referenced script on this behuvior missing” diyor

    • yasirkula dedi ki:

      Bir objede yer alan bir scripti ya silmişsiniz ya da o scriptin bulunduğu konumu Windows Explorer vasıtasıyla değiştirmişsiniz. Tüm objeleri tek tek kontrol edin; eğer bir objedeki bir component’te Missing Behaviour tarzı bir yazı varsa hatanın kaynağı o component’tir.

  102. Arda dedi ki:

    Hocam maşşallah ne sabırlı adammışsınız projeye başlamayı düşündüm ama bütün yorumları okuduktan sonra vazgeçtim 1 ayda bitirmişler 😀

  103. battalgazi dedi ki:

    yasir abi zemin hallettim farklı yerçekimi kullanarak karakteri düzenledim.. ama hala zıplatamıyorum… zıplatmanın kodlamısı nasıl yapabilirim.. mausun tıklamasıyla..

    • battalgazi dedi ki:

      senden çok özürdilerim yasin abi.. sanırım karakteri zıplatmayı buldum.. gayette güzel zıplıyor.. 😀 ama çok sert zıplama yapıyor.. biraz daha kurcalayarak zıplatmayı yumuşatmaya çalışıcam… eğer yapamazsam senden yardım isterim 😀

  104. ali dedi ki:

    yasir abi geçen gün flappy bird deki gibi skor tablosu oluşturmak için scriptimiz ayrı mı olacak yoksa kullandığımız 4 script ten birinde mi yapacağız.

  105. ali dedi ki:

    abi araştırıyorum ama bulamadım şu giriş ekranı ve game over ekranı yapmada yardımcı olurmusun?

  106. ali dedi ki:

    abi peki script olarak arkaplandamı çalışacağım

  107. leyla dedi ki:

    hocam 2. klona başladınız mı ne zaman biter

  108. mahaa dedi ki:

    oyun tarzı aksiyon mu hocam nedir acaba

  109. hakan dedi ki:

    oncelıkle boyle bır konu ıcın tesekkurler hocam engeller gozukmuyor aynı sekılde yaptıgım halde yol gıdıyor fakat engellerle aynı sevıyede oldukları aynı pozıstonda oldukları ıcın gozukmuyorlar nerde hata olabılır ?

  110. Oğuzhan Güven dedi ki:

    Assets/KusHaraket.js(50,9): BCE0005: Unknown identifier: ‘engeleCarptim’.
    bu hatayı alıyorum sorun nedir

  111. ErsanKaran dedi ki:

    Ben bir tane oyun yaptım sizin verdiğiniz bilgilerden yararlanarak kuş yerine uzay aracı borular yerine meteor yaptım bunu koyabilir miyim google play store telif hakkından sıkıntı oluşturur mu?

  112. Oğuzhan Güven dedi ki:

    ben sesleri de yaptım sesler geliyor ama oyunda kuş yerine ses işareti görünüyor bunu nasıl halledicez

  113. Faruk dedi ki:

    Hocam engeller arka planın arkasında kalıyor bu yüzden Game’de görünmüyorlar. Bunu nasıl düzeltebilirim? Bir de sorum var. Android için basit, zevkli bir casual game oluşturmak istiyorum (muhtemelen 2d olur). Bunun için hangi motoru tavsiye edersiniz? Unity biraz daha profesyonel oyunlar için uygun gibi görünüyor.

    • yasirkula dedi ki:

      FlappyBird’ün position’ının z’sini -2, engellerinkini -1 yapmayı deneyin.

      Ben 2D motor olarak Game Maker, 3D motor olarak Unity3D kullandım. İkisinden de memnunum.

  114. umut dedi ki:

    Yasir hocam eline sağlık güzel bir yazı olmuş. Benim projemde ana playerım bir ucak 2boyutlu be ben buna collider atamak istiyorum ama bir türlü tüm yüzeyini kaplayan bir collider bulamadım 3 boyutlu olsa mesh collider işimi görüyordu aynı işlevi 2boyutlu nesnelerde nasıl yaparım

  115. Bulent dedi ki:

    Ekrana tıklama olayı mouse olarak ayarlı ama telefona atınca calısıyor. Fakat dogrusunun bu oldugunu sanmıyorum . Touch fonksiyonu kullanılması gerekiyor herhalde dogrusunu biliyormusunuz ?

  116. maneR dedi ki:

    İyi çalışmalar.Ben kameranın belirli bir hızda aşağı doğru hareket etmesini istiyorum hiçbirşeye bağlı olmadan belirli hızda aşağı inmeli.

    • maneR dedi ki:

      2d oyun yapımında kullanacağım aslında rigidbody 2d eklesem işime yarıyorda ilerde bir problemle karşılaşırmıyım diye sordum bu soruyu

      • maneR dedi ki:

        Bir şey daha var, bu arkaplan öğesini sürekli sağa aktarmasını değilde sürekli aşağıya doğru aktarmasını istiyorum onuda söylerseniz çok memnun olurum

    • yasirkula dedi ki:

      Kameranın Update’inde transform.Translate( Vector3.down * Time.deltaTime ); yazabilirsiniz. Arkaplanlı sorunun cevabını scriptleri araştırarak kendiniz bulunuz.

  117. fatih dedi ki:

    Selamlar herkese.Benim random olarak nesne yerleştirmem gerekiyor gerçekten çok araştırdım ama olmuyo illa bi sıkıntı çıkıyor.elimde bir nesne var bu nesneyi rasgele olarak sağa ve sola yerleştireceğim konum basit ama çözümü bulamadım ne yapmam gerektiğinden biraz bahsederseniz çok sevinirim

    • fatih dedi ki:

      yardımcı olacak msıınız ? 3gündür cvp bekliyorum da

    • yasirkula dedi ki:

      Temple Run örneğimde puan objelerinin çıkabileceği spawn noktaları var ve bunlardan rastgele birisi seçilerek orada puan spawn ediliyor. Onun gibi birşey olabilir: https://yasirkula.com/2015/01/15/unity-3d-temple-run-tarzi-infinite-runner-oyun-ornegi/

      • fatih dedi ki:

        bu kod 3d oyunda çalışıyor fakat 2d oyunda çalışmıyor nedeni nedir acaba

        var Kopya:GameObject;
        function OnMouseUp()
        {
        Instantiate(Kopya, Vector3(0,3,0),Kopya.transform.rotation);

        }

        vektor3 ü vektor2 de yaptım fakat çalışmıyor

      • fatih dedi ki:

        uzun uğraşlar sonu hallettim arkadaşlar collideri eklemeyi unutmayın bu yüzden çalışmıyor kod

      • fatih dedi ki:

        bu kodu bir tuşa nasıl atayabilirim

      • yasirkula dedi ki:

        Input.GetKeyDown inceleyebilirsiniz.

      • fatih dedi ki:

        input.GetKeyDown a baktım fakat benim kod update de çalıştığı zaman milyonlarca kopya oluşuyor ve pc donuyo + oyun oynanmaz hale geliyor benim amacım elimdeki bu kodu bir tuşa aktararak her tuşa basıldığında bir kopya oluşturmak.1 kere bastığımızda 1 kopya o şekilde lazım birçok şey araştırdım denedim ama yanlış olan bişeyler var hata vermiyor ama çalışmıyorda alttaki kod şu haliyle kopya adlı gameobject e mause ile tıklandığında kopya oluşturuyor bana mause değil bir tuş lazım ( update fonksiyonunu kullanmamam gerekiyor )

        var Kopya:GameObject;

        function OnMouseUp()
        {
        Instantiate(Kopya,Vector2(0,8),Kopya.transform.rotation);
        }

      • yasirkula dedi ki:

        Update’siz bildiğim kadarıyla mümkün değil. Sizin sorununuz Kopya oluşturan bu kodu Kopya objesine vermeniz (anladığım kadarıyla). Sahnedeki her Kopya için GetKeyDown çalışır ve sahnedeki Kopya sayısı kadar yeni klon oluşur. Onun yerine kodu, sahnede sadece bir tane olan boş bir GameObject’e verebilirsiniz.

        Örnek kod (A tuşuna basınca çalışır):

        var Kopya:GameObject;
        function Update() {
        if( Input.GetKeyDown( KeyCode.A ) )
        Instantiate(Kopya, Vector3(0,3,0),Kopya.transform.rotation);

        }

      • fatih dedi ki:

        Çok teşekkür ederim valla kafayı yemek üzereydim sorunu halletmeden yatamadımda bir türlü

  118. Osman dedi ki:

    Oyunda pause butonu koydum butona basıncada zıplıyor resume diyincede zıplıyor bunu nasıl onleyebilirim ?

    • yasirkula dedi ki:

      Mouse ile tıklanılan noktanın pause butonu içerisinde olup olmadığını test etmeniz lazım. GUITexture kullandıysanız HitTest, GUI.Button kullandıysanız Rect.Contains fonksiyonundan yararlanabilirsiniz. Araştırma kısmı size kalmış.

  119. Osman dedi ki:

    UI buton kullanıyorum tıklanılan nokta pause içerisinde.

    • yasirkula dedi ki:

      Tıklanılan noktanın pause butonu içerisinde olup olmadığını “kod” vasıtasıyla test etmeniz lazım. UI butonda On Click() yer alır, butona tıklayınca oradaki fonksiyonlar çalıştırılır. Eğer bu fonksiyon çağrılmışsa kuş zıplamamalı öbür türlü zıplamalı.

      Ben genel bir fikir verdim uygulamaya dökmeyi siz halledin.

  120. Ali Sami dedi ki:

    arkadaşlar bana kushareket.js ‘ nin son halini tamamen yazabilir misiniz lütfen? bir de boruların aralıklarını nasıl açabilirim? çok dar karakter çok zor geçiyor.

  121. Kamil dedi ki:

    hocam oyunu yaptık peki nasıl menü ekleyebiliriz oyunun üzerine (Play – Exit falan)? anlattığınız konunuz varsa bana link atabilir misiniz lütfen? ya da nereden bulabilirim?

  122. Kamil dedi ki:

    inceleyeceğim teşekkür ederim. aslında ben menüyü yaptım fakat onu oyuna bağlayamadım. onu öğrenmek istiyorum

  123. Kamil dedi ki:

    on clickte yaptım fakat play game – quit game vs tıklamadan unity player’ın playine tıkladığım gibi oyun başlıyor. yani menüyü beklemiyor. onun için ne yapabilirim?

  124. Kamil dedi ki:

    ve play ile quit oyun esnasında da sürekli ekranda duruyor.

  125. fatih dedi ki:

    merhaba, şimdi elimde yatay bir çubuk var ve ben bir tuşa bastığım zaman y si -8 olan bir çubuk elde ediyorum(-8 aşağıda bir çubuk)
    ben her tuşa bastığım zaman bu çubuk -8 aralıklarla alt alta sürekli kopya oluşturacak
    benim kodumda her bastığımda y si -8 olan çubuklar oluşuyor bana bu lazım değil lazım olan her kopya alt alta gelmesi gerekiyor kopyaların alt alta gelmesi için kodu nasıl düzenlemeliyim ?

    Instantiate(Kopya, Vector2(0,-8),this.transform.rotation);

    • yasirkula dedi ki:

      Bir değişken olur, ilk değeri -8 olur. Her Instantiate sonrası değeri 8 azaltılır. Vector2’nin y’sine ise değer olarak bu değişken girilir.

      • fatih dedi ki:

        her instantiate sonrası değerin düşmesini nasıl sağlayabilirim

      • yasirkula dedi ki:

        Bu soru gerçekten çok basit, lütfen araştırın öğrenin bu tarz şeyleri. Instantiate fonksiyonundan sonra ne kod yazarsanız o kod “instantiate sonrası” çalıştırılır.

      • fatih dedi ki:

        if kullanarak yapsam uygun olurmu örneğin (kodları kısaltarak yazdım)

        int sayi=-8;
        if ( i=1;)
        sayi=sayi*i;
        instantiate Vector2(0,sayi);
        i++;

      • yasirkula dedi ki:

        Anlamadım niye şöyle yapmıyorsun ki:

        Instantiate(Kopya, Vector2(0,sayi),this.transform.rotation);
        sayi -= 8;

      • fatih dedi ki:

        çok pardon ya if yazmışım for döngüsü kullanarak sonuç olarak 2sinide çalıştırdım aynı sonucu veriyor teşekkürler

  126. Özgür dedi ki:

    Merhaba high score sistemi yaptım ama yuksekskor bir el daha oynadıktan sonra yazdırılıyor direk skor yuksek yapınca değil bunu nasıl cozerim

    • yasirkula dedi ki:

      Yüksekskoru siz yazdığınız için bunu benden daha iyi sizin bilmeniz lazım. Yüksekskorun olayı nedir? Yüksek olması. Bir if ile mevcut skorun sistemde kayıtlı yüksekskordan büyük olup olmadığını test etmeniz lazım.

  127. Özgür dedi ki:

    Ben kendimde anlamadım Yuksekskoru PlayerPrefs le almadan once direk yazıyordu alınca sonraki elden sonra yazıyor.. 🙂

  128. Özgür dedi ki:

    Baska sahneye alıyorum skoru ondan yani

  129. Şükrü dedi ki:

    oyunu telefona attığımda borular ve kuş bulanık görünüyor. arka plan normal. nasıl çözünürlük ayarı yapabilirim?

  130. Şükrü dedi ki:

    şöyle bir şey buldum https://www.youtube.com/watch?v=iKyEZ1ne6rQ fakat bunu koyduğumda toprak kalıyor gökyüzünü kaybediyorum

  131. Brkay dedi ki:

    Hocam ben oyunu build etmeden once skorlari sifirlamak istiyorum nasil yapabilirim. ?

    • yasirkula dedi ki:

      Unity editöründe elde ettiğiniz yüksekskorlar build aldığınız oyuna geçmez merak etmeyin. Zaten PlayerPrefs ile kaydedilen yüksekskorlar o cihaza hastır, başka bir cihazda oyunu oynarken o yüksekskorlar orada yer almaz.

  132. Şükrü dedi ki:

    peki oyunun içine nasıl sound on/off butonu koyabiliriz?

    • yasirkula dedi ki:

      AudioListener.volume değişkeni oyunun ses düzeyini ayarlamaya yarar. Bu değer tüm seslere etki eder, yani global bir ayardır. Değerini 0 yaparsanız oyundan çıt çıkmaz, 1 yaparsanız sesler en gür halleriyle çalınır. Varsayılan değeri tahminimce 1’dir bunun.

  133. umut dedi ki:

    Vector2((Background.renderer as SpriteRenderer).sprite.rect.width, (Background.renderer as SpriteRenderer).sprite.rect.height)

    bu kod bir sprite için geçerli ya ben 3boyutlu bir gameObjenin boyutlarını bu şekilde nasıl alabilirim

  134. mert dedi ki:

    Projenin indirme linki mevcutmu şuan acaba

  135. Bahadır dedi ki:

    Hocam Projeyi tamamladım sesler biyomlar kuşu fakat ENGEL aralarından geçerken bir şeyler toplamasını istiyorum nasıl halledebilirim ?

    • yasirkula dedi ki:

      Toplanacak sprite’nin Pivot’unu Center’da varsayarsak, bu objenin oluşması gereken konum şu:

      Vector3( xKoordinati + engelUnityEbatlar.x / 2, yKoordinati – altUstEngelArasiBosluk / 2, 0 )

      Bu objeyle temas etme işini siz halletmelisiniz. Muhtemelen Line Collider’dan da kurtulmanız gerekecek.

  136. Bahadır dedi ki:

    Teşekkür ederim hocam 🙂

  137. mert dedi ki:

    hocam peki bu oyunu yatay değilde dikey eksende yapacak olsam var mı bir anlatım ve ya nelerin değişmesi gerekir ayrıca android kontrolleri ekranda sağa çekince karakter sağa gelecek sola çekince sola gelecek şekilde yardımınızı bekliyorum.

    • yasirkula dedi ki:

      KusHareket ve Arkaplan scriptlerinde değişiklikler gerekecek ancak bu değişiklikleri sizin bulup yapmanız lazım, ben uğraşmak istemiyorum.

  138. keremliler dedi ki:

    Abi biz bu RASTGELE kodu 3d oyun için nasıl uyarlicaz? sadece rastgele kodunu almak istiyorum yapmak ıstedıgım şey şu : kamera sola dogru hareket ediyor(bunu yaptım). kamera hareket ettıkce rastgele engel cıkıyor belli aralıklarla

    • yasirkula dedi ki:

      Bu Flappy Bird dersinde kuş sağa gittikçe rastgele engel çıkıyor; Temple Run ( https://yasirkula.com/2015/01/15/unity-3d-temple-run-tarzi-infinite-runner-oyun-ornegi/ ) örneğinde ise karakter ilerledikçe rastgele engel çıkıyor. İki örnekte kullandığım kodların birbiriyle hemen hemen hiç alakası yok. Yani “rastgele” engellerin nasıl oluşmasını istediğinize göre kod oldukça farklılaşabiliyor. Ve bu kodu yazmak için Unity’de bir miktar kod yazmış olmanız, yani tecrübe sahibi olmuş olmanız gerekiyor. Demem o ki vakit kaybetmeden ya C# ya Javascript dersleri izlemeye başlamalı ve kendi başınıza kodlar yazmalısınız. En nihayetinde kameranın solundan rastgele engel çıkartmak için gereken kodu yazmaya hazır hale geleceksiniz.

  139. ad dedi ki:

    “Bu script biraz karmaşık, kabul ediyorum. Ancak hayıflanmadan önce scripti kaydedin ve Main Camera‘dan Arkaplan (Script) component’indeki Gokyuzu ve Toprak kısımlarına değer olarak gokyuzu ve toprak prefablarını verip oyunu test edin.” kısmındaki gökyüzü ve toprak kısmı bende yok neden acaba?

    • yasirkula dedi ki:

      public değişkenlerin Inspector’da gözükmesi lazım, ben gözükmemesi için olası bir sebep göremiyorum.

      • ad dedi ki:

        Peki, sürüm farklılığından olması gibi bir ihtimal var mı acaba hocam?

      • yasirkula dedi ki:

        Eğer kodlarınızda hata varsa o yüzden olabilir. Konsolda kırmızı bir mesaj varsa önce o hatayı çözmeniz gerekebilir. Sürüm farklılığına olasılık vermiyorum.

  140. gazi dedi ki:

    öncelikle tebrik ve teşekkür ederim!!
    Gerçekten mükemmel bir anlatım:) devamını çok bekliyoruz!!

    sorum ise şöyle : android telefona atınca gökyüzü sağa dogru kayıyor ve arkasından
    mavi (main cameranın background’ı) geliyor.

    Bu sorunu nasıl çözebilirim?

    saygılar..

  141. deeef dedi ki:

    oyun sorunsuz çalışıyordu ama timeScaler la alakalı ekleme yaptım şimdi oyun bulanıklaştı(timeScaler ı tamamen silmeme rağmen ) eski haline getirdim hala görüntü bulanık ..
    Acaba ne yapabilrim?

    • yasirkula dedi ki:

      Unity’i kapatıp tekrar açın bence. Bazen gerçekten bazı sorunları çözmeye yardımcı oluyor.

      • deeef dedi ki:

        yok defalarca denememe rağmen düzelmedi. pc de test ederken sıkıntı yok ama android telefon için build edip telefonda denediğim zaman görüntü bulanık oluyor.

        Acaba player settingsle alakalı bir şey mi?
        Veya çok fazla build ettiğim için mi böyle bir sorun olmuş olabilir?

  142. deeef dedi ki:

    sorun çözüldü:)

    bir de şunu sormak istiyorum.Eski unity de GUITexture lar la çalışabiliyorduk ama şimdi UI var. Mesela oyun bitince ekranda bir UI text in çıkıp oyun başlayınca kaybolmasını nasıl sağlayabiliriz?

    Bu yeni UI ları kodlamakta sıkıntı çekiyorumda???

  143. mustafa s. dedi ki:

    abi arkaplan scripti bende böyle hatalar verdi:

    Assets/Scripts/Arkaplan.js(17,5): BCE0144: ‘UnityEngine.Component.camera’ is obsolete. Property camera has been deprecated. Use GetComponent() instead. (UnityUpgradable)

    Assets/Scripts/Arkaplan.js(17,12): BCE0019: ‘orthographicSize’ is not a member of ‘UnityEngine.Component’.

    Assets/Scripts/Arkaplan.js(18,41): BCE0144: ‘UnityEngine.Component.camera’ is obsolete. Property camera has been deprecated. Use GetComponent() instead. (UnityUpgradable)

    Assets/Scripts/Arkaplan.js(18,78): BCE0019: ‘aspect’ is not a member of ‘UnityEngine.Component’.

    daha var..

  144. Emre dedi ki:

    Abi ben rastgele oluşan engelleri sağa doğru değil de yukarıdan aşşağıya doğru insin istiyorum. Yani şöyle bir topum var top yukardan aşşağıya doğru iniyor engeller de yukardan aşşağıya doğru oluşacak. Kodda neyi değiştirmem gerekiyor

  145. harun dedi ki:

    engeller birleşik ne yapmalıyım

  146. harun dedi ki:

    kuşun altın almasını onlada kuş rengin değişmesini istiyom bunla ilgili bildiğin kod varmı yada araştıracağım site

  147. harun dedi ki:

    unitye kapak resmi koyabilirmiyiz

  148. harun dedi ki:

    oyunda tekrar tuşu olmasını istiyom nasıl yapabilirim

    • yasirkula dedi ki:

      Restart kodu şöyle: Application.LoadLevel( Application.loadedLevel );

      • harun dedi ki:

        bunun resminimi koycaz oyuna. bide nasıl koycaz

      • harun dedi ki:

        bide kodu nereye yazıyoz yeni cscript mı yapcaz

      • yasirkula dedi ki:

        Bu dersi yapabilmek için temel Unity bilgisi sahibi olmanız gerekiyor, bu sorularınızın cevaplarını kendiniz bulmalısınız.

      • harun dedi ki:

        yeni bir kod yazdım kodda void updateyi sildimm restart kodunu yapıştırdım. kod hata vermedi .child obje açtım restart resmimi kodumu bunun içine koydum. sonra hala hata yok ama tıklayınca haraket etmiyor bide resmimin 2d and uı tıkladım.resmimi nasıl köşeye koyabilirim

      • yasirkula dedi ki:

        Butonu sağ üste almak için butonun pivot ve anchor’larını (1, 1) yapmalı, position’ını 0 yapmalısınız. Resme tıklayınca bir kod çalışması için Event Trigger (ya da Button) kullanmalısınız, ikisinden de şu derste bahsettim: https://yasirkula.com/2015/01/21/unity-ui-arayuz-sistemi/

        Resmin hareket etmesi için ise transform.Translate yapmanız yeterli. Resim, girdiğiniz Vector2 parametre kadar pixel hareket eder.

      • harun dedi ki:

        restart resmimi oyunda nasıl haraket ettirebilirim.

  149. omer dedi ki:

    Hocam bide bu oyuncu skorlarını facebookta paylaşmak için bi ders yazsan. adam akıllı bi kaynak bulamadık nette

  150. afsdggdg dedi ki:

    hocam denilenleri uyguladım arkaplan sprictinde hata veriyor

  151. Mehmet TAŞ dedi ki:

    hocam playerprefsi oyunda kaldıgımız yerden oyuna decam etmek için kullanırsak nasıl kullanırız bide playerprefs androiddede kullanılırmı fark varmı

    • yasirkula dedi ki:

      Platformlar arası bir kullanım farkı yok. PlayerPrefs sadece int, float ve string depolamaya yarıyor. Hangi değişkenleri depolayacağınız size kalmış. Karmaşık save işlemleri için XML Serializer kullanmanız daha mantıklı olur.

  152. ahmet dedi ki:

    linklerde bi sorunmu var acaba oyun içi dosyaları indiremiyorum bi türlü

  153. Cenan dedi ki:

    merhaba yasir abi unity kullanmaya yeni başladım java bilgim çok fazla değil
    arkaplan scriptini main cameraya attıktan sonra bir türlü gokyuzu ve toprak game objectleri çıkmıyor nerde yanlış yapmış olabilirm senin spritlerın değilde kendi spritelarımı kullanmak istemiştim

    • yasirkula dedi ki:

      Sprite’lerin pivot noktalarını ayarlamamış olabilirsiniz. Sprite’ın boyutu bir şekilde sıkıntılı olabilir. Başka sebeplerden de olabilir açıkçası aklıma da birşey gelmiyor.

  154. tolga dedi ki:

    Assets/arkaplan.js(14,46): BCE0144: ‘UnityEngine.GameObject.renderer’ is obsolete. Property renderer has been deprecated. Use GetComponent() instead. (UnityUpgradable)
    Assets/arkaplan.js(17,5): BCE0144: ‘UnityEngine.Component.camera’ is obsolete. Property camera has been deprecated. Use GetComponent() instead. (UnityUpgradable)
    Assets/arkaplan.js(17,12): BCE0019: ‘orthographicSize’ is not a member of ‘UnityEngine.Component’.
    Assets/arkaplan.js(20,68): BCE0019: ‘aspect’ is not a member of ‘UnityEngine.Component’.

    bu hataları alıyorum arkaplan scriptinde nasıl çözebiliriz acaba

  155. mario dedi ki:

    Merhabalar, bir sorum olacaktı bu projeyi yapmaya çalıştım fakat kodları kopyala yapıştır yapmama rağmen hep hata aldım. Sonra ben yanlış yapıyor olabilirim diye düşündüm, başka bir uygulama denedim, orada da kopyala yapıştır yöntemiyle yaptım ve unity yine hata verdi. Kodları girdikten ve kaydettikten sonra unity aynı zamanda şöyle bir mesaj veriyo ” This project contains scripts and/ or assemblies that use obsolete APIs ” Ne yapmalıyım ???

    • mario dedi ki:

      Bu sorunu çözdüm, kodlar eski olduğu içinmiş… Fakat bu seferde, yaptığım gökyüzü engellerden ve topraktan önde görünüyor. Engeller ve toprak görünmüyor yani… Position değerlerini değiştirdim ama yerleri bir türlü değişmiyor…

    • yasirkula dedi ki:

      GetComponent hatası alıyorsunuz. Örneğin “rigidbody” yazan yerler “GetComponent(Rigidbody)”, “animation” yazan yerler “GetComponent(Animation)” olmalı vb.

  156. Aslan dedi ki:

    İyi gunler.Oncelikle boyle guzel bi dersi hazirladiginiz icin tesekkurler.Gercekten emek vermissiniz.Ben rastgele olusan engellerden sadece 1 tane yapmak istiyorum ayrica Y ekseni sabit kalicak yani tum engeller Y:0 da sabit olucak sadece aralarindaki mesafe (X ekseni) degisicek bunu nasil yapabilirim
    Simdiden tesekkur ederim.
    Kurban bayraminiz mubarek olsun 😉

    • yasirkula dedi ki:

      Sizlerin de bayramı mübarek olsun. Engeller.js scriptinde yKoordinati’na hep 0 verebilir, xKoordinati’na ise Random.Range ile rastgele eklemeler yapabilirsiniz.

      • Aslan dedi ki:

        İyi aksamlar.Dediklerinizi yapmaya calistim.Ancak tamamen mahvettim kodu ;( Unityde biraz yeniyim.Zahmet olmazsa kod seklinde yazarmisiniz(aciklamali yazarsaniz daha iyi anlarim)
        Simdiden tesekkurler.
        NOT:Olusmasini istedigim objeler soyle olucak;
        | | | | | | -> kisaca reastegele olmyacak sirasiyla aralarinda belli mesafe olacak sekilde siralancak.

      • Aslan dedi ki:

        Tmm suan hepsini sirali yaptim ancak ben oyuna baslayinca engellerin ekranda direk guzekmesini istiyorum.Engellerin pozisyonunu (biraz sola almak istiyorum) nasil degistirebilirim?

      • yasirkula dedi ki:

        xKoordinati’ndaki kameraUnityEbatlar.x değerini silerek veya başka birşeyle değiştirerek sola alabilirsiniz.

  157. Burak dedi ki:

    merhaba oyunu yaptım birebir ama her yandığımızda bir başlat ekranı eklemek daha iyi olur diye düşündüm çünkü direk basar basmaz başlaması kötü oluyor nasıl yapabilim. yardımcı olursanız sevinirim.

  158. Enes dedi ki:

    Merhaba kolay gelsin ya ben şu prefabları oluşturdum dediğiniz gibi pivotu top-left de yaptım fakat top-left yapınca karakter ekranda görünmüyor ama gokyuzunu bottom-left yapınca karakter ekrana geliyor acil cevap yazabilirseniz çok sevinirim.

  159. Arda dedi ki:

    yeni açtığımız objeye tag ekleme yerinde add tag dediğimde List is Empty yazıyor sizde ki gibi element0 elemnt 1 gibi şeyler gelmiyor yardımcı olabilir misiniz?

  160. Arda dedi ki:

    Cevabınız için teşekkürler sorunu çözdüm fakat şimdide yüksek skorun ekranda gözükmesi için verdiğiniz kodda hata buluyor ? GUILayout.Box( “SKOR: ” + skor + “\nYUKSEKSKOR: ” + yuksekSkor );

  161. selcuk dedi ki:

    Hocam eline sağlık çoook güzel bir anlatım olmuş eline emeğine sağlık hakkını helall et..

  162. Ceyhun dedi ki:

    abi gokyuzu ve toprak bir kere duruyor arka arkaya gitmiyor bide flappy bird arkaplanin içinde kaliyor sorun ne acaba?

    • yasirkula dedi ki:

      Flappy bird’ün gözükmemesi position z’sinden olabilir, o değeri değiştirmeye çalışın. Yolun tekrarlamamasını çözmek için en hızlı yol örnek projeyi kendi projenizle kıyaslamanız.

  163. Mehmet dedi ki:

    Abi bana projenin bitmiş hali lazım yollar mısın linkler kırık şimdiden teşekkürler

  164. Mehmet dedi ki:

    Abi bunun hazır build li apk sını verirmisin lazım da acil

  165. murat dedi ki:

    Hocam arkaplan kodlarında şöyle bir hata alıyorum

    assets/arkaplan.js(14,46): BCE0144: ‘UnityEngine.GameObject.renderer’ is obsolete. Property renderer has been deprecated. Use GetComponent() instead. (UnityUpgradable)

    Assets/arkaplan.js(17,5): BCE0144: ‘UnityEngine.Component.camera’ is obsolete. Property camera has been deprecated. Use GetComponent() instead. (UnityUpgradable)

    Assets/arkaplan.js(17,12): BCE0019: ‘orthographicSize’ is not a member of ‘UnityEngine.Component’.

    Assets/arkaplan.js(20,68): BCE0019: ‘aspect’ is not a member of ‘UnityEngine.Component’.

    ne yapabilirim

    • yasirkula dedi ki:

      GetComponent(Renderer) ve GetComponent(Camera) kullanmanız lazım. Unity’de projeyi açtığınızda bi pencere açılıyor, orada “Yes, I’ve made a backup” seçeneğini seçerseniz bu ayarlar kendiliğinden yapılıyor.

  166. muhammet dedi ki:

    slm “”Ardından “flappy1” sprite’sini Assets klasöründen tutup Sprite Renderer‘daki Sprite kısmına sürükleyin: “” demişsiniz ya olmuyo yani sürüklenmio

    • yasirkula dedi ki:

      flappy1 asset’ini seçince Inspector’da “Texture Type” kısmının “Sprite (2D and UI)” olması lazım. Değilse öyle yapın. Başka sorun çıkarabilecek birşey düşünemiyorum.

  167. muhammet dedi ki:

    tam olduda ben oyuna menü koyabilirmiyim anlatırmısınz

  168. muhammet dedi ki:

    sizle iletişim kurmak isterim facebook .ınstegram. flan varsa isminizi soylermisiniz

  169. muhammet dedi ki:

    tmm

  170. sezen dedi ki:

    abi nasıl androide çevirecem çözemedim ya build olarak akydediyorum sdk istiyor yükledim olmuyor

  171. isimsiz dedi ki:

    dostum sürekli hata veriyor buyaw

    • yasirkula dedi ki:

      Projeyi açarken API Updater diye birşey çıkarsa “Yes, I made a backup” seçeneğini seçin. Eskiden mesela rigidbody’e direkt rigidbody diye erişirken artık GetComponent() şeklinde erişiyoruz ve bu yüzden eski projelerde hata almak normal. Dediğim şeyi yaparsanız bu hatalar otomatik olarak gidiyor (API Updater’ı Assets menüsünden elle de çalıştırabilirsiniz).

      Başka bir hata alıyorsanız hatayı burada paylaşırsanız güzel olur.

  172. isimsiz dedi ki:

    bide bundan ilham alınarak yapılan oyunlar telif yermi

    • yasirkula dedi ki:

      Flappy Bird klonu olduğu için telif yiyebilir veya store’dan kaldırılabilir. Ancak kesin bir bilgi veremiyorum, bu tarz bir tecrübem olmadı.

  173. unity game dedi ki:

    Merhaba, ben corel , photoshop gibi programlarla texture yapıyorum yada direk internetten indiriyorum jpeg ve png türü bunu unitye atıyorum sürükle bırak ile ardında sahneye atıyorum ve bu texturelara box collider veriyorum ancak oyunu başlatınca gözükmüyor hiçbirşey ama oyunu durdurunca kapatınca yani sahnede gözüküyor bu textureları nasıl atabilirim

    • yasirkula dedi ki:

      Sahnedeki textureda Rigidbody olmadığından emin olun. Büyük olasılıkla texture, kameranın görüş alanı dışındadır. Bunu öğrenmek için Main Camera objesini seçip Edit-Align View to Selected ile Scene paneli kamerasını Main Camera ile aynı hizaya getirin. Texture ekranda görünmüyorsa onu kameranın görüş alanı içerisine yerleştirin.

  174. semih dedi ki:

    Hocam herkese verdiğiniz cevaplar bile bize yeni birşeyler katıyor.Ben Adobe Flash cs5 i öğrenmekle uğraşıyordum. O da elbette birşeyler kattı. Ben tek tek her bir terimin panel vs. ne olduğunu öğrenmek yerine uygulayarak öğrenmenin daha fazla şey kattığını gördüm.Belkide ingilizce seviyemin çokta kötü olmadığındandır. O yüzden verdiğiniz örnek projenin çok yararı oldu. Yardımlarınız ve paylaşımlarınız için teşekkürler ve başarılar dilerim.

  175. Sinan dedi ki:

    hocam iti iş çıkarmışsınız tebrik ederim. ama gökyüzü ve toprak boyutu çok büyük ve aralarında büyük boşluklar var. boyut ve konumlarını nasıl değiştirebilirm?
    teşekkürler…

    • yasirkula dedi ki:

      Toprakla gökyüzü bitişik olmalı, bende aralarında bir boşluk yok. Eğer toprağın daha ufak olmasını isterseniz de yüksekliği daha az olan bir toprak görseli kullanabilirsiniz. Siz zaten farklı görseller denemişsiniz ve aldığım izlenime göre bir farklılık olmamış. Ancak bu durumda ben gözle görülür bir fark olmasını beklerdim çünkü Arkaplan.js scriptindeki Awake fonksiyonunun ilk iki satırı vasıtasıyla, görsellerin ebatları değişince bunun oyuna yansıması lazımdı.

  176. Sinan dedi ki:

    farklı görseller denedim

  177. Elem dedi ki:

    Merhaba ben dediklerinizi yaptım fakat oyunda arkaplan olmuyor kamerada background oluyor ve nullreferenceException: Object referenfeksiyon not set to an insanca of an Object Arkaplan.Update () (at Assets/Sprites/Arkaplan.js50) hatası veriyor.
    Ben Gokyuzu ve Toprak kısımlarına değer olarak gokyuzu ve toprak prefablarını verdöğünde ben de logo var sizde yoktu yüzden olabilir mi

  178. Çok güzel. Emeğine sağlık

  179. Deniz dedi ki:

    Merhaba hocam eğer buna yeniden başlatmasını nasıl sağlayabiliriz butonu oluşturdum fakat ona nasıl bir kod ekleyebiliriz Teşekkürtler

  180. Deniz dedi ki:

    Daha doğrusu ekliyceğimiz kod nasıl olmalı

  181. Furkan dedi ki:

    sen adamın dibisin ya 🙂

  182. Yunus dedi ki:

    Çok teşekkürler projemde çok yardımcı oldunuz
    function Start()
    {
    kameraUnityEbatlar = Vector2( Camera.main.orthographicSize * Camera.main.aspect, Camera.main.orthographicSize );
    yuksekSkor = PlayerPrefs.GetInt( “YuksekSkor” );
    }
    şu kodu birazcık açabilir misiniz kendi projeme nasıl uyarlayabilirim bide bunu kaydedince farklı scene’lerde görülmesini nasıl sağlayabilirim ?

    • yasirkula dedi ki:

      kameraUnityEbatlar değişkeninde orthographic kameranın genişlik ve yüksekliğinin (görüş alanının) kaç Unity birimi olduğu depolanıyor. Ardından cihazda kayıtlı “YuksekSkor”un değeri, yuksekSkor değişkenine atılıyor. PlayerPrefs komutlarını istediğiniz scene’de kullanabilirsiniz, hepsinde çalışır.

  183. Saliha dedi ki:

    projeyi kaynak kod olarak atabilirmisiniz.

    • yasirkula dedi ki:

      Projenin son halinin linki yazının başında mevcut.

      • Saliha dedi ki:

        projede hiç değişiklik yapmadan apk dosyasını çıkarttım. cihaza yükledim. uygulama yüklenmedi hatası veriyor. oyun game panelinde programda çalışıyor. emulatörde de çalışıyor. cihazda bilinmeyen apk yüklemesi açık yüklenebiliyor apkler. başka apk ler denediğimde yükleniyor. bu unity oyunu yüklenmedi. neden ne olabilir

      • yasirkula dedi ki:

        “Uygulama yüklenemedi” hatası çok genel bir hata olmuş. İşin aslı, bu hatanın sebebi Unity bile olmayabilir. Aklıma gelen tek seçenek, Player Settings’teki “Device Filter” ayarınının yanlış olabilir olması, ancak eğer değeri FAT ise sıkıntı oradan değildir.

  184. Saliha dedi ki:

    sorun unity imiş. android build support seçeneğini işaretlemiştim ama bilgisayardaki bazı ayarlardan dolayı yüklenmemiş. format attım. şimdi yükleniyor. birşey soracağım. flappy bird bir resim ya gif kullansak çalışır mı .

  185. Saliha dedi ki:

    ekran portrait landspace ayarları nerden yapılıyor. uygulama portrait değil de yan çalışsın istiyorum.

  186. Saliha dedi ki:

    oyundaki nesneler her ekran boyutuna göre küçülüp büyüyor iyi de skor kutusunun boyutu hep aynı. o da diğer nesneler gibi orantılı olsun istiyorum. hangi js’ye hangi kodlar. nasıl yapabilirim.

  187. Saliha dedi ki:

    birsey daha soracağım. skor bölümündeki o gri kutuyu bir türlü kaldıramadım. gri kutu olmasın veya saydam olsun, gözükmesin istiyorum. bir de yazı stilini pek kavrayamadım. birkaç bilindik renk hariç renk adı yazılınca çalışmıyor. mesela white blue green çalışıyor pink orange çalışmıyor. kod olarak #996666 bu şekilde yazınca da çalışmıyor. nasıl renk belirleniyor unityde. bir de yazıya kenarlık yapılabiliyormu .gerçek flappy bird’ deki ekrana yazılan sayılar siyah kenarlıklı onlar nasıl yapılmış. kenarlık özelliği var mı unityde.

    • yasirkula dedi ki:

      Bu isteklerinizin hepsini (siyah outline, renk değişimi, gri arkaplan olmaması) aynı anda kolayca karşılayabilmek için UI sistemini kullanmanız daha iyi olur. Bir önceki mesajımda UI dersime link attım zaten.

  188. Saliha dedi ki:

    bu arada oyundaki skor sesine ek olarak bir bip sesi çıkıyor .skor oldukça hem skor sesi geliyor bir de değişik bir bip sesi geliyor. . onu nereden kapatabilirim.

    • yasirkula dedi ki:

      Ben sadece skor sesi duyduğumdan hangi sesi kastettiğinizden emin değilim. Projedeki tüm ses dosyalarını tek tek dinleyip hangi sesin yersiz çıktığını belirlerseniz onun üzerinden sorunun kaynağını bulmaya devam edebilirsiniz.

  189. Furkan dedi ki:

    Arkadaşlar yeni bir uygulama yaptık sizlerinde yorum ve desteğini bekliyoruz https://play.google.com/store/apps/details?id=com.HyperCry.ExitWhere
    Oyun ucretli oldugu icin promosyon kodu cikardik buradan alabilirsiniz;
    C1YF36BXLE0S4G8MPER10Y6
    RWCD4URHFRQSHEEL4XTBCSB
    9L1Z3YXPHEN8G41B64Q4DKB
    E2LJYLVB243MM08VX75QTVK
    PSX431TSPZ017T9ZSC728TU
    D2N6STR50MDW549QUF1DTVF
    3CXS88W93EAGWDPVPF4GWF1
    ACX2KX5HAQU3UPNRKBFBHM5
    Z0KX2PJRWPVVURECXPYQ41H
    DBQMSD4WKY9AYZL8Y5S9VDA
    NDG8FCYV076UDU01YDM9YJV
    VSET61KZ5P244CF8SXEEBZ9
    32T435E0JGTF0CG3EESLDZR
    3PJJVKW0J3M66WZ5EH2MMQ7
    UCAQYWRHQ80AFX5QN0V4E4E
    X6WY4DF9L78X21LZ0XZ18B8
    JE1VK29LS9NQ0MJYLB02E4F
    EPTRRT4ZDM4BZ9PXNPK26PQ
    SWARZAYU9T7AR4EBAT0W22U
    5QJU1950046U2NVH8MC3KJ7
    8DMS94C5B1T79PSNT6GRPK6
    SLMYUMBFMNSDCNHLM3PHZ03
    N7203ZK0HXBMWZRSSSJZC0N
    ET9TKDL236KE7XL2XAP8TAP
    4J0TMZ2A9S70PGV99V1C12S

  190. ahmet dedi ki:

    Hocam Merhablar;Bir sorum olacaktı.unity de oyun yaptım 25 mb boyutu oldu.Am ben buna level eklemek istiyorum.Direk ilk yaptığım leveli duplicate yaptım yeni sahneleri level için eklemeye çalıştım.Ama her yeni eklenen sahne 10 mb civarında boyut fazlalığı oluyor.Tek sahne 25 mb idi.Aynı sahneyi dublicate yaptım boyut 35 mb çıktı.Yeni sahneleri eklemenin başka yolu varmı acaba.
    Şimdiden teşekürler.

  191. Nuri KURUCU dedi ki:

    Hocam merhabalar…

    Tüm dediklerinizi adım adım yaptım ama skor bir türlü çalışmadı. geri kalan her şey mükemmel. hatta en son dedim ben yanlış yapmışımdır sizin scriptinizi aldım (örnek oyundan) onun içeriğini koydum kuş hareket scriptine ama gene çalıştıramadım. ne olabilir problem?

    • Nuri KURUCU dedi ki:

      problemi çözdüm ama bu sefer de alt engel toprağın arkasına gitti. alla alla.. 🙂

      • yasirkula dedi ki:

        Toprak prefab’ının Sprite Renderer’ındaki “Order in Layer” değerini veya Transform Position z değerini değiştirmeyi deneyin.

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Log Out / Değiştir )

Connecting to %s