JavaScript i Engine

Engines skript-API, livssyklusen, koordinatfellene, de tre måtene å lage et objekt på, og hvordan du flytter ett uten å gå gjennom vegger.

På én linje#

Hver entitet kan bære et JavaScript-skript. Det kjører i nettleseren, i din egen fane, mot den levende scenen, og det er det kanoniske skript-API-et: BASIC-dialekten, og Python-, C#- og Rust-visningene i editoren, kompilerer alle ned til den samme overflaten.

Den uttømmende listen er API-referansen: 538 medlemmer i 50 navnerom, én side hver, med søk. Denne siden er det du trenger å vite før du leser den.

Livssyklusen#

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

deltaTime, time, entityId og entity er globale; getComponent(type) leser en komponent fra entiteten som eier skriptet.

Kollisjoner og triggere utløses i både 2D og 3D, og hver side mottar sin egen kontaktnormal: other.contact.normal peker fra den andre entiteten mot denne. Det er det som lar et skript fortelle «jeg landet på den» fra «den landet på meg», en platformer-stomp leser other.contact.normal.y > 0.5.

De to koordinatfellene#

Begge koster reell feilsøkingstid, og ingen av dem feiler høylytt.

Mus: side versus lerret#

input.mousePosition er i side-koordinater (page). scene.pick() vil ha lerret-koordinater (canvas). Å sende den ene i stedet for den andre bommer ikke, den treffer et punkt forskjøvet av viewportens posisjon i vinduet, så en klikk-og-gå-helt går et sted som er plausibelt og feil.

Kall scene.pick() uten noe argument (den sikter mot markøren), eller bruk input.mouseViewport når du trenger tallene.

Virtuelle kontroller er i lerretpiksler#

input.addVirtualJoystick(id, x, y, size) og input.addVirtualButton(...) plasserer sentrene sine i det samme rommet som mouseViewport. En joystick returnerer en normalisert X/Y etter sin radiale dødsone; en knapp eksponerer holdt, trykket-denne-rammen og sluppet-denne-rammen hver for seg. Pekeren deres fanges opp og forbrukes, så en berøring på kontrollen rapporteres ikke også som en rå lerret-handling.

Standard-joysticken er samlet. input.getJoystickX/Y() velger én komplett vektor fra skjerm-spaken, en fysisk kontroller, eller tastaturet (WASD/piltaster), avhengig av hvilken kilde som er sterkest. En inaktiv tilkoblet kontroller deaktiverer derfor ikke tastaturbevegelse. Y-aksen dens følger 2D-skjermrom: opp er -1.

Tre måter å lage et objekt på, og de er ikke utskiftbare#

Hva den lagerLever så lenge som
scene.createEntity(def)en forfattet entitet: den vises i Scene Graph og i Inspectorprosjektet, den lagres
game.spawn(type, pos)en bar primitiv: ingen helse, ingen AI, ingen atferdspilløkten
game.spawnFrom(template, pos, opts)en kopi av en hel entitet: komponenter, fysikk-kropp, tags, lag, skala og den kompilerte atferdsstabelenspilløkten

game.spawn er riktig for rusk og prototyper og ubrukelig for fiender: en spawner matet av den produserer livløse kuler. Malen spawnFrom kopierer, er en ordinær entitet du setter sammen i editoren og slår av med hierarkiets øye: ikke noe eget format, ikke noen ny editor, så det du ser, er det som dukker opp.

// 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' });

template godtar en id, et navn eller en tag. Kopier laget under spilling er begrenset til spilløkten, så stop() fjerner dem, og redigeringsscenen forurenses aldri.

Å slå av en entitet betyr skjult OG stille: node deaktivert, skript stoppet, fysikk-kropp svarer ikke lenger. Å bare skjule den ville latt en usynlig fiende fortsatt treffe deg. game.setEnabled(id, on) styrer det fra et skript, øyeikonet styrer det fra hierarkiet.

Å flytte noe: skyv en kropp, ikke teleporter#

Å skrive transform.position hver ramme går gjennom vegger, gjennom andre monstre og gjennom gulvet. Styr hastigheten i stedet, men bare når det finnes noe å styre:

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
}

physics.setVelocity uten en kropp fyller et felt ingen integrerer, og entiteten stopper dødt. Det er derfor physics.hasBody finnes, og hvorfor hver bevegelig atferd i katalogen stiller spørsmålet i stedet for å anta et svar. Å stoppe betyr å nullstille hastigheten: å ikke gjøre noe lar kroppen fortsette å seile.

Å skrive en posisjon flytter nå faktisk en fysikk-kropp. Havok styrer transformasjonen, aldri omvendt, så å tilordne transform.position på en dynamisk kropp ble tidligere overskrevet på neste steg: hver teleportering feilet stille på en fysisk entitet. Posisjons-proxyen synkroniserer nå kroppen på nytt, slik rotasjons-proxyen alltid har gjort.

Pathfinding er et øyeblikksbilde#

pathfinding.createGrid() raycaster et rutenett én gang, sentrert der den ble kalt. Å forlate den boksen returnerer null, og de fleste kallere faller da tilbake på en rett linje, gjennom vegger, uten et ord.

Rutenettet sentrerer seg på nytt på den forespurte reisen som standard. setAutoRecenter(false) reserverer seg mot det på en fast verden, der gjenoppbygging koster en raycast-omgang for ingenting.

console.log logger. Print tegner.#

Konsollen er console.log / warn / error, som hvor som helst ellers. BASIC-ens Print tegner på skjermen og kalles hver ramme, de to er bevisst separate kommandoer, og å blande dem er det som fyller en konsoll med en rammesløyfe.

Hvor et skript kjører#

I editoren og i et web-bygg kjører skript i nettleseren. I et native desktop- eller mobilbygg kjører de uendret på en innebygd QuickJS-runtime, den samme kilden, ikke en portering.

Skript lagres i nettleserens egen database mens du redigerer, så en ikke- lagret buffer overlever en omlasting; de lagres inn i prosjektet med alt annet.

Begrensninger og vanlige fallgruver#

  • scene.pick() med et argument vil ha lerretkoordinater. Se over; det er den vanligste feilen med feil sted.
  • game.spawn lager en primitiv, ikke en karakter. Bruk spawnFrom.
  • En kropp opprettes for entiteter født midt i spillet. Fysikk ble tidligere bare bygget ved Play, så alt som ble spawnet etterpå, falt stille gjennom verden mens de håndplasserte naboene oppførte seg normalt.
  • Åtte steg er AI-lagkameratens tak, ikke ditt. En skriptsløyfe har ingen slik grense; en løpsk while vil henge rammen som hvor som helst ellers.
  • De fire andre språkvisningene er transpilert fra denne. Å redigere en Python- eller Rust-visning og bytte tilbake går gjennom den lagrede JavaScript-en, se BASIC-siden for hvorfor.