Unity Lerp İle Smoothing (Yumuşatma) İşlemi Yapımı

Yayınlandı: 27 Aralık 2015 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde
Etiketler:, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Yine ve yeniden merhabalar,

Bu mini derste Unity 3D‘de Mathf.Lerp fonksiyonunu kullanarak istediğimiz bir işlemi “yumuşatmayı” göreceğiz. Yumuşatmadan kastım bir değerden başka bir değere geçişi küt diye değil de adım adım yapmak.

Örneğin kameraya oyuncuyu yumuşak bir şekilde takip etmesini söylerseniz, oyuncu ileri giderken kamera biraz geride kalır, oyuncu geri giderken kamera oyuncuya biraz daha yaklaşır. Böylece “yumuşak” bir animasyon ortaya çıkar. Veya bir objenin sürekli fare imlecine doğru hareket etmesini istiyorsunuz diyelim, bu iş için de Lerp’ten faydalanabilirsiniz. Lerp için daha pek çok örnek üretilebilir; bu daha çok sizin hayal gücünüze kalmış bir şey.

Hazırsanız başlayalım…

İlk önce Lerp fonksiyonunun nasıl çalıştığına bakalım. Bu fonksiyon aslında çok basit bir işleyişe sahip: tek yaptığı şey a ve b sayılarının arasındaki bir değeri döndürmek. Bu değerin a veya b’den ne kadar uzak olacağını ise üçüncü parametre belirliyor. Şöyle ki:

  • Mathf.Lerp( 0, 100, 0.5 ): 0 ile 100 sayılarının tam ortasındaki sayıyı (50) döndürür.
  • Mathf.Lerp( 0, 100, 0.75 ): Döndürülen sayının 0’a uzaklığı %75 iken 100’e uzaklığı %25 olacak, yani 75 döndürülecek.
  • Mathf.Lerp( 100, 500, 0.1 ): Döndürülen sayının 100’e uzaklığı %10 iken 500’e uzaklığı %90 olacak. Aradaki uzaklık 400 olduğuna göre bu uzaklığın %10’u 40’tır. O halde döndürülen sayı 100+40=140 olacak.

NOT: Üçüncü parametre 0.0 ile 1.0 arasında değilse Unity tarafından otomatik olarak ya 0 ya da 1 yapılır.

Peki yumuşak geçiş olayı nasıl gerçekleşiyor? Mesela şu kodu ele alalım:

var a : float = 0;
var b : float = 100;

function Update()
{
	a = Mathf.Lerp( a, b, 0.5 );
}

Update ilk kez çağrıldığında a’nın değeri 0 ile 100’ün ortasındaki sayı, yani 50 olacak. Update ikince kez çağrıldığında a’nın değeri 50 ile 100’ün arasındaki sayı, yani 75 olacak. Sonraki frame’de 87.5 diyerekten böyle böyle a sayısı b sayısına yaklaşacak. Fonksiyondaki 0.5’i artırırsak a daha hızlı yaklaşır, azaltırsak daha yavaş yaklaşır.

Bunu bir örnek vasıtasıyla pekiştirelim: ekranımızda ufak bir UI button olsun ve bu buton ekranda fare imlecini takip etsin. Burada a değerimiz butonun pozisyonu, b değerimiz ise fare imlecinin pozisyonu olduğu taktirde buton fareyi takip eder. O halde sahnenizde GameObject-UI-Button yoluyla bir buton oluşturup butona şu kodu verin:

function Update()
{
	var butonPozisyon : Vector3 = transform.position;
	var imlecPozisyon : Vector3 = Input.mousePosition;
	
	transform.position = Vector3.Lerp( butonPozisyon, imlecPozisyon, 0.25 );
}

Burada Vector3.Lerp kullanıyoruz. Bu fonksiyon iki sayıyı birbirine yaklaştırmak yerine iki vektörü birbirine yaklaştırmaya yarar. Bunu kullanıyoruz çünkü butonun konumu (transform.position) bir Vector3’te depolanmakta.

Kodda yaptığımız şey çok basit: a’ya (butonPozisyon) butonun position’ını, b’ye (imlecPozisyon) ise fare imlecinin position’ını depoluyoruz ve a’yı her frame’de %25 oranla b’ye yaklaştırıyoruz. Vector3.Lerp’in döndürdüğü değeri geri butonun position’ına atmayı unutmuyoruz. Buradaki 0.25’i artırıp azaltarak Lerp’in etkisini rahatça görebilirsiniz.

Peki diyelim ki eğer ekrana tıklarsak tam 2 saniye içinde butonun rengini mevcut renginden yeşile çevirmek istiyoruz. Bu geçişi öyle istiyoruz ki her frame’de rengin değeri eşit olarak değişecek. Burada Color.Lerp fonksiyonu kullanacağız. Bu fonksiyon, a renginden b rengine yumuşak geçiş yapmaya yarar. a kısmına butonun ilk rengini, b kısmına ise yeşil rengi yazacağız. Fonksiyonun istediğimiz gibi çalışması için 3. parametrenin 2 saniye içinde 0’dan 1’e değişmesi lazım. Bunun için kodu şöyle güncelleyin:

import UnityEngine.UI;

private var butonIlkRenk : Color;
public var butonSonRenk : Color = Color.green;
public var gecisSuresi : float = 2.0;

private var bitisAni : float = -1.0;

function Start()
{
	butonIlkRenk = GetComponent( Image ).color;
}

function Update()
{
	// Butonu imlece yaklaştır
	var butonPozisyon : Vector3 = transform.position;
	var imlecPozisyon : Vector3 = Input.mousePosition;
	
	transform.position = Vector3.Lerp( butonPozisyon, imlecPozisyon, 0.25 );

	// Ekrana tıklayınca butonun rengini 2 saniye içinde yeşil yap
	if( Input.GetMouseButtonDown( 0 ) )
	{
		bitisAni = Time.time + gecisSuresi;
	}
	
	if( bitisAni > 0 )
	{
		GetComponent( Image ).color = Color.Lerp( butonIlkRenk, butonSonRenk, 1.0 - ( bitisAni - Time.time ) / gecisSuresi );
		
		// Eğer renk geçişi tamamlanmışsa artık Lerp'ü boş yere çağırma
		if( Time.time > bitisAni )
			bitisAni = -1.0;
	}
}

Kod birazcık uzadı ancak her frame’de eşit aralıklarla b’ye yaklaşmak için biraz daha fazla kod gerekiyordu.

Öncelikle Start fonksiyonunda butonIlkRenk değişkenine butonun ilk baştaki rengini değer olarak veriyoruz. Lerp fonksiyonunda da bu değişkenden faydalanıyoruz. Yani a’ya değer olarak butonun mevcut rengini değil de butonun ilk rengini veriyoruz. Sadece Lerp’ün üçüncü parametresi değişiyor ve böylece her frame’de renk değişimi eşit oluyor. Unity’nin UI sistemiyle çalıştığımız için en başta “import UnityEngine.UI;” kullanırken butonun rengine ise “GetComponent( Image ).color” şeklinde erişiyoruz.

Update fonksiyonunda “Input.GetMouseButtonDown(0)” ile ekrana sol tıklanıp tıklanmadığını kontrol ediyor, eğer tıklanmışsa bitisAni değişkeninin değerini mevcut anın (Time.time) 2 saniye sonrası olarak veriyoruz. Buradaki Time.time hazır bir değişken ve oyun başladıktan itibaren kaç saniye geçtiğini depolamakta. Değeri Unity tarafından otomatik olarak güncelleniyor.

Update’in sonunda ise “if( bitisAni > 0 )” koşuluyla ekrana tıklanıp tıklanmadığını kontrol ediyoruz. Eğer tıklanmışsa butonun rengini Color.Lerp’in sonucuna ayarlıyoruz. Asıl kritik satır da tahmin edebileceğiniz üzere burası. a’ya değer olarak butonun ilk rengini, b’ye değer olarak ise yeşil renk (butonSonRenk = Color.green) veriyoruz. Peki %’yi nasıl hesaplıyoruz? Bunu açıklamaya çalışmak yerine matematiksel olarak göstermek daha kolay geliyor (butona tam olarak oyunun 5. saniyesinde (Time.time = 5 iken) bastığımızı varsayalım; bu durumda bitisAni ise 5.0 + 2.0 = 7.0 oluyor):

  • Ekrana tıkladığımız frame’de:

1.0 – ( 7.0 – 5.0 ) / 2.0 = 1.0 – 1.0 = 0.0 (butonun rengi butonIlkRenk)

  • Ekrana tıkladıktan bir saniye sonra (Time.time = 6 iken):

1.0 – ( 7.0 – 6.0 ) / 2.0 = 1.0 – 0.5 = 0.5 (butonun rengi artık butonIlkRenk ile butonSonRenk’in tam ortasındaki renk)

  • Ekrana tıkladıktan iki saniye sonra (Time.time = 7 iken):

1.0 – ( 7.0 – 7.0 ) / 2.0 = 1.0 – 0.0 = 1.0 (butonun rengi artık butonSonRenk, yani yeşil)

Gördüğünüz gibi üçüncü parametremiz 2 saniye içinde 0’dan 1’e çıkıyor. Lerp’ten hemen sonra Time.time’ın bitisAni’na yetişip yetişmediğine bakıyoruz. Eğer yetişmiş ise renk geçişi bitmiş demektir, bu durumda bitisAni’nı 0’dan küçük bir değere ayarlayarak if’in içinin artık çalıştırılmasını önlüyoruz. Eğer bu işlemi yapmasaydık örnek yine birebir aynı çalışırdı zira üçüncü parametre artık 1.0’dan büyük bir değer alır ancak bu değer Unity tarafından otomatik olarak 1.0’a döndürülürdü ancak boş yere neden Lerp çağıralım ki, değil mi?

Bu derse burada noktayı koyuyorum. Umarım konu az biraz anlaşılmıştır. Daha başka derslerde görüşmek üzere!

yorum
  1. çaylak dedi ki:

    emeğinize sağlık hocam

  2. Kimex dedi ki:

    Ders için teşekkürler gerçekten gidip gelme olaylarında çok işe yarıyan bir method.Birde bu derse Slerp ve pingpong fonksiyonlarınıda eklerseniz bence mükemmel olur.2sinide daha önce hiç kullanmadım ama kullanılan yerler gördüm ve kullanışlı geldiler bana 😀

  3. enceweb dedi ki:

    Merhaba Hocam,

    Player ontrigger fonksiyonu ile objeye temas edince belli bir yüksekliğe ulaşıyor 0.3 sn sonra ınvoke ile aşağıdaki fonkisyonu çağırıp konumu eski haline sabitliyorum ama player anında iniyor. İnişi yavaşlatmak için lerp fonkisyonunu burada kullanabilir miyiz? Eğer kullanabiliyorsak playerin havadaki ilk konumunu nasıl buraya ekleyebilirim hocam?Yardımcı olabilirseniz çok sevinirim…

    Saygılarımla…

    void DikeyHiziResetle()
    {
    dikeyHiz = 0;
    Vector3 konum = tr.position;
    konum.y = .44f;
    tr.position = konum;
    // tr.position = Vector3.Lerp(?, konum, .25f);
    Time.timeScale = 1.0f;

  4. enceweb dedi ki:

    Teşekkürler hocam.

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 )

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 )

Google+ fotoğrafı

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

Connecting to %s