JavaScript i Engine
Engines skript-API: livscykeln, koordinatfällorna, de tre sätten att skapa ett objekt, och hur man flyttar ett utan att gå genom väggar.
I korthet#
Varje entitet kan bära ett JavaScript-skript. Det körs i webbläsaren, i din flik, mot den levande scenen, och det är det kanoniska skript-API:et: BASIC- dialekten, och vyerna Python, C# och Rust i editorn, kompileras alla ner till samma yta.
Den uttömmande listan är API-referensen: 538 medlemmar i 50 namnrymder, en sida var, med en sökning. Den här sidan är vad du behöver veta innan du läser den.
Livscykeln#
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 och entity är globaler; getComponent(type)
läser en komponent från entiteten som äger skriptet.
Kollisioner och triggers utlöses i både 2D och 3D, och varje sida får sin
egen kontaktnormal: other.contact.normal pekar från den andra entiteten mot
den här. Det är vad som låter ett skript skilja "jag landade på den" från "den
landade på mig", en plattformsstamp läser other.contact.normal.y > 0.5.
De två koordinatfällorna#
Båda kostar riktig felsökningstid, och ingen av dem misslyckas högljutt.
Musen: sida kontra canvas#
input.mousePosition är i sid-koordinater. scene.pick() vill ha
canvas-koordinater. Att skicka den ena för den andra missar inte, den
träffar en punkt förskjuten med vyfönstrets position i fönstret, så att en
klicka-för-att-gå-hjälte går någonstans som ser rimligt men fel ut.
Anropa scene.pick() utan argument (den siktar på markören), eller använd
input.mouseViewport när du behöver siffrorna.
Virtuella kontroller är i canvas-pixlar#
input.addVirtualJoystick(id, x, y, size) och input.addVirtualButton(...)
placerar sina mittpunkter i samma rymd som mouseViewport. En joystick
returnerar ett normaliserat X/Y efter sin radiella dödzon; en knapp exponerar
held, pressed-this-frame och released-this-frame separat. Deras pekare
fångas och konsumeras, så att en beröring på kontrollen inte också rapporteras
som en rå canvas-åtgärd.
Standardjoysticken är enhetlig.
input.getJoystickX/Y()väljer en komplett vektor från styrspaken på skärmen, en fysisk handkontroll, eller tangentbordet (WASD/piltangenter), vilken källa som än är starkast. En overksam ansluten handkontroll inaktiverar därför inte tangentbordsrörelse. Dess Y-axel följer 2D-skärmrymden: upp är-1.
Tre sätt att skapa ett objekt, och de är inte utbytbara#
| Vad det skapar | Lever i | |
|---|---|---|
scene.createEntity(def) | en författad entitet: den syns i Scene Graph och Inspector | projektet, den sparas |
game.spawn(type, pos) | en naken primitiv: ingen hälsa, ingen AI, inget beteende | spelsessionen |
game.spawnFrom(template, pos, opts) | en kopia av en hel entitet: komponenter, fysikkropp, taggar, lager, skala och den kompilerade beteendestacken | spelsessionen |
game.spawn är rätt för skräp och prototyper och oanvändbart för fiender:
en spawner matad av den producerar livlösa bollar. Mallen spawnFrom kopierar
är en vanlig entitet du sätter ihop i editorn och slår av med hierarkins öga:
inget separat format, ingen andra editor, så det du ser är det som visas.
// 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 accepterar ett id, ett namn eller en tagg. Kopior gjorda under
spelande är play-scoped, så stop() tar bort dem och redigeringsscenen
förorenas aldrig.
Att stänga av en entitet betyder dold OCH tyst: noden inaktiverad, skriptet stoppat, fysikkroppen svarar inte längre. Att bara dölja skulle lämna en osynlig fiende som fortfarande träffar dig.
game.setEnabled(id, on)styr det från ett skript, ögat styr det från hierarkin.
Att flytta något: knuffa en kropp, teleportera inte#
Att skriva transform.position varje bildruta går genom väggar, genom andra
monster och genom golvet. Styr hastigheten i stället, men bara när det
finns något att styra:
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 utan en kropp fyller ett fält ingen integrerar, och
entiteten stannar dödstopp. Det är varför physics.hasBody finns, och varför
varje rörelsebeteende i katalogen ställer frågan i stället för att anta ett
svar. Att stanna betyder att avbryta hastigheten: att inte göra något
lämnar kroppen glidande.
Att skriva en position rör nu verkligen en fysikkropp. Havok styr transformen, aldrig tvärtom, så att tilldela
transform.positionpå en dynamisk kropp brukade skrivas över vid nästa steg: varje teleport misslyckades tyst på en fysisk entitet. Positionsproxyn synkar nu om kroppen, på samma sätt som rotationsproxyn alltid har gjort.
Pathfinding är en ögonblicksbild#
pathfinding.createGrid() skickar ut strålar över ett rutnät en gång,
centrerat där det anropades. Att lämna den lådan returnerar null, och de
flesta anropare faller då tillbaka på en rak linje, genom väggar, utan ett ord.
Rutnätet centrerar om sig på den begärda resan som standard.
setAutoRecenter(false) väljer bort det på en fast värld, där ombyggnad
kostar ett strålpass för ingenting.
console.log loggar. Print ritar.#
Konsolen är console.log / warn / error, som var som helst annars. BASIC:s
Print ritar på skärmen och anropas varje bildruta, de två är
medvetet separata kommandon, och att blanda ihop dem
är vad som fyller en konsol med en bildruteloop.
Var ett skript körs#
I editorn och i ett webbygge körs skript i webbläsaren. I ett native skrivbords- eller mobilbygge körs de oförändrade på en inbäddad QuickJS-runtime, samma källa, ingen portning.
Skript lagras i webbläsarens egen databas medan du redigerar, så att en osparad buffert överlever en omladdning; de sparas in i projektet med allt annat.
Begränsningar och vanliga fallgropar#
scene.pick()med ett argument vill ha canvas-koordinater. Se ovan; det är den enskilt vanligaste fel-plats-buggen.game.spawnskapar en primitiv, inte en karaktär. AnvändspawnFrom.- En kropp skapas för entiteter födda mitt i spelet. Fysik brukade byggas bara vid Play, så allt som spawnades efteråt föll tyst genom världen medan dess handplacerade grannar betedde sig normalt.
- Åtta steg är AI-lagkamratens tak, inte ditt. En skriptloop har ingen
sådan gräns; en skenande
whilekommer att hänga bildrutan som var som helst annars. - De andra fyra språkvyerna transpileras från den här. Att redigera en Python- eller Rust-vy och byta tillbaka går genom den lagrade JavaScript- koden, se BASIC-sidan för varför.