Orta Dünya'yı Mobil WebGL ile Hayata Getirmek
Geçmişte, mobil cihazlara ve tabletlere etkileşimli, web tabanlı, çok fazla multimedya içeren deneyimler sunmak zordu. Temel kısıtlamalar performans, API kullanılabilirliği, cihazlardaki HTML5 sesindeki sınırlamalar ve satır içi videonun sorunsuz oynatılmamasıydı.
Bu yılın başlarında, Google ve Warner Bros.'taki arkadaşlarımızla birlikte yeni Hobbit filmi Hobbit: Smaug'un Çöküşü için mobil öncelikli bir web deneyimi oluşturmaya yönelik bir proje başlattık. Multimedya ağırlıklı bir mobil Chrome denemesi oluşturmak gerçekten ilham verici ve zorlu bir görevdi.
Bu deneyim, artık WebGL ve Web Audio'ya erişebildiğimiz yeni Nexus cihazlardaki Android için Chrome'a göre optimize edilmiştir. Ancak deneyimin büyük bir kısmı, donanım hızlandırmalı kompozisyon ve CSS animasyonlar sayesinde WebGL olmayan cihazlarda ve tarayıcılarda da kullanılabilir.
Tüm deneyim, Orta Dünya haritasına ve Hobbit filmlerindeki konumlara ve karakterlere dayanır. WebGL'yi kullanarak Hobbit üçlemesinin zengin dünyasını dramatize edip keşfedebildik ve deneyimi kullanıcıların kontrol etmesine olanak tanıdık.
Mobil cihazlarda WebGL'nin zorlukları
Öncelikle, "mobil cihazlar" terimi çok geniştir. Cihazların teknik özellikleri çok çeşitlidir. Bu nedenle, geliştirici olarak daha az karmaşık bir deneyimle daha fazla cihazı desteklemek isteyip istemediğinize veya bu örnekte yaptığımız gibi desteklenen cihazları daha gerçekçi bir 3D dünya gösterebilen cihazlarla sınırlamak isteyip istemediğinize karar vermeniz gerekir. "Yüzüklerin Efendisi Yolculuğu" için Nexus cihazlara ve beş popüler Android akıllı telefona odaklandık.
Deneyde, önceki WebGL projelerimizin bazılarında yaptığımız gibi three.js'i kullandık. Uygulamaya, Trollshaw oyununun Nexus 10 tablette iyi çalışacak ilk sürümünü oluşturarak başladık. Cihaz üzerinde ilk testlerden sonra, düşük özellikli bir dizüstü bilgisayarda kullandığımıza benzer optimizasyonlar yapmayı planladık:
- Düşük poligonlu modeller kullanın
- Düşük çözünürlüklü dokular kullanın
- Geometriyi birleştirerek çizim çağrısı sayısını olabildiğince azaltın.
- Malzemeleri ve ışığı basitleştirin
- Sonraki efektleri kaldırma ve kenar yumuşatma özelliğini devre dışı bırakma
- JavaScript performansını optimize etme
- WebGL kanvası yarı boyutta oluşturulur ve CSS ile ölçeklendirilir.
Bu optimizasyonları oyunun ilk kaba sürümüne uyguladıktan sonra, memnun kaldığımız sabit bir 30 FPS kare hızına ulaştık. O dönemdeki hedefimiz, kare hızını olumsuz etkilemeden görselleri iyileştirmekti. Birçok hileyi denedik: Bazılarının performans üzerinde gerçekten etkisi olduğu ortaya çıktı; bazılarının ise umduğumuz kadar büyük bir etkisi olmadı.
Düşük poligonlu modeller kullanın
Modellerle başlayalım. Düşük poligonlu modeller kullanmak, indirme süresinin yanı sıra sahnenin başlatılması için gereken süreyi de kısaltır. Performansı çok fazla etkilemeden karmaşıklığı önemli ölçüde artırabileceğimizi fark ettik. Bu oyunda kullandığımız troll modelleri yaklaşık 5.000 yüze sahip. Sahne ise yaklaşık 40.000 yüze sahip ve bu şekilde sorunsuz çalışıyor.

Deneyimin başka bir yerinde (henüz yayınlanmamış) poligon sayısını azaltmanın performans üzerinde daha fazla etkisi olduğunu gördük. Bu durumda, mobil cihazlar için masaüstüne yüklediğimizden daha az poligonlu nesneler yükledik. Farklı 3D model grupları oluşturmak biraz ek çalışma gerektirir ve her zaman gerekli değildir. Bu, öncelikle modellerinizin ne kadar karmaşık olduğuna bağlıdır.
Çok sayıda nesnenin bulunduğu büyük sahneler üzerinde çalışırken geometriyi nasıl böleceğimiz konusunda stratejik olmaya çalıştık. Bu sayede, tüm mobil cihazlarda çalışan bir ayar bulmak için daha az önemli ağları hızlıca açıp kapatabildik. Ardından, dinamik optimizasyon için çalışma zamanında JavaScript'te geometriyi birleştirmeyi veya istekleri kaydetmek için ön prodüksiyonda birleştirmeyi seçebiliriz.
Düşük çözünürlüklü dokular kullanın
Mobil cihazlardaki yükleme süresini azaltmak için masaüstündekilerin yarısı boyutunda farklı dokular yüklemeyi tercih ettik. Tüm cihazlar 2048x2048 piksel boyutuna kadar dokunma boyutlarını işleyebilir ve çoğu 4096x4096 piksel boyutunu işleyebilir. Tek tek dokularda GPU'ya yüklendikten sonra doku araması yapmak sorun oluşturmuyor. Dokuların sürekli olarak yüklenip indirilmesini önlemek için dokuların toplam boyutu GPU belleğine sığmalıdır ancak bu durum çoğu web deneyimi için büyük bir sorun oluşturmaz. Ancak dokuları mümkün olduğunca az sprite sayfasında birleştirmek, çizim çağrısı sayısını azaltmak için önemlidir. Bu, mobil cihazlardaki performansı büyük ölçüde etkiler.

(orijinal boyut 512x512 piksel)
Malzemeleri ve ışığı basitleştirin
Materyal seçimi de performansı büyük ölçüde etkileyebilir ve mobil cihazlarda akıllıca yönetilmelidir. Performansı optimize etmek için kullandığımız yöntemlerden biri, three.js'de MeshPhongMaterial
(tesel başına ışık hesaplaması) yerine MeshLambertMaterial
(köşe başına ışık hesaplaması) kullanmaktır. Temel olarak, mümkün olduğunca az ışıklandırma hesaplaması içeren basit gölgelendiriciler kullanmaya çalıştık.
Kullandığınız materyallerin bir sahnenin performansını nasıl etkilediğini görmek için sahnenin materyallerini MeshBasicMaterial
ile geçersiz kılabilirsiniz . Bu, iyi bir karşılaştırma yapmanıza olanak tanır.
scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});
JavaScript performansını optimize etme
Mobil cihazlar için oyun geliştirirken GPU her zaman en büyük engel değildir. Özellikle fizik ve iskelet animasyonları olmak üzere CPU'da çok fazla zaman harcanır. Simülasyona bağlı olarak, bu pahalı hesaplamaları yalnızca her iki karede bir çalıştırmak bazen faydalı olabilir. Nesne havuzu, çöp toplama ve nesne oluşturma ile ilgili olarak mevcut JavaScript optimizasyon tekniklerini de kullanabilirsiniz.
Yeni nesneler oluşturmak yerine önceden ayrılmış nesneleri döngülerde güncellemek, oyun sırasındaki "takılma" sorunlarını önlemek için önemli bir adımdır.
Örneğin, aşağıdaki gibi bir kod düşünün:
var currentPos = new THREE.Vector3();
function gameLoop() {
currentPos = new THREE.Vector3(0+offsetX,100,0);
}
Bu döngünün iyileştirilmiş bir sürümü, çöp toplanması gereken yeni nesneler oluşturulmasını önler:
var originPos = new THREE.Vector3(0,100,0);
var currentPos = new THREE.Vector3();
function gameLoop() {
currentPos.copy(originPos).x += offsetX;
//or
currentPos.set(originPos.x+offsetX,originPos.y,originPos.z);
}
Etkinlik işleyiciler mümkün olduğunca yalnızca özellikleri güncellemeli ve requestAnimationFrame
render-loop'un sahneyi güncellemesine izin vermelidir.
Işın izleme işlemlerini optimize etmek ve/veya önceden hesaplamak da iyi bir fikirdir. Örneğin, statik bir yol hareketi sırasında bir nesneyi bir ağaca eklemeniz gerekiyorsa bir döngü sırasında konumları "kaydedip" ardından ağaca ışın izlemek yerine bu verilerden okuyabilirsiniz. Dilerseniz Rivendell deneyiminde yaptığımız gibi, daha basit bir düşük poli görünmez örgüyle fare etkileşimlerini aramak için ışın atma yöntemini de kullanabilirsiniz. Yüksek poligonlu bir ağda çarpışma aramak çok yavaştır ve genel olarak oyun döngüsünde bu işlemden kaçınılmalıdır.
WebGL kanvası yarı boyutta oluşturulur ve CSS ile ölçeklendirilir.
WebGL kanvası boyutu, performansı optimize etmek için değiştirebileceğiniz en etkili parametredir. 3D sahnenizi çizmek için kullandığınız kanvas ne kadar büyükse her karede o kadar fazla piksel çizilmesi gerekir. Bu durum elbette performansı etkiler.Yüksek yoğunluklu 2560x1600 piksel ekrana sahip Nexus 10'un, düşük yoğunluklu bir tabletten 4 kat daha fazla piksel işlemesi gerekir. Bunu mobil cihazlar için optimize etmek amacıyla, kanvasın boyutunu yarıya (%50) indirip ardından donanım hızlandırmalı CSS 3D dönüştürmeleriyle istenen boyuta (%100) ölçeklendirdiğimiz bir hileyi kullanırız. Bunun dezavantajı, ince çizgilerin sorun oluşturabileceği pikselli bir görüntüdür ancak yüksek çözünürlüklü bir ekranda bu etki o kadar da kötü değildir. Bu, ek performansa kesinlikle değer.

Yapı taşı olarak nesneler
Dol Guldur kalesinin büyük labirentini ve sonu gelmeyen Rivendell vadisini oluşturmak için yeniden kullandığımız bir dizi yapı taşı 3D modeli yaptık. Nesneleri yeniden kullanmak, nesnelerin deneyimin ortasında değil, başında oluşturulmasını ve yüklenmesini sağlar.

Rivendell'de, kullanıcının yolculuğu ilerledikçe Z derinliğinde sürekli olarak yeniden konumlandırdığımız çeşitli zemin bölümleri vardır. Kullanıcı bölümlerden geçerken bu bölümler uzak mesafeye yeniden yerleştirilir.
Dol Guldur kalesi için labirentin her oyunda yeniden oluşturulmasını istedik. Bunun için labirenti yeniden oluşturan bir komut dosyası oluşturduk.
Yapının tamamını baştan tek bir büyük ağda birleştirmek çok büyük bir sahne ve düşük performansla sonuçlanır. Bu sorunu gidermek için yapı taşlarını, görünümde olup olmadıklarına göre gizlemeye ve göstermeye karar verdik. Başından beri 2D bir ışın tarayıcı komut dosyası kullanma fikrimiz vardı ancak sonunda yerleşik three.js frustum küçültme özelliğini kullandık. Oyuncunun karşılaştığı "tehlikeyi" yakınlaştırmak için ışın ışını komut dosyasını yeniden kullandık.
Ele alınması gereken bir diğer önemli konu da kullanıcı etkileşimidir. Masaüstünde fare ve klavye girişiniz vardır; mobil cihazlarda ise kullanıcılarınız dokunma, kaydırma, sıkıştırma, cihaz yönü vb. ile etkileşim kurar.
Mobil web deneyimlerinde dokunma etkileşimini kullanma
Dokunma desteği eklemek zor değildir. Bu konuyla ilgili harika makaleler var. Ancak bu süreci daha karmaşık hale getirebilecek bazı küçük şeyler vardır.
Hem dokunmatik ekranı hem de fareyi aynı anda kullanabilirsiniz. Chromebook Pixel ve dokunmatik özellikli diğer dizüstü bilgisayarlarda hem fare hem de dokunma desteği bulunur. Yaygın olarak görülen bir hata, cihazın dokunmatik olup olmadığını kontrol ettikten sonra yalnızca dokunmatik etkinlik işleyicileri ekleyip fare için hiçbir işleyici eklememektir.
Etkinlik işleyicilerinde oluşturmayı güncellemeyin. Bunun yerine dokunma etkinliklerini değişkenlere kaydedin ve requestAnimationFrame oluşturma döngüsünde bunlara tepki verin. Bu, performansı iyileştirir ve çakışan etkinlikleri birleştirir. Etkinlik işleyicilerinde yeni nesneler oluşturmak yerine nesneleri yeniden kullandığınızdan emin olun.
Çoklu dokunma olduğunu unutmayın: event.touches, tüm dokunmaların bir dizisidir. Bazı durumlarda, event.targetTouches veya event.changedTouches'e bakmak ve yalnızca ilgilendiğiniz dokunuşlara tepki vermek daha ilginç olabilir. Dokunmaları kaydırmalardan ayırmak için dokunmanın hareket edip etmediğini (kaydırma) veya sabit durduğunu (dokunma) kontrol etmeden önce bir gecikme kullanırız. İki ilk dokunma arasındaki mesafeyi ve bu mesafenin zaman içinde nasıl değiştiğini ölçerek sıkıştırma işlemini algılarız.
3D bir dünyada, kameranızın fare ve kaydırma hareketlerine nasıl tepki vereceğine karar vermeniz gerekir. Kamera hareketi eklemenin yaygın bir yolu, fare hareketini takip etmektir. Bu işlem, fare konumu kullanılarak doğrudan kontrol veya delta hareketi (konum değişikliği) ile yapılabilir. Mobil cihazlarda her zaman masaüstü tarayıcıyla aynı davranışı istemezsiniz. Her sürüm için en uygun deneyimi belirlemek üzere kapsamlı testler yaptık.
Daha küçük ekranlar ve dokunmatik ekranlarla çalışırken kullanıcının parmaklarının ve kullanıcı arayüzü etkileşim grafiklerinin genellikle göstermek istediğiniz şeyin önünde olduğunu fark edersiniz. Bu, yerel uygulamaları tasarlarken alışageldiğimiz bir şeydir ancak daha önce web deneyimlerinde bu konuyu düşünmek zorunda kalmamıştık. Bu, tasarımcılar ve kullanıcı deneyimi tasarımcıları için gerçek bir zorluktur.
Özet
Bu projedeki genel deneyimimiz, mobil cihazlarda WebGL'nin özellikle yeni ve üst düzey cihazlarda çok iyi çalıştığı yönünde. Performans söz konusu olduğunda, poligon sayısı ve doku boyutu çoğunlukla indirme ve başlatma sürelerini etkiler. Ayrıca, malzemeler, gölgelendiriciler ve WebGL kanvası boyutu, mobil performans için optimize edilmesi gereken en önemli parçalardır. Ancak performansı etkileyen şey parçaların toplamıdır. Bu nedenle, optimizasyon için yapabileceğiniz her şey önemlidir.
Mobil cihazları hedeflemek, dokunma etkileşimlerini düşünmeye alışmanız gerektiği anlamına da gelir. Bu, yalnızca piksel boyutuyla değil, ekranın fiziksel boyutuyla da ilgilidir. Bazı durumlarda, neler olduğunu gerçekten görmek için 3D kamerayı yaklaştırmamız gerekiyordu.
Deneme başlatıldı ve bu muhteşem bir yolculuk oldu. Keyifle izleyeceğinizi umuyoruz.
Denemek ister misiniz? Kendi Orta Dünya yolculuğunuza çıkın.