Gönderi

Godot Engine Eğitim Serisi - Bölüm 10: 3D Düşmanlar, Dinamik Spawn Sistemi, Zıplama, Ezme ve Çarpışma Maskeleri

Godot 3D'de düşman spawn sistemi, zıplama, ezme ve çarpışma maskeleri: Path3D, Dot Product ve fizik katmanları. Türkçe oyun geliştirme.

Godot Engine Eğitim Serisi - Bölüm 10: 3D Düşmanlar, Dinamik Spawn Sistemi, Zıplama, Ezme ve Çarpışma Maskeleri

Oyuncu karakterimizi başarıyla 3D dünyaya entegre ettik. Şimdi oyuncunun kaçması ve üstüne atlayarak ezmesi gereken düşmanları (mob) oluşturma zamanı! Bu kapsamlı rehberde, önce 3D düşman sahnemizi tasarlayacak, ardından bu düşmanları oyun alanının etrafından rastgele konumlarda üretecek (spawn edecek) dinamik bir sistem kuracağız.


3D Düşman (Mob) Sahnesini İnşa Etmek

Düşmanlar için oyuncu sahnesine oldukça benzer bir yapı kuracağız. Yeni bir sahne oluşturun ve kök node olarak CharacterBody3D ekleyip adını Mob yapın.

  1. Tıpkı oyuncuda yaptığımız gibi, modeli kod ile kolayca döndürebilmek için Mob node’una bir Node3D çocuğu ekleyin ve adını Pivot yapın.
  2. Dosya sisteminizdeki (art/ klasörü) mob.glb dosyasını Pivot node’unun üzerine sürükleyerek 3D modeli sahnenize ekleyin. Oluşan node’un adını Character olarak değiştirebilirsiniz.

Mob Modeli Sürükle Bırak mob.glb dosyasını Pivot üzerine sürükleyerek 3D modeli ekliyoruz

İlk Üç Node Mob > Pivot > Character yapısı

  1. Düşmanın fiziksel bir hacme sahip olması için Mob node’una bir CollisionShape3D ekleyin.
  2. Inspector (Denetçi) panelinden şekil (Shape) olarak BoxShape3D (Kutu) atayın ve kutuyu 3D modele uyacak şekilde turuncu noktalarından sürükleyerek boyutlandırın.

BoxShape3D Oluştur CollisionShape3D için BoxShape3D seçiyoruz

Kutu Son Boyutu Çarpışma kutusu modeli hafifçe sarıyor — tabana değiyor, yanlarda biraz daha dar

💡 İpucu: Çarpışma kutusunu modelden çok az daha dar yapmanız oyuncuya haksızlık yapılmasını önler. Eğer kutu modelden büyük olursa, oyuncu düşmana görsel olarak değmeden de hasar almış gibi hissedebilir.

Bellek Yönetimi: Ekrandan Çıkanları Silmek

Düşmanları sürekli olarak sahneye süreceğimiz için, ekranın dışına çıkan düşmanları silmezsek oyunumuzun belleği kısa sürede dolar ve oyun yavaşlar.

Bunu önlemek için Mob node’una bir VisibleOnScreenNotifier3D çocuğu ekleyin. Ekranda beliren pembe kutuyu, tüm 3D modeli kapsayacak şekilde büyütün.

Görünürlük Bildiricisi VisibleOnScreenNotifier3D eklendi — pembe bir kutu belirir

Görünürlük Kutusu Boyutlandırıldı Pembe kutu mob modelini tam olarak kaplıyor

Düşman Yapay Zekasını Kodlamak

Mob node’una sağ tıklayarak yeni bir script ekleyin. Düşmanlarımızın mantığı basit olacak: Belirli bir konumda doğacaklar, oyuncuya doğru yönelecekler ve rastgele bir hızla düz bir çizgide ilerleyecekler.

Aşağıdaki kodu scriptinize yapıştırın:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extends CharacterBody3D

@export var min_speed = 10
@export var max_speed = 18

func _physics_process(_delta):
	# Düşmanı her karede hareket ettiriyoruz
	move_and_slide()

# Bu fonksiyon, düşman sahnede oluşturulduğunda Main sahnesi tarafından çağrılacak
func initialize(start_position, player_position):
	# Düşmanı başlangıç noktasına yerleştir ve oyuncuya bakmasını sağla
	look_at_from_position(start_position, player_position, Vector3.UP)
	
	# Doğrudan oyuncuya gitmemesi için ±45 derecelik rastgele bir açı sapması ekle
	rotate_y(randf_range(-PI / 4, PI / 4))
	
	# Rastgele bir hız belirle ve vektörü düşmanın baktığı yöne çevir
	var random_speed = randi_range(min_speed, max_speed)
	velocity = Vector3.FORWARD * random_speed
	velocity = velocity.rotated(Vector3.UP, rotation.y)

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

  • extends CharacterBody3D: Bu kodun 3D dünyadaki bir düşman gövdesini (Mob) kontrol edeceğini gösterir.
  • @export var min_speed = 10 & @export var max_speed = 18: Düşmanların hangi hız aralığında ekrana geleceğini Inspector (Denetçi) panelinden kolayca değiştirebilmek için @export ile tanımladığımız minimum ve maksimum sürat değişkenleridir.
  • func _physics_process(_delta):: Motorun sürekli çalışan 3D fizik döngüsü. İçindeki _delta parametresinin başına alt çizgi koyduk çünkü döngü içinde o geçici süreyi (delta’yı) bilerek kullanmıyoruz ve Godot’nun bize “Bu değişkeni hiç kullanmadın” diye uyarı (warning) vermesini engelliyoruz.
  • move_and_slide(): Düşmanın, önceden belirlenmiş hızını (velocity) uygulayarak ileri doğru hareket etmesini sağlar.
  • func initialize(start_position, player_position):: Bu fonksiyon motor tarafından otomatik çalışmaz. Özel olarak bizim yazdığımız, düşman ekranda doğduğunda (spawn) ona “Nereden çıkacaksın ve oyuncu şu an nerede?” (start & player_position) bilgisini verdiğimiz başlatıcı/kurucu fonksiyondur.
  • look_at_from_position(...): Üç parametre alır. Düşmanın konumunu önce ekrandaki rastgele çıkış noktasına (start_position) koy, sonra yüzünü doğrudan oyuncunun o anki koordinatlarına (player_position) çevir. Vector3.UP ise “Karakterin kafası yukarı tarafa gelsin, yan veya ters dönmesin” demek için eklenen referans bir diklik vektörüdür.
  • rotate_y(randf_range(-PI / 4, PI / 4)): Yüzümüzü tamamen oyuncuya dönmüştük ama böyle yaparsak tüm düşmanlar tren gibi aynı düz çizgide gelir ve kolay lokma olurlar. Y açısı (sağa-sola dönme) ekseninde -45 ile +45 derece arasında (PI/4 radyan) rastgele bir sapma ekliyoruz ki biraz çapraza doğru gitsinler.
  • var random_speed = randi_range(min_speed, max_speed): En başta belirlediğimiz 10 ile 18 arasında rastgele tam bir sayı (örneğin 14) seç.
  • velocity = Vector3.FORWARD * random_speed: 3D’de ileri yönü temsil eden FORWARD vektörünü (Z ekseninde -1 gidiş), seçtiğimiz bu rastgele hız ile çarp. Yani “Düz ileri gitme gücü” hesapla.
  • velocity = velocity.rotated(Vector3.UP, rotation.y): Saf “düz ileri” gitme gücünü, yukarıda rastgele hesapladığımız kendi kafa açısına (rotation.y) çevir ve nihai hareket hızını (velocity) netleştir. Artık _physics_process içindeki move_and_slide motoru bu hızı kullanarak düşmanı her saniye yürütecek.

Bu kodda neler yaptık?

  • look_at_from_position() metoduyla düşmanı doğduğu noktadan doğrudan oyuncuya çevirdik.
  • Ancak her düşmanın ip gibi oyuncuyu takip etmemesi için rotate_y ile rotasyonuna rastgele bir sapma (-45 ile +45 derece arası) ekledik.
  • velocity.rotated() fonksiyonuyla hız vektörünü düşmanın yönüne uyarladık.

Son olarak bellek yönetimi için VisibleOnScreenNotifier3D node’unuzu seçin, sağdaki Signals sekmesinden screen_exited sinyalini Mob scriptinize bağlayın. Oluşan fonksiyona queue_free() yazarak düşmanın ekrandan çıktığında silinmesini sağlayın. Sahnenizi mob.tscn olarak kaydetmeyi unutmayın.


3D Dinamik Spawn (Oluşturucu) Sistemi

Düşman sahnesi hazır olduğuna göre, onları rastgele konumlarda oyun alanına getirecek sistemi Main (Ana) sahnemizde kurmalıyız.

  1. Main sahnenizi açın.
  2. Düşmanların ekran sınırlarının hemen dışından gelmesi için bir rota çizeceğiz. Main node’una bir Path3D çocuğu ekleyin ve adını SpawnPath yapın.
  3. SpawnPath‘in altına da bir PathFollow3D çocuğu ekleyip adını SpawnLocation yapın.

Spawn Node'ları SpawnPath ve SpawnLocation — spawn mekanizması için hazır

⚠️ Uyarı: 3D uzayda yol çizerken nereye tıkladığınızı tam göremeyebilirsiniz. Kameranızın görüş açısının dört köşesine geçici olarak silindir (CylinderMesh) nesneleri koyarak kendinize referans noktaları oluşturabilir ve Add Point aracıyla bu silindirlerin etrafından saat yönünde bir dörtgen çizebilirsiniz. Yolunuzu tamamladıktan sonra Close Curve (Yolu Kapat) butonuna tıklamayı unutmayın.

Dört Silindir Dört silindir kamera görünümünün dört köşesinde

Yol Sonucu Spawn yolu kamera görünümünü çerçeveleyen bir dörtgen

Timer (Zamanlayıcı) Eklemek

  1. Düşmanların belirli aralıklarla gelmesi için Main node’una bir Timer ekleyin ve adını MobTimer yapın.
  2. Inspector panelinden Wait Time (Bekleme Süresi) değerini 0.5 yapın.
  3. Autostart (Otomatik Başlat) özelliğini açık konuma getirin.

Timer Özellikleri Wait Time: 0.5, Autostart: On

Oluşturucu (Spawner) Kodunu Yazmak

Şimdi Main node’unuza bir script ekleyin. Scriptinizin en üstüne şu değişkeni tanımlayın:

1
2
3
extends Node

@export var mob_scene: PackedScene

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

  • extends Node: Oyunun yöneticisi olan Main adlı kök node’a bağlandığımızı gösterir.
  • @export var mob_scene: PackedScene: Tıpkı 2D oyunumuzda olduğu gibi, düşman iskeletini (mob.tscn) dışarıdan sürükleyip bırakabilmemiz için açtığımız özel @export kutusudur. “PackedScene” olması gerektiğini belirterek, içine yanlışlıkla görsel veya ses atılmasını engelleriz.

Bu kod sayesinde Inspector panelinde Mob Scene adında bir alan açılacaktır. Dosyalarınızdan mob.tscn sahnesini sürükleyip bu alana bırakın.

Son adım olarak, MobTimer node’unun timeout sinyalini Main scriptinize bağlayın ve fonksiyonun içini aşağıdaki gibi doldurun:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func _on_mob_timer_timeout():
	# Düşman şablonundan yeni bir kopya oluştur
	var mob = mob_scene.instantiate()
	
	# PathFollow3D'yi çizdiğimiz yol üzerinde rastgele bir noktaya taşı (0.0 ile 1.0 arası)
	var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
	mob_spawn_location.progress_ratio = randf()
	
	# Oyuncunun anlık konumunu al
	var player_position = $Player.position
	
	# Düşmanın kendi scriptindeki initialize fonksiyonunu çağırarak konumunu ve rotasını ayarla
	mob.initialize(mob_spawn_location.position, player_position)
	
	# Düşmanı sahneye ekle
	add_child(mob)

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

  • func _on_mob_timer_timeout():: Eklediğimiz Timer (Zamanlayıcı) düğümünün her süresi dolduğunda (örneğin yarım saniyede bir) otomatik tetiklediği sinyal fonksiyonudur. Düşman ekleme fabrikamız çalışır.
  • var mob = mob_scene.instantiate(): Editöre sürükleyip verdiğimiz düşman (.tscn) kalıbını al, ondan bir tane daha yepyeni kopyala/yarat (.instantiate()) ve adına mob de.
  • var mob_spawn_location = ...: Yukarıda ekran çevresine manuel olarak çizdiğimiz yolu ve üzerindeki kayan noktayı (PathFollow3D) ismine göre bul.`
  • mob_spawn_location.progress_ratio = randf(): O kayan noktayı yolun tamamı olan 0.0 (Başlangıç) ile 1.0 (Son) arasında tamamen rastgele bir yere (randf()) ışınla.
  • var player_position = $Player.position: Oyuncu karakterin sahnedeki tam o saniyedeki 3D koordinatlarını (X, Y, Z konumunu) kopyalayıp çantaya koy.
  • mob.initialize(...): Yarattığımız yeni düşmanın içine gizli kodlara ulaşıyoruz. Yukarıda açıkladığımız özel başlatıcı fonksiyonuna sırasıyla; 1. Doğman gereken yer burası (sınırda rastgele seçilen konum) ve 2. Oyuncu şu an burada, oraya dön! parametrelerini yolluyoruz. Düşman bu verileri alıp kendi sağa/sola sapmasını ve hızını ayarlıyor.
  • add_child(mob): Nihayet tüm işlemleri bitmiş, fırından yeni çıkmış düşmanı ana sahneye (Main) gerçek bir çocuk olarak ekle (“Motor, ışık, kayıt!”). Artık ekranda koşmaya başlayacaktır.

Düşmanlarımızı 3D dünyamıza başarıyla dahil ettik ve spawn sistemini kurduk. Ancak oyunun temel eğlencesi henüz eksik: Karakterimiz zıplayamıyor, düşmanları ezemiyor ve onlara çarptığında ölmüyor.

Şimdi Godot’nun fizik katmanlarını (Layers ve Masks) yapılandıracak, ardından matematiksel vektör işlemleriyle (Dot Product) zıplama ve ezme mekaniklerini kodlayacağız.


Fizik Katmanları (Layers) ve Maskeleri (Masks)

Godot’daki fizik gövdeleri iki tamamlayıcı özelliğe sahiptir: Layer (Katman) ve Mask (Maske).

  • Layer: Nesnenin fiziksel olarak hangi katmanda bulunduğunu tanımlar.
  • Mask: Nesnenin hangi katmanları “dinleyeceğini”, yani hangileriyle çarpışma algılayacağını belirler.

İki nesnenin etkileşebilmesi için en az birinin, diğerinin katmanına karşılık gelen bir maskesi olması zorunludur. Bu sistemi düzenli tutmak için katmanlarımıza isim verelim:

  1. Project > Project Settings menüsünü açın.
  2. Sol menüden Layer Names > 3D Physics bölümüne gidin.
  3. İlk üç katmanı sırasıyla player, enemies ve world olarak adlandırın.

Fizik Katmanları 3D Physics katman isimlendirme ekranı


Katman Atamalarını Yapmak

Şimdi sahnelerimizdeki node’ların katmanlarını bu yeni isimlendirmelere göre ayarlayalım:

  • Ground (Zemin): main.tscn içindeki Ground node’unu seçin. Layer olarak yalnızca 3’ü (world) işaretleyin ve Mask ayarlarının tamamını kapatın (çünkü zemin hiçbir şeyi dinlemez, sadece üstüne basılır).

Katman ve Mask Değiştir Ground için Layer = world, Mask = boş

  • Player: player.tscn içindeki Player node’unu seçin. Layer olarak 1 (player), Mask olarak ise enemies ve world katmanlarını işaretleyin.

Oyuncu Fizik Maskesi Player: Layer = player, Mask = enemies + world

  • Mob: mob.tscn içindeki Mob node’unu seçin. Layer olarak enemies seçin, Mask ayarlarını ise tamamen kapatın ki mob’lar birbirlerine veya zemine sürtünmesin.

Mob Fizik Maskesi Mob: Layer = enemies, Mask = boş


Zıplama Mekaniği

Karakterin zıplaması için player.gd script dosyamızı açalım. Öncelikle değişkenlerinizin arasına zıplama gücünü belirten şu değişkeni ekleyin:

1
@export var jump_impulse = 20

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

  • @export: Bu değişkenin arayüzden (Inspector) değiştirilebilir olmasını sağlar.
  • jump_impulse = 20: Karakterin zıplama gücü (ivmesi). Zıpla tuşuna basıldığında karakteri ne kadar yukarı atacağımızı temsil eder. Y ekseninde 20 birimlik bir zıplama kuvveti oluştururuz.

Ardından, _physics_process() fonksiyonunun içinde, move_and_slide() çağrısından hemen önce şu kodu ekleyin:

1
2
    if is_on_floor() and Input.is_action_just_pressed("jump"):
        target_velocity.y = jump_impulse

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

  • if is_on_floor() and Input.is_action_just_pressed("jump"):: Burada iki şartımız var. Birincisi is_on_floor() yani karakterin ayağı tamamen yere basıyor mu? Havada değilse. İkincisi and (ve) diyoruz, oyunu oynayan kişi “jump” (örneğin Boşluk) tuşuna just_pressed (tam o karede henüz basılmış mı) kontrolü. is_action_pressed ile arasındaki fark, just_pressed‘in tuşa basılı tutsanız bile sadece ilk basıldığında bir kere çalışmasıdır.
  • target_velocity.y = jump_impulse: Yukarıdaki iki şart da doğruysa (yere basıyorsa VE o an zıplamaya bastıysa) hedef hızımızın Y eksenine (yukarı yöne) az önce belirlediğimiz 20 değerini aktarıyoruz. Karakter füze gibi yukarı fırlar. Fizik motoru zaten sonraki karelerde yerçekimi uygulayarak bu 20 değerini yavaş yavaş sıfıra, sonra eksiye indirip onu yere indirecektir.

💡 Bilgilendirme: 3D dünyada Y ekseni yukarıya doğru pozitiftir. is_on_floor() metodu sayesinde oyuncunun havada art arda zıplamasını (double jump) engellemiş oluyoruz.


Düşmanları Ezme (Squash) Mekaniği

Oyuncunun düşmanı ezdiğini algılaması için öncelikle düşmanları bir grup altında toplamalıyız. mob.tscn sahnesini açın, kök Mob node’unu seçip sağdaki Groups sekmesinden mob adında yeni bir grup oluşturarak node’u bu gruba dâhil edin.

Sahne Grupları “mob” grubu başarıyla oluşturuldu Şimdi player.gd scriptinize geri dönün ve düşman ezildiğinde karakterin hafifçe tekrar zıplamasını sağlayacak değişkeni ekleyin:

1
@export var bounce_impulse = 16

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

  • @export var bounce_impulse = 16: Düşmanı ezdiğimizde (kafasına bastığımızda) oyuncunun zafer zıplaması yaparak havaya hafiften tekrar sıçraması için 16 gücünde bir “sekme ivmesi” tanımlarız.

_physics_process() fonksiyonu içinde, zıplama kodunuzun hemen sonrasına şu döngüyü ekleyin:

1
2
3
4
5
6
7
8
9
10
11
12
    for index in range(get_slide_collision_count()):
        var collision = get_slide_collision(index)
        
        if collision.get_collider() == null:
            continue
            
        if collision.get_collider().is_in_group("mob"):
            var mob = collision.get_collider()
            if Vector3.UP.dot(collision.get_normal()) > 0.1:
                mob.squash()
                target_velocity.y = bounce_impulse
                break

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

  • for index in range(get_slide_collision_count()):: Karakter her move_and_slide() yaptığında bir yerlere (zemine, duvara, düşmana) sürtebilir. get_slide_collision_count() o sürtünmelerin sayısını verir. Biz de bir for döngüsü kurup “1’den çarpışma sayısına kadar tüm temasları tek tek dön” diyoruz. (Örneğin 2 düşmana ve 1 duvara aynı anda değiyorsa, döngü 3 kez çalışır).
  • var collision = get_slide_collision(index): Döngünün o anki temas bilgisini (örneğin ilk temas) alır collision adlı bir kutuya kaydederiz. İçinde “Neye çarptık? Neresinden çarptık? Hangi açıyla çarptık?” gibi bilgiler var.
  • if collision.get_collider() == null: continue: Çok nadir durumlarda (örneğin çarptığımız şey o an silindiyse) çarptığımız nesne null (boş/hiçlik) olabilir. Eğer öğle bir durum varsa hiç hata verme, continue diyerek bu teması atla ve döngüdeki sıradaki teması incele diyoruz.
  • if collision.get_collider().is_in_group("mob"):: Çarptığımız bu nesnenin .is_in_group("mob") yani yapıldığında “mob” kalabalıklar/düşmanlar grubunda olup olmadığını sorarız.
  • var mob = collision.get_collider(): Zemin değil bir düşman olduğunu anladığımıza göre, bu çarptığımız nesneyi geçici olarak mob isminde bir kutuya atarız ki üzerinde rahatça işlem yapabilelim.
  • if Vector3.UP.dot(collision.get_normal()) > 0.1:: Çok kritik bir satır. Bize yandan mı çarptı, üstüne mi bastık ayrımını yapar. Vector3.UP dümdüz yukarı bakan bir oktur. collision.get_normal() ise düşman yüzeyinin “Bana nereden değdin?” okudur. Kafasına bastıysanız bu oklar aynı yöne bakar ve matematiksel Dot Product işlemi 1’e çok yakın (0.1’den büyük) bir değer döndürür. Yanlardan çarptıysanız oklar dik açıyla kesişeceği için değer 0.1’den küçük olur, yani bu eğer bloğuna girmez.
  • mob.squash(): Eğer cidden kafasına bastıysak, az önce kutuya kaydettiğimiz düşmana kendi sahip olduğu “ezil (.squash())” komutunu yolluyoruz.
  • target_velocity.y = bounce_impulse: Düşmanı ezdik, oyuncunun ödülü ise bounce_impulse (sekme ivmesi olan 16) gücünde bir trambolin etkisi yaratılarak havaya zıplatılması.
  • break: İşlemi başarıyla hallettiğimiz için zıpladık, bir karede iki kez üst üste ölümcül aynı işlemi yapmamak için break ile döngüyü tamamen sonlandırır ve oradan çıkarız.

Bu kodda neler yaptık?

  • get_slide_collision_count() ve get_slide_collision() fonksiyonları ile karakterin o karedeki tüm çarpışmalarını tek tek inceliyoruz.
  • Çarptığımız nesne mob grubundaysa bir sonraki aşamaya geçiyoruz.
  • Dot Product (Nokta Çarpımı): Vector3.UP.dot(collision.get_normal()) > 0.1 matematiği, iki vektör arasındaki açıyı ölçer. Çarpışma yüzeyinin normali yukarıya bakıyorsa (yani düşmanın tepesine basıyorsak) bu değer 0.1’den büyük çıkar ve ezme işlemini doğrularız.

Mob Scriptini Güncellemek

Player, mob.squash() fonksiyonunu çağırıyor ancak bu fonksiyon henüz düşman scriptinde yok. mob.gd dosyasını açın, en üste kendi sinyalinizi tanımlayın:

1
signal squashed

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

  • signal squashed: Düşman dosyasında tanımlanan yeni, kendimize ait bir sinyal. “Ezildim/Yok edildim” anlamına gelir. Oyuncudan mob.squash() emri gelince bu sinyali ateşleyeceğiz. (Örneğin skor sistemi “bir düşman ezildim sinyali yaydıysa bana 10 puan ver” demek için bunu bekliyor olacak).

Ve scriptin en altına şu fonksiyonu ekleyin:

1
2
3
func squash():
    squashed.emit()
    queue_free()

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

  • func squash():: Oyuncunun tepeden bastığında bulup tetikleyeceği fonksiyon budur. Düşman ezildiğinde ne olacağını yazarız.
  • squashed.emit(): Yukarıda uydurduğumuz özel squashed sinyalini etrafa (“Ben ezildim!”) diye bağırarak yayar.
  • queue_free(): Görevini başarıyla tamamlayan düşmanımız, kendini oyun sahnesinden siler ve yok olur.

Bu sayede düşman ezildiğinde hem kendini silecek hem de (ileride skoru artırmak için kullanacağımız) bir sinyal yayacaktır.


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


Bölüm Özeti

F6 tuşu ile oyununuzu çalıştırdığınızda, artık her yarım saniyede bir ekranın kenarlarından rastgele düşmanların belirdiğini ve karakterinize doğru yöneldiğini göreceksiniz!

Şu an karakterimiz düşmanların içinden geçiyor. Bir sonraki bölümümüzde oyuncumuzun da ölebilmesini sağlayacağız.

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