Engine'de JavaScript

Engine'in betik API'si: yaşam döngüsü, koordinat tuzakları, bir nesne oluşturmanın üç yolu ve birini duvarlardan geçmeden nasıl hareket ettireceğiniz.

Tek cümleyle#

Her varlık bir JavaScript betiği taşıyabilir. Tarayıcınızda, kendi sekmenizde, canlı sahneye karşı çalışır ve kanonik betik API'sidir: BASIC lehçesi ve editördeki Python, C# ve Rust görünümlerinin tümü aynı yüzeye derlenir.

Eksiksiz liste API referansıdır: 50 ad alanında 538 üye, her biri için bir sayfa, ve bir arama. Bu sayfa, onu okumadan önce bilmeniz gereken şeydir.

Yaşam döngüsü#

function start() {}                     // bir kez, varlık başladığında
function update(dt) {}                  // her karede, dt saniye cinsinden
function fixedUpdate(dt) {}             // sabit zaman adımı, fizik için
function onCollisionEnter(other) {}     // temasın ilk karesi
function onCollisionStay(other) {}      // temas süren her kare
function onCollisionExit(other) {}      // temas az önce bitti
function onTriggerEnter(other) {}       // bir tetikleyici hacme girildi
function onTriggerExit(other) {}
function onMessage(msg, data, senderId) {}   // varlıklar arası mesajlaşma
function onAnimationEvent(name, data) {}
function onDestroy() {}
function main() {}                      // konsol oyunu girişi, async serbest

deltaTime, time, entityId ve entity, genel değişkenlerdir; getComponent(type), betiği taşıyan varlıktan bir bileşen okur.

Çarpışmalar ve tetikleyiciler hem 2D'de hem 3D'de tetiklenir, ve her taraf kendi temas normalini alır: other.contact.normal, diğer varlıktan bu varlığa doğru işaret eder. Bir betiğin "üzerine indim" ile "üzerime indi" arasında ayrım yapmasını sağlayan şey budur; bir platform oyununda üstüne basma other.contact.normal.y > 0.5 olarak okunur.

İki koordinat tuzağı#

İkisi de gerçek hata ayıklama saatlerine mal olur, ve hiçbiri gürültülü başarısız olmaz.

Fare: sayfa mı, tuval mi#

input.mousePosition, sayfa koordinatlarındadır. scene.pick(), tuval koordinatları ister. Birini diğeri yerine geçirmek hedefi kaçırmaz; görüntü alanının pencere içindeki konumu kadar kaymış bir noktaya çarpar, bu yüzden tıklayarak hareket eden bir kahraman makul ama yanlış bir yere yürür.

scene.pick()'i argümansız çağırın (imleci hedefler), ya da sayılara ihtiyaç duyduğunuzda input.mouseViewport kullanın.

Sanal kontroller tuval piksellerindedir#

input.addVirtualJoystick(id, x, y, size) ve input.addVirtualButton(...), merkezlerini mouseViewport ile aynı uzaya yerleştirir. Bir joystick, radyal ölü bölgesinden sonra normalize edilmiş bir X/Y döndürür; bir düğme basılı tutulan, bu karede basılan ve bu karede bırakılan durumlarını ayrı ayrı sunar. İşaretçileri yakalanır ve tüketilir, bu yüzden kontrol üzerindeki bir dokunuş aynı zamanda ham bir tuval eylemi olarak da bildirilmez.

Varsayılan joystick birleşiktir. input.getJoystickX/Y(), ekrandaki çubuk, fiziksel bir oyun kolu ya da klavyeden (WASD/oklar) hangisi daha güçlüyse ondan tek, tam bir vektör seçer. Bu yüzden bağlı ama boşta duran bir kol klavye hareketini devre dışı bırakmaz. Y ekseni 2D ekran uzayını izler: yukarı -1'dir.

Bir nesne oluşturmanın üç yolu, ve bunlar birbirinin yerine geçmez#

Ne oluştururNe kadar yaşar
scene.createEntity(def)yazarın oluşturduğu bir varlık: Scene Graph'ta ve Inspector'da görünürproje boyunca, kaydedilir
game.spawn(type, pos)çıplak bir ilkel: canı, yapay zekâsı, davranışı yokoyun oturumu boyunca
game.spawnFrom(template, pos, opts)bütün bir varlığın kopyası: bileşenler, fizik gövdesi, etiketler, katman, ölçek ve derlenmiş davranış yığınıoyun oturumu boyunca

game.spawn, moloz ve prototipler için doğrudur ve düşmanlar için işe yaramaz: onunla beslenen bir üretici cansız toplar üretir. spawnFrom'un kopyaladığı şablon, editörde bileşimlediğiniz ve hiyerarşinin gözüyle kapattığınız sıradan bir varlıktır: ayrı bir biçim yok, ikinci bir editör yok, yani gördüğünüz şey görünecek olan şeydir.

// Bir kez, başlangıçta: eksik bir şablon bunu söylemeli, her dalgada sessizce başarısız olmamalı
if (!game.hasTemplate('Skeleton')) console.warn('no Skeleton template');

const mob = game.spawnFrom('Skeleton', { x, y, z }, { tag: 'Enemy' });

template, bir kimlik, bir isim ya da bir etiket kabul eder. Oyun sırasında yapılan kopyalar oyuna özeldir, bu yüzden stop() onları kaldırır ve düzenleme sahnesi hiçbir zaman kirlenmez.

Bir varlığı kapatmak hem görünmez HEM sessiz olması demektir: düğüm devre dışı, betik durdurulmuş, fizik gövdesi artık yanıt vermiyor. Yalnızca gizlemek, görünmez bir düşmanın size vurmaya devam etmesine izin verirdi. game.setEnabled(id, on) bunu bir betikten yönetir, hiyerarşideki göz simgesi de aynısını oradan yapar.

Bir şeyi hareket ettirmek: bir gövdeyi itin, ışınlamayın#

transform.position'ı her karede yazmak, duvarların, diğer canavarların ve zeminin içinden geçer. Bunun yerine hızı sürün, ama yalnızca sürülecek bir şey varsa:

if (physics.hasBody) {
  const v = physics.getVelocity();
  physics.setVelocity({ x: vx, y: v.y, z: vz });   // gerçek bir gövdeyi iter
} else {
  transform.position.x += vx * dt;                 // uçan bir şey için meşru
}

physics.setVelocity, gövdesiz çağrıldığında kimsenin entegre etmediği bir alanı doldurur ve varlık anında durur. physics.hasBody'nin var olmasının, ve katalogdaki her hareketli davranışın bir yanıt varsaymak yerine bu soruyu sormasının nedeni budur. Durmak, hızı iptal etmek demektir: hiçbir şey yapmamak, gövdeyi serbest sürüklenmede bırakır.

Bir konum yazmak artık gerçekten bir fizik gövdesini hareket ettiriyor. Havok, dönüşümü sürer, hiçbir zaman tersini değil: dinamik bir gövde üzerinde transform.position'a atama yapmak, bir sonraki adımda üzerine yazılırdı; yani fiziksel bir varlıkta her ışınlanma sessizce başarısız olurdu. Konum vekili artık gövdeyi yeniden senkronlar, tıpkı dönüş vekilinin her zaman yaptığı gibi.

Yol bulma bir anlık görüntüdür#

pathfinding.createGrid(), çağrıldığı yere ortalanmış bir gridi bir kez ışın taramasıyla oluşturur. O kutudan çıkmak null döndürür ve çoğu çağıran bunun üzerine, hiç ses çıkarmadan, duvarların içinden geçen düz bir çizgiye döner.

Grid, varsayılan olarak istenen yolculuğa yeniden ortalanır. setAutoRecenter(false), yeniden inşanın boşuna bir ışın taraması geçişine mal olduğu sabit bir dünyada bundan vazgeçmenizi sağlar.

console.log günlüğe yazar. Print çizer.#

Konsol, her yerde olduğu gibi console.log / warn / error'dır. BASIC'in Print'i ekrana çizer ve her karede çağrılır; ikisi bilerek ayrılmış komutlardır, ve bunları karıştırmak, bir kare döngüsüyle bir konsolu dolduran şeydir.

Bir betik nerede çalışır#

Editörde ve bir web derlemesinde, betikler tarayıcıda çalışır. Bir native masaüstü ya da mobil derlemede, aynı kaynak, bir liman değil, gömülü bir QuickJS çalışma zamanında değişmeden çalışır.

Betikler, siz düzenlerken tarayıcının kendi veritabanında saklanır; bu yüzden kaydedilmemiş bir arabellek bir yeniden yüklemeden kurtulur. Geri kalan her şeyle birlikte projeye kaydedilirler.

Sınırlar ve sık karşılaşılan arızalar#

  • Bir argümanla scene.pick(), tuval koordinatları ister. Yukarıya bakın; bu, en sık karşılaşılan yanlış-yer hatasıdır.
  • game.spawn, bir karakter değil, bir ilkel üretir. spawnFrom kullanın.
  • Oyun ortasında doğan varlıklar için bir gövde oluşturulur. Fizik eskiden yalnızca Play anında inşa edilirdi, bu yüzden ondan sonra üretilen her şey, elle yerleştirilmiş komşuları normal davranırken sessizce dünyanın içinden düşerdi.
  • Sekiz adım, yapay zekâ takım arkadaşının tavanıdır, sizinki değil. Bir betik döngüsünün böyle bir sınırı yoktur; başıboş bir while, her yerde olduğu gibi kareyi kilitler.
  • Diğer dört dil görünümü, bundan aktarılır. Bir Python ya da Rust görünümünü düzenleyip geri dönmek, saklanan JavaScript üzerinden geçer; nedeni için BASIC sayfasına bakın.