<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>agb - a rust framework for making Game Boy Advance games</title> <style> *, *::before, *::after { box-sizing: border-box; } body { font: 1.2em/1.62 sans-serif; background-color: white; font-size: 1.5rem; line-height: 1.6; min-height: 100vh; display: flex; flex-direction: column; margin: 0; } h1, h2, h3 { line-height: 1.2; } .gameDisplay > div { display: flex; justify-content: center; height: 100%; } .gameDisplay { height: clamp(480px, 40vh, calc(100vw / 3)); max-width: 100vw; margin-top: 20px; overflow: hidden; } .gameDisplay .imageWrapper { aspect-ratio: 15 / 31; height: 100%; } .gameDisplay .imageWrapper > img { height: 100%; image-rendering: pixelated; } .gameDisplay iframe { border: 0; height: 100%; max-width: 100vw; aspect-ratio: 240 / 160; } header, .desktopHelp { max-width: 60%; margin-left: auto; margin-right: auto; margin-block-end: 40px; } .red { background-color: #f5755e; flex-grow: 1; } .links { margin-left: auto; margin-right: auto; display: flex; justify-content: space-around; margin-top: 40px; margin-bottom: 40px; max-width: 40rem; } .links > a { text-decoration: none; color: black; background-color: #fad288; border: solid #fad288 2px; border-radius: 5px; padding: 5px 10px; } .links > a:hover { border: solid black 2px; } .mobileControls { display: flex; gap: 10px; justify-content: center; align-items: center; flex-direction: column; margin-bottom: 40px; } .mobileControls img { image-rendering: pixelated; height: 100%; } .mobileControlsRow { display: flex; align-items: center; justify-content: center; gap: 40px; } @media (max-width: 800px) { .desktopHelp { display: none; } header, .desktopHelp { width: 90%; } } @media (min-width: 800px) { .mobileControls { display: none; } } .mobileControlsBig { height: calc(32px * 6); } .mobileControlsSmall { height: calc(32px * 3); } </style> </head> <body> <header> <h1>agb - a rust framework for making Game Boy Advance games</h1> </header> <section> <div class="gameDisplay"> <div> <div class="imageWrapper"><img src="assets/left.png" /></div> <iframe id="gameFrame" onload="this.contentWindow.focus()" src="mgba/index.html#/assets/combo.gba" ></iframe> <div class="imageWrapper"><img src="assets/right.png" /></div> </div> </div> <div id="mobileControls" class="mobileControls"> <div class="mobileControlsRow mobileControlsSmall"> <img id="mobileL" src="assets/L.png" /> <img id="mobileR" src="assets/R.png" /> </div> <div class="mobileControlsRow mobileControlsBig"> <img id="mobileDpad" src="assets/dpad.png" /> <img id="mobileAb" src="assets/ab.png" /> </div> <div class="mobileControlsRow mobileControlsSmall"> <img id="mobileSelect" src="assets/SELECT.png" /> <img id="mobileStart" src="assets/START.png" /> </div> <div class="mobileControlsRow"> <button id="mobileRestart">Restart</button> </div> </div> <div class="desktopHelp"> <p> Press escape to open the menu where you can view or change controls and restart the game. The game provided is a combination of multiple Game Boy Advance games made using agb, you can press left or right on the main menu to switch game. </p> </div> </section> <section class="red"> <div class="links"> <a href="https://github.com/agbrs/agb">GitHub</a> <a href="book/">Book</a> <a href="https://docs.rs/agb/latest/agb/">Docs</a> </div> </section> <script> const addSimpleButton = (ele, key) => { ele.addEventListener("touchstart", (evt) => pressButton(key)); ele.addEventListener("touchend", (evt) => releaseButton(key)); }; mobileRestart.addEventListener("click", () => { gameFrame.contentWindow.postMessage({ reset: true }); }); const pressButton = (key) => { gameFrame.contentWindow.postMessage({ isPressed: true, button: key }); }; const releaseButton = (key) => { gameFrame.contentWindow.postMessage({ isPressed: false, button: key }); }; mobileControls.addEventListener("touchmove", (evt) => evt.preventDefault() ); mobileControls.addEventListener("contextmenu", (evt) => evt.preventDefault() ); addSimpleButton(mobileL, "L"); addSimpleButton(mobileR, "L"); addSimpleButton(mobileStart, "Start"); addSimpleButton(mobileSelect, "Select"); let previouslyPressedButtons = []; const dpadMovement = (touch) => { const target = touch.target.getBoundingClientRect(); const touchPoint = { x: touch.clientX, y: touch.clientY }; const targetArea = { x: target.left, y: target.top, width: target.width, height: target.height, }; const relativePosition = { x: touchPoint.x - targetArea.x, y: touchPoint.y - targetArea.y, }; const touchedBox = { x: Math.floor(relativePosition.x / (targetArea.width / 3)), y: Math.floor(relativePosition.y / (targetArea.height / 3)), }; const buttonBoxMapping = [ [["Up", "Left"], ["Up"], ["Up", "Right"]], [["Left"], [], ["Right"]], [["Down", "Left"], ["Down"], ["Down", "Right"]], ]; const buttonsToPress = (buttonBoxMapping[touchedBox.y] ?? [])[touchedBox.x] ?? []; for (let oldButton of previouslyPressedButtons) { if (!buttonsToPress.includes(oldButton)) { releaseButton(oldButton); } } for (let newButton of buttonsToPress) { if (!previouslyPressedButtons.includes(newButton)) { pressButton(newButton); } } previouslyPressedButtons = buttonsToPress; }; mobileDpad.addEventListener("touchstart", (evt) => dpadMovement(evt.touches[0]) ); mobileDpad.addEventListener("touchmove", (evt) => dpadMovement(evt.touches[0]) ); mobileDpad.addEventListener("touchend", (evt) => { for (let oldButton of previouslyPressedButtons) { releaseButton(oldButton); } previouslyPressedButtons = []; }); let mobileAbAPress = undefined; const mobileAbMovement = (touch) => { const target = touch.target.getBoundingClientRect(); const touchPoint = { x: touch.clientX, y: touch.clientY }; const targetArea = { x: target.left, y: target.top, width: target.width, height: target.height, }; const relativePosition = { x: touchPoint.x - targetArea.x, y: touchPoint.y - targetArea.y, }; const aPress = relativePosition.x > relativePosition.y; if (aPress !== mobileAbAPress) { if (mobileAbAPress === true) { releaseButton("A"); } else if (mobileAbAPress === false) { releaseButton("B"); } } if (aPress) { pressButton("A"); } else { pressButton("B"); } mobileAbAPress = aPress; }; mobileAb.addEventListener("touchstart", (evt) => mobileAbMovement(evt.touches[0]) ); mobileAb.addEventListener("touchmove", (evt) => mobileAbMovement(evt.touches[0]) ); mobileAb.addEventListener("touchend", (evt) => { if (mobileAbAPress === true) { releaseButton("A"); } else if (mobileAbAPress === false) { releaseButton("B"); } mobileAbAPress = undefined; }); </script> </body> </html>