Birlikte öğreniyoruz...

Unity fonksiyonlar ve sınıflar dersine başlamadan önce öğrendiğim konuları biraz sindirmem ve bu sürede aldığım bir e-ticaret sitesi tasarımı yapmam için biraz daha fazla ara vermiş gibi olduk.

Ama merak etmeyin şimdi nesneye yönelik programlama dillerinin en kritik konularına değineceğiz ve bu kısmı iyi anlarsak mantığı kafamızda oluşturarak kolayca kodlarımızı yazabileceğimizi düşünüyorum.

Aslında class ve fonksiyon derslerini C# konularının altında mı incelemem gerekiyor yoksa Unity altında mı incelemem gerekiyor karar vermede gerçekten zorlandım. Nesne yönelimli programlama için C# kullanıyoruz ama arada göreceğimiz sınıf ve fonksiyon yapıları da Unity’e özel. Ama sonra asıl amacın aldığım eğitimin bir düzen şeklinde olması ve sonradan takip edecek arkadaşların ders ders takip etmesinin daha kolay olacağını düşündüğüm için bu şekilde ilerlemeye karar verdim.

Sizde doğru bir tercih mi?
0
Hızlı Yorum...x

Arkadaşlar aşağıda belirttiğim konuda öğrendiğimiz Temel C derslerinde sadece değişkenler, operatörler ve döngüler gibi her yazılım dilinde olan ve programlama dili öğrenmek isteyen arkadaşların genelde bu kısma kadar öğrendikten sonra devam etmeyip kendini geliştirmedikleri yere geldik. O yüzden önceki konuyu iyice kavradıysanız şimdi asıl meseleye gelelim.

Eğer ben zaten C#’da sınıfları, fonksiyonlar biliyorum diyorsanız direk Unity’e özel sınıfları öğrendiğim kısma atlayabilirsiniz.

Unity Temel Fonksiyonları

Küçükten büyüğe doğru ilerleyeceğim için Unity’de önce fonksiyonlar konusuna gireceğim.

Fonksiyon Nedir?

Fonksiyon değer alabilen ve bize geri bir değer döndürme yetisine sahip olan kod parçacıklarıdır. Programlama dillerinde daha çok hesaplama yapması ve kod fazlalığını azaltması için kullanılan yapılardır. Genellikle fonksiyon olarak tabir edilseler de metot olarak da geçmektedirler.

Biraz kaba bir tabir oldu ama örneklerle daha iyi anlayacaksınız. O zaman kısa yoldan hem değer alan, hem değer döndüren hem de bir fonksiyon çağırma örneğine göz atalım.

public int SayilarinToplami(int birinciSayi, int ikinciSayi)
{
     int toplam = birinciSayi + ikinciSayi;
     return toplam;
}

Debug.Log(SayilarinToplami(5, 2));

Örnekte 1. satırda int olduğu gibi fonksiyonlar döndürecekleri değişkenin türünde başlamalılar. Fonksiyon döndüreceği türe göre string, int, double vb değişken isimleriyle başlatabiliriz. Biz bir matematik işlemi yaptıracağımız için ve bize bir Integer değer vereceği için int olarak tanımladık.

Diğer bir olasılık ise eğer fonksiyon bir değer dönmeyecekse void olarak tanımlanırlar. Bu şekilde fonksiyon kendi içinde başlar ve biter yani dışarıya bir değer dönmezler.

Örneğimiz dönelim. Sonrasında ise int SayilarinToplami(int birinciSayi, int ikinciSayi) ile fonksiyona “SayilarınToplami” ismini verdik ve fonksiyon içerisine isimleri birinciSayi ve ikinciSayi olan 2 adet değişken alabileceğinin bilgisini verdik.

Sonrasında ise fonksiyona gelen iki değişkenin toplamını alarak toplam isminde bir fonksiyona atadık. Son olarak ise sonucu return toplam komutu ile fonksiyonun dışarısına gönderdik.

Unity’de console ekranına log olarak bastırmak için ise Debug.Log(SayilarinToplami(5, 2)); yazdık. Burada daha önceden tanımladığımız bir fonksiyonu ya da varsayılan bir fonksiyonu çağırmak için fonksiyon ismi ve alacağı değişkenleri verdiğimiz yapıyı yani SayilarinToplami(5, 2) yapısını kullandık.

Anlamı ise, burada SayilarinToplami isminde bir fonksiyon varmış ve iki adet sayı gönderdiğinde topluyormuş. O zaman biz bu fonksiyon 5 ve 2 sayısını gönderelim ve toplasın.

Aslında oldukça basit, size şöyle güzel bir örnek vermeye çalışayım.

Vize ve final notlarının hesaplayarak ortalamayı veren bir formülünüz var. Siz kendiniz için hesapladınız ama bir arkadaşınız benim içinde hesaplar mısın dedi. Sizde aynı arkadaşınız için aynı hesaplamaları yaparak notunu söylediniz. Sonra bir kaç kişi daha istedi. Herkes için tek tek hesaplama yapmak yerine bir fonksiyon yazıyorsunuz ve içerisine vize ve final notunu yazdıktan sonra otomatik olarak kendisinin hesaplamasını istiyorsunuz.

Mesela Unity üzerinden bir script dosyası oluşturarak scripti açtığımızda karşımıza void Start(){} ve void Update(){} şeklinde hazır iki tane tanımlama görmüştük. İşte bunlar aslında Unity için varsayılan olarak daha önceden tanımlanmış değer dönmeyen void fonksiyonlar oluyorlar.

Peki bu ekranda gördüğümüz public class TemelCDersleri : MonoBehaviour yazısı ne demek diye soruyorsanız işte bunlarda sınıflar oluyor çünkü her fonksiyon bir sınıfın içerisinde olmak zorundadır.

public kelimesi bir erişim belirleyicidir ve her sınıfın bir erişim belirleyicisi bulunmaktadır. O yüzden Class- Sınıf yapısına detaylıca girmeden önce erişim belirleyicileri inceleyelim.

Erişim Belirleyiciler – Access Modifiers

Kısaca özetlemek gerekirse oluşturacağımız bir sınıfa, sınıfın içerisindeki elemanlara ya da sınıfın içindeki fonksiyonlara programımızın hangi bölümlerinden erişebileceğimizi belirlememizi sağlayan anahtar kelimelerdir.

C#’da 5 adet erişim belirleyici bulunmaktadır. Bular public, protected, internal, protected internal, private fakat biz Unity üzerinde public, protected ve private olanları ağırlıklı olarak kullanacağız.

Şimdi her erişim belirleyicisinin hangi yetkilere sahip olduğunu yazacağım ama çoğu tanımlama sınıf ve miras alma gibi tabirler kafanızı karıştırmasın ilerleyen aşamalarda hepsine detaylıca değineceğiz.

public : Bir sınıfı public olarak tanımlarsanız, yazdığınız programın her yerinden sınıfın içinde tanımlı olan değişkenlere ve fonksiyonlara erişebilirsiniz yani herhangi bir kısıtlama yoktur.

protected : Bir sınıfı protected olarak tanımlarsanız eğer, sınıfın içinde yer alan tüm değişken ve fonksiyonlara sadece o sınıf içerisinden ya da o sınıfı miras alan yani türetilmiş diğer sınıflardan erişebilirsiniz.

private : Bir sınıf private olarak tanımlarsanız eğer sınıf içerisinde yer alan değişken ve fonksiyonlara sadece o sınıf içerisinden erişilebilir. En katı erişim belirleyicidir.

internal: Dışarıdan erişim olmayıp sadece bulunduğu proje için erişime izin verir.

protected internal : protected erişim belirleyicisi gibi sadece mevcut sınıfın içerisinden ve miras alan sınıfın içerisinden erişilebilir. Fakat protected için aynı projede olma şartı varken protected internal için yoktur.

C#’da tecrübeli olan arkadaşlar internal ve protected internal için bize biraz detaylı bilgi verebilirler.
0
Hızlı Yorum...x

Class (Sınıf) Nedir?

Sınıflar sayesinde kod tekrarını azaltmaya, okunabilirliği arttırmaya ve daha kolay geliştirme yapmamızı sağlayan yapılardır. Ayrıca erişim belirleyicileri kullanarak kod güvenliğinide sağlamamıza imkan verir.

Bir sınıfın içerisinde çok sayıda değişken, fonksiyon hatta başka sınıflar tanımlayarak ortak özellikleri bir araya toplayarak nesne oluşturabiliriz. Yani sınıflardan oluşturulan veri yapısına nesne diyoruz.

Integer, String gibi veri yapılarını yani değişkenleri görmüştük. Class yani sınıflar ise sizin oluşturduğunuz ve içerisinde farklı veri yapıları saklayabildiğiniz bir değişken yanı aslında bir yapı gibi düşünebilirsiniz. Sınıflar için tam anlamıyla Nesne yönelimli programlamaya girişin ilk adımı diyebiliriz.

Şimdi class nasıl oluşturulur bir inceleyelim. Fakat bu kısımdan sonra biraz Unity üzerinden devam edeceğiz.

Unity oyun motorunu açıyoruz ve daha önceki derslerde Project bölümünde Assets klasörü içerisine Scripts isminde oluşturduğumuz klasörün içerisinde yeni bir C# Script dosyası ekliyoruz. İsmini “BirinciSinif” olarak belirledim. Aslında bu oluşturduğumuz C# Script dosyası ile bir sınıf oluşturmuş olduk.

İsimlendirme yaparken ilk harfleri büyük yapmayı unutmayın. Sınıf isimlerine her zaman büyük harf ile başlanması tavsiye edilir.

Unity sınıf için script oluşturma.

Şimdi ekranda oluşturduğunuz “BirinciSinif” dosyasına tıkladığınızda aşağıdaki gibi bir görüntü ile karşılaşacaksınız. Açılan ekranda gördüğünüz public class BirinciSinif : MonoBehaviour kısmı aslında ilk oluşturduğumuz sınıf oluyor.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BirinciSinif : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

Şimdi bu dosyanın içerisinde gördüğünüz miras alınan MonoBehaviour sınıfını ve namespace(using) konularına sonradan değineceğimiz için içerisinde olan tüm kodları silerek kendimiz baştan yazalım.

public class BirinciSinif 
{ 

}

Tahmin edebiliyorsunuzdur ama ben yine de yazayım. Yukarıda ki sınıf oluşturma kalıbında public bir erişim belirleyici, class ise sınıf oluşturulurken yazılması gereken zorunlu bir anahtar kelime. Son olarak is sınıf ismi ve süslü parantezlerimiz.

Böylece erişime açık olan(public) ve ismi “BirinciSinif” olan bir class yani sınıf oluşturduk.

Şimdi içerisine üç adet fonksiyon ekleyelim. Erişim belirleyiciler test edebilmemiz için her fonksiyona farklı bir erişim belirleyici ekledim.

using UnityEngine; olarak eklediğimiz kısım Unity Oyun programının temel fonksiyonlarını çalıştırmamız için gerekli olan namespace oluyor. Örneğin fonksiyonlar içinde yer alan Debug.Log kısmını ekleyebilmek için bu UnityEngine namespace’i eklememiz gerekiyor. Namespace kısmına ilerleyen süreçlerde değineceğiz.

using UnityEngine;


public class BirinciSinif
{

    public void BirinciFonksiyon()
    {
        Debug.Log("Birinci Sınıftan Birinci Fonksiyon Çalıştı");
    }

    provoid IkinciFonksiyon()
    {
        Debug.Log("Birinci Sınıftan İkinci Fonksiyon Çalıştı");
    }

    private void UcuncuFonksiyon()
    {
        Debug.Log("Birinci Sınıftan Üçüncü Fonksiyon Çalıştı");
    }

}

Bir sınıfın nasıl oluşturulacağını öğrendik ve daha önceden öğrendiğimiz gibi içerisine fonksiyonlar ekledik. Peki elimize ne geçti şimdi diye sorduğunuzu tahmin edebiliyorum.

Unity üzerinde yapacağınız neredeyse tüm işlemler sınıflar aracılığıyla gerçekleşir. Şöyle düşünün mesela bir tane sınıfınız var ve ismi tanklar içinde de ateş etme fonksiyonu yer alıyor. Siz oyununuzun bir aşamasında tanklar sınıfına gel ve ateş et fonksiyonunu kullan diyebileceksiniz.

Tabi bunu sadece tek bir sınıf oluşturarak yapmıyoruz. Sonuçta “birinciSinif” içerinde yer alan fonksiyonlara farklı bir sinif içinden erişmemiz gerekiyor. Bu işleme ise “miras alma” denmektedir.

Miras Alma Nedir?

Bir sınıfın özelliklerini farklı bir sınıf üzerinde kullanmamızı sağlayan özelliğe “Miras Alma” denmektedir.

Yukarıdaki örneğe devam ederek, şimdi aynı şekilde Unity yeni bir C# Script oluşturalım ve ismini “IkinciSinif” verelim ve Visual Studio ile dosyayı açalım.

using UnityEngine;

public class IkinciSinif : BirinciSinif
{
  
  void MirasAlma()
    {
        BirinciFonksiyon();
        IkinciFonksiyon();
    }

}

Yukarıdaki örnekte görüldüğü gibi miras alma işlemi için public class IkinciSinif : BirinciSinif şeklinde bir tanımlama yapmamız gerekiyor. “IkinciSinif” isminin sonuna iki nokta üst üste koyarak özelliklerini almasını istediğimiz sınıf ismini yani “BirinciSinif” yazdık. Böylece ikinci sınıfa, birinci sınıf içinde yetkin olanları kullanabilirsin demiş olduk.

Test edebilmemiz için MirasAlma() isminde bir fonksiyon oluşturduk. İçerisinde bu dosyada olmamasına rağmen BirinciFonksiyon(); yazarak “BirinciSinif” içerisindeki bir fonksiyonu çağırabildik. Böylece diğer sınıftaki özellikleri miras almış olduk.

Eğer fark ettiyseniz “BirinciSinif” içerisinden sadece “BirinciFonksiyon” ve “IkinciFonsiyon” ismindeki fonksiyonları çağırmaya iznimiz var. Bunun sebebi “BirinciFonksiyon” için public ve protected erişim belirleyicilerini kullanmış olmamız.

Peki diğer “UcuncuFonksiyon” ismindeki fonksiyona neden erişemedik?

“UcuncuFonksiyon” ismindeki fonksiyonda private erişim belirleyicisi olduğu için miras alsak bile bu fonksiyona erişemiyoruz. Private yazarak o sınıfa özel olduğunu belirttik.

Şimdi Unity üzerinde bir test yapalım. Oluşturduğumuz her iki sınıf kalsın ve yeni bir dosya oluşturalım ve ismini “BaslangicSinifi” yapalım.

using UnityEngine;

public class BaslangicSinifi : MonoBehaviour
{
    IkinciSinif IlkNesne= new IkinciSinif();
  
   
    void Start()
    {
        
        IlkNesne.BirinciFonksiyon();
        IlkNesne.MirasAlma();     
        
    }

}

Bu sınıfın içeriğini ise yukarıdaki gibi düzenledik. Fark etmiş olacaksınız ki Unity Oyun Motoru otomatik olarak public class BaslangicSinifi : MonoBehaviour isminde bir kısım ekliyor. İki noktayı görünce hemen bunun başka bir sınıftan miras aldığını anlamamız gerekiyor.

Peki IkinciSinif IlkNesne= new IkinciSinif(); şeklide bir yapı gördünüz. Bu yapı ile bir nesne oluşturmuş oluyoruz. Eğer new kelimesini görüyorsanız bir nesne oluştuğunu anlayabilirsiniz. Şahsen benim ilk başlarda en zorlandığım kısım burasıydı.

Bunu şu şekilde düşünebilirsiniz. Örneğin nasıl bir Integer, String, List vs değişken oluşturuyorsak bu da bizim kendi oluşturduğumuz bir yapı diye düşünün. Şu sınıfa ait bir nesne oluşturuyorum ve bu nesne ile o sınıfın istediğim özelliklerine erişebiliyorum.

Peki bu işlemi yapınca elimize ne geçti onu inceleyelim

void Start() fonksiyonu içerisine yazdığımız iki adet satır göreceksiniz. Oluşturduğumuz IlkNesne isimli nesnemizin sonuna nokta koyduktan sonra IlkNesne.MirasAlma(); yazarak “IkinciSinif” içerisindeki “MirasAlma” fonksiyonuna eriştik. Çünkü nesnemiz direk o sınıftan oluşturulmuştu.

Peki nesne.BirinciFonksiyon(); yazarak “BirinciSinif” içerisindeki BiriciFonksiyon’a nasıl eriştik? Birinci sınıftan türetilmemiş ve nesne oluşturulmamış…

Bunun sebebi IkinciSinif‘in BirinciSinif‘tan türetilmiş olması. Yani IkinciSinif, BirinciSinif’i miras aldığı için BirinciSinif içinde yer alan özelliklere de erişebiliyoruz.

MonoBehaviour sınıfı Unity için zorunlu olan sınıflardan biri. Eğer “BaslangicSinifi” script dosyasını Unity üzerindeki bir objeye atamak istiyorsak MonoBehaviour sınıfından türetilmiş olması zorunludur.

Miras alma konusunu hem yazarak hem de en kolaya inmeye çalışarak anlatmaya çalıştım. Bu sebeple biraz karışık olduysa, kafanıza takılanı yorum yazarak sorabilirsiniz.
1
Hızlı Yorum...x

Unity Namespace ve Using Nedir?

Şimdiye kadar fonksiyon ve sınıfları görmüştük. Bunun üzerinden giderek bir örnek vereceğim. Tüm fonksiyonlarımız bir sınıfın içinde yer alıyordu yani sınıf fonksiyonları kapsıyordu. Namespace ise sınıfları tutan bir yapıdır. Hiyerarşi olarak Namespace > Class > Function olarak düşünebilirsiniz.

Projelerimiz büyüdükçe ve birden fazla kişinin kod yazması gereken durumlar olduğu zaman karışıklık oluşması çok muhtemeldir. Çünkü projelerimiz büyüdükçe sınıfların isimleri ya da fonksiyonların isimleri çakışabilmektedir. Bu durumlarda kod karışıklığının önüne geçmek ve kod okunabilirliğini arttırmak için Namespace’ler kullanılır.

Şimdi oyun üzerinden bir örnek vererek Unity Namespace’ler ne işe yarıyor bir bakalım.

Diyelim ki oyunda bir karakterimiz var. Karakterimizin ise sağlık ve vuruş gücü özellikleri olsun.

Karakterimiz için public class Karakter isminde bir sınıf oluşturduk. Özellikleri için ise public void Saglik() ve public void VurusGucu() isminde iki adet fonksiyon oluşturduk.

public class Karakter
{

    public void Saglik()
    {

    }

    public void VurusGucu()
    {

    }

}

Buraya kadar daha önceden öğrendiğimiz bir yapı olduğu için problemimiz yok. Peki oyundaki bir durum için, mesela oyunun farklı bir seviyesinde farklı bir karakterle devam etmeniz gerekiyor. Bu durum için aynı sınıfı tekrar yazmamız gerektiğini düşünün.

Sınıf ismini değiştirip, tekrar oluşturarak yapabiliriz. Ama ileride sınıf seçimlerinde kafamızı karıştıracak ve isim bulma konusunda zorlanacağız. Aşağıdaki örnekte aynı isme sahip iki adet sınıf eklediğimizde hata alacağız. İşte bu tarz durumlarda Namespace’ler devreye giriyor.

public class Karakter
{

    public void Saglik()
    {

    }

    public void VurusGucu()
    {

    }

}

public class Karakter // Sınıf isimler aynı olduğu için hata verecektir.
{

    public void Saglik()
    {

    }

    public void VurusGucu()
    {

    }

}

Şimdi Unity üzerinde yeni bir C# Script dosyası oluşturalım ve ismini “KarakterYonetimi” verelim. İçerisine ise aşağıdaki kodu yazalım.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace BirinciLevel
{
    public class Karakter
    {

        public void Saglik()
        {
            Debug.Log("Birinci Level Karakter Sağlık Fonksiyonu Çalıştı");
        }

        public void VurusGucu()
        {
            Debug.Log("Birinci Level Karakter Vuruş Gücü Fonksiyonu Çalıştı");
        }

    }
}

namespace IkinciLevel
{
    public class Karakter
    {

        public void Saglik()
        {
            Debug.Log("İkinci Level Karakter Sağlık Fonksiyonu Çalıştı");
        }

        public void VurusGucu()
        {
            Debug.Log("İkinci Level Karakter Vuruş Gücü Fonksiyonu Çalıştı");
        }

    }
}

namespace anahtar kelimesi ile iki farklı namespace oluşturduk. Böylece aynı dosya içinde iki adet “Karakter” isminde sınıf olmasına rağmen bize hata vermeyecektir.

Şimdi test için Unity Oyun Motoru üzerinde yeni bir C# Script dosyası oluşturalım ve ismini “BaslangicDosyasi” yapalım.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BirinciLevel;


public class BaslangicDosyasi : MonoBehaviour
{

    Karakter karakter = new Karakter();
    void Start()
    {
        karakter.Saglik();
    }

}

Yukarıda gördüğünüz gibi using BirinciLevel; şeklinde bir giriş yaptık. Oluşturduğumuz namespace’leri bu şekilde using anahtar kelimesi ile kullanabiliyoruz.

Burada hemen her dosya oluşturduğumuzda otomatik olarak gelen using UnityEngine; namespace dikkatinizi çekmiştir umarım. Demek ki UnityEngine‘de bir namespace’miş ve içerisinde MonoBehaviour isminde bir sınıf barındırıyormuş dememiz gerekiyor.

Örnekte ise using BirinciLevel; namespace’ini ekledikten sonra Karakter karakter = new Karakter(); koduyla bir nesne oluşturduk. Sonrasında ise karakter.Saglik(); yazarak oluşturduğumuz karakter nesnesinden saglik() fonksiyonuna eriştik.

Farklı sınıflarda fonksiyon isimlerinin aynı olması problem yaratmamaktadır. Ama ilerleyen süreçlerde kafanızı karıştıracağı için bana göre namaspace kullanımı en çok bu tarafta işe yarıyor. Mesela Düşman namespace içindeki vuruş gücü ya da karakteri namespace içindeki vuruş gücü gibi.

Şimdi sıra geldi Unity fonksiyon-methot larını incelemeye. Bu kısımdan sonra biraz daha Unity özelinde çalışıyor olacağız.

Unity Start ve Update Fonksiyonları

Oyunun ilk açıldığında, çalışması sırasında, bir obje pasif duruma ya da aktif duruma getirilme anında gibi birçok alanda bu fonksiyonları kullanmak zorundayız. Eğer Unity ile mobil oyun yapmak istiyorsak bu kısmı çok iyi anlamamız gerekiyor.

Bu yeni öğreneceğimiz fonksiyonlar unity ile ilk açtığımızda otomatik olarak oluşturulan MonoBehaviour sınıfına ait fonksiyonlar. MonoBehaviour sınıfının oldukça fazla sınıfı bulunuyor ama biz bu eğitimde işimize en çok yarayacak ve en çok kullanılan temel fonksiyonları inceleyeceğiz. Oyunumuzu yapma aşamasında ise diğer fonksiyonlara değindikçe eklemelerini gerçekleştireceğim.

Unity ile ilk C# Script dosyamızı oluşturduğumuzda otomatik olarak MonoBehaviour sınıfını eklediğini söylemiştik. Buna ek olarak void Start() ve void Update() isminde iki adet fonksiyonu da otomatik olarak ekliyor. İşte bu eklenen fonksiyonlar oyunun ilk başladığı andan kapanana kadar arka planda çalışan yapılardır. Hadi biraz detaylı bir şekilde inceleyelim.

Awake Fonksiyonu

private void Awake() fonksiyonu oyunun açılışında ilk olarak çalışan fonksiyondur ve sadece bir kere çalışır. Sahne de yer alan obje pasif durumda bile olsa eğer sonradan obje aktif duruma getirilirse çalışır.

Start() foknsiyonu Awake fonksiyonundan sonra çalışır.

 private void Awake()
    {
        Debug.Log("Awake fonksiyonu çalıştı.");
    }

OnEnable Fonksiyonu

Oyunun ilk açılışında çalışan Awake fonksiyonundan sonra çalışan ikinci fonksiyon private void OnEnable() fonksiyonudur. Fakat çalışması için objenin görünürlüğünün Enable durumda olması gerekmektedir.

Eğer oyun açılışında obje enable durumda ise ilk olarak OnEnable() fonksiyonu çalışır. Awake fonksiyonu gibi sadece oyun çalıştığında eğer obje enable durumda ise bir kere fonksiyon çalışır. Fakat obje tekrar pasif yapılıp aktif yapıldığında bir kere daha çalışacaktır. Yani obje her aktif duruma geldiğinde bir kere çalışır.

 private void OnEnable()
    {
        Debug.Log("OnEnable fonksiyonu çalıştı.");
    }

Start Fonksiyonu

void Start() fonksiyonu Awake ve OnEnable fonksiyonlarından sonra çalışır. Start fonksiyonunun çalışması için objenin aktif olması gerekir.

Awake ve OnEnable fonksiyonlarında olduğu gibi oyun çalıştığında sadece bir kere çalışan ve tekrar etmeyen fonksiyonlardır.

 void Start()
    {
        Debug.Log("Start fonksiyonu çalıştı.");

    }

Yukarıda bahsettiğim 3 fonksiyon oyunun açılışlarında çalışan fonksiyonlardı. Bu Awale, OnEnable ve Start fonksiyonlarını aynı anda ekleyip çalıştırdığımızda sonuç aşağıdaki gibi olacaktır.

Unity fonksiyonları çalışma sırası

Şimdi bahsedeceğim 4 fonksiyon ise oyunun çalışması sırasında sürekli olarak çalışan fonksiyonlardır.

FixedUpdate Fonksiyonu

FixedUpdate() fonksiyonu Update fonksiyonundan önce çalışan bir fonksiyondur. Yani hem Update() hem de FixedUpdate() içine bir kod yazarsanız önce FixedUpdate() çalışacaktır. Oyun başladıktan sonra update fonksiyonu gibi sürekli olarak çalışmaktadır.

private void FixedUpdate()
    {
        Debug.Log("FixedUpdate fonksiyonu çalıştı.");
    }

Update fonksiyonu konusundan sonra ayarlarının yapıldığı ekrandan bir görüntü paylaşacağım buradaki değerlerden, FixesUpdate ve Update arasındaki çalışma süreleri için mantık kurabilirsiniz.

Update Fonksiyonu

void Update() unity fonksiyonu oyun başladıktan sonra Awake, OnEnable ve Start fonksiyonlarından sonra otomatik olarak başlar ve sürekli olarak çalışır. En sık kullanılan update çeşidi olmasına rağmen bu fonksiyonun çok kullanılması arka planda sürekli çalışacağı için performans kayıplarına sebep olabiliyor.

private void Update()
    {
        Debug.Log("Update fonksiyonu çalıştı.");
    }

İlk düşündüğüm şey nasıl yani arka planda sürekli çalışacak şeklinde olmuştu. Belirli bir süre gibi değil de her frame de bir kere çalışacak şeklinde bir mantığı olduğunu öğrendim. Bir frame de bir saniye olduğu için her saniyede bir çalışıyor diye bu kısmı anladım. Ama ilerde kullandıkça daha iyi anlayacağımızı düşünüyorum.

Ek olarak update ve FixedUpdate için unity üzerinde süreleri ayarlayabileceğimiz bir panel bulunuyor.

Unity oyun motoru penceresinin üst kısmında yer alan Edit / Project Settings/ Time alanının içerisinde aşağıdaki gibi time ayarlarını göreceksiniz.

Unity Update ve FixedUpdate Ayarları Ekranı

Burada “Time Scale” olarak belirtilen kısım Update fonksiyonu için belirtilen kısımdır yani her 1 saniyede bir çalışıyor. Fixed “Timestep” alanı ise FixedUpdate için verilen değerdir. Yani Update fonksiyonunun 5 katı hızda çalışıyor.

LateUpdate Fonksiyonu

LateUpdate() ise sahnede olan her obje için geçerli olan Update fonksiyonları çalıştıktan sonra çalışır. Her frame için bir kere çalışmaktadır.

private void LateUpdate()
    {
        Debug.Log("LateUpdate fonksiyonu çalıştı.");
    }

OnDisable Fonksiyonu

OnDisable() fonksiyonu ise OnEnable gibi bir kere çalışan ve OnEnable fonksiyonun aksine obje pasif duruma geçtiği zaman çalışan bir fonksiyondur.

Obje her aktiften pasif durumuna geçtiğinde tekrar OnDisable komutu bir kereye mahsus çalışır.

private void OnDisable()
    {
        Debug.Log("OnDisable fonksiyonu çalıştı.");
    }

Eğer tüm bu unity fonksiyonları ekranda yazarken oyununuzu başlatırsanız göreceksiniz ki başlangıç sınıfına ait olan fonksiyonlar bir kere çalıştıktan sonra update sınıfında olan tüm fonksiyonlar sürekli olarak çalışmaya devam edecektir. Tüm sıralamayı aşağıda görebilirsiniz.

Unity başlangıç fonksiyonları çalışma sırası.

Unity Coroutine Fonsiyonu (IEnumerator )

Coroutine fonksiyonu yapı olarak Update fonksiyonuna benzeyen bir yapıdır. Fakat Update fonksiyonu haricinde, belirli bir süre sonra çalışmasını, belirli aralıklarda çalışmasını ya da istediğimiz zaman durdurabileceğimiz yapılardır.

Basit bir örnek vermek gerekirse, örneğin oyununuzda bir nesne var ve belirli sürelerde yer değiştirmesini ya da renk değiştirmesini istediğiniz zaman kullanabiliriz.

Şimdi en basit kullanım şekli olarak nasıl yazıldığına bir göz atalım.

void Start()
    {
        StartCoroutine(CoroutineTest());    
    }

    IEnumerator CoroutineTest()
    {
        Debug.Log("Başlangıç Kısmı");
        yield return null;
        Debug.Log("Bitiş Kısmı");
    }

Coroutine” kullanabilmemiz için IEnumerator CoroutineTest() ile başlamamız gerekiyor. Süslü parantezlerimizin içine ise yield return null; kalıbını yazdık. Bu kalıp bir kare bekle ve sonra çalış anlamına gelmektedir.

Tabi ki oluşturduğumuz coroutine yapısının kendi başına hiç bir hükmü bulunmuyor. Devreye girmesi için coroutine çağırmamız gerekiyor. Bunun için void Start() fonksiyonu içerisinde StartCoroutine(CoroutineTest()); yazmamız gerekiyor. StartCoroutine kalıbını kullanmamız gerekiyor.

Yukarıdaki kodu çalıştırdığımızda aşağıdaki gibi bir çıktı olacaktır. Bu çıktıdan anlayacağınız üzere bir sadece başlangıçla beraber bir kere çalışmış. Update fonksiyonu gibi değil de sanki Start fonksiyonu gibi gözükmüş olabilir. Şimdi nasıl şekillendirebileceğimizi biraz incleyelim.

Unity coroutine fonksiyonunun kullanımı

WaitForSeconds Kullanımı

Şimdi örneğimizi 3 saniyede bir çalışacak şekilde düzenliyoruz. Eğer aşağıdaki gibi yield return new WaitForSeconds(3); yazarsanız oyun başlar başlamaz Debug.Log("Başlangıç Kısmı"); kısmı başlayacak ve 3 saniye bekledikten sonra Debug.Log("Bitiş Kısmı"); kısmı çalışacak ve duracaktır.

IEnumerator CoroutineTest()
    {
        Debug.Log("Başlangıç Kısmı");
        yield return new WaitForSeconds(3);
        Debug.Log("Bitiş Kısmı");
    }

Tabi bu süreyi her seferinde coroutine içinden belirlememiz gerekmiyor. Aşağıdaki gibi bir değişken(float sure) oluşturarak start fonksiyonu içinden StartCoroutine(CoroutineTest(3)); diyerek süreyi gönderebiliriz.

 void Start()
    {
        StartCoroutine(CoroutineTest(3));    
    }

    IEnumerator CoroutineTest(float sure)
    {
        Debug.Log("Başlangıç Kısmı");
        yield return new WaitForSeconds(sure);
        Debug.Log("Bitiş Kısmı");
    }

Peki biraz daha şekillendirerek Update() fonksiyonu gibi kullanmaya başlayalım.

C# derslerinden while döngüsü konusuna bakmak için Temel C# Dersleri adresine göz atabilirsiniz.

While Kullanımı

Şimdi ise while döngüsüne sokarak sürekli çalışmasını sağlayacağız. while(true) yazarak önce “Başlangıç Kısmı” açıklamasını console ekranına basmasını ve her 3 saniyede bir ise “Bitiş Kısmı” açıklamasını basacaktır.

void Start()
    {
        StartCoroutine(CoroutineTest(3));    
    }

    IEnumerator CoroutineTest(float sure)
    {
        Debug.Log("Başlangıç Kısmı");
        while(true)
        {
            yield return new WaitForSeconds(sure);
            Debug.Log("Bitiş Kısmı");
        }  
    }

Bu kodun çıktısı ise aşağıdaki gibi olacaktır.

Unity coroutine fonksiyonu while döngüsü kullanımı

Coroutine Obje Oluşturmak

Tabi bu şekilde birden fazla coroutine yapımız olabilir ve farklı görevler vermiş olabiliriz. Bu şekilde de bir örnek inceleyelim ama madem obje yönelimli programlama ile yapıyoruz, o zaman obje oluşturarak yapalım.

 private IEnumerator ilkCoroutine, ikinciCoroutine;
    void Start()
    {
        ilkCoroutine = CoroutineBirinciFonk(3);
        ikinciCoroutine = CoroutineIkinciFonk(7);
        StartCoroutine(ilkCoroutine);
        StartCoroutine(ikinciCoroutine);
    }

    IEnumerator CoroutineBirinciFonk(float sure)
    {
        while(true)
        {
            yield return new WaitForSeconds(sure);
            Debug.Log("Birinci fonksiyon çalıştı");
        }    
    }

    IEnumerator CoroutineIkinciFonk(float sure)
    {
        yield return new WaitForSeconds(sure);
        Debug.Log("İkinci fonksiyon çalıştı.");
    }

İlk satırda private IEnumerator ilkCoroutine, ikinciCoroutine; gördüğünüz gibi “ilkCoroutine” ve “ikinciCoroutine” isimlerinde iki adet obje oluşturduk. Sorasında ise bu oluşturduğumuz objelere ilkCoroutine = CoroutineBirinciFonk(3); ve ikinciCoroutine = CoroutineIkinciFonk(7); şeklinde fonksiyonların atamasını yaptık.

Tabi aşağıda fonksiyonlarımızı oluşturduktan sonra son olarak ise StartCoroutine(ilkCoroutine); ve StartCoroutine(ikinciCoroutine); unity komutlarıyla coroutine fonksiyonlarımızı başlattık.

Coroutine fonksiyonlarını incelediğinizde ise her 3 saniyede bir “CoroutineBirinciFonk” içindeki “Birinci fonksiyon çalıştı” çıktısını basacağını ve 7 saniyede ise “CoroutineIkinciFonk” içindeki “İkinci fonksiyon çalıştı.” çıktısını basacağını görebilirsiniz. Unity oyun motoru console üzerindeki çıktısı ise aşağıdaki gibi olacaktır.

Coroutine için Unity Console Çıktısı

StopCoroutine() Kullanımı

Şimdiki örneğimizde ise klavyeden belirli bir tuşa bastığımızda durmasını sağlayalım. Fakat burada klavyeden komut almasını daha öğrenmediğimiz için sadece bir örnek vereceğim ama sonrasında bu konuya detaylıca değineceğiz.

private IEnumerator coroutineObjesi;
    private void Start()
    {
        coroutineObjesi = CoroutineFonksiyon(2);
        StartCoroutine(coroutineObjesi);
    }

    private IEnumerator CoroutineFonksiyon(float sure)
    {
        while (true)
        {
            yield return new WaitForSeconds(sure);
            Debug.Log("Birlikte Unity Öğreniyoruz");
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown("space"))
        {
            StopCoroutine(coroutineObjesi);
        }
    }

Yukarıdaki kod bloklarında 2 saniyede bir “Birlikte Unity Öğreniyoruz” bastırmasını söyledik. Bu kısma kadar zaten öğrenmiştik ama bu sefer update fonksiyonu içerisine Input.GetKeyDown("space") yazarak eğer klavyeden boşluk tuşuna basılırsa durmasını söyledik.

Belirttiğim gibi klavyeden komut alma derslerimiz sonra, burada öğrenmemiz gereken StopCoroutine(coroutineObjesi); yazımı.

Oluşturduğumuz bir Coroutine yapısını durdurmak için StopCoroutine yazıyoruz ve hangi Coroutine fonksiyonunu durdurmak istiyorsak ismini ekliyoruz.

StopAllCoroutines() Kullanımı

Peki birden fazla Coroutine oluşturduysak ve hepsini durdurmak için ne yapacağız. Tabi ki tek tek StopCoroutine yapabiliriz ama bunun yerine StopAllCoroutines(); yazarak tümCoroutine fonksiyonlarını aynı anda durdurabiliriz.

private void Update()
    {
        if (Input.GetKeyDown("space"))
        {
            StopAllCoroutines();
        }
    }

Invoke Fonksiyonları

Invoke fonksiyonu ve türevleri, belirlemiş olduğumuz farklı bir fonksiyonu belirttiğimiz sürelerde çalıştırabilir, durdurabilir ya da tekrar etmesini sağlayabiliriz.

Mantık olarak Coroutines fonksiyonuna benzemektedir. Fakat performans açısından Coroutines fonksiyonları daha iyi sonuç gösterdiği için tercih edilmeseler de kullanmamız gereken kısımlar olacağından dolayı öğrenmemiz bizim için bir artı olacaktır.

Invoke() Fonksiyonu

Invoke() fonksiyonu ile oyun açıldıktan sonra belirli bir süre sonra istediğimiz fonksiyonun çalışmasını sağlayabiliriz.

Yazım şekli olarak Invoke(“Çalışacak Olan Fonsiyon İsmi”, Süre) olarak kullanılmaktadır. Fonksiyon ismini tırnak işaretleri içerisinde string olarak süre bilgisini ise Float olarak vermemiz gerekiyor.

Basit bir örnek vererek detayına girelim.

void Start()
    {
        Invoke("AmacimizNedir", 1f);
    }

    public void AmacimizNedir()
    {
        Debug.Log("Birlikte Unity Öğreniyoruz.");
    }

Önce “AmacimizNedir” isminde bir fonksiyon oluşturdum ve Unity Oyun Motoru console ekranına basması için Debug.Log("Birlikte Unity Öğreniyoruz."); şeklinde bir log attım. Daha sonra ise Start() fonksiyonu içerisinde Invoke("AmacimizNedir", 1f); yazarak oyun açıldıktan 1 saniye sonra otomatik olarak “AmacimizNedir” isimli fonksiyonu çalıştır demiş oldum.

Çıktı olarak ise oyunumuz başladıktan 1 saniye sonra “Birlikte Unity Öğreniyoruz.” yazacaktır ve sonrasında çalışmayı durduracaktır.

InvokeRepeating() Fonksiyonu

Unity InvokeRepeating() fonksiyonu ise Invoke() fonksiyonunun özelliğine ek olarak istediğimiz fonksiyonu hangi aralıklarda tekrar çalışmak istediğimizi belirtebildiğimiz bir fonksiyondır.

Yazım şekli olarak InvokeRepeating(“Çalışacak Olan Fonsiyon İsmi”, Süre, Tekrar Süresi) olarak kullanılmaktadır. Fonksiyon ismini tırnak işaretleri içerisinde string olarak, süre ve tekrar çalışma bilgisini ise Float olarak vermemiz gerekiyor.

InvokeRepeating() içinde bir örnek verelim.

void Start()
    {
        InvokeRepeating("AmacimizNedir", 2f, 1f);
    }

    public void AmacimizNedir()
    {
        Debug.Log("Birlikte Unity Öğreniyoruz.");
    }

InvokeRepeating(“AmacimizNedir”, 2f, 1f); yazarak aslında şunu demiş oluyoruz. AmacimizNedir isminde bir fonksiyonu bul ve 2 saniye sonra(2f) otomatik olarak çalıştır ve ilk çalıştırma işleminden sonra her 1 saniyede(1f) bir tekrar çalıştır.

CancelInvoke() Fonksiyonu

Unity CancelInvoke fonksiyonu ise çalışmakta olan bir fonksiyonu durdurmak istediğimiz zaman kullanacağımız bir fonksiyondur.

Yazım şekli olarak CancelInvoke(“Fonksiyon İsmi”); kullanılmaktadır. Yani sadece fonksiyon ismini vermemiz yeterli olacaktır. Bunun için aşağıda klavyeden boşluk tuşuna basılırsa fonksiyonu durdur örneği vereceğim.

void Start()
    {
        InvokeRepeating("AmacimizNedir", 2f, 1f);
    }

    public void AmacimizNedir()
    {
        Debug.Log("Birlikte Unity Öğreniyoruz.");
    }

    private void Update()
    {
        if (Input.GetKeyDown("space"))
        {
            CancelInvoke("AmacimizNedir");
        }
    }

InvokeRepeating için verdiğimiz örnekteki “AmacimizNedir” fonksiyonu 2 saniye sonra başlasın ve her 1 saniyede bir tekrar çalışsın dedik. Sonra Update() fonksiyonu içerisine CancelInvoke("AmacimizNedir"); yazarak klavyeden boşluk tuşuna bastığında iptal olmasını sağladık.

IsInvoking() Fonksiyonu

IsInvoking() ile bir fonksiyon için Invoke özelliğinin çalışıp, çalışmadığını kontrol edebilirsiniz.

Yazım şekli olarak IsInvoking(“Fonksiyon İsmi”); olarak kullanılmaktadır. Genellikle if fonksiyonu içerisinde kullanmaya uygun bir yapısı vardır. Örnek vermek gerekirse aşağıdaki gibi bir if yapısı kurabilirsiniz.

if (IsInvoking("AmacimizNedir"))
   Debug.Log("Evet Çalışıyor");
else
   Debug.Log("Hayır Çalışmıyor");

Yani IsInvoking(“AmacimizNedir”) yazarak “AmacimizNedir” isminde bir fonksiyon Invoke edildiyse if bloğu içerisine gir edilmediyse else bloğuna gir demek istedik.

Bu arada yukarıdaki örnekteki gibi basit if .. else bloklarında süslü parantez kullanmadan yazabilirsiniz. Standart yazımı da aşağıya bırakayım kafanız karışmasın.

if (IsInvoking("AmacimizNedir"))
{
   Debug.Log("Evet Çalışıyor");
}
else
{
   Debug.Log("Hayır Çalışmıyor");
}