GÜNCELLEME (11.03.2018): Android Studio için talimatlar eklendi ve kod C#’a çevrildi.
Yine ve yeniden merhabalar,
Bu derste Eclipse ADT veya Android Studio kullanarak Unity Android için Java dilinde basit bir plugin yazacağız. Pluginimiz sayesinde ekranda Toast mesajları gösterebileceğiz (Toast dediğimiz şey, ekranın altında belirip kısa sürede yok olan bildirim mesajları oluyor):
Eğer programlama konusunda çok yeniyseniz bu ders sizin için olmayabilir. Ama yok ben Android’in tüm nimetlerinden faydalanmak için kendimi tamamen hazır hissediyorum diyorsanız o halde belki bu yazı ile başlangıç yapabilirsiniz.
NOT: Derste yaptığım herşeyi bir unitypackage‘da depoladım. İhtiyaç duyarsanız indirme linki: https://www.dropbox.com/s/are1b4fgra7q1gr/AndroidPluginOrnegi.unitypackage?dl=0
Hazırsanız derse başlayalım!
Öncelikle eğer bilgisayarınızda Eclipse ADT veya Android Studio kurulu değilse istediğiniz birini kurun. Açıkçası Android pluginleri ile ciddi anlamda uğraşmayı düşünüyorsanız ben Android Studio kurmanızı öneririm.
Android Studio: https://developer.android.com/studio/index.html
Eclipse ADT: Android’in Eclipse ADT desteği uzun süre önce kalktığı için artık bu konuda resmî bir kurulum dokümanı bulunmuyor. Ancak “eclipse adt kurulumu” şeklinde arama yaparak bu konuda yazılı/video dersler bulmanız mümkün.
Şimdi plugin dediğimiz şeyin mantığı şöyle işliyor: Eclipse’te veya Android Studio’da Java kodu yazarak Android’in istediğimiz fonksiyonlarına erişiyoruz. Ardından bu Java kod(lar)ını bir jar dosyasına çevirip Unity’e atıyoruz ve Unity‘nin bize sağladığı AndroidJavaClass ve AndroidJavaObject sınıflarından faydalanarak bu plugin ile Unity içerisinden iletişim kuruyoruz. Static bir Java fonksiyonu çağırmak için AndroidJavaClass, bir Java objesi üzerinde çalışmak için ise AndroidJavaObject kullanıyoruz.
Artık pluginimizi yazmaya başlayalım.
Proje Oluşturmak (Eclipse ADT)
Eclipse ADT’yi açın ve File-New-Android Application Project yolunu izleyin. Application Name kısmında projemize istediğimiz ismi verebiliriz; ben ToastProject adını verdim. Burada Package Name çok önemli. Ben oranın değerini “com.yasir.toast” (tırnaksız) yaptım ama siz bunu kendinizce değiştirebilirsiniz de. Geri kalan değerleri hiç ellemeden olduğu gibi bırakabilirsiniz:
Geri kalan sayfaları direkt Next’leyin ve Finish diyerek projeyi oluşturun. Şimdi Project Explorer‘dan projenize sağ tıklayın ve Properties seçeneğini seçin:
Gelen pencereden Android sekmesine gidin ve Is Library seçeneğini işaretleyin:
Proje Oluşturmak (Android Studio)
Android Studio’yu açın ve açılış ekranındaki “Start a new Android Studio project” butonuna tıklayarak veya uygulama içerisinden File-New-New Project… yolunu izleyerek yeni bir proje oluşturun. Application name kısmında projemize istediğimiz ismi verebiliriz; ben ToastProject adını verdim. Company domain kısmını boş bırakın ve Package name‘in yanındaki Edit butonuna tıklayın. Package Name’in değeri çok önemli. Ben oranın değerini “com.yasir.toast” (tırnaksız) yaptım ama siz bunu kendinizce değiştirebilirsiniz de. Package name’in değerini düzenledikten sonra yanındaki Done butonuna tıklayın. Include C++ support ve Include Kotlin support seçeneklerini kapalı bir şekilde bırakıp Next butonuna tıklayın:
Sonraki ekranda Phone and Tablet altında yer alan API değerinin, Unity projenizin Player Settings-Other Settings‘inde yer alan Minimum API Level‘a eşit veya daha küçük olduğundan emin olun. Bu değer, pluginin desteklediği minimum Android sürümünü belirtmekte:
Next diyince gelen Activity seçme ekranında Add No Activity’i seçip yine Next deyin ve Finish butonuna basarak projenizi oluşturun (bu işlem biraz sürebilir).
Proje açıldıktan sonra sol taraftan Project sekmesini açın ve oradan da Android sekmesine geçiş yapın (eğer Project sekmesi yoksa View-Tool Buttons‘ın açık olduğundan emin olun). Ardından Gradle Scripts‘i genişletip build.gradle (Module: app)‘a çift tıklayın:
Gelen kodda, apply plugin: ‘com.android.application’ satırını şöyle değiştirin: apply plugin: ‘com.android.library’
Sonrasında applicationId ile başlayan satırı bulup silin (bende applicationId “com.yasir.toast” yazıyordu). İşiniz bitince sağ üstten Sync Now butonuna tıklayın ve işlemin bitmesini bekleyin:
Artık Java kodumuzu yazmaya hazırız.
Plugin Kodunu Yazmak (Eclipse ADT)
Project Explorer’dan src klasörüne sağ tıklayın ve New-Class yolunu izleyin. Class’a isim olarak ToastTest adını verin. Diğer değerlere dokunmadan Finish’e basarak class’ı oluşturun:
Şimdi class’ın içeriğini şöyle değiştirin:
package com.yasir.toast; import android.content.Context; import android.widget.Toast; import android.app.Activity; public class ToastTest { private static Context context; public static void SetContext( Context c ) { ToastTest.context = c; } public static void ToastGoster( final String mesaj ) { if( context != null ) { ((Activity) context).runOnUiThread( new Runnable() { public void run() { Toast.makeText( context, mesaj, Toast.LENGTH_LONG ).show(); } }); } } }
Gördüğünüz üzere class’ımızda bir static değişken, iki de static fonksiyon bulunmakta. Context dediğimiz şeyin tam olarak ne olduğunu ben de bilmiyorum ancak bazı Android fonksiyonları (Toast gibi) sizden Context türünde parametre girmenizi bekliyor ve Unity oyunlarında bu context’i Unity’den çekmemiz gerekiyor. Bu yüzden SetContext adında bir fonksiyonumuz var: bu fonksiyon sayesinde context değişkeninin değerini Unity’den belirleyebileceğiz.
İkinci static fonksiyonumuz ise ToastGoster fonksiyonu. Asıl işi bu fonksiyon yapıyor. Orada yazdığım Toast.makeText fonksiyonu Android’e has bir fonksiyon ve bu fonksiyonun bir eşi Unity’nin kendi içerisinde yok. İşte bu yüzden burada plugin yazma ihtiyacı duyuyoruz; aksi taktirde Unity’den ekrana Toast mesajı yazdırmak mümkün değil. ToastGoster içerisinde runOnUiThread ve Runnable gibi yeni yeni icatlarla uğraşıyoruz çünkü arayüzle ilgili bir işlem yapmak için (ekranda Toast mesajı göstermek gibi) böyle bir yol izlememiz gerekiyormuş (ben de bu dersi yazarken öğrendim).
Plugin Kodunu Yazmak (Android Studio)
Yine sol tarafta yer alan Project sekmesindeki app modülüne sağ tıklayın ve New-Java Class yolunu izleyin:
Gelen pencerede, class’a isim olarak ToastTest verin ve Package kısmına da projenizin Package name‘ini girin. Ardından OK butonuna tıklayarak class’ı oluşturun:
Şimdi bu class’ın içine, yukarıdaki Eclipse ADT için olan kısımdaki Java kodunu yapıştırın. Kodun altında açıklaması da mevcut. Geriye kodu derlemek kalıyor. Bunun için Build-Make Project yolunu izleyebilirsiniz.
Java ile işimiz burada bitti; artık Unity’e geçiş yapabiliriz.
Plugini Unity’e Atmak (Eclipse ADT)
Unity’de yeni bir proje açın. Ardından Project panelinin içinde bir Plugins klasörü, Plugins klasörü içinde de bir Android klasörü oluşturun. Şimdi Eclipse’e geri dönün ve Package Explorer‘daki bin klasörü içinde yer alan .jar uzantılı dosyayı Unity’deki Android klasörünüze kopyalayın:
Plugini Unity’e Atmak (Android Studio)
Yine yukarıda bahsedildiği gibi, yeni bir Unity projesi açıp içinde Plugins, onun içinde de Android klasörleri oluşturun. Ardından Android Studio projenizi oluşturduğunuz konumu dosya gezgininde açın ve “app\build\outputs\aar” yolunu izleyin. Buradaki app-debug.aar dosyasını Winrar veya türevi ile açıp içerisindeki classes.jar dosyasını masaüstünüze veya başka bir konuma çıkartın ve dosyanın ismini istediğiniz gibi değiştirin (Eclipse ADT yöntemi ile senkronize gitmek için ben toastproject.jar olarak değiştirdim). Bu işlemin ardından jar dosyasını Unity projenizin Plugins/Android klasörüne taşıyabilirsiniz.
Artık Eclipse ADT ve Android Studio ayrımının sonuna geldik. Geriye kalan işlemler her iki yöntem için de ortak.
Plugini Gereksiz Dosyalardan Arındırmak
Plugins/Android klasörüne attığınız jar dosyasını (toastproject.jar) Winrar veya türevi ile açın. Burada sizin oluşturmadığınızdan emin olduğunuz dosyaları silin. Örneğin Eclipse ADT ile oluşturulan plugin’de META-INF isminde bir klasör bulunmakta. Bizim bu klasörün içindeki dosya(lar)la bir alakamız olmadığı için bu klasörü silebiliriz. Benzer şekilde, com/yasir/toast klasörü içerisinde yer alan MainActivity.class‘ı da biz yazmadığımız için onu da silebiliriz. Android Studio ile oluşturulan pluginlerde ise com/yasir/toast içerisinde yer alan BuildConfig.class güvenle silinebilir.
Diyebilirsiniz ki niçin ToastTest$1.class‘ı da silmiyoruz. Biz Java kodumuzda new Runnable() {} yaptığımız zaman aslında burada yeni bir anonim (anonymous) class oluşturuyoruz. Java kodları derlendiğinde her bir sınıf için ayrı bir .class dosyası oluşturulduğu için de (ya da en azından ben öyle düşünüyorum) bu new Runnable() {} anonim class’ımız için ToastTest$1.class dosyası oluşturuluyor. Bu yüzden bu dosyayı silmememiz lazım. Durumu daha basite vuracak olursak, eğer ki A.java isminde kod yazdıysanız, plugininizden A.class dosyasını silmeyeceğiniz gibi A$ ile başlayan herhangi bir .class dosyasını da silmeyin.
Plugin İle Unity’den İletişim Kurmak
Şimdi Unity’de kod yazmaya hazırız. Android klasörünün içinde ToastPlugin adında yeni bir C# script oluşturalım ve içini şu şekilde değiştirelim:
using UnityEngine; public static class ToastPlugin { #if !UNITY_EDITOR && UNITY_ANDROID // Bu kod sadece Android cihazlarda çalışır // Plugindeki ToastTest class'ına erişmek için kullanacağımız değişken private static AndroidJavaClass toastClass = null; #endif // Ekranda Toast mesajı göstermeye yarayan fonksiyon public static void Toast( string gosterilecekMesaj ) { #if !UNITY_EDITOR && UNITY_ANDROID // Bu kod sadece Android cihazlarda çalışır if( toastClass == null ) { // Plugindeki ToastTest class'ına erişmek için Unity'de bir değişken oluşturuyoruz toastClass = new AndroidJavaClass( "com.yasir.toast.ToastTest" ); using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) ) using( AndroidJavaObject unityContext = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" ) ) { // ToastTest class'ının static SetContext fonksiyonunu, parametre olarak Unity'nin context'ini alacak şekilde, // CallStatic fonksiyonu ile C# tarafından çağırıyoruz toastClass.CallStatic( "SetContext", unityContext ); } } // ToastTest class'ının ToastGoster static fonksiyonunu, gosterilecekMesaj parametresi ile çağırıyoruz toastClass.CallStatic( "ToastGoster", gosterilecekMesaj ); #else // Bu kod Android hariç diğer platformlarda çalışır // Diğer platformlarda toast desteklenmediği için konsola Debug.Log yapmakla yetiniyoruz Debug.Log( "Toast gösteriliyor: " + gosterilecekMesaj ); #endif } }
Bu fonksiyon oldukça karmaşık gelebilir; normaldir zira çok fazla “korkutucu duran” class ve fonksiyon var. Oysa bu fonksiyonlar gerçekten basit. Şimdi neyin ne olduğunu kısaca anlatmaya çalışacağım.
Elimizde toastClass adında static bir AndroidJavaClass değişkeni var. Pluginimizdeki ToastTest class’ına Unity’den direkt ToastTest diye erişmemiz mümkün değil, araya bir aracı koymak zorundayız. İşte bu aracımız da toastClass değişkeni oluyor. Değişkenin türü AndroidJavaClass çünkü biz direkt olarak ToastTest class’ındaki static fonksiyonlardan faydalanacağız. Yani ToastTest class’ının bir instance’ına (objesine) ihtiyacımız yok. Eğer ki elimizde bir ToastTest instance’ı olsun istiyorduysak o zaman AndroidJavaClass ile değil AndroidJavaObject ile çalışırdık.
Scriptimizdeki tek fonksiyon, Toast ismindeki public static bir fonksiyon. Bu fonksiyona parametre olarak, ekranda gösterilmesi istenen mesajı alıyoruz. Fonksiyon içerisinde öncelikle ToastTest class’ına erişmek için kullanacağımız aracının (toastClass) oluşturulup oluşturulmadığına bakıyoruz. Eğer henüz bu değişken değerini almadıysa, ona değerini veriyoruz. Bunun için AndroidJavaClass’ın constructor’ına parametre olarak ToastTest class’ına giden yolu giriyoruz. Bu yol da “package ismi.class ismi” şeklinde oluyor. Yani benim durumumda “com.yasir.toast.ToastTest” oluyor.
ToastTest‘e erişmek için gerekli olan aracıyı (toastClass) oluşturduktan sonra, bu class’ın sahip olduğu context değişkenine değerini veriyoruz. Bu context olmadan Toast’u çağırmamız maalesef mümkün değil. Unity’nin context’ine erişmek için unityClass ve unityContext adında iki değişken oluşturuyoruz. Bu değişkenleri anlamak zorunda değilsiniz, burası biraz ezbere olan birşey (ben de kodu internetten kopyala-yapıştır yaptım yani). Bu değişkenleri oluştururken using() kalıpları kullanıyoruz çünkü AndroidJavaObject class’ı IDisposable türünde bir sınıf ve bu tür sınıflarla işimiz bittiğinde onların Dispose() fonksiyonlarını çağırmak zorundayız. İşte using() kalıbı, içindeki değişkenin Dispose() fonksiyonunu onunla işimiz bitince otomatik olarak çağırmaya yarıyor. Velhasılıkelam, oluşturduğumuz bu unityContext değişkenini, pluginimizdeki ToastTest class’ında yer alan static context değişkenine değer olarak atamak için, ToastTest’e yazdığımız SetContext static fonksiyonunu kullanıyoruz. Plugindeki statik bir fonksiyonu çağırmak için Unity’de kullandığımız fonksiyon ise CallStatic. Parametre olarak, plugindeki çağırmak istediğimiz fonksiyonun ismini ve ardından bu fonksiyonun alacağı parametre(ler)i giriyoruz. Bizim durumumuzda bu parametre unityContext oluyor.
Fonksiyonun geri kalanında yaptığımız tek şey, toastClass vasıtasıyla pluginimizdeki ToastTest class’ının static ToastGoster fonksiyonunu çağırmak (CallStatic kullanarak). Eğer ki Android dışında bir platformda çalışıyorsak da, toast gösteremeyeceğimiz için konsola Debug.Log yapmakla yetiniyoruz.
Kabaca scriptimiz böyle. Anlamadığınız yerler olabilir, normaldir. Bu durumda internette “unity android how to write plugin” araması yaparak bilgilerinizi pekiştirmenizi öneririm.
Artık scriptimizi test edebiliriz. Bunun için ToastTest isminde yeni bir C# script oluşturun:
using UnityEngine; public class ToastTest : MonoBehaviour { void Start() { ToastPlugin.Toast( "Merhaba Dunya" ); } }
Bu yeni scripti sahnedeki kameranıza component olarak ekleyin. Sonrasında Build&Run yaparak oyunu Android cihazınızda çalıştırın. Bir aksilik yoksa oyun açılır açılmaz şöyle bir bildirim göreceksiniz:
Bu dersi de burada bitirmiş oluyorum. Sonraki derslerde görüşmek üzere, hoşçakalın!