Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Open Sourced WasmBoy Amp-script Demo / Experiment (#202)
* Moved over the amp demo from secret repo * Got the amp demo workflow working * Fixed the amp rollup config for prod * Fixed travis npm i errors maybe added building demos to travis steps * Run the demo build before running tests
- Loading branch information
Showing
11 changed files
with
1,099 additions
and
601 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# wasmBoy-amp | ||
|
||
An Experiment of using the WasmBoy TS Core inside of AMP | ||
|
||
# Amp Script Notes | ||
|
||
- Empty amp script wont build. Should default to a 1x1 to force build | ||
- Need better docs on using the core | ||
- Need to export helper functions used on the core into a seperate npm module | ||
- Images are sanitized :p | ||
|
||
This hack didnt work | ||
|
||
``` | ||
// Create an svg with an image tag (hack around img bad list) | ||
const svg = document.createElement('svg'); | ||
svg.setAttribute('width', 160); | ||
svg.setAttribute('height', 144); | ||
const image = document.createElement('image'); | ||
image.setAttribute('xlink:href', imageDataUrl); | ||
image.setAttribute('x', 0); | ||
image.setAttribute('y', 0); | ||
image.setAttribute('width', 160); | ||
image.setAttribute('height', 144); | ||
svg.appendChild(image); | ||
document.body.appendChild(svg); | ||
``` | ||
|
||
- No classList on nodes | ||
- Cant create amp-image with data uri | ||
- c.replace is not a function :p | ||
- Can't set attribute to a number | ||
- Query selector on element not working? | ||
- Couldn't add event listener to button unless I created it | ||
- Can't append child to element gotten by id | ||
- key down event only works on divs sometimes? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// https://stackoverflow.com/questions/6850276/how-to-convert-dataurl-to-file-object-in-javascript | ||
export default function(dataURI) { | ||
const byteString = atob(dataURI.split(',')[1]); | ||
|
||
const mimeString = dataURI | ||
.split(',')[0] | ||
.split(':')[1] | ||
.split(';')[0]; | ||
|
||
// write the bytes of the string to an ArrayBuffer | ||
const ab = new ArrayBuffer(byteString.length); | ||
const ia = new Uint8Array(ab); | ||
for (var i = 0; i < byteString.length; i++) { | ||
ia[i] = byteString.charCodeAt(i); | ||
} | ||
|
||
return ia; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<!doctype html> | ||
<html ⚡> | ||
<head> | ||
<meta charset="utf-8"> | ||
<link rel="canonical" href="self.html" /> | ||
<meta name="viewport" content="width=device-width,minimum-scale=1"> | ||
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> | ||
|
||
<style amp-custom> | ||
html, | ||
body { | ||
margin: 0; | ||
padding: 0; | ||
font-family: Helvetica, Arial, sans-serif; | ||
background: #edf0f0; | ||
|
||
} | ||
|
||
#root { | ||
display: flex; | ||
position: relative; | ||
justify-content: center; | ||
align-items: center; | ||
flex-wrap: wrap; | ||
|
||
width: 100vw; | ||
height: 100vh; | ||
padding: 10px; | ||
max-width: 800px; | ||
} | ||
|
||
#description { | ||
z-index: 2; | ||
width: 100%; | ||
white-space: pre-line; | ||
} | ||
|
||
#controls { | ||
position: fixed; | ||
top: 0; | ||
left: 0; | ||
width: 100vw; | ||
height: 100vh; | ||
z-index: 1; | ||
opacity: 0; | ||
} | ||
|
||
#wasmboy-svg-output { | ||
display: block; | ||
width: 80%; | ||
border: solid 1px #000; | ||
} | ||
|
||
</style> | ||
|
||
<script async src="https://cdn.ampproject.org/v0.js"></script> | ||
<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script> | ||
</head> | ||
|
||
<body> | ||
|
||
<amp-script layout="container" src="wasmboy-amp.js"> | ||
<div id="root"> | ||
<h1>⚡wAsMP Boy⚡</h1> | ||
<p id="description"> | ||
<a href="https://github.com/torch2424/wasmBoy" target="_blank">WasmBoy</a> TS Core Running within <a href="https://github.com/ampproject/amphtml/tree/master/extensions/amp-script" target="_blank">amp-script</a> and <a href="https://github.com/ampproject/worker-dom" target="_blank">Worker DOM</a>. | ||
Runs slow due to experimenal amp-script / Worker DOM limitations. | ||
|
||
<b>NOTE:</b> | ||
Play on desktop. | ||
You must click the game to control it. | ||
|
||
<b>You must toggle the amp-script experiment:</b> | ||
`AMP.toggleExperiment('amp-script')` in Dev tools console. | ||
|
||
<b>ROM played is: <a href="http://tangramgames.dk/tobutobugirl/" target="_blank">Tobu Tobu Girl</a></b> | ||
|
||
<b>Controls:</b> | ||
Up,Down,Left,Right = Arrow Keys, WASD | ||
A = Z | ||
B = X | ||
Start = Enter | ||
Select = Shift | ||
Play/Pause = Space | ||
</p> | ||
|
||
</div> | ||
</amp-script> | ||
|
||
</body> | ||
|
||
</html> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import getWasmBoyTsCore from '../../dist/core/getWasmBoyTsCore.esm.js'; | ||
|
||
import stringToArrayBuffer from 'string-to-arraybuffer'; | ||
|
||
import dataUriToArray from './dataUriToArray.js'; | ||
import rgbaArrayBufferToSvg from './pixelToSvg.js'; | ||
import romUrl from '../../test/performance/testroms/tobutobugirl/tobutobugirl.gb'; | ||
|
||
let imageDataArray; | ||
|
||
const runTask = async () => { | ||
const WasmBoy = await getWasmBoyTsCore(); | ||
console.log('WasmBoy', WasmBoy); | ||
|
||
// Convert the rom Url to an array buffer | ||
const ROM = dataUriToArray(romUrl); | ||
console.log('Rom', ROM); | ||
|
||
// Clear Memory | ||
for (let i = 0; i <= WasmBoy.byteMemory.length; i++) { | ||
WasmBoy.byteMemory[i] = 0; | ||
} | ||
|
||
// Load the ROM into memory | ||
WasmBoy.byteMemory.set(ROM, WasmBoy.instance.exports.CARTRIDGE_ROM_LOCATION); | ||
|
||
// Config the core | ||
const configParams = [0, 1, 0, 0, 0, 0, 0, 0, 0]; | ||
WasmBoy.instance.exports.config.apply(WasmBoy.instance, configParams); | ||
|
||
const keyMap = { | ||
A: { | ||
active: false, | ||
keyCodes: [90] | ||
}, | ||
B: { | ||
active: false, | ||
keyCodes: [88] | ||
}, | ||
UP: { | ||
active: false, | ||
keyCodes: [38, 87] | ||
}, | ||
DOWN: { | ||
active: false, | ||
keyCodes: [40, 83] | ||
}, | ||
LEFT: { | ||
active: false, | ||
keyCodes: [37, 65] | ||
}, | ||
RIGHT: { | ||
active: false, | ||
keyCodes: [39, 68] | ||
}, | ||
START: { | ||
active: false, | ||
keyCodes: [13] | ||
}, | ||
SELECT: { | ||
active: false, | ||
keyCodes: [16] | ||
} | ||
}; | ||
|
||
let isPlaying = true; | ||
const keyMapEventHandler = (event, shouldActivate) => { | ||
event.preventDefault(); | ||
|
||
// First check for play pause | ||
if (event.keyCode === 32 && !shouldActivate) { | ||
console.log('Togling Play/Pause...'); | ||
isPlaying = !isPlaying; | ||
if (isPlaying) { | ||
play(); | ||
} | ||
return; | ||
} | ||
|
||
Object.keys(keyMap).some(key => { | ||
if (keyMap[key].keyCodes.includes(event.keyCode)) { | ||
if (shouldActivate) { | ||
keyMap[key].active = true; | ||
} else { | ||
keyMap[key].active = false; | ||
} | ||
return true; | ||
} | ||
return false; | ||
}); | ||
}; | ||
|
||
// Create an input handler | ||
const controlsOverlay = document.createElement('input'); | ||
controlsOverlay.setAttribute('id', 'controls'); | ||
|
||
controlsOverlay.addEventListener('keydown', event => keyMapEventHandler(event, true)); | ||
controlsOverlay.addEventListener('keyup', event => keyMapEventHandler(event, false)); | ||
document.body.appendChild(controlsOverlay); | ||
|
||
let frameSkip = 0; | ||
let maxFrameSkip = 2; | ||
|
||
// Start playing the rom | ||
const play = () => { | ||
// Run a frame | ||
WasmBoy.instance.exports.executeFrame(); | ||
|
||
// Render graphics | ||
if (frameSkip >= maxFrameSkip) { | ||
// Reset the frameskip | ||
frameSkip = 0; | ||
|
||
// Remove the old svg element | ||
const oldSvg = document.getElementById('wasmboy-svg-output'); | ||
if (oldSvg) { | ||
oldSvg.remove(); | ||
} | ||
|
||
const imageSvg = rgbaArrayBufferToSvg(160, 144, WasmBoy.byteMemory, WasmBoy.instance.exports.FRAME_LOCATION); | ||
imageSvg.setAttribute('id', 'wasmboy-svg-output'); | ||
document.body.appendChild(imageSvg); | ||
} else { | ||
frameSkip++; | ||
} | ||
|
||
// Handle Input | ||
WasmBoy.instance.exports.setJoypadState( | ||
keyMap.UP.active ? 1 : 0, | ||
keyMap.RIGHT.active ? 1 : 0, | ||
keyMap.DOWN.active ? 1 : 0, | ||
keyMap.LEFT.active ? 1 : 0, | ||
keyMap.A.active ? 1 : 0, | ||
keyMap.B.active ? 1 : 0, | ||
keyMap.SELECT.active ? 1 : 0, | ||
keyMap.START.active ? 1 : 0 | ||
); | ||
|
||
// Wait 16ms for 60fps | ||
if (isPlaying) { | ||
setTimeout(() => play(), 0); | ||
} | ||
}; | ||
play(); | ||
|
||
console.log('Playing ROM...'); | ||
}; | ||
runTask(); |
Oops, something went wrong.