L'API ctx
ctx est votre poignée sur la scène en cours — il est passé à init et tout ce qui suit y est
accroché.
Il est délibérément petit. Si vous cherchez quelque chose sans le trouver ici, il y a de bonnes chances que la réponse soit une étape intégrée plutôt qu'une API.
Où vous êtes
ctx.entity | l'objet auquel ce script est attaché |
ctx.scene | la scène à laquelle il appartient |
ctx.space | le monde |
Cycle de vie et événements
ctx.tick((dt, t) => {}); // à chaque image ; dt et t en secondes
ctx.effect(() => {}); // relancé quand ce qu'il lit change ; peut renvoyer un nettoyage
ctx.on(trigger, (payload) => {}); // s'abonner à un déclencheur
ctx.emit(trigger, payload); // en lever un depuis cet objet
Les déclencheurs portent les mêmes noms que pour les événements et les patches. Ceux qui transportent une information utile :
| Déclencheur | Vous obtenez |
|---|---|
on-keydown · on-keyup | { code, ctrl, shift, alt, meta } |
on-state-active · on-state-inactive | { stateId } |
on-collide | { other } — ce que vous avez heurté |
on-divkit-action | { id, … } — quel bouton |
on-game-control | { state } — repos, déplacement, course ou saut |
on-drag · on-pinch · on-rotate | { dx, dy } · { scale } · { angle } |
on-vps-localized | où le visiteur s'est avéré être |
on-launch arrive au moment où votre instance est créée : impossible de le manquer en démarrant
trop tard.
Trouver des objets
ctx.get(id);
ctx.findByName('Door');
ctx.find(LightComponent); // le premier objet avec ces composants
ctx.query(LightComponent, TagsComponent); // tous
ctx.all();
Créer et supprimer
ctx.create({ name: 'Bullet', parent, components: [] });
ctx.spawn(props.bulletModel, { parent });
ctx.destroy(entity);
spawn est le pratique : donnez-lui une ressource et il assemble les composants qui lui
correspondent. Un modèle devient un objet modèle, une image devient un plan texturé, un son devient
une source audio.
Exécuter un comportement intégré
ctx.step('play_animation', { presetId }, { targets: [enemy] });
ctx.startTransition({ durationMs: 400, easing: 'ease-out' }, () => {
// les changements faits ici s'interpolent au lieu de sauter
});
ctx.step exécute n'importe laquelle des étapes intégrées — les
mêmes que vos événements. Animation, changement de state, transitions de scène et transitions sont
à un appel de distance : vous n'avez presque jamais besoin de les réimplémenter.
Navigation
ctx.openScene(sceneOrId);
await ctx.openSpace(spaceRefOrId);
ctx.scenes();
Entrées
Deux couches, pour deux travaux différents.
Les actions nommées lisent les liaisons de touches du projet : un visiteur qui remappe ses touches est respecté.
ctx.input.pressed('jump');
ctx.input.justPressed('fire');
ctx.input.axis('moveX');
Les touches brutes lisent le clavier directement. Une pression dure exactement une image :
lisez-les donc dans ctx.tick.
ctx.keyboard.down('KeyW');
ctx.keyboard.press('Space');
ctx.keyboard.press('ArrowLeft', { every: 200 }); // répétition automatique, en ms
ctx.keyboard.axis('KeyA', 'KeyD'); // -1, 0 ou 1
Une liaison nommée ignore les modificateurs qu'elle ne mentionne pas — sprinter en tenant Maj ne doit pas annuler « avancer ». Un déclencheur de touche fait l'inverse : un modificateur que vous n'avez pas coché veut dire « ne doit pas être tenu ».
Perdre le focus de la fenêtre libère les touches tenues : rien ne reste coincé.
Caméra
ctx.camera.entity(); // l'objet caméra actif
ctx.camera.setActive(target); // changer de caméra ; null restaure celle par défaut
ctx.camera.pose(); // { position, rotation, forward } en coordonnées monde
Le transform de la caméra est la source de vérité dans tous les modes de contrôle : écrivez dedans pour la déplacer, lisez-le pour voir où les contrôles l'ont mise. En modes orbit et première personne, ce sont les contrôles qui possèdent l'orientation : une rotation que vous écrivez est écrasée — la position, elle, est respectée.
Raycast
await ctx.raycast(); // depuis le centre de la caméra
await ctx.raycast({ screen: { x: 0.5, y: 0 }, all: true }); // un point de l'écran, tous les impacts
await ctx.raycast({ origin, direction }); // le rayon de votre choix
await ctx.raycast({ from: entity }); // depuis un objet, le long de son avant
Chaque impact vous donne l'objet, la distance, le point et la normale de surface en coordonnées monde — triés du plus proche au plus loin, un impact par objet.
Le rayon est lancé contre la géométrie réelle côté rendu : la réponse arrive donc à l'image suivante. Les objets invisibles et ceux marqués à ignorer (réticules, gizmos) sont sautés, si bien que ce qui est derrière répond au lieu que le rayon signale un raté.
Physique
ctx.physics.applyImpulse(target, { x: 0, y: 5, z: 0 });
ctx.physics.applyForce(target, vec, point);
ctx.physics.setVelocity(target, vec);
ctx.physics.teleport(target, position, { rotation, keepVelocity });
ctx.physics.setGravity(vec);
ctx.physics.getSpeed(target);
ctx.physics.isSleeping(target);
await ctx.physics.raycast(from, to, { skip: [ctx.entity] });
La physique possède sa position. Utilisez teleport pour le placer et des impulsions ou des forces
pour le déplacer.
Et passez skip quand vous tirez un rayon depuis l'intérieur de votre propre corps, sinon vous
vous toucherez vous-même à chaque fois.
Les lectures viennent du dernier état synchronisé et accusent environ une image de retard — parfait pour « est-ce que je bouge ? », faux pour des calculs instantanés exacts.
Audio
ctx.audio.play(props.hitSound, { at: enemy, volume: 0.6, positional: true });
Chaque appel démarre un son indépendant, ce qui est exactement ce qu'il faut pour des pas, des impacts et des tirs. Le composant audio n'a qu'une voix et se coupera lui-même — ne l'utilisez pas pour des effets.
Retenir des choses
Trois stockages, qui diffèrent par leur portée :
// 1. les valeurs propres à ce script
const store = ctx.store('game', { score: { type: 'number', default: 0 } });
store.set('score', (v) => v + 1);
store.subscribe('score', (v) => {});
// 2. les globals — partagés avec tous les scripts, patches et événements
ctx.setGlobal('level', 3);
ctx.getGlobal('level');
ctx.subscribeGlobal('level', (v) => {});
// 3. les messages entre scripts
ctx.postMessage('enemy-died', { id });
ctx.handleMessage('enemy-died', ({ id }) => {});
| Survit à un changement de scène | Survit au passage entre spaces | Survit à un rechargement | |
|---|---|---|---|
store | oui | non | non |
| globals | oui | oui | non |
| globals liés au space | oui | non — isolés exprès | non |
Aucun de ces trois n'est sauvegardé d'une visite à l'autre. Si quelque chose doit persister, envoyez-le vous-même quelque part tant que vous avez une connexion.
Interface
const ui = ctx.getDivKit(entity);
ui.get('score');
ui.set('score', (v) => v + 1);
ui.subscribe('lives', (v) => {});
ui.onAction('restart', () => {});
Voir Cartes d'interface.
Suite : Ce que vous construirez vraiment — des scripts complets à copier.