waves/public/assets/g/astray/index.html
2025-04-09 17:11:14 -05:00

339 lines
11 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Astray</title>
<script src="Box2dWeb.min.js"></script>
<script src="Three.js"></script>
<script src="keyboard.js"></script>
<script src="jquery.js"></script>
<script src="maze.js"></script>
<script src="/js/main.js"></script>
<script>
var camera = undefined,
scene = undefined,
renderer = undefined,
light = undefined,
mouseX = undefined,
mouseY = undefined,
maze = undefined,
mazeMesh = undefined,
mazeDimension = 11,
planeMesh = undefined,
ballMesh = undefined,
ballRadius = 0.25,
keyAxis = [0, 0],
ironTexture = THREE.ImageUtils.loadTexture("/ball.png"),
planeTexture = THREE.ImageUtils.loadTexture("/concrete.png"),
brickTexture = THREE.ImageUtils.loadTexture("/brick.png"),
gameState = undefined,
// Box2D shortcuts
b2World = Box2D.Dynamics.b2World,
b2FixtureDef = Box2D.Dynamics.b2FixtureDef,
b2BodyDef = Box2D.Dynamics.b2BodyDef,
b2Body = Box2D.Dynamics.b2Body,
b2CircleShape = Box2D.Collision.Shapes.b2CircleShape,
b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape,
b2Settings = Box2D.Common.b2Settings,
b2Vec2 = Box2D.Common.Math.b2Vec2,
// Box2D world variables
wWorld = undefined,
wBall = undefined;
function createPhysicsWorld() {
// Create the world object.
wWorld = new b2World(new b2Vec2(0, 0), true);
// Create the ball.
var bodyDef = new b2BodyDef();
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(1, 1);
wBall = wWorld.CreateBody(bodyDef);
var fixDef = new b2FixtureDef();
fixDef.density = 1.0;
fixDef.friction = 0.0;
fixDef.restitution = 0.25;
fixDef.shape = new b2CircleShape(ballRadius);
wBall.CreateFixture(fixDef);
// Create the maze.
bodyDef.type = b2Body.b2_staticBody;
fixDef.shape = new b2PolygonShape();
fixDef.shape.SetAsBox(0.5, 0.5);
for (var i = 0; i < maze.dimension; i++) {
for (var j = 0; j < maze.dimension; j++) {
if (maze[i][j]) {
bodyDef.position.x = i;
bodyDef.position.y = j;
wWorld.CreateBody(bodyDef).CreateFixture(fixDef);
}
}
}
}
function generate_maze_mesh(field) {
var dummy = new THREE.Geometry();
for (var i = 0; i < field.dimension; i++) {
for (var j = 0; j < field.dimension; j++) {
if (field[i][j]) {
var geometry = new THREE.CubeGeometry(1, 1, 1, 1, 1, 1);
var mesh_ij = new THREE.Mesh(geometry);
mesh_ij.position.x = i;
mesh_ij.position.y = j;
mesh_ij.position.z = 0.5;
THREE.GeometryUtils.merge(dummy, mesh_ij);
}
}
}
var material = new THREE.MeshPhongMaterial({ map: brickTexture });
var mesh = new THREE.Mesh(dummy, material);
return mesh;
}
function createRenderWorld() {
// Create the scene object.
scene = new THREE.Scene();
// Add the light.
light = new THREE.PointLight(0xffffff, 1);
light.position.set(1, 1, 1.3);
scene.add(light);
// Add the ball.
g = new THREE.SphereGeometry(ballRadius, 32, 16);
m = new THREE.MeshPhongMaterial({ map: ironTexture });
ballMesh = new THREE.Mesh(g, m);
ballMesh.position.set(1, 1, ballRadius);
scene.add(ballMesh);
// Add the camera.
var aspect = window.innerWidth / window.innerHeight;
camera = new THREE.PerspectiveCamera(60, aspect, 1, 1000);
camera.position.set(1, 1, 5);
scene.add(camera);
// Add the maze.
mazeMesh = generate_maze_mesh(maze);
scene.add(mazeMesh);
// Add the ground.
g = new THREE.PlaneGeometry(mazeDimension * 10, mazeDimension * 10, mazeDimension, mazeDimension);
planeTexture.wrapS = planeTexture.wrapT = THREE.RepeatWrapping;
planeTexture.repeat.set(mazeDimension * 5, mazeDimension * 5);
m = new THREE.MeshPhongMaterial({ map: planeTexture });
planeMesh = new THREE.Mesh(g, m);
planeMesh.position.set((mazeDimension - 1) / 2, (mazeDimension - 1) / 2, 0);
planeMesh.rotation.set(Math.PI / 2, 0, 0);
scene.add(planeMesh);
}
function updatePhysicsWorld() {
// Apply "friction".
var lv = wBall.GetLinearVelocity();
lv.Multiply(0.95);
wBall.SetLinearVelocity(lv);
// Apply user-directed force.
var f = new b2Vec2(keyAxis[0] * wBall.GetMass() * 0.25, keyAxis[1] * wBall.GetMass() * 0.25);
wBall.ApplyImpulse(f, wBall.GetPosition());
keyAxis = [0, 0];
// Take a time step.
wWorld.Step(1 / 60, 8, 3);
}
function updateRenderWorld() {
// Update ball position.
var stepX = wBall.GetPosition().x - ballMesh.position.x;
var stepY = wBall.GetPosition().y - ballMesh.position.y;
ballMesh.position.x += stepX;
ballMesh.position.y += stepY;
// Update ball rotation.
var tempMat = new THREE.Matrix4();
tempMat.makeRotationAxis(new THREE.Vector3(0, 1, 0), stepX / ballRadius);
tempMat.multiplySelf(ballMesh.matrix);
ballMesh.matrix = tempMat;
tempMat = new THREE.Matrix4();
tempMat.makeRotationAxis(new THREE.Vector3(1, 0, 0), -stepY / ballRadius);
tempMat.multiplySelf(ballMesh.matrix);
ballMesh.matrix = tempMat;
ballMesh.rotation.getRotationFromMatrix(ballMesh.matrix);
// Update camera and light positions.
camera.position.x += (ballMesh.position.x - camera.position.x) * 0.1;
camera.position.y += (ballMesh.position.y - camera.position.y) * 0.1;
camera.position.z += (5 - camera.position.z) * 0.1;
light.position.x = camera.position.x;
light.position.y = camera.position.y;
light.position.z = camera.position.z - 3.7;
}
function gameLoop() {
switch (gameState) {
case "initialize":
maze = generateSquareMaze(mazeDimension);
maze[mazeDimension - 1][mazeDimension - 2] = false;
createPhysicsWorld();
createRenderWorld();
camera.position.set(1, 1, 5);
light.position.set(1, 1, 1.3);
light.intensity = 0;
var level = Math.floor((mazeDimension - 1) / 2 - 4);
$("#level").html("Level " + level);
gameState = "fade in";
break;
case "fade in":
light.intensity += 0.1 * (1.0 - light.intensity);
renderer.render(scene, camera);
if (Math.abs(light.intensity - 1.0) < 0.05) {
light.intensity = 1.0;
gameState = "play";
}
break;
case "play":
updatePhysicsWorld();
updateRenderWorld();
renderer.render(scene, camera);
// Check for victory.
var mazeX = Math.floor(ballMesh.position.x + 0.5);
var mazeY = Math.floor(ballMesh.position.y + 0.5);
if (mazeX == mazeDimension && mazeY == mazeDimension - 2) {
mazeDimension += 2;
gameState = "fade out";
}
break;
case "fade out":
updatePhysicsWorld();
updateRenderWorld();
light.intensity += 0.1 * (0.0 - light.intensity);
renderer.render(scene, camera);
if (Math.abs(light.intensity - 0.0) < 0.1) {
light.intensity = 0.0;
renderer.render(scene, camera);
gameState = "initialize";
}
break;
}
requestAnimationFrame(gameLoop);
}
function onResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
function onMoveKey(axis) {
keyAxis = axis.slice(0);
}
jQuery.fn.centerv = function () {
wh = window.innerHeight;
h = this.outerHeight();
this.css("position", "absolute");
this.css("top", Math.max(0, (wh - h) / 2) + "px");
return this;
};
jQuery.fn.centerh = function () {
ww = window.innerWidth;
w = this.outerWidth();
this.css("position", "absolute");
this.css("left", Math.max(0, (ww - w) / 2) + "px");
return this;
};
jQuery.fn.center = function () {
this.centerv();
this.centerh();
return this;
};
$(document).ready(function () {
// Prepare the instructions.
$("#instructions").center();
$("#instructions").hide();
KeyboardJS.bind.key(
"i",
function () {
$("#instructions").show();
},
function () {
$("#instructions").hide();
}
);
// Create the renderer.
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Bind keyboard and resize events.
KeyboardJS.bind.axis("left", "right", "down", "up", onMoveKey);
KeyboardJS.bind.axis("h", "l", "j", "k", onMoveKey);
$(window).resize(onResize);
// Set the initial game state.
gameState = "initialize";
// Start the game loop.
requestAnimationFrame(gameLoop);
});
</script>
<style>
body {
background: black;
margin: 0;
padding: 0;
font-family: "Helvetica";
}
#instructions {
background-color: rgba(0, 0, 0, 0.75);
color: white;
text-align: center;
padding: 32px;
margin: 0px;
display: inline;
border: 2px solid white;
}
#help {
position: absolute;
left: 0px;
bottom: 0px;
padding: 4px;
color: white;
}
#level {
position: absolute;
left: 0px;
top: 0px;
padding: 4px;
color: yellow;
font-weight: bold;
}
</style>
</head>
<body>
<div id="instructions">
How to play Astray:
<br />
<br />
Use the arrow keys to move the ball and find the exit to the maze.
<br />
<br />
Vim trainees: h, j, k, l
</div>
<div id="help">Hold down the 'I' key for instructions.</div>
<div id="level">Level 1</div>
</body>
</html>