shaku
Version:
A simple and effective JavaScript game development framework that knows its place!
215 lines (185 loc) • 7 kB
HTML
<html>
<head>
<meta charset="UTF-8">
<title>Shaku Effects Sandbox</title>
<meta name="description" content="Shaku - a simple and easy-to-use javascript library for videogame programming.">
<meta name="author" content="Ronen Ness">
<link href="css/style.css" rel="stylesheet" type="text/css" media="all">
</head>
<body>
<style>
html {
min-height: 100%; /* make sure it is at least as tall as the viewport */
position: relative;
}
body {
height: 100%; /* force the BODY element to match the height of the HTML element */
overflow: hidden;
}
.max-height {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
z-index: -1; /* Remove this line if it's not going to be a background! */
}
#editor {
position: absolute;
width: 100%;
min-height: 100%;
}
#editor *{ font-family : monospace ;font-size: 16px ;direction:ltr ;text-align:left !important;}
</style>
<div id="left-pane" class="max-height" style="left:0px; width:40%; display:block; overflow: scroll;">
<div style="padding:0.5em">
<h1 class="demo-title" style="margin-left: 0;">Shaku: Effects Sandbox</h1>
<p>This demo is a sandbox application to write effects and see them update in realtime on the right side.</p>
<button class="view-code-btn" onclick="updateSandbox();" style="position: absolute; z-index: 100; right:0px; margin-top:-0.75em">Apply Effect →</button>
</div>
<!-- Wrapper code -->
<div id="wrapper-code" style="display: none;">
async function runDemo()
{
// init shaku
await Shaku.init();
document.body.appendChild(Shaku.gfx.canvas);
___custom_effect_code___
// create custom effect instance and set as active
let effect;
try {
effect = new CustomEffect();
}
catch (e) {
document.body.innerHTML = e.toString() + "<br />See developer console for details.";
}
// load textures
let texture = await Shaku.assets.loadTexture('assets/shaku.png');
// create a SpriteBatch
let spritesBatch = new Shaku.gfx.SpriteBatch(10);
// do a main loop step.
function step()
{
// make fullscreen
Shaku.gfx.maximizeCanvasSize(false);
// start frame and clear screen
Shaku.startFrame();
Shaku.gfx.clear(Shaku.utils.Color.cornflowerblue);
// custom updates code
CustomEffect.preRenderInit(effect, texture);
// draw with custom effect
spritesBatch.begin(undefined, effect);
const position = Shaku.gfx.getRenderingSize().mul(0.5, 0.5);
spritesBatch.drawSprite(Shaku.gfx.Sprite.build(texture, position, 400));
spritesBatch.end();
// end frame and request next frame
Shaku.endFrame();
Shaku.requestAnimationFrame(step);
}
// start main loop
step();
}
// start demo
runDemo();
</div>
<div id="editor">// define our custom effect
// don't rename it from 'CustomEffect' or it won't work for this demo
class CustomEffect extends Shaku.gfx.SpritesEffect
{
/**
* Override the vertex shader for our custom effect.
*/
get vertexCode()
{
const vertexShader = `
attribute vec3 position;
attribute vec2 uv;
attribute vec4 color;
uniform mat4 projection;
uniform mat4 world;
varying vec2 v_texCoord;
varying vec4 v_color;
void main(void) {
gl_Position = projection * world * vec4(position, 1.0);
gl_PointSize = 1.0;
v_texCoord = uv;
v_color = color;
}
`;
return vertexShader;
}
/**
* Override the fragment shader for our custom effect.
*/
get fragmentCode()
{
const fragmentShader = `
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D mainTexture;
uniform float elapsedTime;
varying vec2 v_texCoord;
varying vec4 v_color;
void main(void) {
gl_FragColor = texture2D(mainTexture, v_texCoord) * v_color;
gl_FragColor.r *= sin(v_texCoord.y * 10.0 + elapsedTime) + 0.1;
gl_FragColor.g *= sin(1.8 + v_texCoord.y * 10.0 + elapsedTime) + 0.1;
gl_FragColor.b *= sin(3.6 + v_texCoord.y * 10.0 + elapsedTime) + 0.1;
gl_FragColor.rgb *= gl_FragColor.a;
}
`;
return fragmentShader;
}
/**
* Override the uniform types dictionary to add our custom uniform types.
*/
get uniformTypes()
{
let ret = super.uniformTypes;
ret['elapsedTime'] = { type: Shaku.gfx.Effect.UniformTypes.Float };
return ret;
}
/**
* This method is called before we render with the effect, to do custom setups
* It's not part of Shaku, its something we add for this specific demo.
*/
static preRenderInit(effectInstance, sprite)
{
// update effect custom uniform
effectInstance.uniforms.elapsedTime(Shaku.gameTime.elapsed);
}
}</div>
</div>
<div id="right-pane" class="max-height" style="left:40%; width:60%; display:block;">
<iframe id="sandbox-iframe" src="sandbox_internal.html" style="left:0px; top:0px; width:100%; height:100%;"></iframe>
</div>
<!-- update sandbox code -->
<script>
function updateSandbox()
{
let code = window._codeEditor.getValue();
let iframe = document.getElementById('sandbox-iframe');
iframe.src = 'sandbox_internal.html';
iframe.onload = () => {
const codeWrapper = document.getElementById("wrapper-code").innerHTML;
iframe.contentWindow.postMessage({call:'setCode', value: codeWrapper.replace('___custom_effect_code___', code)}, '*');
}
}
</script>
<script src="ace/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
(function() {
var editor = ace.edit("editor");
ace.config.loadModule("ace/keybinding/vscode");
editor.setOption('useWorker', false);
editor.setTheme("ace/theme/dracula");
editor.session.setMode("ace/mode/javascript");
window._codeEditor = editor;
updateSandbox();
})();
</script>
</body>
</html>