SDK Web NUGANUGA
Ton jeu tourne dans une iframe en bac à sable sur https://play.nuganuga.com. Le SDK est le seul canal vers la plateforme — tu n'implémentes jamais toi-même l'authentification, le classement, le partage, la publicité ou l'analytique.
1. Charger le SDK
Toujours depuis ce chemin absolu exact. La plateforme s’occupe de la diffusion et des versions.
<script src="/sdk/nuga-sdk.js"></script>
<script src="./game.js"></script>2. Les quatre appels
const ctx = await Nuga.ready(); // attendre la plateforme
await Nuga.game.start(); // quand le joueur démarre vraiment une partie
await Nuga.score.submit({ score }); // exactement un score numérique par partie
await Nuga.game.finish(); // la plateforme dessine l’écran de résultatNe dessine pas ton propre écran de résultat, bouton rejouer, bouton partager ou classement — la plateforme les affiche après finish(), à l’identique pour chaque jeu.
3. Tout le reste
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. Le manifeste
nuga.json à la racine de ton bundle :
{
"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개의 타겟을 빠르게 클릭하세요." }
}
}Types de classement : HIGH_SCORE, LOW_SCORE, LOW_TIME, HIGH_TIME, CUSTOM. Catégories : REACTION, TIMING, ACCURACY, MEMORY, PUZZLE, LUCK, ARCADE.
5. Règles du bac à sable
La validation statique rejette un bundle qui enfreint l’une d’elles, avant même qu’il atteigne un joueur :
- Aucune URL externe — ni CDN, ni police web, ni image distante. Embarque tout dans le bundle.
- Pas de
eval,new Function,document.writenidocument.cookie. - Pas de réseau à toi :
fetchvers une URL absolue,XMLHttpRequest,WebSocketet les service workers sont bloqués par la CSP autant que par la validation. - Pas de navigation de premier niveau ni de fenêtres surgissantes.
- Bundle sous 5 Mo ; les microjeux devraient rester sous 1 Mo.
- Au moins un appel
Nuga.score.submit()— un jeu sans score ne peut pas être classé. - La coque de la plateforme est fixe : index.html, la feuille de style de la coque et le harnais de game.js doivent rester intacts. Seuls le corps de run() et les styles de #field vous appartiennent.
Télécharger le kit de démarrage ↓
6. Les scores sont vérifiés
Chaque partie reçoit une session côté serveur. Ton jeu ne voit jamais le jeton de session — l'enveloppe de la plateforme le garde et soumet à ta place — et le serveur confronte le score à la durée réelle et aux bornes déclarées dans le manifeste avant qu'il n'entre dans un classement.
Tu peux faire mieux. Tire l'aléatoire de la graine fournie par le serveur et enregistre ce qui s'est réellement passé : le serveur recalcule alors la partie à partir de tes événements au lieu de croire le nombre. Une revendication que ton replay ne soutient pas est refusée d'emblée.
const rng = Nuga.random(); // graine fournie par le serveur — reproductible, contrairement à Math.random()
const target = rng.int(0, 8);
Nuga.replay.record('hit', 172); // un résultat lié au score, jusqu’à 400 par partieDéclare la mécanique dans nuga.json pour l’activer. Sans cela, ton jeu reste classé, sur les seules vérifications de durée et de bornes.
"verification": { "kind": "REPLAY_V1", "mechanic": "REACTION",
"params": { "rounds": 5 } }7. Publier
Compresse la racine du bundle (pas le dossier) et envoie-la dans la console créateur. Validation statique, build, suites runtime, responsive, gameplay et sécurité s’exécutent automatiquement ; la publication se débloque une fois qu’elles passent.