2024-04-04 10:54:11 +11:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
2024-04-06 05:47:04 +11:00
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
2024-04-04 10:54:11 +11:00
|
|
|
<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;
|
|
|
|
}
|
2024-04-06 05:47:04 +11:00
|
|
|
|
2024-04-04 10:54:11 +11:00
|
|
|
h1,
|
|
|
|
h2,
|
|
|
|
h3 {
|
|
|
|
line-height: 1.2;
|
|
|
|
}
|
2024-04-06 05:47:04 +11:00
|
|
|
|
2024-04-04 10:54:11 +11:00
|
|
|
.gameDisplay > div {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.gameDisplay {
|
2024-04-06 05:47:04 +11:00
|
|
|
height: clamp(480px, 40vh, calc(100vw / 3));
|
2024-04-04 10:54:11 +11:00
|
|
|
max-width: 100vw;
|
|
|
|
margin-top: 20px;
|
2024-04-06 05:47:04 +11:00
|
|
|
overflow: hidden;
|
2024-04-04 10:54:11 +11:00
|
|
|
}
|
|
|
|
|
2024-04-05 03:46:11 +11:00
|
|
|
.gameDisplay .imageWrapper {
|
|
|
|
aspect-ratio: 15 / 31;
|
2024-04-04 10:54:11 +11:00
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
2024-04-05 03:46:11 +11:00
|
|
|
.gameDisplay .imageWrapper > img {
|
2024-04-04 10:54:11 +11:00
|
|
|
height: 100%;
|
|
|
|
image-rendering: pixelated;
|
|
|
|
}
|
|
|
|
|
2024-04-05 03:46:11 +11:00
|
|
|
.gameDisplay iframe {
|
2024-04-04 10:54:11 +11:00
|
|
|
border: 0;
|
|
|
|
height: 100%;
|
|
|
|
max-width: 100vw;
|
|
|
|
aspect-ratio: 240 / 160;
|
|
|
|
}
|
|
|
|
|
|
|
|
header,
|
2024-04-06 05:47:04 +11:00
|
|
|
.desktopHelp {
|
2024-04-04 10:54:11 +11:00
|
|
|
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;
|
|
|
|
}
|
2024-04-06 05:47:04 +11:00
|
|
|
|
|
|
|
.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;
|
|
|
|
}
|
2024-04-06 08:23:35 +11:00
|
|
|
header,
|
|
|
|
.desktopHelp {
|
2024-04-06 12:17:01 +11:00
|
|
|
max-width: 90%;
|
2024-04-06 08:23:35 +11:00
|
|
|
}
|
2024-04-06 05:47:04 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
@media (min-width: 800px) {
|
|
|
|
.mobileControls {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.mobileControlsBig {
|
|
|
|
height: calc(32px * 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
.mobileControlsSmall {
|
|
|
|
height: calc(32px * 3);
|
|
|
|
}
|
2024-04-04 10:54:11 +11:00
|
|
|
</style>
|
|
|
|
</head>
|
2024-04-06 05:47:04 +11:00
|
|
|
|
2024-04-04 10:54:11 +11:00
|
|
|
<body>
|
|
|
|
<header>
|
|
|
|
<h1>agb - a rust framework for making Game Boy Advance games</h1>
|
|
|
|
</header>
|
|
|
|
<section>
|
|
|
|
<div class="gameDisplay">
|
|
|
|
<div>
|
2024-04-05 03:46:11 +11:00
|
|
|
<div class="imageWrapper"><img src="assets/left.png" /></div>
|
2024-04-04 10:54:11 +11:00
|
|
|
<iframe
|
2024-04-06 05:47:04 +11:00
|
|
|
id="gameFrame"
|
2024-04-04 10:54:11 +11:00
|
|
|
onload="this.contentWindow.focus()"
|
2024-04-05 03:57:06 +11:00
|
|
|
src="mgba/index.html#/assets/combo.gba"
|
2024-04-04 10:54:11 +11:00
|
|
|
></iframe>
|
2024-04-05 03:46:11 +11:00
|
|
|
<div class="imageWrapper"><img src="assets/right.png" /></div>
|
2024-04-04 10:54:11 +11:00
|
|
|
</div>
|
|
|
|
</div>
|
2024-04-06 05:47:04 +11:00
|
|
|
<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" />
|
2024-04-06 08:23:47 +11:00
|
|
|
<img id="mobileStart" src="assets/START.png" />
|
2024-04-06 05:47:04 +11:00
|
|
|
</div>
|
|
|
|
<div class="mobileControlsRow">
|
|
|
|
<button id="mobileRestart">Restart</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="desktopHelp">
|
2024-04-04 10:54:11 +11:00
|
|
|
<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>
|
2024-04-04 11:07:10 +11:00
|
|
|
<a href="book/">Book</a>
|
2024-04-06 05:53:38 +11:00
|
|
|
<a href="https://docs.rs/agb/latest/agb/">Docs</a>
|
2024-04-04 10:54:11 +11:00
|
|
|
</div>
|
|
|
|
</section>
|
2024-04-06 05:47:04 +11:00
|
|
|
|
|
|
|
<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");
|
|
|
|
|
2024-04-06 12:14:15 +11:00
|
|
|
let previouslyPressedButtons = new Set();
|
2024-04-06 05:47:04 +11:00
|
|
|
|
|
|
|
const dpadMovement = (touch) => {
|
2024-04-06 12:14:15 +11:00
|
|
|
const currentlyPressed = new Set();
|
|
|
|
for (let touch of touches) {
|
|
|
|
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 button of buttonsToPress) {
|
|
|
|
currentlyPressed.add(button);
|
|
|
|
}
|
|
|
|
}
|
2024-04-06 05:47:04 +11:00
|
|
|
|
|
|
|
for (let oldButton of previouslyPressedButtons) {
|
2024-04-06 12:14:15 +11:00
|
|
|
if (!buttonsToPress.has(oldButton)) {
|
2024-04-06 05:47:04 +11:00
|
|
|
releaseButton(oldButton);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let newButton of buttonsToPress) {
|
2024-04-06 12:14:15 +11:00
|
|
|
if (!previouslyPressedButtons.has(newButton)) {
|
2024-04-06 05:47:04 +11:00
|
|
|
pressButton(newButton);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
previouslyPressedButtons = buttonsToPress;
|
|
|
|
};
|
|
|
|
|
|
|
|
mobileDpad.addEventListener("touchstart", (evt) =>
|
2024-04-06 12:14:15 +11:00
|
|
|
dpadMovement(evt.targetTouches)
|
2024-04-06 05:47:04 +11:00
|
|
|
);
|
|
|
|
|
|
|
|
mobileDpad.addEventListener("touchmove", (evt) =>
|
2024-04-06 12:14:15 +11:00
|
|
|
dpadMovement(evt.targetTouches)
|
2024-04-06 05:47:04 +11:00
|
|
|
);
|
|
|
|
|
|
|
|
mobileDpad.addEventListener("touchend", (evt) => {
|
2024-04-06 12:14:15 +11:00
|
|
|
dpadMovement(evt.targetTouches);
|
|
|
|
});
|
|
|
|
|
|
|
|
mobileDpad.addEventListener("touchcancel", (evt) => {
|
|
|
|
dpadMovement(evt.targetTouches);
|
2024-04-06 05:47:04 +11:00
|
|
|
});
|
|
|
|
|
2024-04-06 12:12:39 +11:00
|
|
|
let mobileAbAPress = new Set();
|
|
|
|
const mobileAbMovement = (touches) => {
|
|
|
|
const currentTouches = new Set();
|
|
|
|
for (let touch of touches) {
|
|
|
|
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;
|
|
|
|
currentTouches.add(aPress ? "A" : "B");
|
|
|
|
}
|
2024-04-06 05:47:04 +11:00
|
|
|
|
2024-04-06 12:12:39 +11:00
|
|
|
for (let oldTouch of mobileAbAPress) {
|
|
|
|
if (!currentTouches.has(oldTouch)) {
|
|
|
|
console.log("Release", oldTouch);
|
|
|
|
releaseButton(oldTouch);
|
2024-04-06 05:47:04 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 12:12:39 +11:00
|
|
|
for (let newTouch of currentTouches) {
|
|
|
|
if (!mobileAbAPress.has(newTouch)) {
|
|
|
|
console.log("Press", newTouch);
|
|
|
|
pressButton(newTouch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mobileAbAPress = currentTouches;
|
2024-04-06 05:47:04 +11:00
|
|
|
};
|
|
|
|
|
2024-04-06 12:12:39 +11:00
|
|
|
mobileAb.addEventListener("touchstart", (evt) => {
|
|
|
|
mobileAbMovement(evt.targetTouches);
|
|
|
|
});
|
2024-04-06 05:47:04 +11:00
|
|
|
|
2024-04-06 12:12:39 +11:00
|
|
|
mobileAb.addEventListener("touchmove", (evt) => {
|
|
|
|
mobileAbMovement(evt.targetTouches);
|
|
|
|
});
|
2024-04-06 05:47:04 +11:00
|
|
|
|
|
|
|
mobileAb.addEventListener("touchend", (evt) => {
|
2024-04-06 12:12:39 +11:00
|
|
|
mobileAbMovement(evt.targetTouches);
|
|
|
|
});
|
2024-04-06 05:47:04 +11:00
|
|
|
|
2024-04-06 12:12:39 +11:00
|
|
|
mobileAb.addEventListener("touchcancel", (evt) => {
|
|
|
|
mobileAbMovement(evt.targetTouches);
|
2024-04-06 05:47:04 +11:00
|
|
|
});
|
|
|
|
</script>
|
2024-04-04 10:54:11 +11:00
|
|
|
</body>
|
|
|
|
</html>
|