Padrão ROM SkyPlay v2.0
Cada jogo é um pacote autocontido — como uma ROM de emulador — em wwwroot/games/{id}/.
Estrutura da ROM
meu-jogo/
├── manifest.json # Header da ROM (obrigatório)
├── engine.js # Lógica do jogo na tela host
├── thumbnail.png # Capa no catálogo
├── pad.css # Estilos custom do controle mobile
├── screen.css # Estilos custom da tela de jogo
└── assets/ # Sprites, sons, fundos...
└── board-bg.png
manifest.json
{
"schemaVersion": "2.0",
"id": "meu-jogo",
"name": "Meu Jogo",
"description": "...",
"players": { "min": 2, "max": 4 },
"display": {
"thumbnail": "thumbnail.png",
"screen": {
"background": "#16213e",
"backgroundImage": "assets/board-bg.png",
"aspectRatio": "16/10",
"stylesheet": "screen.css",
"waitingTitle": "Aguardando...",
"waitingMessage": "Conecte os controles"
},
"host": {
"accentColor": "#6c5ce7",
"sidebarWidth": "320px",
"stylesheet": "host.css"
}
},
"pad": {
"stylesheet": "pad.css",
"theme": {
"background": "#0f0f1a",
"backgroundImage": "assets/pad-bg.png",
"primaryColor": "#6c5ce7",
"accentColor": "#00cec9"
},
"layout": {
"type": "grid",
"columns": 2,
"gap": "12px",
"elements": [
{ "type": "dpad", "id": "dpad", "col": 1, "row": 1, "colSpan": 2, "rowSpan": 2 },
{ "type": "button", "id": "roll", "label": "Rolar", "icon": "🎲", "col": 1, "row": 3 },
{ "type": "joystick", "id": "aim", "label": "Mira", "col": 2, "row": 3 },
{ "type": "slider", "id": "power", "label": "Força", "min": 0, "max": 100, "col": 1, "row": 4, "colSpan": 2 },
{ "type": "toggle", "id": "ready", "label": "Pronto", "col": 1, "row": 5 },
{ "type": "label", "text": "Seu turno!", "col": 1, "row": 6, "colSpan": 2 },
{ "type": "image", "image": "assets/logo.png", "col": 1, "row": 7, "colSpan": 2 }
]
}
},
"engine": {
"type": "threejs",
"script": "engine.js",
"export": "SkyPlayGame",
"dependencies": [
"https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js",
"/js/skyplay-three.js"
]
},
"audio": {
"music": "assets/bgm.ogg",
"volume": { "music": 0.35, "sfx": 0.75 },
"effects": {
"click": "assets/sfx-click.ogg",
"win": "assets/sfx-win.ogg",
"start": "assets/sfx-start.ogg"
}
}
}
Tipos de elemento do pad (pad.layout.elements)
| type | Descrição | Input SignalR |
|---|---|---|
button | Botão com id, label, icon | buttonId + true |
dpad | Direcional ▲◀▶▼ | up/down/left/right |
joystick | Analógico virtual | { x, y } normalizado |
slider | Controle deslizante | valor numérico |
toggle | Interruptor on/off | true/false |
label | Texto estático | — |
image | Imagem da ROM | — |
spacer | Espaço vazio | — |
row / column | Grupo de elementos | — |
Three.js (jogos 3D)
Use engine.type: "threejs" e declare dependências. Helper incluso: /js/skyplay-three.js
SkyPlayThree.mount(container)— cena, câmera, luzes, loopSkyPlayThree.createBoard()— tabuleiro circular 3DSkyPlayThree.createToken(color)— peça 3D
Exemplo completo: ROM sky-pong na biblioteca.
Áudio
Campo opcional audio no manifest. Arquivos ficam na ROM (ex.: assets/sfx-win.ogg). Se o arquivo não existir, a plataforma usa tons sintetizados como fallback.
audio.music— trilha de fundo (loop)audio.effects— mapa id → arquivo (ex.:"win": "assets/sfx-win.ogg")audio.volume—musicesfx(0–1)
Helper global /js/skyplay-audio.js:
SkyPlayAudio.init({ assetBase, audio })SkyPlayAudio.playSfx('win')— efeito localSkyPlayAudio.playSynced(connection, roomId, 'win')— sync multiplayer via hubSkyPlayAudio.playMusic()/stopMusic()- Botão mute na host e no pad; preferência salva em
localStorage
Hub: PlaySound(roomId, soundId) → evento PlaySound para todos na sala.
Presets sintetizados built-in: click, win, guess, round, tick, wrong, start.
Engine JavaScript
Exporte window.SkyPlayGame (ou nome em engine.export):
init(canvas, config)— config.rom traz metadados da ROMonInput(slot, buttonId, value)— inputs do pad customizadogetState()— estado para sync
Manifests v1.0 com controls.buttons e controls.dpad são migrados automaticamente.