Godot'ta 2D karakter hareketi üç parçayla kurulur: fiziksel gövde olarak CharacterBody2D, tuşları adlandıran Input Map eylemleri ve her fizik karesinde velocity değerini belirleyip move_and_slide() çağıran kısa bir GDScript. Bu yazıda klavyeyle dört yöne hareket eden tek bir karakter yapacaksın. Sahneyi kurup tuşları tanımlayacak, ardından çalışan kodu yazacaksın.
Sahne ağacı nasıl kurulur: CharacterBody2D, Sprite2D ve CollisionShape2D
Yeni bir sahne oluştur ve kök düğüm olarak CharacterBody2D ekle. Altına bir Sprite2D ve bir CollisionShape2D çocuk düğümü koy. Ağaç şöyle görünmelidir:
Player (CharacterBody2D)
├── Sprite2D
└── CollisionShape2D
Her düğümün işi ayrıdır:
| Düğüm | Görevi |
|---|---|
| CharacterBody2D | Hareket ve çarpışma mantığını taşır, scriptin bağlanacağı yerdir |
| Sprite2D | Karakterin görünüşünü verir |
| CollisionShape2D | Fiziksel gövdeyi tanımlar |
Sprite2D'in Texture özelliğine projendeki herhangi bir görseli sürükleyebilirsin. Görsel sadece ekranda görünür, fizik motoru onu hesaba katmaz.
Fizik açısından asıl önemli düğüm CollisionShape2D'dir. Şekil atanmamışsa düğümün yanında sarı bir uyarı üçgeni belirir. Çünkü CharacterBody2D'nin çarpışma hesabı yapabilmesi için bir şekle ihtiyacı vardır. Şekil olmadan fizik motoru karakteri bir gövde olarak tanıyamaz ve duvarlarla etkileşim beklediğin gibi çalışmaz.
Düğümü seçip Inspector'daki Shape özelliğine yeni bir şekil ata. Dikdörtgen ya da kapsül bu iş için uygundur. Şeklin boyutunu sprite'ın boyutuna yaklaşık uyacak biçimde ayarla. Birebir olması gerekmez, ama şekil sprite'tan çok küçük ya da büyük olursa karakter duvarlara havada takılıyor ya da içine giriyor gibi görünür.
Scripti kök düğüme, yani CharacterBody2D'ye bağlayacaksın. Düğüm adları ve bu yapı Godot'un 2D fizik düğümleri için bilinen standart düzendir. Farklı bir sürümde editör ayrıntıları biraz değişirse resmî dokümantasyondaki düğüm sayfalarına bakabilirsin.
Input Map'te dört yön eylemi nasıl tanımlanır?

Kodda tuşları tek tek ("A'ya basıldı mı?") sormak yerine eylem adlarıyla çalışacaksın. Tuş ataması editörde, kod ise eylem adıyla yapılır. İleride tuşu değiştirmek istersen koda dokunman gerekmez.
- Üst menüden Project > Project Settings penceresini aç.
- Input Map sekmesine geç.
- Üstteki kutuya eylem adını yaz ve Add düğmesine bas.
- Eklenen eylemin sağındaki + simgesine tıkla, açılan pencerede istediğin tuşa bas ve onayla.
- Her eylem için bir tuş daha eklemek istersen aynı adımı tekrarla.
Dört eylemi şu eşleştirmeyle tanımla:
move_left: A ve sol ok, kodda"move_left"move_right: D ve sağ ok, kodda"move_right"move_up: W ve yukarı ok, kodda"move_up"move_down: S ve aşağı ok, kodda"move_down"
Kritik nokta şudur: eylem adları koddaki metinlerle harfi harfine eşleşmelidir. Büyük küçük harf ve alt çizgi dahil her karakter önemlidir. Move_Left ile move_left farklı adlardır. Tek harflik bir fark karakterin hiç tepki vermemesine ya da konsolda hata mesajı görmene yol açar.
Godot'ta hazır ui_left, ui_right, ui_up ve ui_down eylemleri de bulunur ve istersen onları kullanabilirsin. Bu yazıdaki örnekte kendi adlarımızı kullanıyoruz, çünkü eylemin ne işe yaradığı adından okunuyor ve menü gezinmesi için ayrılmış ayarlarla karışmıyor.
Hareket kodu: Input.get_vector, velocity ve move_and_slide
CharacterBody2D düğümüne yeni bir script ekle ve içeriğini şöyle yaz:
extends CharacterBody2D
@export var speed: float = 300.0
func _physics_process(_delta: float) -> void:
var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = direction * speed
move_and_slide()
Satır satır ne olduğuna bakalım:
@export var speed: Hızı Inspector'da görünen bir ayara çevirir. Kodu açmadan değeri değiştirip deneyebilirsin. Birim, saniyede piksel olarak düşünülebilir.Input.get_vector(...): Dört eylemi okuyup x ve y bileşenli bir yön vektörü üretir. Parametre sırası sol, sağ, yukarı, aşağıdır. Hiçbir tuşa basılmıyorsa sonuç sıfır vektördür.velocity = direction * speed: Yönü hızla çarpar.velocity,CharacterBody2D'nin kendi özelliğidir ve karakterin bu kareki hızını tutar.move_and_slide(): Karakterivelocity'ye göre hareket ettirir ve çarpışmaları işler.
Kod _physics_process içinde çalışır, çünkü hareket ve çarpışma hesabı fizik karesinde yürür. Bu fonksiyon sabit aralıklarla çağrılır.
Sahneyi çalıştırdığında şunu görmelisin: bir yön tuşuna basınca karakter o yönde sabit hızla ilerler, tuşu bırakınca durur. İki tuşa birlikte basarsan çapraz hareket eder.
Bu örnek velocity'yi doğrudan bir özellik olarak kullanan yazım biçimidir. Farklı bir Godot sürümü kullanıyorsan move_and_slide ve velocity yazımı değişmiş olabilir. Bu durumda o sürümün resmî dokümantasyonundaki CharacterBody2D sayfasına bakman en doğrusu olur.
Konumu doğrudan değiştirmek yerine velocity neden tercih edilir?
Karakteri hareket ettirmenin en kısa yolu position += direction * speed * delta yazmaktır ve boş bir sahnede çalışır. Sorun, engel girince ortaya çıkar. Konumu doğrudan değiştirdiğinde fizik motoruna "bu hareket çarpışır mı?" diye sormamış olursun. Karakter duvarın içine ışınlanabilir, çarpışma tepkisi de devreye girmez.
velocity ile move_and_slide() akışında ise yalnızca gitmek istediğin hızı söylersin. CharacterBody2D hareketi çarpışmaları hesaba katarak uygular. Karakter bir engele çarpınca durmak yerine engelin yüzeyi boyunca kayabilir. Bu davranış resmî dokümantasyonda move_and_slide() için anlatılan temel işlevdir. Konumu elle değiştirmenin hiçbir yerde işe yaramayacağı anlamına gelmez; ama çarpışan bir karakter için doğru başlangıç velocity'dir.
Aşağıdaki tablo, projedeki üç katmanın nasıl bir zincir kurduğunu gösterir: sahne ağacı (şekil), giriş (eylemler) ve fizik (hareket).
| Parça | Görevi | Eksik ya da yanlışsa belirti |
|---|---|---|
| CollisionShape2D | Karaktere çarpışma alanı verir | Karakter duvarın içinden geçer; editörde düğüm yanında uyarı işareti görünür |
| Input Map eylemleri | Tuşları isimli eylemlere bağlar | Tuşlara basınca yön vektörü sıfır kalır; Output panelinde eylem bulunamadı hatası çıkabilir |
| get_vector ve velocity | Yönü üretir, hızla çarpıp hareket isteğine çevirir | Karakter yanlış yöne gider ya da beklenenden yavaş ve hızlı hareket eder |
| _physics_process ve move_and_slide | Velocity'yi her fizik adımında çarpışmaya göre uygular | Velocity değişir ama karakter yerinden kıpırdamaz |
Köşegen harekette hız neden artmaz?

Yatay ve dikey girişi ayrı ayrı okuyup velocity.x ile velocity.y değerlerini 1 ile beslediğinde, sağ ve yukarı tuşları birlikte basılı olduğunda yön vektörü (1, 1) olur. Bu vektörün uzunluğu yaklaşık 1,41'dir. Hız 300 ise karakter çapraz giderken saniyede yaklaşık 424 birim hızla gider, yani köşegen hareket kendiliğinden daha hızlı olur.
Çözüm, yön vektörünü normalleştirmektir: yönü korur, uzunluğunu 1'e sabitler. Elle yazman gerekirse direction.normalized() tam bunu yapar. Input.get_vector() ise dönen vektörün uzunluğunu zaten 1 ile sınırlar. Bu yüzden önceki bölümdeki kodda köşegen harekette ek bir hız artışı olmaz.
Mini deney: Hızı ekrana yazdırıp kendin sına. _physics_process içine tek satır ekle:
func _physics_process(delta):
var direction = Input.get_vector("sol", "sag", "yukari", "asagi")
velocity = direction * speed
print(velocity.length())
move_and_slide()
Karakter dururken Output panelinde 0 görürsün. Tek yöne giderken de, iki tuşa basıp köşegene giderken de çıktı yaklaşık speed değerini verir (hız 300 ise 300 civarı). Köşegende 424 görürsen yön vektörünü kendin üretiyorsun demektir.
Karakter hareket etmiyorsa kontrol edilecek noktalar
Önce doğru sahnenin çalıştığından emin ol: ana sahne ayarını kontrol et ve Çalıştır tuşuyla karakter sahnesinin açıldığını gör. Sonra şu üç noktaya bak:
- Eylem adları eşleşiyor mu? Belirti: karakter hiç kıpırdamaz ya da Output panelinde eylemle ilgili hata çıkar. Input Map'teki adla koddaki dizgeyi harf harf karşılaştır;
"sag"ile"sağ"farklı adlardır, büyük ve küçük harf de ayrımı bozar. - Script doğru düğümde mi, fonksiyon adı doğru mu? Belirti: kod hata vermez ama hiçbir şey olmaz. Script'in kök
CharacterBody2Ddüğümüne bağlı olduğunu Sahne panelinde script simgesinden doğrula; fonksiyonun adı da tam olarak_physics_processolmalı. - CollisionShape2D hazır mı? Belirti: karakter hareket eder ama engelleri geçer, ya da editörde uyarı çıkar. Düğümün
CharacterBody2D'nin çocuğu olduğunu ve Inspector'da bir şekil (Shape) atandığını kontrol et.
Hâlâ sonuç yoksa sorunu ikiye böl. Output panelindeki kırmızı hata satırını oku; satır numarası genellikle doğrudan ilgili koda götürür. Hata yoksa direction değişkenini hesapladıktan hemen sonra print(direction) yaz. Tuşa basınca ekranda (0, 0) dışında bir değer görüyorsan giriş katmanı çalışıyordur ve sorun fizik tarafındadır; hep (0, 0) görüyorsan Input Map ve eylem adlarına dön.
Bu, hata ayıklamanın genel mantığıdır: büyük bir "çalışmıyor" sorununu küçük, test edilebilir adımlara bölmek. Bu beceriyi dilden bağımsız ölçmek istersen ücretsiz algoritmik düşünme testi ile seviyene bakabilirsin.
Prototipi bir sonraki adımda nasıl genişletirsin?
Elindeki karakter dört yöne hareket ediyor. Aynı projeden çıkmadan, her biri birkaç dakikalık üç küçük deneyle devam edebilirsin.
- Hız değerini koddaki sabit sayıdan çıkarıp
@export var speedsatırıyla tanımla; böylece değeri Inspector panelinden, kodu açmadan değiştirirsin. - Yatay yöne göre Sprite2D'nin
flip_hözelliğini ayarla; karakter sola giderken görseli ters dönsün. - Sahneye bir StaticBody2D ekle, altına bir CollisionShape2D koyup şekil ata ve karakteri duvara doğru yürüt.
Üçüncü deneyde beklenen sonuç şudur: karakter duvara dayandığında duvarın içinden geçmez, duvara çapraz yaklaşırsan duvar boyunca kayabilir. Bunu move_and_slide() çarpışma sonrası hareketi kalan yöne doğru uzattığı için görürsün. Oyun geliştirme ve diğer yazılım konularında yeni başlıyorsan blog arşivindeki yazılara da göz atabilirsin.
Sık Sorulan Sorular
Godot'ta karakterim neden duvarın içinden geçiyor?
Önce hem karakterde hem duvarda bir CollisionShape2D bulunduğunu ve ikisine de şekil atandığını kontrol et. Sonra karakteri position değerini doğrudan değiştirerek mi yoksa velocity ile move_and_slide() kullanarak mı hareket ettirdiğine bak; konumu doğrudan değiştirmek çarpışma denetimini atlar. Son olarak iki nesnenin çarpışma katmanı (layer) ve maskesi (mask) ayarlarının birbiriyle eşleştiğinden emin ol.
Hareket hızını editörden nasıl değiştiririm?
Script'in başında @export var speed = 300.0 gibi bir değişken tanımla. Script'i kaydettikten sonra karakter düğümünü seçtiğinde bu değişken Inspector panelinde görünür ve oradan değiştirebilirsin.
Godot'ta karakter hareketi için _process mi _physics_process mi kullanılmalı?
Çarpışma ve move_and_slide() içeren hareket için _physics_process uygundur. Bu fonksiyon fizik motoruyla uyumlu, sabit aralıklarla çağrılır; _process ise çoğunlukla görsel ve arayüz güncellemeleri için kullanılır.
Bu mini projeyle sahne ağacı, Input Map ve velocity mantığını bir arada çalıştırdın. Bundan sonra yeni bir özellik eklerken aynı sırayı izleyebilirsin: önce küçük bir deney kur, sonra davranışı gözlemle.