JavaScript az Engine-ben

Az Engine szkript API-ja: az életciklus, a koordináta-csapdák, a három módja egy objektum létrehozásának, és hogyan mozgass egyet anélkül, hogy falakon sétálna át.

Egy mondatban#

Minden entitás hordozhat egy JavaScript szkriptet. A böngésződben fut, a saját lapodon, az élő jelenet ellen, és ez a kanonikus szkriptelési API: a BASIC nyelvjárás, valamint a Python, C# és Rust nézetek a szerkesztőben, mind ugyanarra a felületre fordulnak le.

A kimerítő lista az API referencia: 538 tag 50 névtérben, oldalanként egy, kereséssel. Ez az oldal az, amit érdemes tudni, mielőtt azt olvasnád.

Az életciklus#

function start() {}                     // once, when the entity starts
function update(dt) {}                  // every frame, dt in seconds
function fixedUpdate(dt) {}             // fixed timestep, for physics
function onCollisionEnter(other) {}     // first frame of contact
function onCollisionStay(other) {}      // every frame while touching
function onCollisionExit(other) {}      // contact just ended
function onTriggerEnter(other) {}       // entered a trigger volume
function onTriggerExit(other) {}
function onMessage(msg, data, senderId) {}   // entity-to-entity messaging
function onAnimationEvent(name, data) {}
function onDestroy() {}
function main() {}                      // console-game entry, async allowed

A deltaTime, a time, az entityId és az entity globálisok; a getComponent(type) egy komponenst olvas le arról az entitásról, amelyik birtokolja a szkriptet.

Az ütközések és a triggerek 2D-ben és 3D-ben is elsülnek, és mindkét oldal megkapja a saját érintkezési normálisát: az other.contact.normal a másik entitástól mutat efelé. Ez teszi lehetővé, hogy egy szkript megkülönböztesse, hogy "ráálltam erre" attól, hogy "ez rámállt", egy platformer taposás az other.contact.normal.y > 0.5-öt olvassa.

A két koordináta-csapda#

Mindkettő valódi debugolási időbe kerül, és egyik sem hibázik hangosan.

Egér: oldal versus vászon#

Az input.mousePosition oldal-koordinátákban van. A scene.pick() vászon-koordinátákat vár. Az egyiket a másik helyett átadni nem hibázik látványosan, egy olyan pontot talál el, amely a nézetablak ablakon belüli pozíciójával el van tolva, így egy kattintással mozgó hős valahová elfogadhatóan, de rosszul sétál.

Hívd a scene.pick()-et argumentum nélkül (a kurzorra céloz), vagy használd az input.mouseViewport-ot, amikor a számokra van szükséged.

A virtuális vezérlők vászon-pixelben vannak#

Az input.addVirtualJoystick(id, x, y, size) és az input.addVirtualButton(...) ugyanabba a térbe helyezi a középpontjukat, mint a mouseViewport. Egy joystick egy normalizált X/Y-t ad vissza a radiális holtzónája után; egy gomb held, pressed-this-frame és released-this-frame állapotokat mutat, külön-külön. A mutatójuk elfogott és felhasznált, így egy érintés a vezérlőn nem jelentődik le egyúttal nyers vászon-akcióként is.

Az alapértelmezett joystick egységesített. Az input.getJoystickX/Y() egyetlen, teljes vektort választ ki a képernyőn megjelenő botból, egy fizikai gamepadből, vagy a billentyűzetből (WASD/nyilak), amelyik forrás a legerősebb. Egy tétlen, csatlakoztatott pad ezért nem tiltja le a billentyűzetes mozgást. Az Y tengelye a 2D képernyőteret követi: fel -1.

Három mód egy objektum létrehozására, és ezek nem felcserélhetők#

Mit hoz létreMeddig él
scene.createEntity(def)egy megszerkesztett entitást: megjelenik a Scene Graph-ban és az Inspectorbana projektig, mentve van
game.spawn(type, pos)egy csupasz primitívet: nincs élet, nincs AI, nincs viselkedésa játékmenetig
game.spawnFrom(template, pos, opts)egy teljes entitás másolatát: komponensek, fizikai test, tag-ek, réteg, méret és az összeállított viselkedés-verema játékmenetig

A game.spawn törmelékhez és prototípusokhoz jó, és haszontalan ellenségekhez: egy vele táplált spawner élettelen golyókat termel. A sablon, amit a spawnFrom másol, egy közönséges entitás, amit a szerkesztőben állítasz össze, és a hierarchia szemével kapcsolsz ki: nincs külön formátum, nincs második szerkesztő, így amit látsz, az jelenik meg.

// Once, at start: a missing template should say so, not fail every wave silently
if (!game.hasTemplate('Skeleton')) console.warn('no Skeleton template');

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

A template elfogad egy id-t, egy nevet vagy egy tag-et. A játék közben készült másolatok játékmenet-hatókörűek, így a stop() eltávolítja őket, és a szerkesztő jelenet sosem szennyeződik be.

Egy entitás kikapcsolása azt jelenti: rejtett ÉS néma: a node letiltva, a szkript leállítva, a fizikai test már nem válaszol. A puszta elrejtés egy láthatatlan, de továbbra is támadó ellenséget hagyna maga után. A game.setEnabled(id, on) egy szkriptből vezérli, a szem ikon a hierarchiából.

Valami mozgatása: told a testet, ne teleportálj#

A transform.position minden képkockán való írása falakon, más szörnyeken és a padlón keresztül sétál. Ehelyett a sebességet vezéreld, de csak akkor, ha van mit vezérelni:

if (physics.hasBody) {
  const v = physics.getVelocity();
  physics.setVelocity({ x: vx, y: v.y, z: vz });   // pushes a real body
} else {
  transform.position.x += vx * dt;                 // legitimate for a flyer
}

A physics.setVelocity test nélkül egy olyan mezőt tölt ki, amit senki nem integrál, és az entitás holtan megáll. Ezért létezik a physics.hasBody, és ezért kérdez rá minden mozgó viselkedés a katalógusban ahelyett, hogy egy választ feltételezne. A megállás azt jelenti: a sebesség törlése: a semmittevés a testet tovább siklatja.

Egy pozíció írása most már mozgat egy fizikai testet. A Havok vezérli a transzformációt, sosem fordítva, így a transform.position beállítása egy dinamikus testen régen felülíródott a következő lépésnél: minden teleportálás csendben meghiúsult egy fizikai entitáson. A pozíció-proxy most újraszinkronizálja a testet, ahogy a forgás-proxy mindig is tette.

A pathfinding egy pillanatfelvétel#

A pathfinding.createGrid() egyszer raycastel egy rácsot, ott középpontosítva, ahol meghívták. Ezt a dobozt elhagyva null-t ad vissza, és a legtöbb hívó ekkor egy egyenes vonalra esik vissza, falakon keresztül, szó nélkül.

A rács alapértelmezés szerint újraközéppontosul a kért útra. A setAutoRecenter(false) kilép ebből egy rögzített világon, ahol az újraépítés hiába kerül egy raycast menetbe.

A console.log naplóz. A Print rajzol.#

A konzol a console.log / warn / error, mint bárhol máshol. A BASIC Print a képernyőre rajzol, és minden képkockán meghívódik, a kettő szándékosan külön parancs, és az összekeverésük az, ami megtölt egy konzolt.

Hol fut egy szkript#

A szerkesztőben és egy webes buildben a szkriptek a böngészőben futnak. Egy natív asztali vagy mobil buildben változatlanul futnak egy beágyazott QuickJS futtatókörnyezeten, ugyanaz a forrás, nem egy port.

A szkriptek a böngésző saját adatbázisában tárolódnak, amíg szerkeszted, így egy mentetlen puffer túlél egy újratöltést; mindennel együtt a projektbe mentődnek.

Korlátok és gyakori buktatók#

  • A scene.pick() argumentummal vászon-koordinátákat vár. Lásd fent; ez az egyetlen leggyakoribb rossz-hely hiba.
  • A game.spawn egy primitívet készít, nem egy karaktert. Használd a spawnFrom-ot.
  • Egy test létrejön a játék közben született entitásokhoz. A fizika régen csak a Play gombnál épült fel, így minden, ami azután spawnolt, csendben átesett a világon, miközben a kézzel elhelyezett szomszédai viselkedtek.
  • Nyolc lépés az AI csapattárs plafonja, nem a tiéd. Egy szkriptciklusnak nincs ilyen korlátja; egy elszabadult while lefagyasztja a képkockát, mint bárhol máshol.
  • A másik négy nyelvi nézet ebből van transzpilálva. Egy Python vagy Rust nézet szerkesztése és visszaváltás átmegy a tárolt JavaScripten, lásd a BASIC oldalt, miért.