Add GitHub Pages site with wasmboy WebAssembly emulator
This commit is contained in:
+278
@@ -0,0 +1,278 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>A Scream from the Dark — WebAssembly Game Boy</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #0a0a0f;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Courier New', monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h1 {
|
||||
color: #c41e3a;
|
||||
text-shadow: 2px 2px 0 #000, 0 0 20px rgba(196, 30, 58, 0.5);
|
||||
font-size: 2em;
|
||||
letter-spacing: 2px;
|
||||
margin: 0 0 8px 0;
|
||||
text-align: center;
|
||||
}
|
||||
h2 {
|
||||
color: #8b0000;
|
||||
font-size: 0.85em;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
margin: 0 0 24px 0;
|
||||
text-align: center;
|
||||
}
|
||||
canvas {
|
||||
background-color: #000;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
width: 320px;
|
||||
height: 288px;
|
||||
border: 4px solid #2a2a2a;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 30px rgba(196, 30, 58, 0.3),
|
||||
inset 0 0 20px rgba(0,0,0,0.5);
|
||||
display: block;
|
||||
}
|
||||
.controls {
|
||||
margin-top: 20px;
|
||||
background: #1a1a1a;
|
||||
padding: 12px 20px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
border: 1px solid #2a2a2a;
|
||||
}
|
||||
.key {
|
||||
background: #2a2a2a;
|
||||
color: #c41e3a;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
border: 1px solid #3a3a3a;
|
||||
}
|
||||
#mobile-controls {
|
||||
display: none;
|
||||
margin-top: 20px;
|
||||
gap: 20px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
#mobile-controls { display: flex; }
|
||||
.controls { display: none; }
|
||||
}
|
||||
.btn {
|
||||
background: #1a1a1a;
|
||||
color: #c41e3a;
|
||||
border: 2px solid #2a2a2a;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
touch-action: manipulation;
|
||||
user-select: none;
|
||||
}
|
||||
.btn:active { background: #2a2a2a; }
|
||||
.dpad {
|
||||
display: grid;
|
||||
grid-template-columns: 50px 50px 50px;
|
||||
grid-template-rows: 50px 50px 50px;
|
||||
gap: 4px;
|
||||
}
|
||||
.dpad .btn { border-radius: 6px; }
|
||||
.d-up { grid-column: 2; grid-row: 1; }
|
||||
.d-left { grid-column: 1; grid-row: 2; }
|
||||
.d-right { grid-column: 3; grid-row: 2; }
|
||||
.d-down { grid-column: 2; grid-row: 3; }
|
||||
.action-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.btn-round {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.btn-pill {
|
||||
padding: 8px 14px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.start-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
#game-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
#game-container:fullscreen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #0a0a0f;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
gap: 20px;
|
||||
}
|
||||
#game-container:fullscreen canvas {
|
||||
width: 80vmin;
|
||||
height: 72vmin;
|
||||
max-width: 100%;
|
||||
max-height: 75vh;
|
||||
}
|
||||
.btn-fs {
|
||||
margin-top: 15px;
|
||||
background: #1a1a1a;
|
||||
color: #c41e3a;
|
||||
border: 1px solid #2a2a2a;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.btn-fs:hover {
|
||||
background: #2a2a2a;
|
||||
color: #fff;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
color: #555;
|
||||
font-size: 11px;
|
||||
}
|
||||
.footer a { color: #c41e3a; text-decoration: none; }
|
||||
.footer a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>A SCREAM FROM THE DARK</h1>
|
||||
<h2>— Game Boy survival-horror in WebAssembly —</h2>
|
||||
|
||||
<div id="game-container">
|
||||
<canvas id="gameboy"></canvas>
|
||||
|
||||
<button class="btn-fs" id="btn-fullscreen">⛶ Schermo Intero</button>
|
||||
|
||||
<div id="mobile-controls">
|
||||
<div class="dpad">
|
||||
<button class="btn d-up" id="btn-up">▲</button>
|
||||
<button class="btn d-left" id="btn-left">◀</button>
|
||||
<button class="btn d-right" id="btn-right">▶</button>
|
||||
<button class="btn d-down" id="btn-down">▼</button>
|
||||
</div>
|
||||
<div class="start-select">
|
||||
<button class="btn btn-pill" id="btn-select">SELECT</button>
|
||||
<button class="btn btn-pill" id="btn-start">START</button>
|
||||
</div>
|
||||
<div class="action-btns">
|
||||
<button class="btn btn-round" id="btn-b">B</button>
|
||||
<button class="btn btn-round" id="btn-a">A</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls" id="keyboard-controls">
|
||||
<strong>Comandi Tastiera:</strong>
|
||||
<span class="key">↑↓←→</span> muovi ·
|
||||
<span class="key">Z</span> A (salto) ·
|
||||
<span class="key">X</span> B (corsa) ·
|
||||
<span class="key">Enter</span> Start ·
|
||||
<span class="key">Spazio</span> Select
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://github.com/Enne2/ascreamfromthedark-gb">Codice sorgente</a> ·
|
||||
<a href="https://git.enne2.net/enne2/ascreamfromthedark-gb">Mirror Gitea</a> ·
|
||||
GB © 1989 Nintendo · Questo gioco è un homebrew non ufficiale
|
||||
</div>
|
||||
|
||||
<script src="wasmboy.wasm.iife.js"></script>
|
||||
<script>
|
||||
const canvas = document.getElementById('gameboy');
|
||||
const wasmBoyObj = WasmBoy.WasmBoy || WasmBoy;
|
||||
|
||||
wasmBoyObj.setCanvas(canvas);
|
||||
|
||||
fetch('AScreamFromTheDark.gb')
|
||||
.then(res => res.arrayBuffer())
|
||||
.then(romBuffer => {
|
||||
console.log('ROM caricata, avvio emulatore...');
|
||||
return wasmBoyObj.loadROM(new Uint8Array(romBuffer));
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Avvio...');
|
||||
return wasmBoyObj.play();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Errore:', err);
|
||||
});
|
||||
|
||||
// WasmBoy native: Z=A, X=B, Enter=Start, Space=Select
|
||||
const bindButton = (id, keyDef) => {
|
||||
const btn = document.getElementById(id);
|
||||
const trigger = (type) => (e) => {
|
||||
e.preventDefault();
|
||||
let ev;
|
||||
try {
|
||||
ev = new KeyboardEvent(type, { key: keyDef.key, code: keyDef.code, bubbles: true, cancelable: true });
|
||||
} catch (e) {
|
||||
ev = document.createEvent('KeyboardEvent');
|
||||
ev.initKeyboardEvent(type, true, true, window, keyDef.key, 0, false, false, false, false);
|
||||
}
|
||||
document.dispatchEvent(ev);
|
||||
};
|
||||
btn.addEventListener('touchstart', trigger('keydown'), { passive: false });
|
||||
btn.addEventListener('touchend', trigger('keyup'), { passive: false });
|
||||
btn.addEventListener('mousedown', trigger('keydown'));
|
||||
btn.addEventListener('mouseup', trigger('keyup'));
|
||||
btn.addEventListener('mouseleave', trigger('keyup'));
|
||||
};
|
||||
|
||||
bindButton('btn-up', { key: 'ArrowUp', code: 'ArrowUp' });
|
||||
bindButton('btn-down', { key: 'ArrowDown', code: 'ArrowDown' });
|
||||
bindButton('btn-left', { key: 'ArrowLeft', code: 'ArrowLeft' });
|
||||
bindButton('btn-right', { key: 'ArrowRight', code: 'ArrowRight' });
|
||||
bindButton('btn-a', { key: 'z', code: 'KeyZ' });
|
||||
bindButton('btn-b', { key: 'x', code: 'KeyX' });
|
||||
bindButton('btn-start', { key: 'Enter', code: 'Enter' });
|
||||
bindButton('btn-select',{ key: ' ', code: 'Space' });
|
||||
|
||||
// Fullscreen toggle
|
||||
const gameContainer = document.getElementById('game-container');
|
||||
const fsButton = document.getElementById('btn-fullscreen');
|
||||
fsButton.addEventListener('click', () => {
|
||||
if (!document.fullscreenElement) {
|
||||
gameContainer.requestFullscreen().then(() => {
|
||||
fsButton.innerHTML = '✕ Esci Fullscreen';
|
||||
}).catch(err => console.error(`Errore fullscreen: ${err.message}`));
|
||||
} else {
|
||||
document.exitFullscreen().then(() => {
|
||||
fsButton.innerHTML = '⛶ Schermo Intero';
|
||||
});
|
||||
}
|
||||
});
|
||||
document.addEventListener('fullscreenchange', () => {
|
||||
fsButton.innerHTML = document.fullscreenElement ? '✕ Esci Fullscreen' : '⛶ Schermo Intero';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user