Recetas — juegos elegantes por motor
Recetas copiables para los 5 motores de las plantillas. Todas asumen que ya tienes el cliente conectado:
import { createClient } from '@tikxander/live-events';
const tx = createClient();
Elige tu motor
| Motor | 2D/3D | Física | Curva | Ideal para |
|---|---|---|---|---|
| Vanilla (Canvas/DOM) | 2D | manual | baja | Contadores, metas, textos flotantes, listas/ranking. |
| Phaser | 2D | Arcade/Matter | media | Bolitas-avatar, arcade, colisiones, partículas. |
| PixiJS | 2D | ninguna (render) | media | Miles de sprites, efectos visuales pesados. |
| Babylon.js | 3D | Havok/cannon | alta | Escenas 3D completas, cámaras, luces. |
| Three.js | 3D | manual/cannon | alta | 3D a medida, shaders, arte generativo. |
Regla general: si es UI/2D simple, vanilla o Phaser. Si necesitas muchos sprites, PixiJS. Si es 3D, Babylon (más batería incluida) o Three (más control).
Receta 1 — spawn de avatar al join
Un viewer entra y aparece su avatar.
Vanilla (DOM):
tx.on('join', ({ user }) => {
const el = document.createElement('img');
el.src = user.avatarUrl ?? '/anon.png';
el.className = 'avatar';
el.style.left = Math.random() * innerWidth + 'px';
document.body.appendChild(el);
setTimeout(() => el.remove(), 6000);
});
Phaser:
tx.on('join', ({ user }) => {
const x = Phaser.Math.Between(50, scene.scale.width - 50);
const ball = scene.matter.add.image(x, 0, 'avatar').setCircle(24).setBounce(0.8);
scene.load.image(user.userId, user.avatarUrl); // precarga real fuera del handler
});
Receta 2 — reacción a gift con intensidad por diamantes
La clave es la intensidad diamonds * count.
tx.on('gift', ({ user, diamonds, count }) => {
const intensity = diamonds * count; // 1 rosa = 1; 100 rosas = 100; un gift caro escala
const scale = Math.min(1 + intensity / 50, 6);
burstParticles({ amount: Math.min(intensity, 300), scale, who: user.username });
});
PixiJS (partículas):
function burstParticles({ amount, scale }) {
for (let i = 0; i < amount; i++) {
const p = new PIXI.Sprite(spark);
p.position.set(app.screen.width / 2, app.screen.height / 2);
p.scale.set(0.2 * scale);
const a = Math.random() * Math.PI * 2, v = 2 + Math.random() * 4 * scale;
p.vx = Math.cos(a) * v; p.vy = Math.sin(a) * v;
stage.addChild(p); particles.push(p);
}
}
Receta 3 — texto flotante de chat
tx.on('chat', ({ user, message }) => {
floatText(`${user.displayName}: ${message}`, {
x: Math.random() * innerWidth, y: innerHeight - 40, ttl: 4000,
});
});
Receta 4 — ranking por gifts (acumula tú, el canal no persiste)
const score = new Map();
tx.on('gift', ({ user, diamonds, count }) => {
score.set(user.username, (score.get(user.username) ?? 0) + diamonds * count);
renderLeaderboard([...score].sort((a, b) => b[1] - a[1]).slice(0, 10));
});
Receta 5 — 3D: aparecer un cubo por follow (Three.js)
tx.on('follow', ({ user }) => {
const cube = new THREE.Mesh(geo, mat.clone());
cube.position.set((Math.random() - 0.5) * 8, 4, 0);
scene.add(cube);
bodies.push({ cube, vy: 0 }); // integra gravedad en tu loop
});
Babylon.js (con física Havok):
tx.on('follow', () => {
const sphere = BABYLON.MeshBuilder.CreateSphere('s', { diameter: 1 }, scene);
sphere.position.y = 6;
new BABYLON.PhysicsAggregate(sphere, BABYLON.PhysicsShapeType.SPHERE, { mass: 1 }, scene);
});
Buenas prácticas
- Cuenta el streak una vez: procesa el gift cuando
streakEnd === true(en v1 siempre lo es). - Precarga assets: no cargues texturas dentro del handler de un evento; cachéalas por
userId. - Procesa barato: un live grande emite decenas de eventos/s. Encola y consume en tu game loop.
- Modo demo: con
?txmode=demoel SDK simula eventos; úsalo para la preview de la ficha.