Unity Android Galeri Etkileşimi (Resim/Video Kaydetmek veya Yüklemek)

Yayınlandı: 04 Haziran 2018 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde
Etiketler:, , , , , , , , , ,

Merhabalar,

Bu Unity dersinde, Android ve iOS platformlarda nasıl cihazın galerisine resim/video kaydedebileceğinizi veya galeriden resim/video çekebileceğinizi göstereceğim. Bu iş için kendi yazdığım NativeGallery plugin’ini kullanacağız.

Hazırsanız başlayalım!

Kurulum

Öncelikle şu adresteki unitypackage‘ı Assets-Import Package yoluyla projenize import edin: https://github.com/yasirkula/UnityNativeGallery/raw/master/NativeGallery.unitypackage

Eğer Android’e build alacaksanız Edit-Project Settings-Player‘daki Write Permission‘ı External (SDCard) yapın.

Eğer iOS’a build alacaksanız, Plugins/NativeGallery/Editor/NGPostProcessBuild.cs scriptindeki PHOTO_LIBRARY_USAGE_DESCRIPTION‘ın değerini dilerseniz değiştirin. Buraya girdiğiniz yazı, kullanıcıdan galeriye erişim izni isterken gözükecek.

Fonksiyonlar

A) Resim Kaydetmek

Galeriye resim kaydetmek için 3 farklı fonksiyon mevcut:

  • NativeGallery.SaveImageToGallery( byte[] mediaBytes, string album, string filenameFormatted, MediaSaveCallback callback = null )

Eğer resmi bir byte array‘inde tutuyorsanız bu fonksiyonu kullanın. Bir texture‘un byte array’ine EncodeToPNG veya EncodeToJPG fonksiyonlarıyla erişebilirsiniz.

album: Resmin kaydedileceği albümü belirler.

filenameFormatted: Resim dosyasının ismini belirler. Dilerseniz filenameFormatted’ın içerisine {0} ekleyebilirsiniz; bu durumda {0} otomatik olarak benzersiz bir rakamla değiştirilir ve bu sayede mevcut bir resmin üzerine yazmamış olursunuz. Yani mesela filenameFormatted’ı “Resim {0}.png” yaparsanız, resimler galeriye “Resim 1.png“, “Resim 2.png” vb. şekilde kaydedilir. Örnekten de anlayacağınız üzere, filenameFormatted’a dosyanın uzantısını da yazmayı unutmayın.

Resim Android’de DCIM/album/filenameFormatted konumuna yazılırken iOS’ta ise cihazın galeri için rezerve ettiği özel bir konuma yazılır.

callback: Eğer buraya string parametre alan bir fonksiyon verirseniz, resmin kaydedilmesi bittikten sonra bu fonksiyon çağrılır. Eğer resim başarıyla kaydedildiyse bu string’in değeri null olurken bir hata olduysa hata mesajı bu string’de depolanır.

  • NativeGallery.SaveImageToGallery( string existingMediaPath, string album, string filenameFormatted, MediaSaveCallback callback = null )

Eğer halihazırda varolan bir resim dosyasını galeriye kaydetmek istiyorsanız bu fonksiyonu kullanın. Bu durumda resim dosyası ilgili albüme kopyalanacaktır.

existingMediaPath: Varolan resim dosyasının konumunu buraya girmelisiniz.

  • NativeGallery.SaveImageToGallery( Texture2D image, string album, string filenameFormatted, MediaSaveCallback callback = null )

Bir Texture2D objesini galeriye kaydetmek için bu fonksiyonu kullanabilirsiniz. Eğer filenameFormatted parametresi .jpg veya .jpeg ile biterse texture JPEG formatında, yoksa PNG formatında kaydedilir.

B) Video Kaydetmek

  • NativeGallery.SaveVideoToGallery( byte[] mediaBytes, string album, string filenameFormatted, MediaSaveCallback callback = null )

Eğer video’yu bir byte array‘inde tutuyorsanız bu fonksiyonu kullanın. Mesela projenizde yer alan bir video asset’ini bu fonksiyon ile galeriye kaydetmek isterseniz, videoyu scriptinizdeki bir TextAsset değişkenine değer olarak verebilir ve mediaBytes‘a değer olarak textAssetDegiskeni.bytes verebilirsiniz.

  • NativeGallery.SaveVideoToGallery( string existingMediaPath, string album, string filenameFormatted, MediaSaveCallback callback = null )

Eğer halihazırda varolan bir video dosyasını galeriye kaydetmek istiyorsanız bu fonksiyonu kullanın. Bu durumda video dosyası ilgili albüme kopyalanacaktır.

C) Galeriden Resim Çekmek

  • NativeGallery.GetImageFromGallery( MediaPickCallback callback, string title = "", string mime = "image/*", int maxSize = -1 )

callback: Kullanıcı galeriden bir resim seçince veya işlemi iptal edince bu fonksiyon çağrılır. Buraya vereceğiniz fonksiyon bir string parametre almak zorundadır. Eğer işlem iptal edildiyse bu parametrenin değeri null olurken aksi taktirde seçilen resim dosyasının konumu bu string’de depolanır.

title: Kullanıcının karşısına gelen resim seçme diyaloğunun başlığında yazan yazıyı belirler. Bu parametrenin iOS’ta bir etkisi yoktur.

mime: Galerideki resimleri filtrelemeye yarar. Varsayılan olarak tüm resimler gözükürken, mime’ı örneğin “image/jpeg” yaparsanız sadece JPEG resimler görünür. Bu parametrenin iOS’ta bir etkisi yoktur çünkü iOS’ta seçilen resimler daima PNG formatında olur.

maxSize: Eğer resmin genişliği veya yüksekliği bu değerden büyük olursa, resmin bu boyuta küçültülmüş bir versiyonu döndürülür. Varsayılan olarak bir büyüklük limiti yoktur ancak resim ne kadar büyük olursa hafızada o kadar çok yer kaplayacağı için bu parametreye 512, 1024 vs. gibi işinizi görmeye yetecek bir değer vermeniz şiddetle önerilir. Bu parametrenin Android’de bir etkisi yoktur.

D) Galeriden Video Çekmek

  • NativeGallery.GetVideoFromGallery( MediaPickCallback callback, string title = "", string mime = "video/*" )

Bu fonksiyon, GetImageFromGallery fonksiyonuna benzer şekilde çalışır. Galeriden bir video seçilince veya işlem iptal edilince callback fonksiyonu çağrılır.

E) Çalışma Zamanı İzinleri (Runtime Permissions) Hakkında

Android 6.0 sürümü itibariyle artık önemli bir Android fonksiyonuna erişmeden önce, çalışma zamanında bu fonksiyona erişim izni istemek zorundayız. NativeGallery fonksiyonları çalışmadan önce otomatik olarak izin isterler ancak dilerseniz kendi başınıza da izinlerin mevcut durumunu sorgulayabilir veya izin isteyebilirsiniz.

  • NativeGallery.CheckPermission()

İzinlerin durumunu sorgular ve bir NativeGallery.Permission enum‘u döndürür. Eğer galeriye erişim iznimiz varsa bu enum’un değeri Permission.Granted olurken eğer henüz iznimiz yoksa Permission.ShouldAsk olur. Eğer kullanıcı karşısına gelen izin ekranını “Bir daha sorma” seçili bir şekilde reddederse veya kullanıcının cihazında aktif olan bir ebeveyn kontrol sistemi bu iznin verilmesini engelliyorsa, Permission.Denied döndürülür. Bu durumda kullanıcı izni cihazın ayarlar menüsünden elle vermek zorundadır. Böyle bir durumla baş başa kalırsanız, kullanıcıya ayarlar menüsünden izin vermesini söyleyen bir uyarı penceresi göstermenizi öneririm.

  • NativeGallery.RequestPermission()

Galeriye erişim izni ister ve sonucu bir NativeGallery.Permission enum’unda döndürür. Resim kaydetmeye/çekmeye yarayan NativeGallery fonksiyonları bu fonksiyonu otomatik olarak çağırıp sonucu döndürürler.

F) Seçilen Resim Dosyasını Texture’a Çevirmek

  • NativeGallery.LoadImageAtPath( string imagePath, int maxSize = -1, bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false )

Bu fonksiyon vasıtasıyla bir resim dosyasını hızlıca bir Texture2D objesine çevirebilirsiniz.

imagePath: Texture2D olmasını istediğiniz resim dosyasının konumunu buraya girmelisiniz.

maxSize: Eğer resmin genişliği veya yüksekliği bu değerden büyük olursa, resmin bu boyuta küçültülmüş bir versiyonu döndürülür. Varsayılan olarak bir büyüklük limiti yoktur ancak resim ne kadar büyük olursa hafızada o kadar çok yer kaplayacağı için bu parametreye 512, 1024 vs. gibi işinizi görmeye yetecek bir değer vermeniz şiddetle önerilir.

markTextureNonReadable: Texture2D objesinin RAM’deki kopyasını silerek hafızayı rahatlatır. Bunun dezavantajı, artık resmin piksellerine elle erişemezsiniz; yani eğer GetPixel veya SetPixel fonksiyonlarını kullanıyorsanız bu parametreyi false yapın.

generateMipmaps: Texture’un mipmap’lerinin olup olmayacağını belirler.

linearColorSpace: Texture’un linear renk uzayını mı yoksa gamma renk uzayını mı (varsayılan) kullanacağını belirler.

Örnek Kod

Aşağıdaki örnek kodun 3 işlevi bulunmaktadır:

  • ekranın soluna tıklarsanız bir screenshot alınır ve bu screenshot galeriye kaydedilir
  • ekranın ortasına tıklarsanız galeriden bir resim seçmeniz istenir ve seçilen resim geçici bir küp objesine texture olarak verilir
  • ekranın sağına tıklarsanız galeriden bir video seçmeniz istenir ve seçilen video oynatılır
void Update()
{
	if( Input.GetMouseButtonDown( 0 ) )
	{
		if( Input.mousePosition.x < Screen.width / 3 )
		{
			// Screenshot al ve galeriye kaydet
			StartCoroutine( ScreenshotAlVeKaydet() );
		}
		else
		{
			// Eğer halihazırda bir resim/video seçme diyaloğu aktifse,
			// o diyalog kapatılana kadar bir şey yapma
			if( NativeGallery.IsMediaPickerBusy() )
				return;

			if( Input.mousePosition.x < Screen.width * 2 / 3 ) // >_>
			{
				// Galeriden bir PNG resim çek
				// Eğer resmin genişliği veya yüksekliği 512 pikselden büyükse, resmi ufalt
				GaleridenResimCek( 512 );
			}
			else
			{
				// Galeriden bir video çek
				GaleridenVideoCek();
			}
		}
	}
}

private IEnumerator ScreenshotAlVeKaydet()
{
	yield return new WaitForEndOfFrame();

	Texture2D ss = new Texture2D( Screen.width, Screen.height, TextureFormat.RGB24, false );
	ss.ReadPixels( new Rect( 0, 0, Screen.width, Screen.height ), 0, 0 );
	ss.Apply();

	// Screenshot'ı galeriye kaydet
	Debug.Log( "İzin durumu: " + NativeGallery.SaveImageToGallery( ss, "Örnek albüm", "Resim {0}.png" ) );

	// Artık screenshot'a ihtiyacımız kalmadı
	Destroy( ss );
}

private void GaleridenResimCek( int maksimumBuyukluk )
{
	NativeGallery.Permission izin = NativeGallery.GetImageFromGallery( ( konum ) =>
	{
		Debug.Log( "Seçilen resmin konumu: " + konum );
		if( konum != null )
		{
			// Seçilen resmi bir Texture2D'ye çevir
			Texture2D texture = NativeGallery.LoadImageAtPath( konum, maksimumBuyukluk );
			if( texture == null )
			{
				Debug.Log( konum + " konumundaki resimden bir texture oluşturulamadı." );
				return;
			}

			// Texture'u geçici bir küp objesine ver
			GameObject kup = GameObject.CreatePrimitive( PrimitiveType.Cube );
			kup.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 5f;
			kup.transform.forward = -Camera.main.transform.forward;
			kup.transform.localScale = new Vector3( 1f, texture.height / (float) texture.width, 1f );

			Material material = kup.GetComponent<Renderer>().material;
			if( !material.shader.isSupported ) // eğer Standard shader desteklenmiyorsa Diffuse shader'ı kullan
				material.shader = Shader.Find( "Legacy Shaders/Diffuse" );

			material.mainTexture = texture;

			// 5 saniye sonra küp objesini yok et
			Destroy( kup, 5f );

			// Küp objesi ile birlikte Texture2D objesini de yok et
			// Eğer prosedürel bir objeyi (Texture2D) işiniz bitince yok etmezseniz,
			// mevcut scene'i değiştirene kadar obje hafızada kalmaya devam eder
			Destroy( texture, 5f );
		}
	}, "Bir resim seçin", "image/png", maksimumBuyukluk );

	Debug.Log( "İzin durumu: " + izin );
}

private void GaleridenVideoCek()
{
	NativeGallery.Permission izin = NativeGallery.GetVideoFromGallery( ( konum ) =>
	{
		Debug.Log( "Seçilen videonun konumu: " + konum );
		if( konum != null )
		{
			// Seçilen videoyu oynat
			Handheld.PlayFullScreenMovie( "file://" + konum );
		}
	}, "Bir video seçin" );

	Debug.Log( "İzin durumu: " + izin );
}

Böylece bu dersin sonuna geldik. Sonraki derslerde görüşmek üzere!

yorum
  1. Erol Şıvkın dedi ki:

    Hocam çok yararlı bir ders olmuş. Teşekkürler. Ben basit bir puzzle yaptım da android için. Galeriden resim seçtirdim sizin metodunuzla. Seçtiğim resmi sahneye alabilir miyim? Sahnede hazır bir resim var ve ben sprite editor ile 6 ya böldüm. Kodlar şöyle;

    // Use this for initialization

    [SerializeField]
    private Transform[] pictures;

    [SerializeField]
    private GameObject winText;

    public static bool youWin;

    private void Start()
    {

    winText.SetActive(false);
    youWin = false;
    }

    private void Update()
    {

    if (pictures[0].rotation.z == 0 &&
    pictures[1].rotation.z == 0 &&
    pictures[2].rotation.z == 0 &&
    pictures[3].rotation.z == 0 &&
    pictures[4].rotation.z == 0 &&
    pictures[5].rotation.z == 0)

    {
    winText.SetActive(true);
    youWin = true;
    }
    }

    public void Kapat()
    {

    Application.Quit();

    }
    public void GaleridenResimCek()
    {
    // ÖRNEK KOD
    }

    İyi çalışmalar.

    • yasirkula dedi ki:

      Resmi kaç satır kaç sütun olacak şekilde böldünüz (örneğin 2×3)? Ayrıca, rotation.z yerine eulerAngles.z yapmanız gerekebilir.

      • Erol ŞIVKIN dedi ki:

        Hocam 6 hücre yani 2 satır 3 sütun olacak şekilde.

      • yasirkula dedi ki:

        Öncelikle projenize şu plugin’i import edin: https://github.com/yasirkula/UnityTextureOps/raw/master/TextureOps.unitypackage

        Ardından NativeGallery’den elde ettiğiniz resmi bir Texture’a çevirip bu Texture’u şu fonksiyon vasıtasıyla 6 parçaya bölebilirsiniz:

        public Sprite[] ResmiParcalaraBol( Texture2D resim )
        {
        	Texture2D[] parcalar = TextureOps.Slice( resim, resim.width / 3, resim.height / 2, TextureFormat.RGB24 ); // 2 satır 3 sütun
        	
        	Sprite[] sonuc = new Texture2D[parcalar.Length];
        	for( int i = 0; i < parcalar.Length; i++ )
        		sonuc[i] = Sprite.Create( parcalar[i], new Rect( 0.0f, 0.0f, parcalar[i].width, parcalar[i].height ), new Vector2( 0.5f, 0.5f ), 100.0f );
        	
        	return sonuc;
        }
        

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. Çıkış  Yap /  Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.