lucky / index 25.html
Studytime171's picture
Duplicate from Studytime171/Lalitgangwani
89610f7
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>GBA.js - Mobile Touch Controls</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style>
body {
margin: 0;
padding: 0;
background: #000;
overflow: hidden;
font-family: Arial, sans-serif;
color: white;
touch-action: none;
}
#screen {
display: block;
width: 100%;
height: auto;
image-rendering: pixelated;
}
#touchControls {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 20;
padding: 12px;
box-sizing: border-box;
display: none;
}
.controls-container {
display: flex;
justify-content: space-between;
align-items: flex-end;
height: 100%;
pointer-events: none;
}
button.touch-btn {
background: rgba(80,80,80,0.75);
color: white;
font-weight: bold;
border: none;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.6);
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
user-select: none;
}
#dpad {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 8px;
width: 40%;
max-width: 180px;
aspect-ratio: 1 / 1;
pointer-events: auto;
}
#dpad button { font-size: clamp(2rem, 10vw, 3rem); }
.face-btn {
width: clamp(70px, 20vw, 100px);
height: clamp(70px, 20vw, 100px);
border-radius: 50%;
font-size: clamp(2.2rem, 9vw, 3rem);
pointer-events: auto;
}
#a { background: rgba(220, 40, 40, 0.8); }
#b { background: rgba(40, 120, 220, 0.8); }
.shoulder-btn {
width: clamp(60px, 16vw, 90px);
height: clamp(36px, 10vw, 48px);
font-size: clamp(1.2rem, 5vw, 1.6rem);
border-radius: 10px;
background: rgba(110,110,110,0.8);
}
.action-btn {
width: clamp(70px, 18vw, 100px);
height: clamp(40px, 11vw, 52px);
font-size: clamp(1.1rem, 4.5vw, 1.4rem);
border-radius: 12px;
background: rgba(50, 150, 50, 0.8);
}
#loader-area {
position: absolute;
top: 12px;
left: 12px;
z-index: 30;
background: rgba(0,0,0,0.6);
padding: 12px;
border-radius: 10px;
pointer-events: auto;
}
#status {
margin: 8px 0 0;
font-size: 0.95rem;
}
@media (max-width: 400px) {
#dpad { max-width: 150px; gap: 6px; }
.face-btn { width: 65px; height: 65px; font-size: 2rem; }
.shoulder-btn, .action-btn { width: 55px; height: 35px; font-size: 1rem; }
}
</style>
</head>
<body>
<canvas id="screen" width="480" height="320"></canvas>
<div id="loader-area">
<button onclick="document.getElementById('romInput').click()">Load GBA ROM</button>
<input id="romInput" type="file" accept=".gba" style="display:none;">
<div id="status">Select a .gba file to start</div>
</div>
<div id="touchControls">
<div class="controls-container">
<!-- D-Pad (left) -->
<div id="dpad">
<div></div>
<button id="up">↑</button>
<div></div>
<button id="left">←</button>
<div></div>
<button id="right">β†’</button>
<div></div>
<button id="down">↓</button>
<div></div>
</div>
<!-- Right side: L/R + Select/Start + A/B -->
<div style="display: flex; flex-direction: column; align-items: flex-end; gap: 12px; width: 55%; max-width: 240px; pointer-events: auto;">
<div style="display: flex; gap: 12px; width: 100%; justify-content: flex-end;">
<button id="l" class="shoulder-btn">L</button>
<button id="r" class="shoulder-btn">R</button>
</div>
<div style="display: flex; gap: 12px; width: 100%; justify-content: flex-end;">
<button id="select" class="action-btn">Select</button>
<button id="start" class="action-btn">Start</button>
</div>
<div style="display: flex; gap: 24px; justify-content: flex-end;">
<button id="b" class="face-btn">B</button>
<button id="a" class="face-btn">A</button>
</div>
</div>
</div>
</div>
<!-- GBA.js scripts from jsDelivr (reliable mirror) -->
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/util.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/core.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/arm.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/thumb.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/mmu.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/io.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/audio.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/video.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/video/software.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/irq.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/keypad.js"></script>
<script src="https://cdn.jsdelivr.net/gh/endrift/gbajs@master/js/gba.js"></script>
<script>
// Global variables
let gba = null;
const canvas = document.getElementById('screen');
const statusEl = document.getElementById('status');
// Initialize emulator
try {
gba = new GameBoyAdvance();
gba.keypad.eatInput = true;
gba.setCanvas(canvas);
gba.logLevel = gba.LOG_ERROR; // Only show real errors
console.log("GBA emulator core ready");
} catch (err) {
statusEl.textContent = "Emulator failed to start: " + err.message;
alert("Emulator init error: " + err.message);
}
// Show touch controls on mobile/touch devices
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
document.getElementById('touchControls').style.display = 'block';
canvas.style.width = '100%';
canvas.style.height = 'auto';
}
// Touch β†’ Keyboard mapping
function pressKey(code) {
window.dispatchEvent(new KeyboardEvent('keydown', {keyCode: code, bubbles: true}));
}
function releaseKey(code) {
window.dispatchEvent(new KeyboardEvent('keyup', {keyCode: code, bubbles: true}));
}
const keyMap = {
up: 38, // ArrowUp
down: 40,
left: 37,
right: 39,
a: 88, // X key β†’ GBA A
b: 90, // Z key β†’ GBA B
l: 65, // A key β†’ GBA L
r: 83, // S key β†’ GBA R
start: 13, // Enter β†’ Start
select: 16 // Shift β†’ Select
};
Object.keys(keyMap).forEach(id => {
const btn = document.getElementById(id);
if (btn) {
btn.addEventListener('touchstart', e => { e.preventDefault(); pressKey(keyMap[id]); });
btn.addEventListener('touchend', e => { e.preventDefault(); releaseKey(keyMap[id]); });
btn.addEventListener('touchcancel', e => { e.preventDefault(); releaseKey(keyMap[id]); });
}
});
// ROM loading
document.getElementById('romInput').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
statusEl.textContent = `Loading ${file.name} (${(file.size / 1024 / 1024).toFixed(1)} MB)...`;
const reader = new FileReader();
reader.onload = function() {
try {
const buffer = reader.result;
if (!buffer || buffer.byteLength < 16384) {
throw new Error("File too small - not a valid GBA ROM");
}
gba.loadRom(buffer, success => {
if (success) {
statusEl.textContent = "ROM loaded β†’ starting game...";
gba.runStable();
setTimeout(() => {
document.getElementById('loader-area').style.display = 'none';
}, 1200);
} else {
throw new Error("ROM load failed (invalid format?)");
}
});
} catch (err) {
statusEl.textContent = "Error: " + err.message;
alert("Failed to load ROM:\n" + err.message + "\n\nTry a clean .gba file (not zipped).");
console.error(err);
}
};
reader.onerror = () => {
statusEl.textContent = "Cannot read file";
alert("File read error - file may be damaged or browser blocked access.");
};
reader.readAsArrayBuffer(file);
});
</script>
</body>
</html>