shaku
Version:
A simple and effective JavaScript game development framework that knows its place!
254 lines (210 loc) • 11.3 kB
HTML
<html>
<head>
<meta charset="UTF-8">
<title>Shaku</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>
<div class="noselect">
<button class="view-code-btn" onclick="showSampleCode();">View Code</button>
<h1 class="demo-title">Shaku Gfx Demo: 3D</h1>
<p>Shaku is designed for 2D, but it can also do some basic 3D rendering. This demo shows how.<br /><br />
Use <b>Arrows</b> or <b>WASD</b> to move the sprite.</p>
<span class="credits">Knight image is from: https://opengameart.org/content/knight-sprite <br/>
Tree image is from: https://opengameart.org/content/gnarled-tree<br/>
Grass image is from: https://opengameart.org/content/10-seamless-grass-textures-that-are-2048-x-2048-grass-1png</span>
<!-- include shaku -->
<script src="js/demos.js"></script>
<script src="js/shaku.js"></script>
<!-- demo code -->
<script>
async function runDemo()
{
// init shaku
Shaku.gfx.setContextAttributes({depth:true});
await Shaku.init();
// screen size
let screenX = 1000;
let screenY = 700;
// add canvas to document
document.body.appendChild(Shaku.gfx.canvas);
Shaku.gfx.setResolution(screenX, screenY, true);
// load textures and font
let spriteTexture = await Shaku.assets.loadTexture('assets/sprite.png');
let grassTexture = await Shaku.assets.loadTexture('assets/grass.png');
let treeTexture = await Shaku.assets.loadTexture('assets/baum.png');
let fontTexture = await Shaku.assets.loadFontTexture('assets/DejaVuSansMono.ttf', {fontName: 'DejaVuSansMono'});
// create 3d sprites batch
let spritesBatch3d = new Shaku.gfx.SpriteBatch3D();
spritesBatch3d.setPerspectiveCamera();
// sprite size and position
const spriteSize = new Shaku.utils.Vector2(spriteTexture.width * 1.5, spriteTexture.height * 1.5);
const position = new Shaku.utils.Vector3(0, 0, 0);
// create random trees
const trees = [];
for (let i = 0; i < 25; ++i) {
const maxPosOffset = 2600;
const treeSize = new Shaku.utils.Vector2(treeTexture.width * 3, treeTexture.height * 3);
const treePosition = new Shaku.utils.Vector2(Math.random() * maxPosOffset - maxPosOffset / 2, Math.random() * maxPosOffset - maxPosOffset / 2);
const treeOffsetY = -20;
const tv1 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-treeSize.x / 2 + treePosition.x, treeOffsetY, treePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(0, 1));
const tv2 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(treeSize.x / 2 + treePosition.x, treeOffsetY, treePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(1, 1));
const tv3 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-treeSize.x / 2 + treePosition.x, treeSize.y + treeOffsetY, treePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(0, 0));
const tv4 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(treeSize.x / 2 + treePosition.x, treeSize.y + treeOffsetY, treePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(1, 0));
trees.push([tv1, tv2, tv3, tv4]);
}
// create sprite vertices
const v1 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-spriteSize.x / 2, 0, 0))
.setTextureCoords(new Shaku.utils.Vector2(0, 1));
const v2 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(spriteSize.x / 2, 0, 0))
.setTextureCoords(new Shaku.utils.Vector2(1, 1));
const v3 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-spriteSize.x / 2, spriteSize.y, 0))
.setTextureCoords(new Shaku.utils.Vector2(0, 0));
const v4 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(spriteSize.x / 2, spriteSize.y, 0))
.setTextureCoords(new Shaku.utils.Vector2(1, 0));
// create floor vertices
const floorPosY = 0;
const floorWidth = grassTexture.width * 2.5;
const floorDepth = grassTexture.height * 2.5;
const vf1 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-floorWidth, floorPosY, -floorDepth))
.setTextureCoords(new Shaku.utils.Vector2(0, 1));
const vf2 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(floorWidth, floorPosY, -floorDepth))
.setTextureCoords(new Shaku.utils.Vector2(1, 1));
const vf3 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-floorWidth, floorPosY, floorDepth))
.setTextureCoords(new Shaku.utils.Vector2(0, 0));
const vf4 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(floorWidth, floorPosY, floorDepth))
.setTextureCoords(new Shaku.utils.Vector2(1, 0));
// do a main loop step.
function step() {
Shaku.startFrame();
Shaku.gfx.clear(Shaku.utils.Color.cornflowerblue);
Shaku.gfx.clearDepth();
// begin drawing
spritesBatch3d.begin();
// set view matrix
spritesBatch3d.camera.setViewLookat(
new Shaku.utils.Vector3(position.x, 500, 600 + position.z),
new Shaku.utils.Vector3(position.x, 0, position.z)
);
// create transformation matrix
const matrix = Shaku.gfx.Matrix.createTranslation(position.x, position.y, position.z);
// draw floor
spritesBatch3d.drawVertices(grassTexture, [vf1, vf2, vf3, vf4]);
// draw sprite from vertices
const vertices = [Shaku.gfx.Matrix.transformVertex(matrix, v1),
Shaku.gfx.Matrix.transformVertex(matrix, v2),
Shaku.gfx.Matrix.transformVertex(matrix, v3),
Shaku.gfx.Matrix.transformVertex(matrix, v4)];
spritesBatch3d.drawVertices(spriteTexture, vertices);
// draw trees
for (let tree of trees)
{
spritesBatch3d.drawVertices(treeTexture, tree);
}
// update sprite position
if (Shaku.input.down('left') || Shaku.input.down('a')) {
position.x -= Shaku.gameTime.delta * 175;
}
if (Shaku.input.down('right') || Shaku.input.down('d')) {
position.x += Shaku.gameTime.delta * 175;
}
if (Shaku.input.down('up') || Shaku.input.down('w')) {
position.z -= Shaku.gameTime.delta * 175;
}
if (Shaku.input.down('down') || Shaku.input.down('s')) {
position.z += Shaku.gameTime.delta * 175;
}
// draw everything
spritesBatch3d.end();
// end frame
Shaku.endFrame();
Shaku.requestAnimationFrame(step);
}
step();
}
// start demo
runDemo();
</script>
</div>
<!-- code example part -->
<div id="sample-code-modal" class="modal">
<div class="modal__overlay jsOverlay"></div>
<div class="modal__container">
<p class="noselect">The following is a code example on how to draw 3D quads with vertices.</i>
</p>
<pre><code class="language-js">// init shaku with depth enabled
// this is important for z-order
Shaku.gfx.setContextAttributes({depth:true});
await Shaku.init();
// load a test texture
let texture = await Shaku.assets.loadTexture('assets/sprite.png');
// create 3d sprites batch and set default perspective camera (check out setPerspectiveCamera params for more options)
let spritesBatch3d = new Shaku.gfx.SpriteBatch3D();
spritesBatch3d.setPerspectiveCamera();
// do a main loop step.
function step() {
// start frame and clear buffers + depth
// you need to clear depth in addition to color when using z buffers
Shaku.startFrame();
Shaku.gfx.clear(Shaku.utils.Color.cornflowerblue);
Shaku.gfx.clearDepth();
// set camera view matrix
// eyePosition should be Vector3 with the position of your camera
// lookatPosition should be Vector3 with the position the camera looks at
spritesBatch3d.camera.setViewLookat(
eyePosition,
lookatPosition
);
// create vertices for our 3d quad
const spriteSize = new Shaku.utils.Vector2(texture.width, texture.height);
const spritePosition = new Shaku.utils.Vector2(Math.random() * maxPosOffset - maxPosOffset / 2, Math.random() * maxPosOffset - maxPosOffset / 2);
const spriteOffsetY = 0;
const v1 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-spriteSize.x / 2 + spritePosition.x, spriteOffsetY, spritePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(0, 1));
const v2 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(spriteSize.x / 2 + spritePosition.x, spriteOffsetY, spritePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(1, 1));
const v3 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(-spriteSize.x / 2 + spritePosition.x, spriteSize.y + spriteOffsetY, spritePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(0, 0));
const v4 = (new Shaku.gfx.Vertex())
.setPosition(new Shaku.utils.Vector3(spriteSize.x / 2 + spritePosition.x, spriteSize.y + spriteOffsetY, spritePosition.y))
.setTextureCoords(new Shaku.utils.Vector2(1, 0));
// draw a 3D quad (note: quad origin point is bottom-center, making it appear as if its standing)
// vertices should be an array with 4 Vertex objects that have 3d Vectors for positions
spritesBatch3d.begin();
spritesBatch3d.drawVertices(texture, [v1,v2,v3,v4]);
spritesBatch3d.end();
// end frame and request next one
Shaku.endFrame();
Shaku.requestAnimationFrame(step);
}
step();
</code></pre>
<button class="modal__close" onclick="closeModal('sample-code-modal')">✕</button>
</div>
</div>
<link href="prism/prism.css" rel="stylesheet" />
<script src="prism/prism.js"></script>
</body>
</html>