JavaScript v Engine

Skriptovací API Engine: životní cyklus, souřadnicové pasti, tři způsoby, jak vytvořit objekt, a jak ho pohnout, aniž by procházel zdmi.

V jedné větě#

Každá entita může nést skript v JavaScriptu. Běží v prohlížeči, ve vaší záložce, proti živé scéně, a je to kanonické API pro skriptování: dialekt BASIC a pohledy Python, C# a Rust v editoru se všechny kompilují do stejného povrchu.

Vyčerpávající seznam je reference API: 538 členů v 50 jmenných prostorech, jedna stránka na každý, s vyhledáváním. Tato stránka je to, co potřebujete vědět předtím, než si ji přečtete.

Životní cyklus#

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 a entity jsou globální proměnné; getComponent(type) čte komponentu z entity, které skript patří.

Kolize a triggery se spouští v 2D i 3D, a každá strana dostane svůj vlastní kontaktní normál: other.contact.normal míří od té druhé entity k této. Právě to umožňuje skriptu rozlišit "dopadl jsem na to" od "to dopadlo na mě", stomp v plošinovce čte other.contact.normal.y > 0.5.

Dvě souřadnicové pasti#

Obě stojí skutečný čas ladění, a žádná neselhává nahlas.

Myš: page versus canvas#

input.mousePosition je v souřadnicích page. scene.pick() chce souřadnice canvas. Předání jedněch místo druhých nemine úplně, trefí bod posunutý o pozici viewportu v okně, takže hrdina, kterého ovládáte kliknutím, dojde někam věrohodně vypadajícího, ale špatného místa.

Volejte scene.pick() bez argumentu (zamíří na kurzor), nebo použijte input.mouseViewport, když potřebujete čísla.

Virtuální ovládací prvky jsou v pixelech canvasu#

input.addVirtualJoystick(id, x, y, size) a input.addVirtualButton(...) umístí svůj střed do stejného prostoru jako mouseViewport. Joystick vrátí normalizované X/Y po své radiální mrtvé zóně; tlačítko odhaluje odděleně held, pressed-this-frame a released-this-frame. Jejich pointer se zachytí a spotřebuje, takže dotyk na ovládacím prvku se nehlásí zároveň jako syrová akce canvasu.

Výchozí joystick je jednotný. input.getJoystickX/Y() vybere jeden kompletní vektor z ovládacího prvku na obrazovce, fyzického gamepadu, nebo klávesnice (WASD/šipky), podle toho, který zdroj je nejsilnější. Nečinný připojený gamepad tedy nevypíná ovládání klávesnicí. Jeho osa Y sleduje 2D prostor obrazovky: nahoru je -1.

Tři způsoby, jak vytvořit objekt, a nejsou zaměnitelné#

Co vytváříŽije po dobu
scene.createEntity(def)autorskou entitu: objeví se ve Scene Graph a v Inspectoruprojektu, ukládá se
game.spawn(type, pos)holý primitiv: žádné zdraví, žádná AI, žádné chováníherního sezení
game.spawnFrom(template, pos, opts)kopii celé entity: komponenty, fyzikální tělo, tagy, vrstvu, měřítko a zkompilovaný zásobník chováníherního sezení

game.spawn se hodí na trosky a prototypy a je k ničemu pro nepřátele: spawner živený tímto příkazem produkuje netečné koule. Šablona, kterou kopíruje spawnFrom, je obyčejná entita, kterou složíte v editoru a vypnete okem v hierarchii: žádný samostatný formát, žádný druhý editor, takže co vidíte, je to, co se objeví.

// 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 přijímá id, jméno nebo tag. Kopie vytvořené během hraní jsou vázané na hru, takže stop() je odstraní a editační scéna se nikdy neznečistí.

Vypnutí entity znamená skrytá A tichá zároveň: uzel deaktivovaný, skript zastavený, fyzikální tělo už nereaguje. Pouhé skrytí by nechalo neviditelného nepřítele, který vás pořád zasahuje. game.setEnabled(id, on) to řídí ze skriptu, ikona oka to řídí z hierarchie.

Pohyb objektu: postrčte tělo, neteleportujte ho#

Zapisování transform.position každý snímek prochází zdmi, jinými nestvůrami i podlahou. Místo toho řiďte rychlost (velocity), ale jen tam, kde je co řídit:

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 bez těla vyplní pole, které nikdo neintegruje, a entita tvrdě zastaví. Proto existuje physics.hasBody, a proto se každé pohybové chování v katalogu na tuto otázku ptá místo toho, aby předpokládalo odpověď. Zastavení znamená zrušit rychlost: nedělat nic nechá tělo dál klouzat.

Zápis pozice teď fyzikální tělo skutečně pohne. Havok řídí transform, nikdy naopak, takže přiřazení transform.position na dynamické tělo bývalo na dalším kroku přepsáno: každý teleport na fyzikální entitě tiše selhal. Proxy pozice teď tělo znovu synchronizuje, tak jako to vždy dělala proxy rotace.

Pathfinding je snímek v čase#

pathfinding.createGrid() jednou provede raycast mřížky, vystředěný tam, kde byl zavolán. Opuštění toho boxu vrátí null, a většina volajících se pak vrátí k přímé čáře, skrz zdi, beze slova.

Mřížka se ve výchozím stavu znovu vystředí na požadovanou trasu. setAutoRecenter(false) to vypne na pevném světě, kde by přestavba stála raycast navíc zbytečně.

console.log loguje. Print kreslí.#

Konzole je console.log / warn / error, jako kdekoli jinde. Print z BASICu kreslí na obrazovku a volá se každý snímek, ty dva jsou záměrně oddělené příkazy, a jejich záměna je to, co zaplaví konzoli.

Kde skript běží#

V editoru a v buildu pro web skripty běží v prohlížeči. V nativním buildu pro desktop nebo mobil běží beze změny na vestavěném runtime QuickJS, stejný zdroj, ne port.

Skripty se během editace ukládají do vlastní databáze prohlížeče, takže neuložený buffer přežije obnovení stránky; ukládají se do projektu spolu se vším ostatním.

Limity a časté zádrhely#

  • scene.pick() s argumentem chce souřadnice canvasu. Viz výše; je to nejčastější chyba ve špatném místě.
  • game.spawn vytváří primitiv, ne postavu. Použijte spawnFrom.
  • Tělo se vytváří i pro entity vzniklé uprostřed hry. Fyzika se dřív budovala jen při Play, takže cokoli spawnutého později tiše propadalo světem, zatímco jeho ručně umístění sousedé se chovali normálně.
  • Osm kroků je strop AI parťáka, ne váš. Skriptová smyčka žádný takový limit nemá; utržená while zamrazí snímek stejně jako kdekoli jinde.
  • Ostatní čtyři jazykové pohledy jsou transpilované z tohoto. Úprava pohledu Python nebo Rust a přepnutí zpět prochází přes uloženou verzi v JavaScriptu, viz stránka o BASICu pro důvod.