GÜNCELLEME (01.07.2018): Kod C#’a çevrildi ve Input.touches değişkeni yerine Input.touchCount ve Input.GetTouch kullanıldı.
Yine ve yeniden merhaba,
Bu UNITY dersinde, Android için uygulama geliştirmenin temellerini öğreneceğiz. Dersin bu ilk parçasında dokunmatik ekranın nimetlerinden faydalanmayı görecek, ikinci derste ise telefonun hareket sensörünü kullanmayı öğreneceğiz (2. derse gitmek için tıklayın: https://yasirkula.com/2013/07/22/unity-ile-androide-uygulama-gelistirmek-2-telefon-sensorunu-kullanmak/). Bu derse başlamadan önce UNITY için Android SDK kurulumunu yapmış olmanız gerekiyor. Eğer yapmadıysanız şu dersimize göz atabilirsiniz: https://yasirkula.com/2013/07/17/unity-android-sdk-kurulumu-resimli-anlatim/
Hazırsanız touchscreen’e dalışı yapalım…
Şimdi, yapacağımız örnek uygulamadan bahsetmek istiyorum. Bu uygulamada ekranda bir küp olacak ve bu küp bizim dokunmatik ekranda tıkladığımız konuma ışınlanacak. Ayrıca bu küpün boyutlarını, ekranda elimizi sağa-sola ya da yukarı-aşağı kaydırarak değiştirebileceğiz. Bu uygulamayı yaparken öğreneceklerinizi kolaylıkla kendi uygulamalarınıza da uygulayabileceğinizi umuyorum.
Öncelikle UNITY’i açın ve yeni bir proje oluşturun. Main Camera‘yı (0, 0, -10) konumuna yerleştirin ve ardından yeni bir Cube GameObject oluşturup bu objenin Transform component‘indeki değerleri resetleyin. Şimdi yeni bir C# script oluşturup bu script’i küp objesine atayın. Son olarak, sahnede bir Directional Light oluşturun ki küpün hareketlerini rahatlıkla gözlemleyebilelim.
Aşama 1 : PC
Uygulamamızı Android’e yazmadan önce PC platformu için yazıp sonra Android’e convert etmeyi uygun gördüm. Bu yüzden öncelikle projeyi bilgisayar üzerinden test edecek, sonra birkaç ufak değişiklikle Android’e çevireceğiz.
İlk önce, küpün ekranda fare ile tıkladığımız yere ışınlanmasını sağlayalım. Bunun için Update() fonksiyonunu kullanmamız uygun olur. Scripti açın ve Update() fonksiyonuna şu kısacık kodu yazın:
if( Input.GetMouseButtonDown( 0 ) )
{
transform.position = Camera.main.ScreenToWorldPoint( new Vector3( Input.mousePosition.x, Input.mousePosition.y, 10 ) );
}
Bu kod sayesinde eğer herhangi bir anda farenin sol tuşuna basılırsa, farenin ekrandaki konumu, Main Camera’nın mevcut pozisyon ve eğimine göre 3 boyutlu uzayda bir noktaya çevriliyor ve obje bu noktaya ışınlanıyor. Şimdi biraz daha detaylı açıklayayım:
Camera.main ibaresi, sahnedeki “MainCamera” tag‘ına sahip kameraya ulaşmak için kullanılır. Camera class’ının içerisinde ScreenToWorldPoint adında bir fonksiyon bulunmaktadır. Bu fonksiyonun parantezinin içine bir Vector3 değer girilir. Girilen bu Vector3’ün x ve y değerleri ekrandaki bir pikselin konumuna işaret etmelidir, bizim durumumuzda bu fare imlecinin ekrandaki konumunun x ve y koordinatlarına denk geliyor. Vector3’ün z değeri ise, 3 boyutlu uzaya çevrilecek noktanın kameradan kaç birim uzak olması gerektiğini belirtiyor, bunun değeri ise bizim örneğimizde 10. Yani Main Camera’nın z koordinatının -10’da olduğunu ele alırsak küp objemizin z değeri bundan 10 birim fazla olacak, yani 0 olacak. ScreenToWorldPoint fonksiyonu, içine girilen bu Vector3’ü alır ve bize 3 boyutlu uzayda bir nokta döndürür. Bu nokta döndürülürken hangi kameranın referans alınacağını ise en başta belirtmiştik zaten: Camera.main.
Şimdi de küpün boyutunun biz fareyi sürükledikçe değişmesini ayarlayalım. Bunun için script’in içinde şöyle değişiklik yapın:
Vector3 mouseEskiKonum;
void Update ()
{
if( Input.GetMouseButtonDown( 0 ) )
{
transform.position = Camera.main.ScreenToWorldPoint( new Vector3( Input.mousePosition.x, Input.mousePosition.y, 10 ) );
mouseEskiKonum = Input.mousePosition;
}
else if( Input.GetMouseButton( 0 ) )
{
transform.localScale += ( Input.mousePosition - mouseEskiKonum ) / 100;
mouseEskiKonum = Input.mousePosition;
}
}
Burada görüldüğü üzere, öncelikle mouseEskiKonum adında bir Vector3 değişken oluşturduk ve ekranda bir noktaya tıklandığında, bu değişkene fare imlecinin koordinatlarını atadık. Ardından Input.GetMouseButton(0) fonksiyonuyla, farenin sol tuşunun basılı olduğu her frame’de, objenin boyutunun (transform.localScale) fare imlecinin mevcut konumuyla eski konumu arasındaki farkın 1/100’ü kadar değişmesini sağladık. Neden 1/100? Çünkü ekran koordinatları piksellerle alakalı ve fareyi birazcık bile oynatsak ekranda epey piksel katedebiliriz. Ancak 3 boyutlu dünyada (World koordinatlarında) metrelerle işlem yapıyoruz ve 1 metrelik bir değişiklik bile küpün boyutunda ciddi değişikliklere yol açar. Bu yüzden her 100 piksellik oynamada küpün 1 metre büyümesini sağladık. Son olarak da, mouseEskiKonum değişkeninin değerini fare imlecinin güncel konumuna ayarladık.
Ve böylece ufak uygulamamızı PC için yazmış olduk. Şimdi sıra bunu Android’e uyarlamaya geldi!
Aşama 2 : Android
Uygulamayı Android’e çevirmeden önce, touchscreen ile alakalı önemli komutları açıklamak istiyorum.
Android’e uygulama geliştirirken, dokunmatik ekranla işlem yapmak için en aşina olmanız gereken iki komut, Input.GetTouch fonksiyonu ve Input.touchCount değişkeni. Bu komutlar ne işe yarar? Input.touchCount, ekrana dokunan parmak sayısını döndürürken, Input.GetTouch ise bu parmaklardan birisi hakkında detaylı bilgi döndürür. Peki bu detaylı bilgi ne türde depolanır? Vector3 mü? Transform mu? Hayır! Bu parmaklar çok daha gelişmiş bir tür olan Touch türünde depolanır.
Touch türünde depolanmakta olan bir parmak ile neler yapılabilir? Touch dediğim şey bir struct olup içerisinde çeşitli değişkenler barındırır. Mesela bunlardan biri position‘dır ve bu değişken, parmağın dokunmatik ekrandaki konumunu bir Vector2 olarak tutar. Bunun haricinde deltaPosition değişkeni de oldukça önemli olup, parmağın ekranda son frame’den itibaren yatay ve dikey eksende kaç piksel hareket ettiğini bir Vector2 olarak döndürür. O halde deltaPosition.x dediğimiz zaman, parmağın yatay eksende kaç piksel hareket ettiğini, deltaPosition.y dediğimiz zaman da parmağın dikey eksende kaç piksel hareket ettiğini bulmuş oluruz. Touch‘ın depoladığı bir başka önemli değişken ise phase değişkenidir. Bu değişken TouchPhase türündedir ve parmağın mevcut durumunu depolar. Daha açık konuşmak gerekirse, parmağın ekrana yeni mi dokunduğunu (TouchPhase.Began), yoksa ekranda hareket halinde olan bir parmak mı olduğunu (TouchPhase.Moved), ekranda hareketsiz bir şekilde basılı olarak duran bir parmak mı olduğunu (TouchPhase.Stationary) ya da ekrandan yeni kaldırılan bir parmak mı olduğunu (TouchPhase.Ended) depolar. Ekrana aynı anda beşten fazla parmağın dokunması gibi nadir olaylarda ise değeri TouchPhase.Canceled olur. Touch, bir başka önemli değişken daha depolar: fingerId. Bu değişken, ekrandaki o parmağa has bir int değeridir. Bu değişkenle ne yapılabilir? Örneğin oyununuzda sadece ekrandaki bir parmağı kaâle almak istiyor, ekrana sonradan dokunan parmakların hiçbir etki etmemesini istiyorsanız, o zaman bir değişkene o parmağın fingerId‘sini atayabilir ve ardından bir parmakla iş yaparken önce o parmağın fingerId‘sinin depoladığınız değişkene eşit olup olmadığına bakabilirsiniz.
Evet, dokunmatik ekranla ilgili önemli olan komutları böylece öğrenmiş olduk. Ne kadar az idi, değil mi! İşte bu yüzden UNITY ile Android’e uygulama geliştirmek aslında hiç de zor değil. Şimdi hazırsanız yarıda bırakmış olduğumuz örnek uygulamamızı Android’e transfer edelim.
Önce küpümüzü ekranda dokunduğumuz konuma ışınlandırmayı yapacağız. Bunun için bize gerekli olan şey ne? Ekrana dokunan bir parmağın (Touch) konumu. Peki bu Touch türündeki parmağa nasıl ulaşıyorduk? Evet, Input.GetTouch fonksiyonu ile. O halde Input.GetTouch ve Input.touchCount vasıtasıyla ekrandaki tüm Touch‘ların phase değişkenine bakmalı ve, eğer parmak ekrana yeni dokunmuşsa (TouchPhase.Began) küpü parmağın ekrandaki konumuna (position) ışınlamalıyız. Bunun için küp objesine atadığınız scripti açın ve Update() fonksiyonunu şöyle değiştirin:
int parmakSayisi = Input.touchCount;
for( int i = 0; i < parmakSayisi; i++ )
{
Touch parmak = Input.GetTouch( i );
if( parmak.phase == TouchPhase.Began )
{
transform.position = Camera.main.ScreenToWorldPoint( new Vector3( parmak.position.x, parmak.position.y, 10 ) );
}
}
Sanıyorum bu kodu açıklamaya gerek yok, çünkü neyi niçin yaptığımızı zaten yukarıda öğrendik. Burada, PC için olan kodu Android’e uyarlamanın ne kadar kolay olduğuna dikkat çekmek istiyorum. Sadece birkaç satırla uygulamamıza Android desteği sunduk. Uygulamanın ikinci aşamasını Android’e aktarmak da bir bu kadar kolay. Tek yapmamız gereken, for döngüsünün içini şu hale getirmek:
for( int i = 0; i < parmakSayisi; i++ )
{
Touch parmak = Input.GetTouch( i );
if( parmak.phase == TouchPhase.Began )
{
transform.position = Camera.main.ScreenToWorldPoint( new Vector3( parmak.position.x, parmak.position.y, 10 ) );
}
else if( parmak.phase == TouchPhase.Moved )
{
transform.localScale += (Vector3) parmak.deltaPosition / 100;
}
}
Gördüğünüz gibi içeriye bir “else if” ekledik ve eğer parmak ekranda hareket ettirilmişse (TouchPhase.Moved), küp objesinin boyutlarını değiştirdik. Bu esnada parmakEskiKonum gibi gereksiz bir değişkene de ihtiyacımız olmadı, çünkü hatırlayacağınız gibi, “Touch” türü içerisinde zaten bir deltaPosition barındırıyor! Yine hatırlayacağınız üzere, bu deltaPosition bir Vector3 değil ama bir Vector2. Ancak transform.localScale değişkeni bir Vector3 ve bu yüzden transform.localScale‘e bir Vector2 ekleyemezdik. Bunu çözmek için, parmak.deltaPosition‘ı bir Vector3‘e dönüştürdük (typecast).
Evet, bitti! Buraya kadar gelmişseniz bir Android developer olma yolunda büyük bir basamak atladığınızı söyleyebilirim. Şimdi dilerseniz uygulamayı Android’e Build edip telefonunuzda test edebilirsiniz. Peki şimdi ne yapmalı? Öğrendiklerinizi pekiştirmek için niçin kendi başınıza basit bir Android oyunu yapmıyorsunuz? Heyecan verici!
Umarım faydalı olmuştur, dersin ikinci kısmında görüşmek üzere!
NOT: if( GUI.Button( blabla ) ) şeklinde oluşturduğunuz tüm GUI elemanları aynı zamanda otomatik olarak dokunmatik ekran desteğine de sahip. Yani onlar için ekstra kod yazmanız gerekmiyor.