Unity Bir Script’ten Başka Bir Script’teki Değişkene Ulaşmak

Yayınlandı: 27 Temmuz 2019 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde

Yeniden merhabalar,

Bu derste, Unity‘de yazdığınız bir C# script’inden başka bir C# script’indeki değişkene ulaşmanın birkaç yolunu göstereceğim:

  • Hedef script’i public bir değişkende tutmak
  • Hedef script’e kod vasıtasıyla ulaşmak
  • Değişkeni static yapmak

Bu konuda sıklıkla soru aldığım için, umarım bu ders akıllardaki bir takım soruları cevaplayacaktır. Hazırsanız derse başlayalım!

Öncelikle, işleri kolaylaştırması adına iki script’e ve ulaşmak istediğimiz değişkene isimler verelim. Ders boyunca, Dusman script’inden Player script’indeki “public Vector3 konum;” değişkenine ulaşmaya çalışacağız. Ulaşmaya çalıştığımız değişkenin ya public olması lazım, ya da o değişkene ulaşmaya yarayan public bir property veya fonksiyonun Player script’inde tanımlı olması lazım. Aksi taktirde bu değişkene ulaşamayız.

Hedef script’i public bir değişkende tutmak

En basit yöntem bu sayılabilir. Dusman script’inde public Player player; değişkeni oluşturun ve Dusman objesinin Inspector’undan Player değişkene değer olarak, Player objesini verin. Artık player.konum şeklinde konum değişkenine ulaşabilirsiniz.

Eğer Dusman script’i bir prefab’da yer alıyorsa ve Player objesi sahnenizde yer alan bir obje ise (yani prefab değilse), Dusman objesinin Player değişkenine Player objesini değer olarak veremezsiniz. Çünkü sahnedeki objeler prefab’ların değişkenlerine değer olarak verilemez. Bu durumda, bahsedeceğim diğer yöntemlere başvurmak zorundasınız.

Hedef script’e kod vasıtasıyla ulaşmak

Oyun esnasında bir objeye veya component’e dinamik olarak erişmek için birkaç fonksiyon mevcut:

GameObject obje = GameObject.Find(“Objenin ismi”): ismi girilen objeyi GameObject olarak döndürür. Aynı isimde birden çok obje varsa, ilk bulunan obje döndürülür. Eğer bu isimde bir obje yoksa null döndürülür

GameObject obje = GameObject.FindWithTag(“Objenin tag’ı”): tag’ı girilen objeyi GameObject olarak döndürür. Aynı tag’e sahip birden çok obje varsa, ilk bulunan obje döndürülür. Eğer bu tag’e sahip bir obje yoksa null döndürülür

Player playerScripti = obje.GetComponent<Player>(): obje’nin Player component’ini döndürür. Eğer objede Player component’i yoksa null döndürülür

Player playerScripti = FindObjectOfType<Player>(): sahnedeki Player component’ine sahip objeyi bulur ve bu objenin Player component’ini döndürür. Eğer birden çok objede Player component’i varsa, ilk bulunan objenin Player component’i döndürülür. Sahnedeki hiçbir objede Player component’i yoksa null döndürülür

Fark edeceğiniz üzere, bu fonksiyonlar vasıtasıyla oyun esnasında sahnedeki herhangi bir objeye dinamik olarak erişebilirsiniz. Ancak bu fonksiyonlar biraz yavaş fonksiyonlardır (özellikle FindObjectOfType ve GameObject.Find), o yüzden bu fonksiyonları Update gibi bir fonksiyonda kullanırsanız bu performans sorunlarına yol açabilir. Onun yerine, ulaşmak istediğiniz objeyi tutacak private bir değişken oluşturup, bu fonksiyonlar yardımıyla Start fonksiyonunda değişkene değerini verebilir ve script’in geri kalanında objeye bu değişken vasıtasıyla ulaşabilirsiniz. Örneğin:

private Player player;

void Start()
{
	// Player objesinin tag'ının Player olması lazım!
	player = GameObject.FindWithTag( "Player" ).GetComponent<Player>();
	
	// veya (daha yavaş)
	player = GameObject.Find( "Player" ).GetComponent<Player>();
	
	// veya (çok daha yavaş)
	player = FindObjectOfType<Player>();
}

void Update()
{
	// Artık değişkene player.konum şeklinde ulaşabiliriz
	Debug.Log( player.konum );
}

Player fonksiyonuna kod vasıtasıyla ulaşmanın daha hızlı bir yolu ise, Player script’ine hızlıca ulaşmaya yarayan bir static değişken oluşturmaktır. Bunun için Player script’inde public static Player instance; şeklinde bir değişken tanımlayıp Awake fonksiyonunda (Awake, Start fonksiyonundan önce çağrılır) instance=this; komutunu çalıştırabilirsiniz:

public static Player instance;

void Awake()
{
	// Player script'i, instance değişkenine değer olarak kendisini (this) veriyor
	instance = this;
}

Bundan böyle Dusman script’inden konum değişkenine Player.instance.konum şeklinde rahatça ulaşabilirsiniz. Bu yöntem, yukarıda bahsettiğim fonksiyonları kullanmaya göre çok daha hızlıdır.

Değişkeni static yapmak

Aslında bu yöntem, bir önceki yöntemde bahsettiğim static instance değişkeni oluşturmakla bayağı benzerdir. Tek fark, static instance değişkeni oluşturmak yerine direkt konum değişkenini static yapmamızdır. Yani public Vector3 konum; değişkenini, public static Vector3 konum; olarak değiştirmek yeterlidir. Artık Dusman script’inden bu değişkene direkt Player.konum şeklinde erişebiliriz.

Bu yöntemin kötü yanı, artık konum değişkenine Inspector’dan değerini veremeyecek olmanızdır çünkü static değişkenler Inspector’da gözükmez. Yani eğer konum değişkenine değerini script’inizden veriyorsanız bu yöntem idealken, konum değişkenine Inspector’dan değer veriyor idiyseniz bu yöntem yerine bir önceki static instance değişkenli yöntemi tercih etmelisiniz.

Bu yöntemle ilgili bilmeniz gereken bir başka husus ise, static değişkenlerin varsayılan değerlerini yalnızca bir kere aldıklarıdır. Bir örnek ile biraz daha açayım: diyelim ki ulaşmak istediğiniz değişken public int skor; (veya public int skor = 0;) olsun. Burada skor değişkeninin varsayılan değeri 0’dır. Bu değişkene sahip script’in olduğu obje her yok olup yeniden oluştuğunda, skor‘un değeri tekrar varsayılan değer olan 0’a döner. Ancak siz bu değişkeni public static int skor; şeklinde static yaparsanız, değişkene ilk kez ulaşırken değişken yine 0 döndürürken, değişkene sahip script’in olduğu obje yok olup yeniden oluştuğunda skor‘un değeri sıfırlanmaz, en son değeri neyse o şekilde kalır. Yani mesela level bitti, ana menüye dönüş yapıldı ve ardından level tekrar başlatıldı diyelim. Bu durumda normalde skorun sıfırlanması beklenirken, en son level’da skor ne idiyse değeri o şekilde devam eder. Her level başında skor’un değeri sıfırlansın isterseniz, skor’un olduğu script’in Awake fonksiyonunda skor = 0; şeklinde değeri elle sıfırlamanız gereklidir.

Böylelikle bu dersin de sonuna geldik, sonraki derslerde görüşmek dileğiyle!

yorum
  1. Murat dedi ki:

    Hocam merahab
    2 sahnem var ilk sahnemde köşede highscore yazdıracagım ikinci sahnemde oyunum mevcut
    ilk sahneme public Text oluşturyorum UI panelden text olusturup ekliyorum ınspectorden atıyorum high score yazılıyor sorun yok sorun surada oluşuyor ikinci sahne bu text game objecti olmadığı için nesneyi bulamıyor ilk ekrana döndüğümde haliyle highsocre bulamadığ için ekrana basımıyor. Bunu inpectorden değilde sciript ile nasıl atama yapabilirim

  2. kareka17 dedi ki:

    Kolay gelsin,
    Hocam ben 3D Mobil proje üzerinde uğraşıyorum asset store dan hazır sahne aldığım zaman hiçbir kasma olmadan çok akıcı oynayabiliyorum. Ama kendim o free objeleri kullanıp sahne tasarladığımda çok takılma oluyor. Hatta aynı objeleri bile kullansam kasıyor ama o demo sahneyi kullandığımda çok akıcı bir şekilde oynanıyor. Render ayarlarıyla alakalı bir tavsiyeniz var mı? Nasıl düzeltebilirim bu durumu? Demo sahnenin objelerinin aynısını bir kare evin içine koysam bile kasma oluyor çünkü

    • yasirkula dedi ki:

      Kameranın ayarlarına, sahnedeki ışık sayısına ve Window-Lighting Settings’deki ayarlara da bakın. İki sahne arasında bunlar farklılık gösteriyor olabilir. Mümkünse Skybox kullanmayın, gölgeleri kapatın ve ışık sayısına minimuma indirgeyin.

  3. kareka17 dedi ki:

    Kolay Gelsin,
    Editör ekranında objeleri hareket ettirirken mesela iki tane küpü nasıl üst üste getirebiliriz. x,y ve z ayarlarıyla ayarlamak zamanı çok alıyor. Bir videoda gördüm ancak kısayol tuşunu bilmediğim için soruyorum. Adam kısayol tuşuna basıp objeyi köşesinden tutuyor ve direkt üst üste biniyor yani x,y ve z ile manuel uğraşmıyor. Yardımın için teşekkür ederim.

  4. kareka17 dedi ki:

    Merhaba Süleyman Bey,
    Oyunumda butona tıkladığımda belirli bir obje bulunduğu konumdan 20 birim yer değiştiriyor ve tekrar tıkladığımda aynı yerine geliyor. Bunu:
    transform.positiyon=new vector3(…) yöntemi ile yaptım. Bu yer değiştirmeyi bir anda değilde zamana bölerek yani obje bir güç tarafından o noktaya itiyormuş gibi nasıl yaparım. Yapmak istediğim obje direkt olarak 20 birim kaymak yerine saniyeler içinde gidecek.

  5. kareka17 dedi ki:

    Kolay gelsin Süleyman Bey,
    Mesela sahnemde aynı componenti kullanan 3 tane obje var. Her obje kendi üzerine tıklandığında hareket ediyor vs. Ben kod ile bu 3 objenin aynı anda componentine ulaşabilir miyim? Yani şöyle açıklayayım; Objelerin içerisinde bir bool değişkenim var bu true olduğunda sola doğru gidiyorlar false olduğunda sağa doğru. Kod ile bunların 1 tanesi hariç hepsini istediğim şekilde (true/false) tanımlayabilir miyim? Teşekkür ederim..

    • yasirkula dedi ki:

      Script’inizde 3 tane public değişken tanımlayıp bu 3 objeyi Inspector’dan değişkenlere değer olarak verin derim. Eğer 3 obje oyun esnasında spawn oluyorsa veya başka bir durum varsa onu belirtirseniz alternatif bir çözüm yolu düşünebiliriz.

      • kareka17 dedi ki:

        3 tane değişken olayı amacıma ulaşmamı sağlıyor ancak her seferinde 3 obje olması gerekiyor. Benim bir bölümde 3 bir bölümde 10 tane objem oluyor bunu dinamik yapabilmem için bir tavsiyeniz var mı?

      • yasirkula dedi ki:

        Bir array (mesela GameObject[]) veya list (mesela List<GameObject>) değişken kullanabilirsiniz. Bu değişkene Inspector’dan istediğiniz kadar objeyi değer olarak verebilirsiniz.

  6. Emrullah Ertaş dedi ki:

    merhabalar hocam, böyle bir sorun ile karşı karşıyayım. https://youtu.be/i45ikEs9qBo

    8 saniyelik video.
    Video da gördüğünüz gibi adobe animate ve adobe after da hiçbir sıkıntı yok. Ama unity de titreşimli oluyor. Acaba adobe animate üzerinden farklı format ilemi render almam gerekiyor? Yardımcı olursanız çok sevinirim video 8 saniye fark çok anlaşılıyor karşılaştırma yaptım teşekkürler şimdiden.

  7. kareka17 dedi ki:

    İyi Günler Süleyman Bey,
    Acaba elinizde kamera ile ilgili bir script var mı?
    Android oyun yapmaya çalışıyorum 3D şeklinde ve karakterimin kamerasını sağa sola çevirmek için bir scripte ihtiyacım var yani ekranda elimizi kaydırarak kameranın açısını/karakterin yönünü değiştirmem lazım. Yardımlarınızı bekliyorum kolay gelsin..

    • yasirkula dedi ki:

      Şu script ile kameranın hedefi yumuşak bir şekilde takip etmesini sağlayabilirsiniz:

      using UnityEngine;
      
      public class KameraTakip : MonoBehaviour
      {
      	public Transform hedef;
      	public Vector3 kameraUzaklik = new Vector3( 0f, 3f, -5f );
      
      	public float kameraHareketHizi = 10f;
      	public float kameraDonmeHizi = 10f;
      
      	// Karakter fizik ile FixedUpdate içerisinde hareket ediyorsa, burada da LateUpdate yerine FixedUpdate kullan
      	private void LateUpdate()
      	{
      		// Kameranın olması gerektiği noktayı bul
      		Vector3 kameraKonum = hedef.position + new Vector3( 0f, kameraUzaklik.y, 0f );
      
      		if( kameraKonum.z != 0f )
      		{
      			Vector3 ileriYon = hedef.forward;
      			ileriYon.y = 0f;
      			ileriYon.Normalize();
      
      			kameraKonum += ileriYon * kameraUzaklik.z;
      		}
      
      		if( kameraUzaklik.x != 0f )
      		{
      			Vector3 sagYon = hedef.right;
      			sagYon.y = 0f;
      			sagYon.Normalize();
      
      			kameraKonum += sagYon * kameraUzaklik.x;
      		}
      
      		// Kamerayı hedef noktaya yumuşak bir şekilde (Lerp) yaklaştır
      		Vector3 yeniKonum = Vector3.Lerp( transform.position, kameraKonum, Time.deltaTime * kameraHareketHizi );
      		transform.position = yeniKonum;
      
      		// Kamerayı hedef eğime yumuşak bir şekilde döndür
      		Quaternion egim = Quaternion.LookRotation( hedef.position - yeniKonum );
      		transform.rotation = Quaternion.Lerp( transform.rotation, egim, Time.deltaTime * kameraDonmeHizi );
      	}
      }
      
  8. Levent dedi ki:

    Yasir bey
    Unity android için yaptığım oyunda karakter titriyor( yalpalanıyor ) ve nesnelerin kaplamaları yarılıyor.
    oyun fps si ortalama 80 fps nasıl dzeltebilirim yardımcı olurmusun . Teşekkurler

    • yasirkula dedi ki:

      Eğer kamera hareket ederken karakter titriyorsa belki titreyen şey kameradır. Karakteri Update’te hareket ettiriyorsanız kamerayı LateUpdate’te, karakteri FixedUpdate’te hareket ettiriyorsanız kamerayı da FixedUpdate’te hareket ettirin. Kaplamaların yarılmasını örnek bir resimle gösterebilir misiniz?

      • Levent dedi ki:

        Yasir bey
        Kamerayı karakterin içine attım .

      • yasirkula dedi ki:

        Karakterin hareket script’inde bir sıkıntı vardır tahminimce. Script’in tüm alakasız kısımlarını silip script’i burada paylaşırsanız inceleyebilirim.

  9. Eren dedi ki:

    Merhaba, ben bir prefab’dan canvas içindeki imageyi SetActive(true) yapacağım. Önce public olarak bir GameObje değişken tanımlıyorum oradan prefab içindeki scripe sürüklemem lazım fakat hierarhcy panelinde sadece prefab gözüküyor. Ben bu prefab dan nasıl imageyi setActive yapacağım? teşekkürler 🙂

    • yasirkula dedi ki:

      Bu durumda yazıda bahsettiğim “Hedef script’e kod vasıtasıyla ulaşmak” yöntemini kullanmanız lazım. Ben olsam oradaki “static instance” değişkenli yolu kullanırdım.

      • Faruk dedi ki:

        public Canvas canvasb;
        canvasb = GameObject.FindGameObjectWithTag(“Canvas”).GetComponent();

        bu kodu denediğimde NullReferenceException bu hatayı alıyorum

      • yasirkula dedi ki:

        Kodu çalıştırdığınız esnada sahnede Canvas tag’ına sahip bir obje bulunamadı diyor. Objenin isminin değil tag’ının Canvas olduğundan ve objenin o anda inaktif değil aktif olduğundan emin olun.

  10. Faruk dedi ki:

    hocam bir scripten başka bir scripte getcomponet ile ekliyorum sonra hareket.bullet = true diyorum ve nullreferanceexeption hatası alıyorm nasıl çözerim

    • yasirkula dedi ki:

      GetComponent’i çağırdığınız esnada objede o component henüz yokmuş olabilir. Ama daha büyük bir olasılıkla, GetComponent ile değişkene attığınız component’e sahip obje yok olmuştur (Destroy ile veya sahneler arası geçiş esnasında) ve bu yüzden hata alıyorsunuzdur.

  11. Drunkeinstein dedi ki:

    Hocam sizin yazıları Ali kaya denilen şahış izinsiz olarak kendi blogunda yayınlıyor ve Yazar olarak kendi adını yazıyor. https://alikaya.info/unity3d-ogrenimi-ders-3-2017/ buyurun link bu diğer derslerine bakmadım ama vardır büyük ihtimalle.

  12. kareka17 dedi ki:

    Kolay gelsin hocam,
    Sayfanı beğenerek takip ediyorum ve başarılarının devamını diliyorum. Konu ile alakalı değil ama internet üzerinde bulamadığım bir sorunun cevabını sormak istiyorum belki yanlış araştırdığım için bulamadım bilmiyorum ama yardımcı olabilirsen minnettar olurum.
    Hocam, ana sayfamda; başlat, ayarlar ve oyundan çık butonu var. Bu butonların anchorlarını ayarlayarak sayfaya göre şekillenmesini sağladım. Ancak içlerinde bulunan yazılar sayfa büyüyünce çok küçük oluyor ve okunmuyor. Buton boyutlarında sorun yok sayfaya göre şekilleniyor ama içerisindeki yazının fontunu sayfaya göre bir türlü ayarlayamadım. Yardımını bekliyorum, saygılar..

    • yasirkula dedi ki:

      İlk önerim Canvas objenizin Canvas Scaler’ının Scale Mode’u ile oynamanız. Gerekirse de yazının “Best Fit” ayarını açarak dinamik olarak boyutunun değişmesini sağlayabilirsiniz.

  13. Arda dedi ki:

    başka bir scriptden başka bir objenin ontriggerenter2d sini nasıl kullanabilirim

    • yasirkula dedi ki:

      O fonksiyonu public yapıp, dersteki değişkene ulaşma yöntemlerinin aynısını uygulayarak fonksiyona ulaşabilirsiniz (yani A script’inin b değişkenine A.b ile erişecek idiyseniz, onun yerine istediğiniz fonksiyona A.OnTriggerEnter2D(…) ile erişebilirsiniz).

  14. Yasin Çakıcı dedi ki:

    Tek bir hedefim var oda mobil oyun için, ama bir türlü c# dilinin mantığını kavrayamadım.
    elinizde varmıdır bir ders veya kaynak?

    Yani demek istediğim C# dilini (mantığını) hiçbir bilgisi olmayan birine nasıl anlatabilirsiniz?

    bu soruya cevap 🙂

    • yasirkula dedi ki:

      Benim önerim C#’ın temellerini Unity’den bağımsız olarak öğrenmeniz çünkü Unity ile C# programlamaya başlamak konusunda Türkçe çok iyi kaynak bulmakta belki zorlanırsınız ama “c# giriş dersleri -visual” diye arama yaparsanız, Türkçe ve kaliteli C# başlangıç dersleri bulabilirsiniz. Bu dersler ile C# ile programlamanın temelini iyice kavradığınıza inandıktan sonra Unity’e geçiş yapabilirsiniz.

      Sadece şuna dikkat edin: C# ile konsol tabanlı ve pencere tabanlı (visual) uygulamalar yapılabilir, siz başlangıç seviyesinde olduğunuz için konsol tabanlı derslere bakın, visual C# yazan derslere hiç bakmayın. Zaten temeli kaptıktan sonra görsel kısmı Visual C# ile değil Unity’de C# kullanarak yapacaksınız. Konsol tabanlı derslerin kodlarında Console.WriteLine gibi satırlar görürsünüz.

  15. ali dedi ki:

    Hocam scripti çağırıyorum çağırdığım scriptten public olmasına rağmen değişkenlere de metotlarada ulaşamıyorum. Sizce sorun ne olabilir ?

  16. Kerem Güneş dedi ki:

    Öncelikle merhaba hocam benimde bir sorum olucaktı Unity 3d ile bir oyun geliştiriyorum bu oyununda başlangıçta eğer daha önceden takım olusturulmadiysa takım oluştur sayfasına geçicek daha önceden takım olusturduysa direk ana menüye geçicek birde takım oluşturduğunda takımın logosu adı felan bunları nasıl ana menüde gösterebilirim bunun ile ilgili bir yazınız varsa linkini atabilirmisiniz yada dili farketmez bir ders varsa atarsanız çok sevinirim ne zamandır googlda arıyorum bir türlü bulamıyorum nasıl arayacagimi da bilemiyorum 😊 bu konuda yardimci olursaniz cok sevinirim.

    • yasirkula dedi ki:

      Kendiniz araştırırken “unity persist data” şeklinde arayabilirsiniz. Unity oyunlarında veri kaydetmenin en kolay yolu PlayerPrefs’tir. PlayerPrefs ile kaydettiğiniz veriler oyun kapansa da değerini korur (ama oyun cihazdan silinirse değerler kaybolabilir). Diğer alternatiflerden bazıları JsonUtility, XMLSerializer ve BinaryFormatter’dır. Bunlarla veriyi bir dosyaya çeşitli formatlarda kaydedebilir, daha sonra veriyi geri yükleyebilirsiniz.

      Takımın adı gibi basit şeyleri PlayerPrefs ile çok hızlı bir şekilde kaydedebilirsiniz ama eğer logoyu kullanıcı kendisi bilgisayarından seçecekse (yani var olan bir kaç logo arasından tercih yapmayacaksa), o zaman bu logonun bir kopyasını Application.persistentDataPath klasöründe tutup onun konumunu PlayerPrefs veya bahsettiğim diğer metotlar ile kaydedebilirsiniz. Daha sonra bu konumdan logoyu Texture2D olarak yüklemek için, Texture2D.LoadImage fonksiyonunu kullanabilirsiniz.

  17. Emir Yılmaz dedi ki:

    Merhaba yasir bey. İyisinizdir umarım 🙂 Bir sorum olacak bu konuyla pek alakası yok ama nereye yazacağımı bilemedim. Ben bir mobil oyun yapıyorum. Oyun başında logo geldikten sonra loading ekranı gelicek. Bu loading ekranı 3-4 saniye tutucam. Bu sürede arkaplanda nesneler objeler yüklenicek. Bir nevi optimizasyon. Bunu nasıl yaparım ? Bir kaynak var mıdır İngilizce türkçe ? Ya da siz daha önce bu konuda makale yazdıysanız link atabilir misiniz ? Bu benim için çok önemli 🙂 İyi günler iyi çalışmalar

  18. Chucklestone dedi ki:

    Yaşadığını öğrendiğimiz için Mutluyuz : D

yasirkula için bir cevap yazın Cevabı iptal et

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.