SDK web de NUGANUGA
Tu juego corre en un iframe aislado en https://play.nuganuga.com. El SDK es el único canal hacia la plataforma: nunca implementás vos la autenticación, la clasificación, el compartir, la publicidad ni la analítica.
1. Cargar el SDK
Siempre desde esta ruta absoluta exacta. La plataforma lo sirve y lo versiona.
<script src="/sdk/nuga-sdk.js"></script>
<script src="./game.js"></script>2. Las cuatro llamadas
const ctx = await Nuga.ready(); // esperar a la plataforma
await Nuga.game.start(); // cuando el jugador arranca de verdad una partida
await Nuga.score.submit({ score }); // exactamente un puntaje numérico por partida
await Nuga.game.finish(); // la plataforma dibuja la pantalla de resultadoNo dibujes tu propia pantalla de resultado, botón de reintento, botón de compartir ni clasificación: la plataforma los renderiza después de finish(), igual para todos los juegos.
3. Todo lo demás
Nuga.game.getContext();
Nuga.player.get();
Nuga.challenge.get();
Nuga.storage.get(key);
Nuga.storage.set(key, value);
Nuga.audio.setMuted(true);
Nuga.locale.get(); // 'en' | 'ko' | ...
Nuga.event.track('level_up', { level: 3 });
Nuga.on('pause', () => pauseLoop());4. El manifiesto
nuga.json en la raíz de tu paquete:
{
"schemaVersion": 1,
"name": "Reaction Hunter",
"slug": "reaction-hunter",
"description": "Click 10 targets as fast as possible.",
"rule": "Tap every target as fast as you can.",
"category": "REACTION",
"orientation": "PORTRAIT",
"duration": { "min": 5, "max": 30 },
"input": ["POINTER", "TOUCH"],
"ranking": { "type": "LOW_TIME", "unit": "ms" },
"entry": "index.html",
"locales": {
"ko": { "name": "리액션 헌터", "description": "10개의 타겟을 빠르게 클릭하세요." }
}
}Tipos de clasificación: HIGH_SCORE, LOW_SCORE, LOW_TIME, HIGH_TIME, CUSTOM. Categorías: REACTION, TIMING, ACCURACY, MEMORY, PUZZLE, LUCK, ARCADE.
5. Reglas del entorno aislado
La validación estática rechaza cualquier paquete que incumpla alguna de estas, antes de que llegue a un jugador:
- Sin URL externas: ni CDN, ni fuentes web, ni imágenes remotas. Empaquetá todo vos.
- Nada de
eval,new Function,document.writenidocument.cookie. - Nada de red propia:
fetcha una URL absoluta,XMLHttpRequest,WebSockety los service workers están bloqueados tanto por la CSP como por la validación. - Sin navegación de nivel superior ni ventanas emergentes.
- Paquete por debajo de 5 MB; los microjuegos deberían quedar por debajo de 1 MB.
- Al menos una llamada a
Nuga.score.submit(): un juego que no produce puntaje no puede clasificar. - La carcasa de la plataforma es fija: index.html, la hoja de estilos de la carcasa y el arnés de game.js deben quedar intactos. Solo el cuerpo de run() y los estilos de #field son tuyos.
6. Los puntajes se verifican
Cada partida recibe una sesión en el servidor. Tu juego nunca ve el token de sesión (lo guarda la cáscara de la plataforma, que envía por vos) y el servidor contrasta el puntaje con la duración real y con los límites declarados en el manifiesto antes de que entre a una clasificación.
Podés hacerlo mejor. Generá la aleatoriedad a partir de la semilla que emite el servidor y registrá lo que pasó de verdad: el servidor va a recalcular la partida con tus eventos en vez de confiar en el número. Un puntaje que tu repetición no respalde se rechaza directamente.
const rng = Nuga.random(); // con semilla del servidor: reproducible, a diferencia de Math.random()
const target = rng.int(0, 8);
Nuga.replay.record('hit', 172); // un resultado relevante para el puntaje, hasta 400 por partidaDeclará la mecánica en nuga.json para activarlo. Sin eso tu juego igual clasifica, solo con las verificaciones de duración y límites.
"verification": { "kind": "REPLAY_V1", "mechanic": "REACTION",
"params": { "rounds": 5 } }7. Publicalo
Comprimí la raíz del paquete (no la carpeta) y subila en la consola de creador. La validación estática, la compilación y las pruebas de runtime, adaptabilidad, jugabilidad y seguridad corren automáticamente; la publicación se desbloquea cuando pasan.