Gönderi

Godot Engine Eğitim Serisi - Bölüm 11: Ses, Müzik, UI (Kullanıcı Arayüzü), Animasyon

Godot 3D oyununa ses, müzik, UI ve AnimationPlayer ile animasyon ekleme. Autoload (Singleton) ile kesintisiz müzik sistemi. Türkçe.

Godot Engine Eğitim Serisi - Bölüm 11: Ses, Müzik, UI (Kullanıcı Arayüzü), Animasyon

3D oyun projemizin (“Squash the Creeps!”) temel mekaniklerini başarıyla kurduk. Zıplayabiliyor, düşmanları ezebiliyoruz ancak ölmüyoruz ve oyunumuz şu an sessiz, skorsuz ve biraz cansız görünüyor.

Bu final bölümünde; oyuncumuzun ölmesini sağlayacak, oyunumuza bir UI (Kullanıcı Arayüzü) giydirecek, sahne geçişlerinde kesilmeyen bir müzik sistemi (Autoload) kuracak ve son olarak AnimationPlayer ile 3D nesnelerimize hayat vereceğiz.


Oyuncunun Ölmesi ve Hitbox

Düşmanın üstüne basarsak onu eziyoruz, peki ya düşman bize yandan çarparsa? Bunu algılamak için oyuncuya bir Hitbox (Çarpışma Kutusu) eklemeliyiz.

  1. player.tscn sahnesini açın ve kök Player node’una bir Area3D çocuğu ekleyip adını MobDetector yapın.
  2. MobDetector‘a bir CollisionShape3D çocuğu ekleyin ve şeklini CylinderShape3D olarak ayarlayın.
  3. Silindirin boyutlarını oyuncu küresinden biraz daha geniş yapın ve üst kısma doğru konumlandırın. Böylece oyuncu zıpladığında silindir yukarıda kalır ve alttan geçen düşmanlar yanlışlıkla çarpışma tetiklemez.

  4. MobDetector‘ın özelliklerinden Monitorable ayarını kapatın, Monitoring ayarını açık bırakın. Collision Mask olarak sadece enemies katmanını seçin.

Sinyali Bağlamak ve Oyuncuyu Silmek

  1. MobDetector seçiliyken Signals sekmesinden body_entered sinyalini Player scriptinize bağlayın.

  2. player.gd dosyanızın en üstüne bir hit sinyali tanımlayın:

1
signal hit

Kodun Satır Satır Açıklaması:

  • signal hit: Tıpkı düşmanın ezildiğinde yaydığı sinyal gibi, bu sefer biz oyuncu için kendimiz yepyeni bir “hit” (hasar aldım / vuruldum / öldüm) sinyali tanımlıyoruz.
  1. Scriptin en altına da şu fonksiyonları ekleyin:
1
2
3
4
5
6
func _on_mob_detector_body_entered(body):
    die()

func die():
    hit.emit()
    queue_free()

Kodun Satır Satır Açıklaması:

  • func _on_mob_detector_body_entered(body):: Oyuncunun gölgesine bir Area3D (tehlike algılayıcı sensör) eklemiştik. Bu fonksiyon, o sensöre yabancı bir fiziksel cisim (body) değdiği an (entered) tetiklenir. Çarpan şey düşmandır (çünkü maskesinden sadece düşmanları dinlemesini söylemiştik, yeri veya havayı görmezden gelir).
  • die(): Eğer sensöre biri değdiyse aşağıya yazdığımız kendi die (ölüm) fonksiyonumuzu çalıştır.
  • func die():: Oyuncu öldüğünde olmasını istediğimiz şeyleri yazdığımız blok.
  • hit.emit(): Yukarıda oluşturduğumuz hit sinyalini yakar. “Yardım edin, vuruldum!” diye bağırır. Bunu yapıyoruz ki ana oyun döngüsündeki (Main) müzik dursun, ekranda Game Over yazsın.
  • queue_free(): Kendimizi oyun sahnesinden tamamen sileriz. Karakter yok olur.
  1. Son dokunuş olarak main.tscn ana sahnenizi açın, Player instance’ınızı seçip yeni oluşturduğunuz hit sinyalini Main scriptinize bağlayın. Oyun bittiğinde düşman akışını kesmek için fonksiyonun içini şöyle doldurun:
1
2
func _on_player_hit():
    $MobTimer.stop()

Kodun Satır Satır Açıklaması:

  • func _on_player_hit():: (Bu kod Main ana sayfasındadır). Oyuncu vurulup hit sinyalini yayınladığında otomatik tetiklenmesi için bağladığımız özel durumdur.
  • $MobTimer.stop(): Oyuncu öldüğüne göre artık yeni düşman yağdırmanın anlamı yok, dolayısıyla o fırını durdur ki ekrana akın etmeyi kessinler.

3D Oyuna UI (Kullanıcı Arayüzü) Giydirmek

Godot’da 2D veya 3D oyun yapıyor olmanız fark etmez; UI elemanları her zaman Control node’ları kullanılarak 2D düzlemde çizilir.

  1. main.tscn sahnenizi açın. Main node’una bir Control çocuğu ekleyin ve adını UserInterface yapın.
  2. İşlemleri kolayca yapabilmek için editörün üstünden 2D çalışma alanına geçin.

  3. UserInterface node’una bir Label çocuğu ekleyip adını ScoreLabel yapın ve metin (Text) olarak Score: 0 yazın.

Skor Yer Tutucusu ScoreLabel metni “Score: 0” olarak ayarlandı

  1. 3D dünyamızın arka planı parlak olduğu için beyaz yazı okunmayacaktır. Inspector’dan Theme Overrides > Colors altından Font Color’ı siyah yapın.

Skor Yazı Rengi Font Color siyah yapıldı — beyaz 3D sahnede okunabilir

  1. Oyuncu öldüğünde çıkacak “Yeniden Oyna” ekranı için UserInterface node’una bir ColorRect ekleyip adını Retry yapın. Tüm ekranı kaplaması için Anchor ayarlarından Full Rect seçin ve rengini koyu/yarı saydam siyah yapın.

Full Rect Seçeneği Full Rect ile node tüm viewport’u kaplar

Retry Renk Seçici Koyu, yarı saydam renk — arka planı kararttır

  1. Bunun altına da bir Label ekleyip Press Enter to retry. yazın ve ekrana ortalayın.

Skoru ve Yeniden Oynamayı Koda Dökmek

ScoreLabel node’una bir script ekleyin. Düşmanların squashed sinyalini bu scripte bağlayarak skoru güncelleyeceğiz:

1
2
3
4
5
6
extends Label
var score = 0

func _on_mob_squashed():
    score += 1
    text = "Score: %s" % score

Kodun Satır Satır Açıklaması:

  • extends Label: Bu kodun, ekranda yazı göstermeye yarayan bir “Label” (Etiket) düğümüne ait olduğunu belirtir. Arayüzümüze eklediğimiz ScoreLabel‘dir.
  • var score = 0: Oyun ilk başladığında oyuncunun puanını tutacağımız değişkeni yaratır ve 0 yaparız.
  • func _on_mob_squashed():: Düşmanlar ezildiğinde yaydıkları squashed sinyalinin yakalandığı fonksiyondur. Okyanustaki herhangi bir düşman “Ben ezildim!” diye bağırdığında bu etiket onu duyar.
  • score += 1: Düşman ezildiğini duyduğumuza göre, skoru 1 artırırız.
  • text = "Score: %s" % score: Etiketin ekranda görünen yazısını (text) güncelleriz. %s kısmı bir yer tutucudur. Cümlenin sonundaki % score ifadesi ile, bu yer tutucunun yerine güncel skorumuzu yazarız. Böylece ekranda “Score: 1”, “Score: 2” şeklinde sürekli güncellenen bir yazı görürüz.

main.gd scriptinizi de güncelleyerek oyun bitişi ve yeniden başlama mantığını kurmalısınız. Oyuncu öldüğünde $UserInterface/Retry.show() ile karartma ekranını gösterebilirsiniz. Yeniden başlamak için _unhandled_input() fonksiyonu içinde oyuncunun Enter tuşuna basmasını dinleyip, get_tree().reload_current_scene() komutuyla sahneyi sıfırdan yükleyebilirsiniz.


Kesintisiz Müzik İçin Autoload (Singleton) Kullanımı

Eğer müziği doğrudan Main sahnesine eklerseniz, oyuncu her öldüğünde ve sahne reload_current_scene() ile yenilendiğinde müzik de başa döner. Müziğin arka planda sürekli çalması için Autoload (Singleton) kullanmalıyız.

  1. Yeni boş bir sahne oluşturun ve bir AudioStreamPlayer ekleyip adını MusicPlayer yapın.

MusicPlayer Node AudioStreamPlayer — MusicPlayer olarak adlandırıldı

  1. Ses dosyanızı (örneğin House In a Forest Loop.ogg) Stream alanına sürükleyin ve Autoplay (Otomatik Oynat) ayarını aktif edin.

Müzik Node Özellikleri Stream atandı ve Autoplay açık

  1. Sahneyi music_player.tscn olarak kaydedin.
  2. Üst menüden Project > Project Settings > Globals > Autoload sekmesine gidin. Kaydettiğiniz bu sahneyi seçip Add butonuna tıklayın.

Autoload Kaydı music_player.tscn Autoload olarak kaydedildi

Tebrikler! Artık bu node oyun başladığında arka planda global olarak yüklenecek ve sahneler değişse bile müzik asla kesilmeyecek.


AnimationPlayer ile 3D Objeleri Canlandırmak

Karakterimiz hareket ediyor ama sadece kayıyor gibi duruyor. Godot’nun güçlü AnimationPlayer node’unu kullanarak ona süzülme (float) animasyonu ekleyelim.

  1. player.tscn sahnesini açın ve Player node’una bir AnimationPlayer çocuğu ekleyin.

Animasyon Oynatıcı Dock Animation dock: araç çubuğu, track editörü ve zaman çizelgesi

  1. Ekranın altında açılan Animation panelinden Animation > New diyerek float adında bir animasyon oluşturun.
  2. Animasyon süresini 1.2 saniye yapın, Autoplay ve Loop (Döngü) butonlarını aktif hale getirin.

Autoplay ve Loop Her ikisi de etkin olmalı

Animasyon Süresi Animasyon süresi 1.2 saniye

Keyframe (Anahtar Kare) ve Easing Eğrileri

Animasyonları hazırlarken Player veya Pivot node’unu değil, doğrudan 3D modelimiz olan Character node’unu hareket ettireceğiz.

  1. Zaman çizelgesinde 0.3 saniyeye gelip Character’in Position (Konum) özelliğine, 0.1 saniyeye gelip Rotation (Döndürme) özelliğine birer keyframe (anahtar) ekleyin.
  2. 0.5 saniyeye gelip karakteri Y ekseninde yukarı taşıyın ve biraz öne eğerek (Rotation X: 8) tekrar keyframe alın.
  3. 1.2 saniyede karakteri tekrar başlangıç noktasına yakın bir yere indirip geriye yatırarak (Rotation X: -9) bitiş keyframe’lerini ekleyin.

💡 İpucu: Animasyonun robotik görünmemesi için eklediğiniz keyframe’leri farenizle seçip Inspector panelindeki Easing eğrisini sağa (ease-in) veya sola (ease-out) kaydırabilirsiniz. Bu sayede hareket başlangıçta hızlı, bitişe doğru yavaşlayarak “doğal bir zıplama/süzülme” hissi verir.

Koda Bağlamak: Hıza Göre Animasyon İvmesi

Karakter yürürken animasyonun hızlanmasını istiyoruz. player.gd dosyanızdaki _physics_process fonksiyonuna şu kodları ekleyebilirsiniz:

1
2
3
4
5
6
7
8
# Karakter hareket ediyorsa animasyonu 4 kat hızlandır
if direction != Vector3.ZERO:
    $AnimationPlayer.speed_scale = 4.0
else:
    $AnimationPlayer.speed_scale = 1.0

# Zıplarken karakterin öne/arkaya eğilmesi
$Pivot.rotation.x = PI / 6.0 * velocity.y / jump_impulse

Kodun Satır Satır Açıklaması:

  • if direction != Vector3.ZERO:: Oyuncu herhangi bir tuşa basıp hareket ediyorsa (yön sıfır değilse).
  • $AnimationPlayer.speed_scale = 4.0: Yukarıda yaptığımız süzülme (float) animasyonunu, karakter yürürken daha canlı / koşturuyormuş gibi görünmesi için normalız hızının 4 katına (4.0) çıkarırız.
  • else:: Karakter hareket etmiyorsa (duruyorsa).
  • $AnimationPlayer.speed_scale = 1.0: Animasyon hızını normal (1x) değerine geri alırız ki karakter yerinde sakince soluklansın.
  • $Pivot...: Bu uzun satır son bir profesyonel dokunuştur. Karakterin zıplarken gerçekçi biçimde öne / arkaya eğilmesini sağlar.
    • velocity.y / jump_impulse: Karakter havalandığında hızı çok yüksektir, tepeye ulaştığında sıfırlanır, düşerken eksi olur. Bu değeri zıplama gücüne (20) bölerek -1 ile 1 arasında yumuşak bir oran (+ hızlanış, - düşüş) elde ederiz.
    • PI / 6.0: 30 derecelik bir açıyı temsil eder. (Animasyon çok abartılı yatmasın diye).
    • $Pivot.rotation.x = ...: Bulduğumuz bu oranı, 30 dereceyle çarpıp modelimizin (Pivot) ileri yatma ekseni olan X’e işleriz. Böylece karakter yukarı çıkarken şahlanır (geriye yatar), düşerken ise tam tersi peline doğru süzülerek süper kahraman inişi yapar.

Aynı animasyon yapısını Mob (Düşman) sahnenize de kopyalayabilir ve speed_scale değerini düşmanın rastgele hızına orantılayarak her düşmanın kendi hızına uygun animasyonla hareket etmesini sağlayabilirsiniz.


Konuyla ilgili Youtube videosu aşağıdadır…


Bölüm Özeti

Harika bir ilerleme kaydettiniz! Bu bölümde;

  • Katman (Layer) ve Maske (Mask) mantığıyla 3D çarpışma kurallarını yazdınız.
  • Vektör matematiğini (Dot Product) kullanarak “üstten basma” mekaniğini kodladınız.
  • Area3D kullanarak zıplamaya duyarlı bir “Game Over” hitbox’ı oluşturdunuz.

Serinin Özeti ve Final

Başardınız! 🎉 Sıfırdan başlayarak Godot Engine’in felsefesini, node ve sinyal yapılarını, 2D oyun dinamiklerini ve nihayetinde 3D dünyaya geçişi, fizik kurallarını ve animasyon sistemini öğrendiniz.

Bu süreçte yazdığınız kodlar ve kurduğunuz sahneler, bundan sonra yapacağınız çok daha büyük ve gelişmiş projeler için yıkılmaz bir temel oluşturdu. Godot Engine yolculuğunuzda kendi oyunlarınızı tasarlamaya artık tamamen hazırsınız.

Başka rehberlerde ve yeni oyunlarınızda görüşmek üzere, iyi geliştirmeler!


Bu gönderi CC BY 4.0 lisansı altındadır.