Modern web geliştirmede klasik ve web uygulamaları arasındaki sınırlar her geçen gün bulanıklaşıyor. Bugün yalnızca etkileşimli web siteleri değil, aynı zamanda doğrudan tarayıcıda tam teşekküllü oyunlar da oluşturabiliyoruz. Bunu mümkün kılan araçlardan biri, React teknolojisini kullanarak Three.js tabanlı 3D grafikler oluşturmaya yönelik güçlü bir araç olan React Three Fiber kitaplığıdır.
React Three Fiber, web üzerinde 3D grafikler oluşturmak için React'in yapısını ve ilkelerini kullanan, Three.js üzerinde bir sarmalayıcıdır. Bu yığın, geliştiricilerin Three.js'nin gücünü React'ın rahatlığı ve esnekliğiyle birleştirmesine olanak tanıyarak uygulama oluşturma sürecini daha sezgisel ve organize hale getirir.
React Three Fiber'in temelinde, bir sahnede yarattığınız her şeyin bir React bileşeni olduğu fikri yatmaktadır. Bu, geliştiricilerin tanıdık kalıpları ve metodolojileri uygulamalarına olanak tanır.
React Three Fiber'in temel avantajlarından biri React ekosistemiyle entegrasyon kolaylığıdır. Bu kütüphaneyi kullanırken diğer React araçları hala kolayca entegre edilebilir.
Web-GameDev son yıllarda basit 2D Flash oyunlarından masaüstü uygulamalarıyla karşılaştırılabilecek karmaşık 3D projelere doğru evrimleşerek büyük değişiklikler geçirdi. Popülerlik ve yeteneklerdeki bu artış, Web-GameDev'i göz ardı edilemeyecek bir alan haline getiriyor.
Web oyunlarının ana avantajlarından biri erişilebilirliğidir. Oyuncuların herhangi bir ek yazılım indirmesine ve yüklemesine gerek yoktur; tarayıcılarındaki bağlantıya tıklamanız yeterlidir. Bu, oyunların dağıtımını ve tanıtımını basitleştirerek dünya çapında geniş bir kitleye ulaşmasını sağlar.
Son olarak, web oyunu geliştirme, geliştiricilerin tanıdık teknolojileri kullanarak gamedev'de ellerini denemeleri için harika bir yol olabilir. Mevcut araçlar ve kütüphaneler sayesinde, 3D grafiklerde deneyiminiz olmasa bile ilginç ve kaliteli projeler yaratmak mümkün!
Modern tarayıcılar, oldukça basit web tarama araçlarından, karmaşık uygulamaları ve oyunları çalıştırmak için güçlü platformlara dönüşerek uzun bir yol kat etti. Chrome , Firefox , Edge ve diğerleri gibi önemli tarayıcılar, yüksek performans sağlamak için sürekli olarak optimize ediliyor ve geliştiriliyor; bu da onları karmaşık uygulamalar geliştirmek için ideal bir platform haline getiriyor.
Tarayıcı tabanlı oyunların gelişimini hızlandıran temel araçlardan biri WebGL'dir . Bu standart, geliştiricilerin, 3D oyunların performansını önemli ölçüde artıran donanım grafik hızlandırmasını kullanmasına olanak tanıdı. Diğer webAPI'lerle birlikte WebGL , doğrudan tarayıcıda etkileyici web uygulamaları oluşturmaya yönelik yeni olanaklar sunar.
Bununla birlikte, tarayıcı için oyun geliştirirken performansın çeşitli yönlerini dikkate almak çok önemlidir: kaynak optimizasyonu, bellek yönetimi ve farklı cihazlara uyarlama, bir projenin başarısını etkileyebilecek kilit noktalardır.
Bununla birlikte, kelimeler ve teori başka bir şeydir, ancak pratik deneyim tamamen başka bir şeydir. Web oyunu geliştirmenin tüm potansiyelini gerçekten anlamak ve takdir etmek için en iyi yol, kendinizi geliştirme sürecine dahil etmektir. Bu nedenle başarılı web oyunu geliştirme örneği olarak kendi oyunumuzu yaratacağız. Bu süreç, geliştirmenin temel yönlerini öğrenmemize, gerçek sorunlarla yüzleşmemize ve bunlara çözüm bulmamıza ve bir web oyunu geliştirme platformunun ne kadar güçlü ve esnek olabileceğini görmemize olanak tanıyacak.
Bir dizi makalede, bu kütüphanenin özelliklerini kullanarak birinci şahıs nişancı oyununun nasıl oluşturulacağına bakacağız ve web-gamedev'in heyecan verici dünyasına dalacağız!
GitHub'daki depo
Şimdi başlayalım!
Öncelikle bir React proje şablonuna ihtiyacımız olacak. Öyleyse onu yükleyerek başlayalım.
npm create vite@latest
Ek npm paketlerini yükleyin.
npm install three @react-three/fiber @react-three/drei @react three/rapier zustand @tweenjs/tween.js
Daha sonra projemizdeki gereksiz her şeyi silin .
Main.jsx dosyasına sayfada kapsam olarak görüntülenecek bir div öğesi ekleyin. Bir Canvas bileşeni ekleyin ve kameranın görüş alanını ayarlayın. Canvas bileşeninin içine Uygulama bileşenini yerleştirin.
Kullanıcı arayüzü öğelerini ekranın tam yüksekliğine kadar genişletmek ve kapsamı ekranın ortasında bir daire olarak görüntülemek için index.css'ye stiller ekleyelim.
Uygulama bileşenine, oyun sahnemizde arka plan olarak gökyüzü şeklinde görüntülenecek bir Gökyüzü bileşeni ekliyoruz.
Bir Ground bileşeni oluşturalım ve onu App bileşenine yerleştirelim.
Ground'da düz bir yüzey elemanı oluşturun. Y ekseninde, bu düzlem kameranın görüş alanı içinde olacak şekilde onu aşağı doğru hareket ettirin. Ayrıca düzlemi yatay hale getirmek için X ekseninde çevirin.
Malzeme rengi olarak griyi belirtmemize rağmen düzlem tamamen siyah görünüyor.
Varsayılan olarak sahnede aydınlatma yoktur, bu nedenle nesneyi her taraftan aydınlatan ve yönlendirilmiş bir ışına sahip olmayan ambientLight ışık kaynağını ekleyelim. Bir parametre olarak ışığın yoğunluğunu ayarlayın.
Zemin yüzeyinin homojen görünmemesi için doku ekleyeceğiz. Zemin yüzeyinin tüm yüzey boyunca tekrarlanan hücreler şeklinde bir desenini yapın.
Varlıklar klasörüne dokulu bir PNG görüntüsü ekleyin.
Sahneye bir doku yüklemek için @react- three/drei paketindeki useTexture kancasını kullanalım. Ve kancanın parametresi olarak dosyaya aktarılan doku görüntüsünü aktaracağız. Görüntünün yatay eksenlerdeki tekrarını ayarlayın.
@react-third/drei paketindeki PointerLockControls bileşenini kullanarak, fareyi hareket ettirdiğinizde hareket etmeyecek, ancak kameranın sahnedeki konumunu değiştirecek şekilde imleci ekrana sabitleyin.
Ground bileşeni için küçük bir düzenleme yapalım.
Netlik sağlamak için sahneye basit bir küp ekleyelim.
<mesh position={[0, 3, -5]}> <boxGeometry /> </mesh>
Şu anda sadece boşlukta asılı duruyor.
Sahneye "fizik" eklemek için @react-third/rapier paketindeki Fizik bileşenini kullanın. Parametre olarak, yerçekimi kuvvetlerini eksenler boyunca ayarladığımız yerçekimi alanını yapılandırın.
<Physics gravity={[0, -20, 0]}> <Ground /> <mesh position={[0, 3, -5]}> <boxGeometry /> </mesh> </Physics>
Ancak küpümüz fizik bileşeninin içinde ama ona hiçbir şey olmuyor. Küpün gerçek bir fiziksel nesne gibi davranmasını sağlamak için onu @react- three/rapier paketindeki RigidBody bileşenine sarmamız gerekiyor.
Bundan sonra sayfa her yeniden yüklendiğinde küpün yerçekiminin etkisiyle aşağıya düştüğünü hemen göreceğiz.
Ancak şimdi başka bir görev daha var - zemini küpün etkileşime girebileceği ve ötesine düşmeyeceği bir nesne haline getirmek gerekiyor.
Ground bileşenine geri dönelim ve zemin yüzeyinin üzerine sarmalayıcı olarak bir RigidBody bileşeni ekleyelim.
Artık düşerken küp gerçek bir fiziksel nesne gibi yerde kalıyor.
Sahnedeki karakteri kontrol edecek Player bileşeni oluşturalım.
Karakter, eklenen küple aynı fiziksel nesnedir, bu nedenle sahnedeki küpün yanı sıra zemin yüzeyiyle de etkileşime girmelidir. Bu yüzden RigidBody bileşenini ekliyoruz. Ve karakteri kapsül şeklinde yapalım.
Player bileşenini Fizik bileşeninin içine yerleştirin.
Artık karakterimiz sahneye çıkmıştır.
Karakter WASD tuşları kullanılarak kontrol edilecek ve Ara Çubuğu kullanılarak atlanacaktır.
Kendi reaksiyon kancamızla karakteri hareket ettirme mantığını uyguluyoruz.
Bir hooks.js dosyası oluşturalım ve buraya yeni bir usePersonControls fonksiyonu ekleyelim.
{"keycode": "gerçekleştirilecek eylem"} biçiminde bir nesne tanımlayalım. Daha sonra, klavye tuşlarına basmak ve bırakmak için olay işleyicileri ekleyin. İşleyiciler tetiklendiğinde, gerçekleştirilen mevcut eylemleri belirleyeceğiz ve bunların aktif durumlarını güncelleyeceğiz. Nihai sonuç olarak kanca, {"işlem devam ediyor": "durum"} biçiminde bir nesne döndürecektir.
usePersonControls kancasını uyguladıktan sonra, karakteri kontrol ederken kullanılmalıdır. Oyuncu bileşeninde hareket durumu izlemeyi ekleyeceğiz ve karakterin hareket yönünün vektörünü güncelleyeceğiz.
Ayrıca hareket yönlerinin durumlarını saklayacak değişkenleri de tanımlayacağız.
Karakterin konumunu güncellemek için @react- three/fiber paketi tarafından sağlanan Frame'i kullanalım. Bu kanca, requestAnimationFrame'e benzer şekilde çalışır ve işlevin gövdesini saniyede yaklaşık 60 kez çalıştırır.
Kod Açıklaması:
1. const playerRef = useRef(); Oynatıcı nesnesi için bir bağlantı oluşturun. Bu bağlantı, sahnedeki oynatıcı nesnesi ile doğrudan etkileşime izin verecektir.
2. const { ileri, geri, sol, sağ, atlama } = usePersonControls(); Bir kanca kullanıldığında, oynatıcının o anda hangi kontrol düğmelerine basıldığını gösteren boole değerlerine sahip bir nesne döndürülür.
3. useFrame((durum) => { ... }); Kanca, animasyonun her karesinde çağrılır. Bu kancanın içinde oyuncunun konumu ve doğrusal hızı güncellenir.
4. if (!playerRef.current) geri dönerse; Bir oynatıcı nesnesinin varlığını kontrol eder. Oynatıcı nesnesi yoksa işlev, hataları önlemek için yürütmeyi durduracaktır.
5. sabit hız = playerRef.current.linvel(); Oyuncunun mevcut doğrusal hızını alın.
6. frontVector.set(0, 0, geri - ileri); Basılan düğmelere göre ileri/geri hareket vektörünü ayarlayın.
7. sideVector.set(sol - sağ, 0, 0); Sol/sağ hareket vektörünü ayarlayın.
8. Direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(MOVE_SPEED); Oyuncu hareketinin son vektörünü, hareket vektörlerini çıkararak, sonucu normalleştirerek (vektör uzunluğu 1 olacak şekilde) ve hareket hızı sabitiyle çarparak hesaplayın.
9. playerRef.current.wakeUp(); Değişikliklere tepki verdiğinden emin olmak için oynatıcı nesnesini "uyandırır". Bu yöntemi kullanmazsanız, bir süre sonra nesne "uykuya" girecek ve konum değişikliklerine tepki vermeyecektir.
10. playerRef.current.setLinvel({ x: yön.x, y: hız.y, z: yön.z }); Hesaplanan hareket yönüne göre oyuncunun yeni doğrusal hızını ayarlayın ve mevcut dikey hızı koruyun (atlamaları veya düşmeleri etkilemeyecek şekilde).
Sonuç olarak WASD tuşlarına basıldığında karakter sahnede hareket etmeye başladı. Ayrıca küple de etkileşime girebilir çünkü her ikisi de fiziksel nesnelerdir.
Atlamayı uygulamak için @dimforge/rapier3d-compat ve @react-third/rapier paketlerindeki işlevselliği kullanalım. Bu örnekte karakterin yerde olduğunu ve atlama tuşuna basıldığını kontrol edelim. Bu durumda karakterin yönünü ve ivme kuvvetini Y eksenine ayarlıyoruz.
Oyuncu için kütle ekleyeceğiz ve tüm eksenlerde dönüşü engelleyeceğiz, böylece sahnedeki diğer nesnelerle çarpıştığında farklı yönlere düşmeyecek.
Kod Açıklaması:
- const dünyası = rapier.world; Rapier fizik motoru sahnesine erişim elde ediliyor. Tüm fiziksel nesneleri içerir ve etkileşimlerini yönetir.
- const ray = world.castRay(new RAPIER.Ray(playerRef.current.translation(), { x: 0, y: -1, z: 0 })); Burası "raycasting"in (raycasting) gerçekleştiği yerdir. Oyuncunun mevcut konumundan başlayan ve y ekseninin aşağısını gösteren bir ışın oluşturulur. Bu ışın, sahnedeki herhangi bir nesneyle kesişip kesişmediğini belirlemek için sahneye "dökülür".
- const grounded = ray && ray.collider && Math.abs(ray.toi) <= 1.5; Oyuncunun yerde olup olmadığı kontrol edilir:
- ışın - ışının yaratılıp yaratılmadığı;
- ray.collider - ışının olay yerindeki herhangi bir nesneyle çarpışıp çarpışmadığı;
- Math.abs(ray.toi) - ışının "maruz kalma süresi". Bu değerin verilen değerden küçük veya ona eşit olması, oyuncunun "yerde" sayılacak kadar yüzeye yakın olduğunu gösterebilir.
Ayrıca, sahnedeki diğer nesnelerle etkileşime girecek fiziksel bir nesne ekleyerek "iniş" durumunu belirlemek için kullanılan ışın izlemeli algoritmanın doğru çalışması için Zemin bileşenini değiştirmeniz gerekir.
Sahneyi daha iyi görebilmek için kamerayı biraz daha yukarı kaldıralım.
Bölüm kodu
Kamerayı hareket ettirmek için oyuncunun mevcut konumunu alacağız ve çerçeve her yenilendiğinde kameranın konumunu değiştireceğiz. Ve karakterin kameranın yönlendirildiği yörünge boyunca tam olarak hareket etmesi için applicationEuler'ı eklememiz gerekiyor.
Kod Açıklaması:
ApplyEuler yöntemi, belirtilen Euler açılarına dayalı olarak bir vektöre rotasyon uygular. Bu durumda kamera döndürme yön vektörüne uygulanır. Bu, oyuncunun kameranın döndürüldüğü yönde hareket etmesi için hareketi kamera yönüne göre eşleştirmek için kullanılır.
Player'ın boyutunu biraz ayarlayalım ve küpe göre daha uzun hale getirelim, CapsuleCollider'ın boyutunu artıralım ve "atlama" mantığını düzeltelim.
Bölüm kodu
Sahnenin tamamen boş hissettirmemesi için küp oluşturmayı ekleyelim. Json dosyasında küplerin her birinin koordinatlarını listeleyin ve ardından bunları sahnede görüntüleyin. Bunu yapmak için, içinde bir koordinat dizisini listeleyeceğimiz cubes.json dosyasını oluşturun.
[ [0, 0, -7], [2, 0, -7], [4, 0, -7], [6, 0, -7], [8, 0, -7], [10, 0, -7] ]
Cube.jsx dosyasında, döngü halinde küpler oluşturacak bir Küpler bileşeni oluşturun. Ve Cube bileşeni doğrudan oluşturulan nesne olacaktır.
import {RigidBody} from "@react-three/rapier"; import cubes from "./cubes.json"; export const Cubes = () => { return cubes.map((coords, index) => <Cube key={index} position={coords} />); } const Cube = (props) => { return ( <RigidBody {...props}> <mesh castShadow receiveShadow> <meshStandardMaterial color="white" /> <boxGeometry /> </mesh> </RigidBody> ); }
Oluşturduğumuz Küpler bileşenini önceki tek küpü silerek Uygulama bileşenine ekleyelim.
Şimdi sahneye bir 3D model ekleyelim. Karakter için bir silah modeli ekleyelim. Bir 3D model arayarak başlayalım. Mesela şunu ele alalım.
Modeli GLTF formatında indirin ve projenin kökündeki arşivi açın.
Modeli sahneye aktarmak için ihtiyacımız olan formatı elde etmek için gltf-pipeline eklenti paketini kurmamız gerekecek.
npm i -D gltf-pipeline
gltf-pipeline paketini kullanarak modeli GLTF formatından GLB formatına yeniden dönüştürün, çünkü bu formatta tüm model verileri tek bir dosyaya yerleştirilir. Oluşturulan dosyanın çıktı dizini olarak ortak klasörü belirtiyoruz.
gltf-pipeline -i weapon/scene.gltf -o public/weapon.glb
Daha sonra sahneye eklemek için bu modelin işaretlemesini içerecek bir reaksiyon bileşeni oluşturmamız gerekiyor. @react- three/fiber geliştiricilerinin resmi kaynağını kullanalım.
Dönüştürücüye gitmek, dönüştürülen silah.glb dosyasını yüklemenizi gerektirecektir.
Sürükle ve bırak veya Explorer aramasını kullanarak bu dosyayı bulun ve indirin.
Dönüştürücüde, kodunu projemize yeni bir WeaponModel.jsx dosyasına aktaracağımız, bileşenin adını dosyayla aynı adla değiştireceğimiz, oluşturulan reaksiyon bileşenini göreceğiz.
Şimdi oluşturulan modeli sahneye aktaralım. App.jsx dosyasına WeaponModel bileşenini ekleyin.
Sahnemizin bu noktasında nesnelerin hiçbiri gölge düşürmüyor.
Sahnede gölgeleri etkinleştirmek için Canvas bileşenine gölgeler niteliğini eklemeniz gerekir.
Daha sonra yeni bir ışık kaynağı eklememiz gerekiyor. Sahnede zaten ambientLight olmasına rağmen, yönlü bir ışık huzmesine sahip olmadığından nesneler için gölge oluşturamaz. O halde directionLight adında yeni bir ışık kaynağı ekleyelim ve onu yapılandıralım. " Yayınlama " gölge modunu etkinleştirme özelliği castShadow'dur . Bu nesnenin diğer nesnelere gölge düşürebileceğini belirten bu parametrenin eklenmesidir.
Daha sonra Ground bileşenine bir başka özellik olan takeShadow'u ekleyelim, bu da sahnedeki bileşenin gölgeleri kendi üzerinde alıp görüntüleyebilmesi anlamına gelir.
Sahnedeki diğer nesnelere de benzer özellikler eklenmelidir: küpler ve oynatıcı. Küpler için castShadow ve getShadow'u ekleyeceğiz, çünkü hem gölge oluşturabilir hem de gölge alabilirler ve oyuncu için yalnızca castShadow'u ekleyeceğiz.
Player için castShadow'u ekleyelim.
Cube için castShadow'u ve getShadow'u ekleyin.
Şimdi yakından bakarsanız, gölgenin düştüğü yüzey alanının oldukça küçük olduğunu göreceksiniz. Ve bu alanın ötesine geçerken gölge basitçe kesiliyor.
Bunun nedeni, varsayılan olarak kameranın, DirectionLight'tan görüntülenen gölgelerin yalnızca küçük bir alanını yakalamasıdır. Bu görünürlük alanını genişletmek için ilave gölge-kamera-(üst, alt, sol, sağ) niteliklerini ekleyerek directionLight bileşenini kullanabiliriz. Bu nitelikleri ekledikten sonra gölge biraz bulanıklaşacaktır. Kaliteyi artırmak için shadow-mapSize özelliğini ekleyeceğiz.
Şimdi birinci şahıs silah gösterimini ekleyelim. Silah davranışı mantığını ve 3D modelin kendisini içerecek yeni bir Silah bileşeni oluşturun.
import {WeaponModel} from "./WeaponModel.jsx"; export const Weapon = (props) => { return ( <group {...props}> <WeaponModel /> </group> ); }
Bu bileşeni karakterin RigidBody'si ile aynı seviyeye yerleştirelim ve useFrame kancasında kameradan gelen değerlerin konumuna göre konumu ve dönüş açısını ayarlayacağız.
Karakterin yürüyüşünü daha doğal hale getirmek için hareket ederken silaha hafif bir hareket ekleyeceğiz. Animasyonu oluşturmak için kurulu tween.js kütüphanesini kullanacağız.
Silah bileşeni, useRef kancası aracılığıyla ona bir referans ekleyebilmeniz için bir grup etiketine sarılacaktır.
Animasyonu kaydetmek için biraz useState ekleyelim.
Animasyonu başlatmak için bir fonksiyon oluşturalım.
Kod Açıklaması:
- const twSwayingAnimation = new TWEEN.Tween(currentPosition) ... Geçerli konumundan yeni bir konuma "sallanan" bir nesnenin animasyonunu oluşturma.
- const twSwayingBackAnimation = new TWEEN.Tween(currentPosition) ... İlk animasyon tamamlandıktan sonra nesnenin başlangıç konumuna geri dönmesini içeren bir animasyon oluşturma.
- twSwayingAnimation.chain(twSwayingBackAnimation); İki animasyonu birbirine bağlayarak ilk animasyon tamamlandığında ikinci animasyonun otomatik olarak başlamasını sağlar.
UseEffect'te animasyon başlatma fonksiyonunu çağırıyoruz.
Şimdi hareketin gerçekleştiği anı belirlemek gerekiyor. Bu, karakterin yönünün geçerli vektörünü belirleyerek yapılabilir.
Karakter hareketi meydana gelirse animasyonu yenileyeceğiz ve bittiğinde tekrar çalıştıracağız.
Kod Açıklaması:
- const isMoving = yön.uzunluk() > 0; Burada nesnenin hareket durumu kontrol edilir. Yön vektörünün uzunluğu 0'dan büyükse bu, nesnenin bir hareket yönüne sahip olduğu anlamına gelir.
- if (isMoving && isSwayingAnimationFinished) { ... } Bu durum, nesne hareket ediyorsa ve "sallanma" animasyonu bitmişse yürütülür.
App bileşeninde ara animasyonunu güncelleyeceğimiz bir useFrame ekleyelim.
TWEEN.update(), TWEEN.js kitaplığındaki tüm etkin animasyonları günceller. Bu yöntem, tüm animasyonların sorunsuz çalışmasını sağlamak için her animasyon karesinde çağrılır.
Bölüm kodu:
Atışın yapıldığı anı, yani fare tuşuna basıldığı anı tanımlamamız gerekiyor. Bu durumu depolamak için useState'i , silah nesnesine bir referansı depolamak için useRef'i ve fare düğmesine basıp bırakmak için iki olay işleyicisini ekleyelim.
Fare tuşuna basıldığında geri tepme animasyonu uygulayalım. Bu amaçla tween.js kütüphanesini kullanacağız.
Geri tepme kuvveti ve animasyon süresi için sabitler tanımlayalım.
Silah kıpırdatma animasyonunda olduğu gibi, geri tepme ve başlangıç pozisyonuna dönüş animasyonu için iki kullanım Durumu durumu ve animasyon bitiş durumu olan bir durum ekliyoruz.
Rastgele bir geri tepme animasyonu vektörü elde etmek için işlevler oluşturalım - createdRecoilOffset ve createdNewPositionOfRecoil .
Geri tepme animasyonunu başlatmak için bir işlev oluşturun. Ayrıca, her çekimde animasyonun yeniden başlatılması ve yeni bitiş koordinatlarının oluşturulması için "çekim" durumunu bağımlılık olarak belirleyeceğimiz useEffect'i de ekleyeceğiz.
Ve useFrame'e , ateş etmek için fare tuşunu "basılı tutmak" için bir kontrol ekleyelim, böylece ateşleme animasyonu, tuş bırakılana kadar durmaz.
Karakter için "hareketsizlik" animasyonunu gerçekleştirin, böylece oyunun "asılı" olduğu hissi oluşmaz.
Bunu yapmak için useState aracılığıyla bazı yeni durumlar ekleyelim.
Durumdan gelen değerleri kullanmak için "kıpırdatma" animasyonunun başlatılmasını düzeltelim. Buradaki fikir, farklı durumların (yürüme veya durma) animasyon için farklı değerler kullanması ve her seferinde animasyonun ilk olarak başlatılmasıdır.
Bu bölümde sahne oluşturma ve karakter hareketlerini uyguladık. Ayrıca bir silah modeli, ateş ederken ve boştayken geri tepme animasyonu ekledik. Bir sonraki bölümde yeni işlevler ekleyerek oyunumuzu geliştirmeye devam edeceğiz.
Burada da yayınlandı.