Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add standalone player example to playground #2156

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added src/playground/playground.sb3
Binary file not shown.
69 changes: 69 additions & 0 deletions src/playground/standalone-player.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<style>
.body {
height: 100%;
margin: 0;
overflow: hidden;
text-align: center;
background-color: black;
color: #cccccc;
}

.stage-container {
display: inline-block;
width: 133.33333vh;
height: 100vh;
margin: 0 auto;
}

.stage {
touch-action: manipulation;
width: inherit;
}

.hidden {
visibility: hidden;
}

#overlay {
position: absolute;
height: inherit;
width: inherit;
background-color: rgba(0, 0, 0, .35);
display: flex;
justify-content: center;
align-items: center;
}

.green-flag-container {
width: 15vh;
height: 15vh;
}

@media ( max-aspect-ratio: 4/3 ) {
.stage-container {
width: 100vw;
height: 75vw;
margin-top: calc(50vh - 37.5vw)
}
}
</style>
</head>
<body class="body">
<div class="stage-container">
<div id="overlay" class="hidden">
<div class="green-flag-container">
<svg id="green-flag" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16.63 17.5"><defs><style>.cls-1,.cls-2{fill:#4cbf56;stroke:#45993d;stroke-linecap:round;stroke-linejoin:round;}.cls-2{stroke-width:1.5px;}</style></defs><title>icon--green-flag</title><path class="cls-1" d="M.75,2A6.44,6.44,0,0,1,8.44,2h0a6.44,6.44,0,0,0,7.69,0V12.4a6.44,6.44,0,0,1-7.69,0h0a6.44,6.44,0,0,0-7.69,0"/><line class="cls-2" x1="0.75" y1="16.75" x2="0.75" y2="0.75"/>
</svg>
</div>
</div>
<canvas class="stage" id="scratch-stage" width="480" height="360"></canvas>
</div>

<script src="./standalone-player.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
157 changes: 157 additions & 0 deletions src/playground/standalone-player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
const AudioEngine = require('scratch-audio');
const ScratchStorage = require('scratch-storage');
const ScratchRender = require('scratch-render');
const ScratchSVGRenderer = require('scratch-svg-renderer');
const VirtualMachine = require('..');

// This file is an example of how to create a standalone, full screen
// minimal scratch player without the editor view.
// This file does not presentally include monitors, which are drawn by the GUI.

/**
* @param {Asset} projectAsset - calculate a URL for this asset.
* @returns {string} a URL to download a project file.
*/
const projectGetConfig = function (projectAsset) {
return `https://projects.scratch.mit.edu/${projectAsset.assetId}`;
};

/**
* @param {Asset} asset - calculate a URL for this asset.
* @returns {string} a URL to download a project asset (PNG, WAV, etc.)
*/
const assetGetConfig = function (asset) {
return `https://assets.scratch.mit.edu/internalapi/asset/${asset.assetId}.${asset.dataFormat}/get/`;
};

window.onload = function () {

// Get the project id from the hash, or use the default project.
let projectId;
if (window.location.hash) {
projectId = window.location.hash.substring(1);
}

// Instantiate the VM.
const vm = new VirtualMachine();
vm.attachV2BitmapAdapter(new ScratchSVGRenderer.BitmapAdapter());
vm.attachV2SVGAdapter(new ScratchSVGRenderer.SVGRenderer());

// Initialize storage
const storage = new ScratchStorage();
const AssetType = storage.AssetType;
storage.addWebStore([AssetType.Project], projectGetConfig);
storage.addWebStore([AssetType.ImageVector, AssetType.ImageBitmap, AssetType.Sound], assetGetConfig);

vm.attachStorage(storage);

// Compatibility mode will set the frame rate to 30 TPS,
// which is the standard for the scratch player.
vm.setCompatibilityMode(true);

if (projectId) {
vm.downloadProjectId(projectId);
} else {
// If no project ID is supplied, load a local project
fetch('./playground.sb3').then(response => response.arrayBuffer())
.then(arrayBuffer => {
vm.loadProject(arrayBuffer);
});
}

vm.on('workspaceUpdate', () => {
document.getElementById('overlay').classList.remove('hidden');
});

// Instantiate the renderer and connect it to the VM.
const canvas = document.getElementById('scratch-stage');
const renderer = new ScratchRender(canvas);
vm.attachRenderer(renderer);
const audioEngine = new AudioEngine();
vm.attachAudioEngine(audioEngine);

// Resets size of canvas directly for proper image calcuations
// when the window is resized
const resize = () => {
renderer.resize(canvas.clientWidth, canvas.clientHeight);
};
window.addEventListener('resize', resize);

resize();

// Start project after green flag clicked and attempt to go
// fullscreen
let attemptFullscreen = Boolean(document.body.requestFullscreen);
document.getElementById('green-flag').addEventListener('click', () => {
document.getElementById('overlay').classList.add('hidden');
vm.greenFlag();
if (attemptFullscreen) {
document.body.requestFullscreen();
attemptFullscreen = false;
}
});

// Feed mouse events as VM I/O events.
document.body.addEventListener('mousemove', e => {
const rect = canvas.getBoundingClientRect();
const coordinates = {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
canvasWidth: rect.width,
canvasHeight: rect.height
};
vm.postIOData('mouse', coordinates);
});
canvas.addEventListener('mousedown', e => {
const rect = canvas.getBoundingClientRect();
const data = {
isDown: true,
x: e.clientX - rect.left,
y: e.clientY - rect.top,
canvasWidth: rect.width,
canvasHeight: rect.height
};
vm.postIOData('mouse', data);
e.preventDefault();
});
canvas.addEventListener('mouseup', e => {
const rect = canvas.getBoundingClientRect();
const data = {
isDown: false,
x: e.clientX - rect.left,
y: e.clientY - rect.top,
canvasWidth: rect.width,
canvasHeight: rect.height
};
vm.postIOData('mouse', data);
e.preventDefault();
});

// Feed keyboard events as VM I/O events.
document.body.addEventListener('keydown', e => {
// Don't capture keys intended for Blockly inputs.
if (e.target !== document && e.target !== document.body) {
return;
}
vm.postIOData('keyboard', {
key: e.code,
isDown: true
});
e.preventDefault();
});
document.body.addEventListener('keyup', e => {
// Always capture up events,
// even those that have switched to other targets.
vm.postIOData('keyboard', {
key: e.code,
isDown: false
});
// E.g., prevent scroll.
if (e.target !== document && e.target !== document.body) {
e.preventDefault();
}
});

// Run threads
vm.start();
};
1 change: 1 addition & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ module.exports = [
target: 'web',
entry: {
'benchmark': './src/playground/benchmark',
'standalone-player': './src/playground/standalone-player',
'video-sensing-extension-debug': './src/extensions/scratch3_video_sensing/debug'
},
output: {
Expand Down