Godot Engine Oyun Mekanikleri - Bölüm 3: Candy Blast — Şeker Seçme ve Takas (Swap)
Bu bölümde şekerlere tıklama, seçili şekeri vurgulama ve iki komşu şekeri yer değiştirme mekaniklerini ekleyeceğiz. Bölüm sonunda iki komşu şekere tıklayarak yerlerini değiştirebileceksiniz.
3.1 — Piksel → Grid Dönüşümü
Bölüm 2’de grid koordinatlarını piksele çeviren _grid_to_pixel() yazmıştık. Şimdi tersini yapacağız: oyuncu ekrana tıkladığında, o piksel konumunun hangi hücreye denk geldiğini bulacağız.
game.gd dosyasında _grid_to_pixel() fonksiyonunun altına şu fonksiyonu ekleyin:
pixel: Vector2 → Ekrandaki tıklama pozisyonu (x, y piksel cinsinden). Godot fare tıklamalarını Vector2 olarak verir.
-> Vector2i → Tam sayı (integer) vektör döndürür. Grid koordinatları her zaman tam sayıdır (3. satır, 5. sütun gibi).
1
var col := int((pixel.x - GRID_OFFSET.x) / CELL_SIZE)
pixel.x - GRID_OFFSET.x → Tıklanan x pikselinden grid’in sol kenarını çıkarıyoruz. Böylece grid’in içindeki konumu buluyoruz. Örneğin pixel.x = 150, GRID_OFFSET.x = 24 → 150 - 24 = 126 piksel (grid’in solundan itibaren).
/ CELL_SIZE → Hücre genişliğine bölüyoruz. 126 / 64 = 1.97 → 1. sütunun (0’dan başlıyor) sonuna yakınız.
int(...) → Ondalık kısmı atarak tam sayıya çeviriyoruz. int(1.97) → 1. Bu bize sütun numarasını verir.
1
var row := int((pixel.y - GRID_OFFSET.y) / CELL_SIZE)
Aynı mantık dikey eksen için. GRID_OFFSET.y = 225 çıkarılır, CELL_SIZE‘a bölünür, tam sayıya çevrilir.
1
return Vector2i(row, col)
Sonucu Vector2i(satır, sütun) formatında döndürüyoruz. Dikkat:Vector2i‘nin x değeri satır, y değeri sütun olarak kullanılıyor. Bu grid tabanlı oyunlarda yaygın bir konvansiyondur.
cell.x < GRID_SIZE → Satır numarası 8’den küçük olmalı (0-7 arasında). Grid’in altına tıklanmışsa bu koşul sağlanmaz.
cell.y >= 0 ve cell.y < GRID_SIZE → Aynı kontrol sütun için.
and operatörü → Tüm koşullar true olmalı. Biri bile false ise sonuç false döner.
Oyuncu grid dışına (boş alana, skor bölgesine vs.) tıkladığında bu fonksiyon false döner ve tıklama yok sayılır.
3.3 — Seçim Değişkenleri
Oyuncunun hangi şekeri seçtiğini takip etmemiz gerekiyor. Dosyanın üst kısmındaki değişkenler bölümüne (var candy_textures satırının altına) şunları ekleyin:
1
2
varselected_cell:=Vector2i(-1,-1)# Seçili hücre (-1,-1 = seçim yok)varis_animating:=false# Animasyon sırasında girişi engelle
Açıklama:
selected_cell — Oyuncunun ilk tıkladığı hücrenin koordinatları. (-1, -1) demek henüz seçim yapılmamış.
is_animating — Şekerler yer değiştirirken (animasyon sırasında) oyuncunun tekrar tıklamasını engellemek için kullanacağız. Yoksa animasyon bitmeden tekrar tıklanırsa kaos olur.
3.4 — Tıklama Girişini Yakalama
Godot’da kullanıcı girişlerini yakalamak için _input() fonksiyonu kullanılır. Bu fonksiyon her tuş basımında, fare tıklamasında vs. otomatik olarak Godot tarafından çağrılır.
_input() → Godot’un yerleşik fonksiyonudur. _ready() gibi otomatik çağrılır ama fark şudur: _ready() bir kez çalışır, _input() ise her kullanıcı etkileşiminde çağrılır (fare hareketi, tıklama, tuş basımı vs.).
event: InputEvent → Godot, olayın ne olduğunu bu parametre ile bildirir. Fare tıklaması mı, klavye mi, dokunma mı — hepsi InputEvent‘in alt sınıflarıdır.
1
2
if is_animating:
return
Eğer bir animasyon devam ediyorsa (şekerler hareket ediyor, yer değiştiriyor vs.) fonksiyondan hemen çıkıyoruz. return fonksiyonu sonlandırır, altındaki kodlar çalışmaz. Bu sayede oyuncu animasyon bitmeden tıklayarak oyunu bozamaz.
1
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
event is InputEventMouseButton → is operatörü ile olayın tipini kontrol ediyoruz. Bu bir fare tuşu olayı mı? (Fare hareketi InputEventMouseMotion olurdu, onu istemiyoruz.)
event.pressed → Buton basıldı mı? Fare tuşu bırakıldığında da olay gelir (pressed = false), onu filtreliyoruz.
event.button_index == MOUSE_BUTTON_LEFT → Hangi fare tuşu? Sol tuş mu? Sağ tuş veya orta tuş olaylarını yok sayıyoruz.
Üç koşul and ile birleştirilmiş: hepsi true olmalı ki içeri girelim.
1
var cell := _pixel_to_grid(event.position)
event.position → Godot, fare tıklamasının ekrandaki piksel konumunu bu özellikle verir.
func_on_cell_clicked(cell:Vector2i)->void:# Boş hücreye tıklandıysa yok sayifgrid[cell.x][cell.y]=="":return# Henüz seçim yapılmamışsa → bu hücreyi seçifselected_cell==Vector2i(-1,-1):selected_cell=cell_highlight_cell(cell,true)return# Aynı hücreye tekrar tıklandıysa → seçimi iptal etifselected_cell==cell:_highlight_cell(cell,false)selected_cell=Vector2i(-1,-1)return# Komşu hücreye tıklandıysa → takas yapif_is_adjacent(selected_cell,cell):_highlight_cell(selected_cell,false)_swap_candies(selected_cell,cell)selected_cell=Vector2i(-1,-1)else:# Komşu değilse → önceki seçimi kaldır, yenisini seç_highlight_cell(selected_cell,false)selected_cell=cell_highlight_cell(cell,true)
Satır satır açıklama:
1
2
3
func _on_cell_clicked(cell: Vector2i) -> void:
if grid[cell.x][cell.y] == "":
return
Tıklanan hücre boşsa (şeker yok) → hiçbir şey yapma. İleride yerçekimi sonrası boş hücreler oluşacak, onlara tıklamayı engellemek gerekiyor.
selected_cell == Vector2i(-1, -1) → Henüz hiçbir şeker seçili değilse: bu hücreyi seçili olarak işaretle, görsel olarak vurgula ve fonksiyondan çık. Oyuncu şimdi ikinci tıklamayı yapacak.
Komşu değilse → önceki seçimi kaldır, yeni hücreyi seç. Oyuncu uzak bir yere tıkladığında seçimini o hücreye kaydırıyor.
Akış diyagramı:
1
2
3
4
5
6
7
8
9
10
11
Tıklama
│
├── Boş hücre? → Yok say
│
├── İlk seçim? → Seç ve vurgula
│
├── Aynı hücre? → Seçimi iptal et
│
├── Komşu mu? → TAKAS YAP
│
└── Komşu değil? → Eski seçimi kaldır, yenisini seç
3.6 — Komşuluk Kontrolü
İki hücrenin komşu olup olmadığını kontrol eden fonksiyon. Match-3 oyunlarında sadece yatay veya dikey komşular geçerlidir (çapraz değil).
CANDY_SCALE * 1.2 → Normal ölçeğin %20 büyüğü. 0.63 × 1.2 = 0.756. Şeker biraz büyüyerek “seçildi” hissi verir.
sprite.modulate → Sprite’ın renk çarpanıdır. Her piksel bu renkle çarpılır. Color(1.2, 1.2, 1.2, 1.0) → R, G, B kanalları 1.0’dan büyük olduğu için görsel normalden daha parlak görünür. Son değer 1.0 alfa (saydamlık) kanalıdır.
Klasik üç değişkenli takas algoritması. Bir geçici değişken (temp) kullanarak iki hücrenin içeriğini yer değiştiriyoruz. Örneğin A=”red”, B=”blue” ise: temp=”red” → A=”blue” → B=”red”. Bu mantıksal veri takasıdır, ekranda henüz bir şey değişmez.
2. Sprite referanslarını takas et:
1
2
3
4
var sprite_a: Sprite2D = candy_sprites[cell_a.x][cell_a.y]
var sprite_b: Sprite2D = candy_sprites[cell_b.x][cell_b.y]
candy_sprites[cell_a.x][cell_a.y] = sprite_b
candy_sprites[cell_b.x][cell_b.y] = sprite_a
candy_sprites dizisindeki referansları da takas ediyoruz. Grid verisi ile sprite referanslarının senkron kalması şart. Yoksa ileride yanlış sprite’ı silmeye veya taşımaya çalışırız.
3. Hedef pozisyonları hesapla:
1
2
var pos_a := _grid_to_pixel(cell_a.x, cell_a.y)
var pos_b := _grid_to_pixel(cell_b.x, cell_b.y)
Her iki hücrenin ekrandaki piksel pozisyonunu hesaplıyoruz. Dikkat: Grid verisi zaten takas edildiği için, pos_a artık sprite_b‘nin gitmesi gereken yer, pos_b ise sprite_a‘nın gitmesi gereken yer.
4. Tween animasyonu oluştur:
1
var tween := create_tween()
create_tween() → Godot’un Tween sistemidir. Bir değeri belirli sürede A noktasından B noktasına yumuşak geçişle değiştirir. Tek satır kodla profesyonel animasyon yaratır.
1
tween.set_parallel(true)
Bundan sonra eklenen tween işlemleri aynı anda (paralel) çalışsın. İki şeker eş zamanlı hareket etmeli, biri bitmeden diğeri başlamamalı.
tween_property() → Bir düğümün belirli özelliğini animasyonla değiştirir.
1. parametre: Hangi düğüm (sprite_a)
2. parametre: Hangi özellik ("position" — sprite’ın ekrandaki konumu)
3. parametre: Hedef değer (pos_b — gitmesi gereken piksel konumu)
4. parametre: Süre (0.2 saniye — hızlı ve akıcı)
.set_ease(Tween.EASE_IN_OUT) → Hareket eğrisi. Başta yavaş başlar, ortada hızlanır, sonda yine yavaşlar. Bu, doğal ve profesyonel bir hareket hissi verir. Sabit hız kullanılsa robot gibi görünürdü.
1
tween.set_parallel(false)
Paralel modu kapatıyoruz. Bundan sonra eklenen işlem, üstteki animasyonlar bittikten sonra çalışacak.
1
tween.tween_callback(_on_swap_finished)
tween_callback() → Tween tamamlandığında verilen fonksiyonu çağır. Yani iki sprite hedef pozisyonlarına ulaştığında _on_swap_finished() otomatik çalışır.
Animasyon tamamlandı, kilidi aç. Oyuncu artık tekrar tıklayabilir.
Not: Şu an takas sonrası eşleşme kontrolü yapmıyoruz. Eşleşme yoksa geri takas da yok. Bunları bir sonraki bölümde ekleyeceğiz. Şimdilik sadece takasın çalıştığını doğruluyoruz.
3.9 — Tam Kod (game.gd)
İşte game.gd dosyasının bu bölüm sonundaki tam hali: